Інтерфейс проти базового класу


767

Коли я повинен використовувати інтерфейс і коли я повинен використовувати базовий клас?

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

Якщо у мене є клас Собака і Кішка. Чому я хотів би реалізувати IPet замість PetBase? Я можу зрозуміти наявність інтерфейсів для ISheds або IBarks (IMakesNoise?), Тому що їх можна розміщувати на домашніх тваринах за домашньою твариною, але я не розумію, який використовувати для загального Pet.


11
Я думаю, що ви повинні врахувати, що інтерфейси можуть становити кілька меж, про які ви можете не знати до дуже пізніх етапів. Наприклад, за допомогою .NET ви не можете серіалізувати змінну члена інтерфейсу, тому, якщо у вас є клас Zoo та масив змінних членів IAnimals, ви не зможете серіалізувати Zoo (а це означає, що написання WebServices чи інших речей, що вимагають серіалізації, було б біль).
синхершко

1
Це питання може допомогти зрозуміти поняття інтерфейсів. stackoverflow.com/q/8531292/1055241
gprathour

Мені просто цікаво. Я зустрів в CLR через C # наступний уривок: I tend to prefer using the interface technique over the base type technique because the base type technique doesn’t allow the developer to choose the base type that works best in a particular situation.. Я не можу зрозуміти, що мається на увазі уривку. Ми можемо створити кілька базових типів і створити похідний тип для будь-якого з них, тому розробник може вибрати базовий тип. Може хтось пояснить, будь ласка, що я пропускаю? Я вважаю, що це може бути частиною цього питання. Або я повинен розмістити ще один про конкретний уривок?
qqqqqqq

Відповіді:


501

Візьмемо ваш приклад класу Собака і Кішка, і давайте проілюструємо, використовуючи C #:

І собака, і кішка - це тварини, зокрема, чотиригранні ссавці (тварини є надто загальними). Припустимо, у вас є абстрактний клас Ссавець для обох:

public abstract class Mammal

Цей базовий клас, ймовірно, матиме методи за замовчуванням, такі як:

  • Корм
  • Мате

Все це поведінка, яка має більш-менш однакову реалізацію між обома видами. Щоб визначити це, вам доведеться:

public class Dog : Mammal
public class Cat : Mammal

Тепер припустимо, що є інші ссавці, яких ми зазвичай бачимо в зоопарку:

public class Giraffe : Mammal
public class Rhinoceros : Mammal
public class Hippopotamus : Mammal

Це все ще буде дійсним, тому що в основі функціональності Feed()і Mate()все одно буде те саме.

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

public interface IPettable
{
    IList<Trick> Tricks{get; set;}
    void Bathe();
    void Train(Trick t);
}

Виконання вищевказаного договору не буде однаковим між котом та собакою; розміщення їх реалізацій у абстрактному класі для спадкування буде поганою ідеєю.

Тепер ваші визначення собак та кішок повинні виглядати так:

public class Dog : Mammal, IPettable
public class Cat : Mammal, IPettable

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

Отже, оскільки ви зазвичай можете успадковувати лише один абстрактний клас (у більшості статично типованих мов OO, який є ... винятками є C ++), але бути в змозі реалізувати декілька інтерфейсів, це дозволяє будувати об'єкти строго за необхідною основою.


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

18
Інтерфейс - це договір. Ви виставляєте лише ту частину договору, яку вимагає послуга. Якщо у вас є "PettingZoo", ви, звичайно, не хочете виставляти "Mate'-ing користувачеві!
Ентоні Мастрен

10
@David Touche, хоча я це зробив, щоб краще проілюструвати, що таке інтерфейс і що таке абстрактний клас для його розуміння. Собака і кішка, схоже, не є жорсткою вимогою!
Джон Лім’яп

2
Слід зазначити, що в інтерпретованих середовищах JIT-ing (особливо JVM) виклики віртуальних методів значно швидше, ніж виклики методу інтерфейсу , залежно від контексту. Я наголошую на "контексті", оскільки JVM часто може оптимізувати повільний пошук методів. (наприклад, якщо список спадкоємців інтерфейсу, як правило, екземпляри одного класу. Це, на жаль, ускладнює тестування тестів.) Якщо ви намагаєтеся оптимізувати щось чутливе до продуктивності, і лише тоді ви повинні це врахувати.
Філіп Гін

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

146

