Чи повинен кожен клас, який я пишу, дотримуватися інтерфейсу?


10

Я пишу гру в Typescript, і вирішив, що я намагаюся дотримуватися ідеї " інтерфейсу на основі програмування ", де ви пишете код на основі інтерфейсу замість реалізації об'єкта.

Я написав велику кількість інтерфейсів і класів, які їх реалізують, потім зробив крок назад і зрозумів, що класи досить прості, що мені, ймовірно, ніколи не потрібно буде змінювати реалізацію, оскільки дійсно існує лише один спосіб зробити те, що клас робить (переміщення Phaser.Spriteобмеженим способом, щоб діяти як танк).

Тоді я пам'ятаю, як читав кілька років тому про ідею YAGNI , яка в основному полягає в тому, що ви не повинні надмірно розробляти свій код, щоб включати речі, які ви ніколи не можете використовувати.

Дотримуючись найкращих практик, чи повинен кожен клас впроваджувати інтерфейс, чи слід обмежувати його класами, які, як ви очікуєте, в майбутньому можуть бути замінені?


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

Відповіді:


6

Причина наявності інтерфейсів полягає в тому, що це спрощує поліморфізм. Це означає, що ви можете надсилати екземпляри за контрактом, а не знати їх фактичну реалізацію. Наприклад, ви можете надіслати "Читач" методу, щоб такий викликаний метод міг використовувати його метод "read ()". Заявивши інтерфейс "Reader", ви можете зробити будь-який об'єкт відповідним цьому договору, реалізуючи вказані ним методи. Таким чином, будь-який абонент може припустити, що існуватимуть певні методи, навіть якщо об'єкти, що лежать в основі договору, можуть бути абсолютно іншими.

Це відокремлює поліморфізм від базового класу і робить можливим також між кордонами ланцюга класу, передбачаючи контракт.

Тепер ... Це корисно лише в тому випадку, якщо у вас є потреба в декількох ланцюгах спадкування, щоб діяти однаково, або якщо у вас буде багато базових класів із загальною поведінкою, які ви хочете передати іншим користувачам.

Якщо у вас є лише ОДИН ланцюг успадкування (baseclass-> підклас) або просто база, або у вас немає необхідності фактично ПАСУВАТИ пов’язані об’єкти комусь іншому або використовувати їх загально, тоді ви не додаватимете реалізацій інтерфейсу, оскільки це поняття тоді марний.


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

7

Якщо ви не впевнені, чи справді вам потрібні інтерфейси, ви, мабуть, цього не зробите. Це суть принципу YAGNI. Коли вам потрібно мати можливість замінити реалізацію, тоді ви введете інтерфейс.


6

Створення інтерфейсу для кожного класу - дещо зайве. З чисто об'єктно-орієнтованої точки зору, кожен клас вже має інтерфейс . Інтерфейс - це не що інше, як загальнодоступні методи та дані членів класу.

Зазвичай ми використовуємо "інтерфейс", щоб означати "клас Java, визначений за допомогою interfaceключового слова" або "клас C ++ з лише публічними, чистими віртуальними функціями". Це корисні абстракції, оскільки вони відокремлюють інтерфейс класу від його реалізації.

Зважаючи на це, використовуйте інтерфейси, коли це доречно.

  • Чи буде декілька реалізацій для інтерфейсу? Якщо так, то ця ситуація може стати кандидатом для вилучення загальних, публічно орієнтованих методів в інтерфейс.

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

Зауважте вищенаведені слова: "може" бути кандидатом ", може бути" корисним. Не існує жорсткого і швидкого правила, яке говорить "так" або "ні" для даної ситуації.

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

interface I {
  int getA()
  int getB()
  ...
  int getY()
  int getZ()
}

class C : I {
  int getA() { ... }
  int getB() { ... }
  ...
  int getY() { ... }
  int getZ() { ... }
}

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

@Darien: Причина того, що тестувальна система може вимагати інтерфейсів, полягає в тому, що тест фактично використовує іншу (глузувальну) реалізацію, що повертає вас до першої кулі, коли інтерфейс є відповідним.
Барт ван Іґен Шенау

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

@RobbieDee це правда, наприклад, для Java interface, так буває, що все в Java interfaceтакож є її інтерфейсом для громадськості (концепція ОО).

Усі! Запишіть відповіді в другому та третьому реченнях. Ламінуйте його. Покладіть його у свій гаманець. Посилання часто.
радар

5

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

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

Знущальні рамки

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

Ін'єкційна залежність

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


Ці підходи узагальнені в принципі D з принципів SOLID . Треба це - використовувати абстракції, а не конкременти.

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


Зауважте, що введення залежностей та контейнери IoC - це дві абсолютно різні речі (одна - це принцип дизайну, а друга - один із способів конкретного втілення цього принципу на практиці за допомогою різних рамок). Ви можете використовувати DI без використання контейнера IoC.
сара

@kai "Ви можете використовувати DI, не використовуючи контейнер IoC" . Я думаю, що більш зрілі цехи розвитку можуть (і робити). Просто не потрібно забруднювати код для того, що, начебто, проста концепція. Джоел схожий на думку.
Роббі Ді

ЯГНІ дуже хитрий. Яка б філософія не була, в більшості випадків насправді YAGNI використовується як привід для вирізання кутів. Уникайте зв'язку, слід сприймати серйозніше. Розчаровувати на багатьох зустрічах з роздратуванням засмучує, що розробник називає себе заблокованим, оскільки хтось ще не закінчив свою роботу. Чому блокування іншого розробника економить нікому час? Моя пропозиція - використовувати інтерфейс, щоб уникнути зв'язку. Звичайно, не потрібно використовувати інтерфейс для класів Model.
Ріпал Баро
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.