Як найкраще впорядкувати файли класів та інтерфейсів?


17

Гаразд .. після усієї дискусії я трохи змінюю своє питання, щоб краще відобразити конкретний приклад, з яким я маю справу.


У мене є два класи, ModelOneі ModelTwoці класи виконують аналогічні функції, але не пов'язані між собою. Однак у мене є третій клас, CommonFuncякий містить деяку публічну функціональність, яка реалізована в обох, ModelOneі ModelTwoрозроблена як відповідно DRY. Дві моделі є примірниками в межах ModelMainкласу (який сам інстанціюється на більш високому рівні і т. Д. - але я зупиняюся на цьому рівні).

Контейнер IoC, який я використовую, - це Microsoft Unity . Я не претендую на те, що я є експертом у цьому, але я розумію, що ви реєструєте кордон інтерфейсу та класу з контейнером, і коли вам потрібно конкретний клас, ви запитаєте контейнер IoC про те, що об'єкт відповідає конкретному інтерфейсу. Це означає, що для кожного об'єкта, який я хочу створити з Unity, повинен бути відповідний інтерфейс. Оскільки кожен з моїх занять виконує різні (і не перекриваються) функціональні можливості, це означає, що між інтерфейсом та класом 1 існує співвідношення 1: 1 . Однак це не означає, що я по-рабськи пишу інтерфейс для кожного класу, який я пишу.

Таким чином, код мудрий я закінчую з 2 :

public interface ICommonFunc 
{ 
}

public interface IModelOne 
{ 
   ICommonFunc Common { get; } 
   .. 
}

public interface IModelTwo
{ 
   ICommonFunc Common { get; } 
   .. 
}

public interface IModelMain 
{ 
  IModelOne One { get; } 
  IModelTwo Two { get; } 
  ..
}

public class CommonFunc : ICommonFunc { .. }

public class ModelOne : IModelOne { .. }

public class ModelTwo : IModelTwo { .. }

public class ModelMain : IModelMain { .. }

Питання полягає в тому, як організувати своє рішення. Чи слід тримати клас та інтерфейс разом? Або я повинен тримати класи та інтерфейси разом? EG:

Варіант 1 - Організоване за назвою класу

MySolution
  |
  |-MyProject
  |   |
      |-Models
      |   |
          |-Common
          |   |
          |   |-CommonFunc.cs
          |   |-ICommonFunc.cs
          |
          |-Main
          |   |
          |   |-IModelMain.cs
          |   |-ModelMain.cs
          |
          |-One
          |   |
          |   |-IModelOne.cs
          |   |-ModelOne.cs
          |
          |-Two
              |
              |-IModelTwo.cs
              |-ModelTwo.cs
              |

Варіант 2 - Організований за функціональністю (в основному)

MySolution
  |
  |-MyProject
  |   |
      |-Models
      |   |
          |-Common
          |   |
          |   |-CommonFunc.cs
          |   |-ICommonFunc.cs
          |
          |-IModelMain.cs
          |-IModelOne.cs
          |-IModelTwo.cs
          |-ModelMain.cs
          |-ModelOne.cs
          |-ModelTwo.cs
          |

Варіант 3 - роздільний інтерфейс та реалізація

MySolution
  |
  |-MyProject
      |
      |-Interfaces
      |   |
      |   |-Models
      |   |   |
      |       |-Common
      |       |   |-ICommonFunc.cs
      |       |
      |       |-IModelMain.cs
      |       |-IModelOne.cs
      |       |-IModelTwo.cs
      |
      |-Classes
          | 
          |-Models
          |   |
              |-Common
              |   |-CommonFunc.cs
              |
              |-ModelMain.cs
              |-ModelOne.cs
              |-ModelTwo.cs
              |

Варіант 4 - Додавання додаткового прикладу функціональності

MySolution
  |
  |-MyProject
  |   |
      |-Models
      |   |
          |-Components
          |   |
          |   |-Common
          |   |   |
          |   |   |-CommonFunc.cs
          |   |   |-ICommonFunc.cs
          |   |   
          |   |-IModelOne.cs
          |   |-IModelTwo.cs
          |   |-ModelOne.cs
          |   |-ModelTwo.cs
          |
          |-IModelMain.cs
          |-ModelMain.cs
          |

Мені не подобається варіант 1 через назву класу на шляху. Але оскільки я прагну до співвідношення 1: 1 через свій вибір / використання IoC (і це може бути дискусійним), це має переваги в баченні зв’язку між файлами.

Варіант 2 мені подобається, але зараз я затуманив води між ModelMainта підмоделями.

Варіант 3 працює на відокремлення визначення інтерфейсу від реалізації, але тепер у мене є ці штучні перерви в назвах шляхів.

Варіант 4. Я взяв варіант 2 і змінив його, щоб відокремити компоненти від батьківської моделі.

Чи є вагомі причини для того, щоб віддавати перевагу одному перед іншим? Або будь-які інші потенційні макети, які я пропустив?