Що ж, Джош Блох сказав себе в « Ефективній Java 2d» :

Віддавайте перевагу інтерфейсам над абстрактними класами

Деякі основні моменти:

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

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

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

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

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

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

PS .: Купіть книгу. Це набагато докладніше.


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

5
у нас є "Принцип поділу інтерфейсу". Цей принцип вчить нас дбати про те, як ми пишемо свої інтерфейси. Коли ми пишемо свої інтерфейси, ми повинні подбати про те, щоб додати лише ті методи, які там повинні бути. Якщо ми додамо методи, яких не повинно бути там, класам, що реалізують інтерфейс, доведеться також реалізовувати ці методи. Наприклад, якщо ми створимо інтерфейс під назвою Worker і додамо перерву на метод обіду, всі працівники повинні будуть його реалізувати. Що робити, якщо працівник робот? Як висновок, інтерфейси, що містять не специфічні для нього методи, називаються забрудненими або жировими інтерфейсами.
Еклавія

Оскільки Java 8, методи за замовчуванням дозволяють вам додавати нові функціональні можливості в інтерфейси та забезпечувати сумісність із зворотніми для існуючих класів, які реалізують цей інтерфейс. Методи за замовчуванням викликаються за замовчуванням, якщо вони не перекрито в класі реалізації. Усі реалізаційні класи можуть або замінити методи за замовчуванням, або вони можуть безпосередньо викликати їх за допомогою instance.defaultMethod ()
Arpit Rastogi

118

Інтерфейси та базові класи представляють дві різні форми зв’язків.

Спадщина (базові класи) являють собою відносини "є-а". Напр. Собака чи кішка домашня тварина. Цей взаємозв'язок завжди представляє (єдину) мету класу (у поєднанні з "принципом єдиної відповідальності" ).

Інтерфейси , з іншого боку, представляють додаткові особливості класу. Я б назвав це відносинами "є", як у " Fooє одноразовим", отже, IDisposableінтерфейс у C #.


10
З усіх відповідей ця дає найкращу суміш стислості, не втрачаючи ясності
Метт Вілько,

Хтось одного разу сказав мені використовувати інтерфейс, коли є відношення "має". Я не впевнений, чи це завжди правда; Мій ноутбук має екран, тож чи повинен ноутбук реалізувати IScreen чи мати властивість екрана? Останнє здається мені більш природним.
Беренд

1
@berend смішно, що ви це пишете, оскільки екрани реалізовані через інтерфейси - VGA, HDMI тощо
Костянтин

Просто тому, що це OOP, ми повинні розтягнути свою уяву, щоб наслідувати реальні сценарії життя. Це не завжди застосовується.
Крізмограма

110

Сучасний стиль - це визначення IPet та PetBase.

Перевагою інтерфейсу є те, що інший код може використовувати його без будь-яких зв’язків з іншим виконуваним кодом. Повністю "чисто". Також інтерфейси можна змішувати.

Але базові класи корисні для простої реалізації та загальних утиліт. Тож надайте абстрактний базовий клас, а також заощадите час та код.


мати свій торт і їсти його теж!
Даррен Копп

3
Інтерфейс визначає, як інші класи можуть використовувати ваш код. Базовий клас допомагає виконавцям реалізувати ваш інтерфейс. Дві різні речі для двох різних цілей.
Білл Мішель

27
Тут немає нічого "сучасного". Базовий клас та інтерфейс з тим самим API просто зайві. Ви можете використовувати такий підхід у деяких випадках, але не варто узагальнювати!
smentek

3
Найбільш підтримуваний коментар до другої підтримуваної відповіді насправді не погоджується з самою відповіддю, цікаво. Треба сказати, що я бачу багато прикладів інтерфейсу та базового класу. У цьому сенсі це "сучасний" спосіб. Наприклад, у шаблоні MVVM існує фактично клас ViewModelBase, який реалізує INotifyPropertyChanged. Але коли мій колега запитав мене, чому у мене базовий клас, замість того, щоб реалізовувати інтерфейс у кожній моделі перегляду, я не знаю, як його переконати
тет

1
Правильно. Це не питання ні того, ні іншого. Вони існують для вирішення 2 дуже різних проблем. Інтерфейси - це договори, яким повинен відповідати клас реалізації. Вони знайшли недавню прихильність (іноді фанатично) щодо аспектів IoC та TDD. Анотаційні / базові класи служать для групування ієрархічно загальної логіки та властивостей. Це зменшує дублюваний код, що, в свою чергу, збільшує ремонтопридатність рішення та робить його менш схильним до помилок.
Приходьте

63

Інтерфейси

  • Визначає контракт між двома модулями. Неможливо реалізувати.
  • Більшість мов дозволяють реалізувати декілька інтерфейсів
  • Модифікація інтерфейсу - це суттєва зміна. Усі реалізації потрібно перекомпілювати / модифікувати.
  • Усі члени є публічними. Реалізацію мають виконувати всі члени.
  • Інтерфейси допомагають у роз'єднанні. Ви можете використовувати макетні рамки, щоб висміювати що-небудь за інтерфейсом
  • Інтерфейси зазвичай вказують на тип поведінки
  • Реалізації інтерфейсу відокремлюють / ізолюють одна від одної

Базові класи

  • Дозволяє додати деяку реалізацію за замовчуванням, яку ви отримаєте безкоштовно шляхом виведення
  • Крім C ++, ви можете походити лише з одного класу. Навіть якщо це можливо з декількох класів, це зазвичай погана ідея.
  • Змінити базовий клас відносно просто. Виведення не потрібно робити нічого особливого
  • Базові класи можуть оголошувати захищені та публічні функції, до яких можна отримати доступ
  • Абстрактні базові класи не можуть бути легко висмійовані, як інтерфейси
  • Базові класи зазвичай вказують ієрархію типів (IS A)
  • Виведення класів може залежати від деякої базової поведінки (мати складні знання про реалізацію батьків). Речі можуть бути безладними, якщо ви внесете зміни в базову реалізацію для одного хлопця і зламаєте інших.

Примітка: в керівництві по дизайну рамок рекомендується використовувати базові класи (на відміну від інтерфейсів), оскільки вони краще версії. Додавання нового методу до абстрактного базового класу в vNext - це безперебійна зміна ..
Gishu

59

Загалом, вам слід віддавати перевагу інтерфейсам над абстрактними класами. Однією з причин використовувати абстрактний клас є якщо у вас є спільна реалізація серед конкретних класів. Звичайно, ви все ж повинні оголосити інтерфейс (IPet) і мати абстрактний клас (PetBase), який реалізує цей інтерфейс. Використовуючи невеликі, чіткі інтерфейси, ви можете використовувати кратні для подальшого підвищення гнучкості. Інтерфейси дозволяють отримати максимальну кількість гнучкості та портативності типів через межі. Передаючи посилання через межі, завжди передайте інтерфейс, а не конкретний тип. Це дозволяє отримуючому кінцеві визначати конкретну реалізацію та забезпечує максимальну гнучкість. Це абсолютно вірно при програмуванні в режимі TDD / BDD.

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


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

Ніхто не сказав, що ваші інтерфейси повинні бути величезними. Менші, декілька інтерфейсів і багатші базові класи створюють чудовий API.
Кілхоффер

3
Це тільки я, або більшість класів «звичайних робітників» поділяють спільну реалізацію? У цьому контексті це суперечить вашому загальному правилу переваги інтерфейсів. Я б переказав ваше узагальнення на 2 узагальнення: Ті загальні класи, які не містять або мало логіки, повинні реалізувати інтерфейс. Ті загальні класи, які містять "пристойну" кількість логіки, повинні виходити з базового класу (оскільки, швидше за все, вони поділять функціональність).
goku_da_master

@Kilhoffer "Інтерфейси дозволяють отримати максимальну кількість гнучкості та переносимості типів через межі", будь ласка, розробимо це твердження.
JAVA

49

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

Кшиштоф Кваліна говорить на сторінці 81:

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

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


19

Хуан,

Мені подобається розглядати інтерфейси як спосіб характеризувати клас. Певний клас породи собак, скажімо, Йоркшир-Тер'єр, може бути походженням від батьківського класу собак, але він також реалізує IFurry, IStubby та IYippieDog. Тож клас визначає, що таке клас, але інтерфейс розповідає нам про нього.

Перевагою цього є те, що я можу, наприклад, зібрати всі IYippieDog і кинути їх у свою колекцію Ocean. Тож тепер я можу охопити певний набір об’єктів і знайти ті, які відповідають критеріям, які я переглядаю, не надто уважно вивчаючи клас.

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

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