1. Френк прокоментував, що співвідношення 1: 1 повертається до C ++ днів файлів .h та .cpp. Я знаю, звідки він родом. Моє розуміння Єдності, здається, поставило мене в цей кут, але я також не впевнений, навіть як вийти з нього, якщо ви також слідкуєте за прислів’ям Program to an interface Але це дискусія ще один день.

2. Я покинув деталі кожного конструктора об'єктів. Тут контейнер IoC вводить об'єкти за потребою.


1
Тьфу. Пахне тим, що у вас співвідношення інтерфейсу / класу 1: 1. Який контейнер IoC ви використовуєте? Ninject забезпечує механізми, які не вимагають співвідношення 1: 1.
RubberDuck

1
@RubberDuck FWIW Я використовую Unity. Я не претендую на те, що я є експертом у цьому, але якщо мої заняття добре розроблені з єдиними обов'язками, як я не закінчуюся майже співвідношенням 1: 1?
Пітер М

Вам потрібна IBase або база може бути абстрактною? Чому IDerivedOne, коли він вже реалізує IBase? Ви повинні залежати від IBase, а не похідних. Я не знаю про Unity, але інші контейнери IoC дозволяють робити «контекстно-залежні» ін'єкції. В основному, коли Client1потребує IBase, він надає Derived1. Коли Client2потребує IBase, IoC надає Derived2.
RubberDuck

1
Ну, якщо база абстрактна, то немає ніяких причин її мати interface. interfaceДійсно просто абстрактний клас з усіма віртуальними членами.
RubberDuck

1
RubberDuck правильно. Зайві інтерфейси просто дратують.
Френк Хілеман

Відповіді:


4

Оскільки інтерфейс абстрактно схожий на базовий клас, використовуйте ту саму логіку, яку ви використовували б для базового класу. Класи, що реалізують інтерфейс, тісно пов'язані з інтерфейсом.

Я сумніваюся, що ви віддаєте перевагу каталогу під назвою "Основні класи"; більшість розробників не хотіли б цього, а також каталог під назвою "Інтерфейси". У c # каталоги за замовчуванням також є просторами імен, що робить їх подвійним заплутаним.

Найкращий підхід - це подумати про те, як би розбити класи / інтерфейси, якби вам довелося помістити деякі в окрему бібліотеку, і організувати простори імен / каталоги подібним чином. Вказівки щодо дизайну .net рамки містять пропозиції щодо простору імен, які можуть бути корисними.


Я начебто знайомий із наданим вами посиланням, але я не впевнений, як це стосується організації файлів. Незважаючи на те, що VS за замовчуванням назначає імена для початкового простору імен, я знаю, що це довільний вибір. Також у мене є оновлення коду "зразок" та можливості компонування.
Пітер М

@PeterM Я не впевнений, який IDE ви використовуєте, але Visual Studio використовує імена каталогів, щоб, наприклад, автоматично генерувати декларації простору імен при додаванні класу. Це означає, що, хоча ви можете мати відмінності між іменами каталогів та іменами простору імен, працювати так болючіше. Тож більшість людей цього не роблять.
Френк Хілеман

Я використовую VS. І так, я знаю біль. Він повертає спогади про макет файлів Visual Source Safe.
Пітер М

1
@PeterM Що стосується співвідношення інтерфейсу до класу 1: 1. Деякі інструменти вимагають цього. Я розглядаю це як дефект інструменту - .net має достатньо можливостей відображення, щоб не потребувати таких обмежень у жодному такому інструменті.
Френк Хілеман

1

Я використовую другий підхід, маючи ще кілька папок / просторів імен у складних проектах. Це означає, що я роз’єдную інтерфейси від конкретних реалізацій.

В іншому проекті вам, можливо, доведеться знати визначення інтерфейсу, але зовсім не потрібно знати конкретний клас, який його реалізує - особливо, коли ви використовуєте контейнер IoC. Тож ці інші проекти потребують лише посилання на інтерфейсні проекти, а не на проекти реалізації. Це може зберігати посилання на низькому рівні і може запобігати циркулярним посиланням.


Я переглянув приклад коду та додав ще кілька варіантів
Пітер М,

1

Як завжди з будь-яким дизайнерським питанням, перше, що потрібно зробити, це визначити, хто ваші користувачі. Як вони збираються використовувати вашу організацію?

Це кодери, які будуть використовувати ваші заняття? Тоді відокремлення інтерфейсів від коду може бути найкращим.

Вони підтримують ваш код? Тоді зберігати інтерфейси та класи разом, можливо, найкраще.

Сядьте та створіть кілька випадків використання. Тоді перед вами може з’явитися найкраща організація.


-2

Я думаю, що краще зберігати інтерфейси в окремій бібліотеці. По-перше, такий вид конструкції збільшує залежність. Ось чому реалізація цих інтерфейсів може здійснюватися іншою мовою програмування.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.