Тож якщо ви думаєте, як я, ви б точно сказали, що Кіт і Собака є IPettable. Це характеристика, яка відповідає їм обом.

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

Скажіть, я хочу зібрати всі класи про тварин і покласти їх у свій контейнер з ковчегом.

Або вони повинні бути ссавцями? Можливо, нам потрібна якась перехресна фабрика для доїння тварин?

Чи взагалі їх потрібно пов’язувати разом? Чи достатньо просто знати, що вони обидва IPettable?

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

Ви також можете в якийсь момент виявити, що не всі Коти є IPettable (як, наприклад, цей без волосся). Тепер ви можете перемістити цей інтерфейс до всіх похідних класів, які підходять. Ви побачите, що набагато менш руйнівна зміна, що раптом Коти вже не походять від PettableBase.


18

Ось основний та простий визначення інтерфейсу та базового класу:

  • Базовий клас = успадкування об'єкта.
  • Інтерфейс = функціональне успадкування.

ура


12

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

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

Таким чином, замість IPet або PetBase ви можете поставити улюбленця, у якого є параметр IFurBehavior. Параметр IFurBehavior встановлюється методом CreateDog () PetFactory. Саме цей параметр викликається методом shed ().

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

Я рекомендую цю схему навіть для мов із множинним успадкуванням.


12

Це добре пояснено в цій статті Java World .

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

Не рідкість, що у мене буде клас, що реалізує один або кілька інтерфейсів.

Абстрактні заняття я використовую як основу для чогось іншого.

Далі йде витяг із вищезгаданої статті статті JavaWorld.com, автор Тоні Сінтес, 20.04.01


Інтерфейс проти абстрактного класу

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

Абстрактні заняття дозволяють визначити деяку поведінку; вони змушують ваші підкласи надавати іншим. Наприклад, якщо у вас є додатки, абстрактний клас може надавати послуги за замовчуванням, такі як обробка подіями та повідомленнями. Ці служби дозволяють вашій програмі підключатися до вашої програми. Однак є певна функціональна функція, яку може виконувати лише ваша програма. Така функціональність може включати завдання запуску та відключення, які часто залежать від додатків. Тож замість того, щоб намагатися визначити саму поведінку, абстрактний базовий клас може оголосити абстрактні методи вимкнення та запуску. Базовий клас знає, що йому потрібні ці методи, але абстрактний клас дозволяє вашому класу визнати, що він не знає, як виконувати ці дії; він знає лише, що він повинен ініціювати дії. Коли настав час запускати, абстрактний клас може викликати метод запуску. Коли базовий клас викликає цей метод, Java викликає метод, визначений дочірнім класом.

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

Клас проти інтерфейсу

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

Наприклад, шаблон стратегії дозволяє поміняти нові алгоритми та процеси у вашій програмі, не змінюючи об'єктів, які ними користуються. Медіаплеєр може знати, як відтворювати компакт-диски, MP3 та файли WAV. Звичайно, ви не хочете жорстко кодувати ці алгоритми відтворення в програвач; це ускладнить додавання нового формату, наприклад AVI. Крім того, ваш код буде заповнений марними заявами. І щоб додати образи до травми, вам потрібно буде оновлювати ці заяви кожного разу, коли ви додаєте новий алгоритм. Загалом, це не дуже об’єктно-орієнтований спосіб програмування.

За допомогою шаблону стратегії ви можете просто інкапсулювати алгоритм за об'єктом. Якщо ви це зробите, ви можете будь-коли надати нові плагіни для медіа. Давайте назвемо клас плагінів MediaStrategy. Цей об’єкт мав би один метод: playStream (Потік s). Отже, щоб додати новий алгоритм, ми просто розширимо наш клас алгоритму. Тепер, коли програма стикається з новим типом медіа, вона просто делегує програвання потоку нашій медіа-стратегії. Звичайно, вам знадобиться сантехніка, щоб правильно створити необхідні стратегії алгоритму.

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


2
+1. "Покладатися на спадщину за ідентичністю типу небезпечно; воно фіксує вас у певній ієрархії спадкування." Це речення чудово описує мої причини віддати перевагу інтерфейсам.
Інженер

І крім цього, а не розширювати, складіть реалізацію за кожним методом вашого інтерфейсу.
Інженер

10

Також майте на увазі, щоб не заблукали в ОО ( див. Блог ), а завжди моделюйте об’єкти на основі необхідної поведінки, якщо ви розробляли додаток, де єдиною необхідною для вас поведінкою було загальне ім’я та види для тварини, тоді вам знадобиться лише тварина одного класу із властивістю назви, а не мільйони класів для кожної можливої ​​тварини у світі.


10

У мене грубе правило виконання

Функціональність: ймовірно, буде різною у всіх частинах: Інтерфейс.

Дані та функціональність, частини будуть здебільшого однаковими, частини різні: абстрактний клас.

Дані та функціональність, фактично працюючі, якщо вони розширені лише з незначними змінами: звичайний (конкретний) клас

Дані та функціональність, ніяких змін не планується: звичайний (конкретний) клас із кінцевим модифікатором.

Дані та, можливо, функціональність: лише для читання: члени enum.

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


7

Інтерфейси повинні бути невеликими. Дійсно невеликий. Якщо ви дійсно руйнуєте свої об'єкти, то, можливо, ваші інтерфейси містять лише кілька дуже конкретних методів та властивостей.

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

Абстрактні заняття також обмежують. Хоча вони дають вам чудовий ярлик для створення дочірніх об'єктів, будь-який даний об'єкт може реалізувати лише один абстрактний клас. Багато разів я вважаю це обмеженням класів «Анотація», і саме тому я використовую безліч інтерфейсів.

Абстрактні класи можуть містити кілька інтерфейсів. Ваш абстрактний клас PetBase може реалізовувати IPet (домашні тварини мають власників) та IDigestion (домашні тварини їдять або принаймні повинні). Однак PetBase, ймовірно, не реалізує IMammal, оскільки не всі домашні тварини є ссавцями та не всі ссавці - домашні тварини. Ви можете додати MammalPetBase, що розширює PetBase, і додати IMammal. FishBase може мати PetBase та додати IFish. IFish матиме ISwim та IUnderwaterBreather як інтерфейси.

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


7

Джерело : http://jasonroell.com/2014/12/09/interfaces-vs-abrief-classes-what-should-you-use/

C # - чудова мова, яка дозріла та розвивалася за останні 14 років. Це чудово для нас, розробників, оскільки зріла мова надає нам безліч мовних особливостей, які є в нашому розпорядженні.

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

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

Це все добре і добре і забезпечує велике повторне використання коду та дотримується принципу DRY (не повторюй себе) розробки програмного забезпечення. Абстрактні заняття чудово використовувати, коли у вас є стосунки "є".

Наприклад: Золотистий ретривер "- це" тип собаки. Так і є пудель. Вони обидва можуть гавкати, як і всі собаки. Однак ви можете сказати, що парк пуделя суттєво відрізняється від собачого гавкоту «за замовчуванням». Для цього може мати сенс здійснити щось таке:

public abstract class Dog
{
      public virtual void Bark()
      {
        Console.WriteLine("Base Class implementation of Bark");
      }
}

public class GoldenRetriever : Dog
{
   // the Bark method is inherited from the Dog class
}

public class Poodle : Dog
{
  // here we are overriding the base functionality of Bark with our new implementation
  // specific to the Poodle class
  public override void Bark()
  {
     Console.WriteLine("Poodle's implementation of Bark");
  }
}

// Add a list of dogs to a collection and call the bark method.

void Main()
{
    var poodle = new Poodle();
    var goldenRetriever = new GoldenRetriever();

    var dogs = new List<Dog>();
    dogs.Add(poodle);
    dogs.Add(goldenRetriever);

    foreach (var dog in dogs)
    {
       dog.Bark();
    }
}

// Output will be:
// Poodle's implementation of Bark
// Base Class implementation of Bark

// 

Як бачите, це буде чудовим способом зберегти ваш код DRY та дозволити викликати реалізацію базового класу, коли будь-який із типів може просто покластися на Bark за замовчуванням, а не на реалізацію спеціального випадку. Такі класи, як GoldenRetriever, Boxer, Lab, могли б успадкувати «за замовчуванням» (клас басів) Bark безкоштовно, тому що вони реалізують абстрактний клас Dog.

Але я впевнений, що ви це вже знали.

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

Тепер, що, до біса, це означає? Ну, наприклад: Людина - не качка ... а качка - не людина. Досить очевидно. Однак і качка, і людина мають "здатність" плавати (враховуючи, що людина пройшла уроки плавання в 1 класі :)). Крім того, оскільки качка не є людиною чи навпаки, це не взаємозв’язок "є", а натомість відносини "здатні", і ми можемо використовувати інтерфейс, щоб проілюструвати, що:

// Create ISwimable interface
public interface ISwimable
{
      public void Swim();
}

// Have Human implement ISwimable Interface
public class Human : ISwimable

     public void Swim()
     {
        //Human's implementation of Swim
        Console.WriteLine("I'm a human swimming!");
     }

// Have Duck implement ISwimable interface
public class Duck: ISwimable
{
     public void Swim()
     {
          // Duck's implementation of Swim
          Console.WriteLine("Quack! Quack! I'm a Duck swimming!")
     }
}

//Now they can both be used in places where you just need an object that has the ability "to swim"

public void ShowHowYouSwim(ISwimable somethingThatCanSwim)
{
     somethingThatCanSwim.Swim();
}

public void Main()
{
      var human = new Human();
      var duck = new Duck();

      var listOfThingsThatCanSwim = new List<ISwimable>();

      listOfThingsThatCanSwim.Add(duck);
      listOfThingsThatCanSwim.Add(human);

      foreach (var something in listOfThingsThatCanSwim)
      {
           ShowHowYouSwim(something);
      }
}

 // So at runtime the correct implementation of something.Swim() will be called
 // Output:
 // Quack! Quack! I'm a Duck swimming!
 // I'm a human swimming!

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

Знову ж таки, це допомагає вашому коду залишатися СУХОМО, щоб вам не довелося писати кілька методів, які викликають об'єкт, щоб виконати одну і ту ж основну функцію (ShowHowHumanSwims (людина), ShowHowDuckSwims (качка) тощо)

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

Підсумок:

Отже, моє головне правило - використовувати абстрактний клас, коли ви хочете реалізувати функцію «за замовчуванням» для ієрархії класів або / і для класів або типів, з якими ви працюєте, відносини «є» (наприклад, пудель) - це Тип собаки).

З іншого боку, використовуйте інтерфейс, коли у вас немає стосунків "є", але у вас є типи, які поділяють "здатність" щось робити чи мати щось (наприклад, качка "не є людиною". Однак качка і частка людини "Здатність" плавати).

Ще одна відмінність між абстрактними класами та інтерфейсами полягає в тому, що клас може реалізовувати один до багатьох інтерфейсів, але клас може успадковувати лише ОДИН абстрактний клас (або будь-який клас з цього приводу). Так, ви можете вкладати класи та мати ієрархію успадковування (яку багато і мають програми і повинні мати), але ви не можете успадкувати два класи в одному похідному визначенні класу (це правило стосується C #. На деяких інших мовах ви, як правило, це можете зробити. лише через відсутність інтерфейсів у цих мовах).

Також пам’ятайте, коли використовуєте інтерфейси для дотримання принципу розбиття інтерфейсу (ISP). ISP заявляє, що жоден клієнт не повинен змушувати залежати від методів, які він не використовує. З цієї причини інтерфейси повинні бути зосереджені на конкретних завданнях і зазвичай дуже малі (наприклад, IDisposable, IComparable).

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

Сподіваюсь, це дещо прояснить справи!

Також якщо ви можете придумати кращі приклади або хочете щось зазначити, будь ласка, зробіть це в коментарях нижче!


6

Випадок базових класів над інтерфейсами було добре пояснено в Посібниках з кодування підмайстра .NET:

Базові класи та інтерфейси Тип інтерфейсу - це частковий опис значення, який потенційно підтримується багатьма типами об'єктів. Використовуйте базові класи замість інтерфейсів, коли це можливо. З точки зору версії, класи більш гнучкі, ніж інтерфейси. За допомогою класу ви можете доставити версію 1.0, а потім у версії 2.0 додати новий метод до класу. Поки метод не є абстрактним, будь-які існуючі похідні класи продовжують функціонувати без змін.

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

  1. Кілька непов'язаних класів хочуть підтримувати протокол.
  2. Ці класи вже мають встановлені базові класи (наприклад, деякі елементи управління інтерфейсом користувача (UI), а деякі - веб-сервісами XML).
  3. Агрегація не є доцільною або практичною. У всіх інших ситуаціях успадкування класів є кращою моделлю.

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

5

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


4

Коли я вперше почав вивчати об'єктно-орієнтоване програмування, я зробив просту і, ймовірно, звичайну помилку використання спадкування для спільної поведінки - навіть там, де така поведінка не була суттєвою для природи об'єкта.

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

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

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

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

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


Чисто абстрактний клас може надавати методи поведінки за замовчуванням для методів. Це корисно, коли всі ваші конкретні заняття поділяться загальними методами, які були б зайвими для повторної реалізації знов і знов.
Адам Хьюз

4

Віддавайте перевагу інтерфейсам над абстрактними класами

Обґрунтування, основні моменти, які слід враховувати [два вже згадувані тут]:

  • Інтерфейси є більш гнучкими, оскільки клас може реалізувати декілька інтерфейсів. Оскільки Java не має багаторазового успадкування, використання абстрактних класів заважає вашим користувачам використовувати будь-яку іншу ієрархію класів. Загалом, віддайте перевагу інтерфейсам, коли немає типових реалізацій або стану. Колекції Java пропонують хороші приклади цього (Карта, Набір тощо).
  • Абстрактні класи мають перевагу в тому, що вони забезпечують кращу сумісність вперед. Після того, як клієнти використовують інтерфейс, ви не можете його змінити; якщо вони використовують абстрактний клас, ви все одно можете додати поведінку, не порушуючи існуючий код. Якщо сумісність викликає занепокоєння, подумайте про використання абстрактних класів.
  • Навіть якщо у вас є реалізація за замовчуванням або внутрішній стан, подумайте про те, запропонувати інтерфейс та абстрактну його реалізацію . Це допоможе клієнтам, але все ж дозволить їм отримати більшу свободу за бажанням [1].
    Звичайно, ця тема досить довго обговорювалася в інших місцях [2,3].

[1] Він, звичайно, додає більше коду, але якщо стислість - ваша головна проблема, ви, мабуть, мали б уникати Java в першу чергу!

[2] Джошуа Блох, Ефективна Java, пункти 16-18.

[3] http://www.codeproject.com/KB/ar ...


3

Попередні коментарі щодо використання абстрактних класів для загальної реалізації, безумовно, знакові. Однією з переваг, яку я ще не бачив, є те, що використання інтерфейсів значно спрощує реалізацію макетних об'єктів з метою одиничного тестування. Визначення IPet та PetBase як описано Джейсоном Коеном дозволяє легко знущатися над різними умовами даних, без накладних витрат фізичної бази даних (поки ви не вирішите, що настав час перевірити реальну річ).


3

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

Громадське успадкування надмірно використовується в OOD і виражає набагато більше, ніж більшість розробників усвідомлює або готові жити. Дивіться Принцип заміщення Ліскова

Коротше кажучи, якщо A "є" B, то A вимагає не більше B і забезпечує не менше B, для кожного способу, який він піддається.


3

Іншим варіантом, який слід пам’ятати, є використання відносин «has-a», так само «реалізовано в термінах» або «композиція». Іноді це більш чіткий, гнучкіший спосіб структурування речей, ніж використання спадщини "є-а".

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

public class Pet
{
    void Bathe();
    void Train(Trick t);
}

public class Dog
{
    private Pet pet;

    public void Bathe() { pet.Bathe(); }
    public void Train(Trick t) { pet.Train(t); }
}

public class Cat
{
    private Pet pet;

    public void Bathe() { pet.Bathe(); }
    public void Train(Trick t) { pet.Train(t); }
}

Так, цей приклад показує, що існує багато дублювання коду і не вистачає елегантності в тому, щоб робити такі дії. Але також слід враховувати, що це допомагає утримувати Собаку і Кішку, відірванусь від класу Pet (оскільки собака і Кішка не мають доступу до приватних членів Домашніх тварин), і це залишає можливість Собаці і Кішці успадковувати щось інше - -можливо клас ссавців.

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

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


3

Концептуально інтерфейс використовується для формального та напівформального визначення набору методів, які надаватиме об’єкт. Формально означає набір методів і підписів методів, а напівформально означає читану людиною документацію, пов'язану з цими методами.

Інтерфейси - це лише описи API (зрештою, API розшифровується як інтерфейс програмування прикладних програм ), вони не можуть містити жодної реалізації, а також використовувати або запускати інтерфейс неможливо. Вони лише укладають договір про те, як слід взаємодіяти з об'єктом.

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

Існує різниця між базовим класом і абстрактним базовим класом (ABC). ABC поєднують інтерфейс та реалізацію разом. Анотація поза комп'ютерним програмуванням означає "резюме", тобто "абстракт == інтерфейс". Потім абстрактний базовий клас може описувати як інтерфейс, так і порожню, часткову або повну реалізацію, яка має бути успадкована.

Думки про те, коли використовувати інтерфейси проти абстрактних базових класів проти просто класів , залежать від того, що ви розробляєте, і мови, на якій ви розробляєтесь, різко змінюються. Інтерфейси часто асоціюються лише зі статично типовими мовами, такими як Java або C #, але динамічно набрані мови також можуть мати інтерфейси та абстрактні базові класи . В Python, наприклад, відмінність стає зрозумілим між класом, який заявляє , що вона реалізує в інтерфейс , і об'єкт, який є екземпляром класу , і , як кажуть забезпечити , що інтерфейс. Можливо, що в динамічній мові два об'єкти, що є обома екземплярами одного класу , можуть заявити, що вони забезпечують абсолютно різні інтерфейси. У Python це можливо лише для атрибутів об'єкта, тоді як методи поділяються станом між усіма об'єктами класу . Однак у Ruby в об'єктах можуть бути методи екземпляра, тому можливо, що інтерфейс між двома об'єктами одного класу може змінюватись стільки, скільки бажає програміст (однак, у Ruby немає явного способу декларування інтерфейсів).

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


2

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

Недоліком реалізації базового класу є вимога до override(або new) існуючих методів. Це робить їх віртуальними методами, а це означає, що вам потрібно бути обережними щодо використання об'єкта об'єкта.

Нарешті, одне успадкування .NET вбиває мене. Наївний приклад: скажіть, що ви здійснюєте контроль над користувачем, тому ви успадковуєте UserControl. Але тепер ви також не успадковуєтесь PetBase. Це змушує вас реорганізуватись, наприклад зробити PetBaseчленом класу.


2

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


2

Що стосується C #, то в деяких сенсах інтерфейси та абстрактні класи можуть бути взаємозамінними. Однак відмінності полягають у тому, що: i) інтерфейси не можуть реалізувати код; ii) через це інтерфейси не можуть надалі викликати стек до підкласу; та iii) лише клас може бути успадкований абстрактним класом, тоді як для класу може бути реалізовано кілька інтерфейсів.


2

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


2

Я виявив, що шаблон інтерфейсу> Анотація> Бетон працює у таких випадках використання:

1.  You have a general interface (eg IPet)
2.  You have a implementation that is less general (eg Mammal)
3.  You have many concrete members (eg Cat, Dog, Ape)

Абстрактний клас визначає типові спільні атрибути конкретних класів, але при цьому застосовує інтерфейс. Наприклад:

public interface IPet{

    public boolean hasHair();

    public boolean walksUprights();

    public boolean hasNipples();
}

Тепер, оскільки всі ссавці мають волосся і соски (AFAIK, я не зоолог), ми можемо перенести це в абстрактний базовий клас

public abstract class Mammal() implements IPet{

     @override
     public walksUpright(){
         throw new NotSupportedException("Walks Upright not implemented");
     }

     @override
     public hasNipples(){return true}

     @override
     public hasHair(){return true}

І тоді конкретні заняття просто визначають, що вони йдуть вертикально.

public class Ape extends Mammal(){

    @override
    public walksUpright(return true)
}

public class Catextends Mammal(){

    @override
    public walksUpright(return false)
}

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

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


1

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


1

Використовуйте інтерфейси для забезпечення контракту сімействами ACROSS неспоріднених класів. Наприклад, у вас можуть бути загальні методи доступу для класів, які представляють колекції, але містять докорінно різні дані, тобто один клас може представляти набір результатів запиту, а інший може представляти зображення в галереї. Крім того, ви можете реалізувати кілька інтерфейсів, тим самим дозволяючи поєднувати (і означати) можливості класу.

Використовуйте успадкування, коли класи мають спільний взаємозв'язок і тому мають подібний структурно-поведінковий підпис, тобто автомобіль, мотоцикл, вантажівка та позашляховик - це всі типи дорожнього транспортного засобу, які можуть містити кількість коліс, максимальну швидкість

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