Це може бути загальне питання про ООП. Я хотів зробити загальне порівняння між інтерфейсом та абстрактним класом на основі їх використання.
Коли хотілося б використовувати інтерфейс і коли потрібно використовувати абстрактний клас ?
Це може бути загальне питання про ООП. Я хотів зробити загальне порівняння між інтерфейсом та абстрактним класом на основі їх використання.
Коли хотілося б використовувати інтерфейс і коли потрібно використовувати абстрактний клас ?
Відповіді:
Я написав статтю про це:
Абстрактні класи та інтерфейси
Підсумовуючи:
Коли ми говоримо про абстрактні класи, ми визначаємо характеристики типу об'єкта; уточнення, що таке об’єкт .
Коли ми говоримо про інтерфейс і визначаємо можливості, які ми обіцяємо надати, ми говоримо про встановлення договору про те, що може зробити об'єкт.
Interfaces do not express something like "a Doberman is a type of dog and every dog can walk" but more like "this thing can walk"
. Дякую
Use abstract classes and inheritance if you can make the statement “A is a B”. Use interfaces if you can make the statement “A is capable of [doing] as”
Абстрактний клас може мати загальний стан або функціонал. Інтерфейс - це лише обіцянка надати стан або функціонал. Хороший абстрактний клас зменшить кількість коду, який потрібно переписати, оскільки його функціональність або стан можуть бути спільними. В інтерфейсі немає визначеної інформації, якою слід ділитися
Особисто мені майже ніколи не виникає потреби писати абстрактні заняття.
У більшості випадків я бачу, як використовуються (неправильно) абстрактні класи, це тому, що автор абстрактного класу використовує шаблон "Метод шаблонів".
Проблема "Методу шаблонів" полягає в тому, що він майже завжди дещо перевтілюється - "похідний" клас знає не тільки про "абстрактний" метод його базового класу, який він реалізує, а й про публічні методи базового класу. , навіть незважаючи на те, що в більшості випадків не потрібно їх називати.
(Надто спрощений) приклад:
abstract class QuickSorter
{
public void Sort(object[] items)
{
// implementation code that somewhere along the way calls:
bool less = compare(x,y);
// ... more implementation code
}
abstract bool compare(object lhs, object rhs);
}
Отож, автор цього класу написав загальний алгоритм і має намір люди використовувати його, "спеціалізуючись" на ньому, надаючи власні "гачки" - в даному випадку метод "порівняння".
Тож передбачуване використання приблизно таке:
class NameSorter : QuickSorter
{
public bool compare(object lhs, object rhs)
{
// etc.
}
}
Проблема в цьому полягає в тому, що ви надмірно поєднали два поняття:
У наведеному вище коді теоретично автор методу "зіставити" може повторно передзвонити в суперклас "Сортувати" метод, хоча на практиці вони ніколи цього не хочуть чи не потребують.
Ціна, яку ви платите за цю непотрібну сполуку, полягає в тому, що важко змінити суперклас, а в більшості мов OO неможливо змінити його під час виконання.
Альтернативним методом є використання схеми дизайну "Стратегія":
interface IComparator
{
bool compare(object lhs, object rhs);
}
class QuickSorter
{
private readonly IComparator comparator;
public QuickSorter(IComparator comparator)
{
this.comparator = comparator;
}
public void Sort(object[] items)
{
// usual code but call comparator.Compare();
}
}
class NameComparator : IComparator
{
bool compare(object lhs, object rhs)
{
// same code as before;
}
}
Тож зауважте зараз: Все, що ми маємо, це інтерфейси та конкретні реалізації цих інтерфейсів. На практиці вам не потрібно нічого іншого, щоб зробити дизайн високого рівня.
Щоб "приховати" той факт, що ми реалізували "сортування імен" за допомогою класу "QuickSort" та "NameComparator", ми можемо де-небудь написати де-небудь заводський метод:
ISorter CreateNameSorter()
{
return new QuickSorter(new NameComparator());
}
Кожен раз, коли у вас є абстрактний клас, ви можете це зробити ... навіть коли між базовим та похідним класом існують природні відносини для повторного вступу, зазвичай це платить, щоб зробити їх явними.
Остання заключна думка: все, що ми зробили вище, - це "скласти" функцію "NameSorting", використовуючи функцію "QuickSort" та функцію "NameCompparement" ... у функціональній мові програмування цей стиль програмування стає ще більш природним, з меншим кодом.
Якщо ви розглядаєте java як мову OOP,
" інтерфейс не забезпечує реалізацію методу " більше не діє при запуску Java 8. Тепер java забезпечує реалізацію в інтерфейсі для методів за замовчуванням.
Простіше кажучи, я хотів би використати
інтерфейс: для реалізації договору декількома непов'язаними об'єктами. Він забезпечує можливість " HAS A ".
абстрактний клас: реалізувати однакову або різну поведінку серед декількох пов'язаних об'єктів. Він встановлює відношення " IS A ".
Веб-сайт Oracle пропонує ключові відмінності між класом interface
та abstract
класом.
Подумайте про використання абстрактних класів, якщо:
Подумайте про використання інтерфейсів, якщо:
Serializable
інтерфейс.Приклад:
Абстрактний клас (відношення IS )
Читач - абстрактний клас.
BufferedReader - цеReader
FileReader - цеReader
FileReader
і BufferedReader
використовуються для загальної мети: Читання даних, і вони пов'язані через Reader
клас.
Інтерфейс (можливість HAS A )
Serializable - це інтерфейс.
Припустимо, що у вашій програмі є два класи, які реалізують Serializable
інтерфейс
Employee implements Serializable
Game implements Serializable
Тут ви не можете встановити будь-яке відношення через Serializable
інтерфейс між Employee
і Game
, який призначений для різних цілей. Обидва здатні серіалізувати державу і порівняння закінчується там.
Подивіться на ці пости:
Як я повинен пояснити різницю між класом інтерфейсу та абстрактним?
Гаразд, щойно "пограбував" цим самим - ось це з точки зору мирян (не соромтеся виправити мене, якщо я помиляюся) - я знаю, що ця тема є оооооооооооооохоляді, але хтось інший може натрапити на неї одного дня ...
Абстрактні класи дозволяють створити креслення і дозволяють додатково ПОБУДОВАТИ (реалізувати) властивості та методи, якими ви хочете ВСІХ своїх нащадків.
Інтерфейс, з іншого боку, дозволяє лише заявити, що ви хочете, щоб властивості та / або методи з певним іменем існували у всіх класах, які його реалізують - але не визначає, як слід його реалізувати. Також клас може реалізувати МНОГО інтерфейси, але може розширювати лише ОДИН клас абстрактних. Інтерфейс - це скоріше архітектурний інструмент високого рівня (який стає зрозумілішим, якщо ви починаєте розуміти шаблони дизайну) - Анотація має ноги в обох таборах і також може виконувати деякі брудні роботи.
Навіщо використовувати один над іншим? Перший дозволяє більш конкретно визначити нащадків - другий дозволяє посилити поліморфізм . Цей останній момент важливий для кінцевого користувача / кодера, який може використовувати цю інформацію для реалізації AP I (nterface) у різноманітних комбінаціях / формах відповідно до їх потреб.
Я думаю, що це був момент "лампочки" для мене - подумай про інтерфейси менше з точки зору автора і більше від будь-якого кодера, який з’явиться пізніше в ланцюжку, який додає реалізацію до проекту чи розширює API.
Мої два центи:
Інтерфейс в основному визначає контракт, якого повинен дотримуватися будь-який клас реалізації (реалізувати учасників інтерфейсу). Він не містить жодного коду.
З іншого боку, абстрактний клас може містити код, і можуть бути деякі методи, позначені як абстрактні, які повинен наслідувати клас успадкування.
Рідкісні ситуації, в яких я використовував абстрактні класи, коли у мене є деяка функціональність за замовчуванням, від якої спадковий клас може бути не цікавим для переосмислення, скажімо, абстрактного базового класу, від якого успадковують деякі спеціалізовані класи.
Приклад (дуже зародковому один!): Розглянемо базовий клас з ім'ям клієнта , який має абстрактні методи , як CalculatePayment()
, CalculateRewardPoints()
і деякі не абстрактні методи , як GetName()
, SavePaymentDetails()
.
Спеціалізовані класи люблять RegularCustomer
і GoldCustomer
будуть успадковуватись від Customer
базового класу та реалізовувати власну CalculatePayment()
та CalculateRewardPoints()
методику логіки, але повторно використовувати GetName()
і SavePaymentDetails()
методи.
Ви можете додати більше функціональності до абстрактного класу (тобто не абстрактних методів), не впливаючи на дочірні класи, які використовували старішу версію. Тоді як додавання методів до інтерфейсу вплине на всі класи, що реалізовують його, оскільки тепер їм потрібно буде реалізувати щойно додані члени інтерфейсу.
Абстрактний клас із усіма абстрактними членами буде подібний до інтерфейсу.
Коли робити те, що є дуже простою справою, якщо у вас в думці є чітка концепція.
Абстрактні класи можна отримати, тоді як інтерфейси можуть бути реалізовані. Існує деяка різниця між ними. Коли ви отримуєте абстрактний клас, відношення між похідним класом і базовим класом є "є" відношенням. наприклад, собака - тварина, вівця - тварина, що означає, що похідний клас успадковує деякі властивості від базового класу.
Тоді як для реалізації інтерфейсів взаємозв'язок "може бути". наприклад, собака може бути собакою-шпигуном. Собака може бути цирковою собакою. Собака може бути расовою собакою. Що означає, що ви реалізуєте певні методи, щоб щось придбати.
Я сподіваюся, що я зрозумів.
1. Якщо ви створюєте щось, що забезпечує загальну функціональність для непов'язаних класів, використовуйте інтерфейс.
2.Якщо ви створюєте щось для об'єктів, тісно пов'язаних в ієрархії, використовуйте абстрактний клас.
Я написав статтю про те, коли використовувати абстрактний клас та коли використовувати інтерфейс. Різниця між ними є набагато більше, ніж "один IS-A ... і один CAN-DO ...". Мені це відповіді в консервах. Я згадую кілька причин, коли використовувати будь-яку з них. Сподіваюся, це допомагає.
http://codeofdoom.com/wordpress/2009/02/12/learn-this-when-to-use-an-abrief-class-and-an-interface/
Я вважаю, що найуспішнішим способом цього є:
Спільні властивості => абстрактний клас.
Спільний функціонал => інтерфейс.
І менш виразно ...
Приклад абстрактного класу:
public abstract class BaseAnimal
{
public int NumberOfLegs { get; set; }
protected BaseAnimal(int numberOfLegs)
{
NumberOfLegs = numberOfLegs;
}
}
public class Dog : BaseAnimal
{
public Dog() : base(4) { }
}
public class Human : BaseAnimal
{
public Human() : base(2) { }
}
Оскільки тварини мають спільну властивість - кількість ніг у цьому випадку - має сенс скласти абстрактний клас, що містить це спільне властивість. Це також дозволяє нам написати загальний код, який діє на цю властивість. Наприклад:
public static int CountAllLegs(List<BaseAnimal> animals)
{
int legCount = 0;
foreach (BaseAnimal animal in animals)
{
legCount += animal.NumberOfLegs;
}
return legCount;
}
Приклад інтерфейсу:
public interface IMakeSound
{
void MakeSound();
}
public class Car : IMakeSound
{
public void MakeSound() => Console.WriteLine("Vroom!");
}
public class Vuvuzela : IMakeSound
{
public void MakeSound() => Console.WriteLine("VZZZZZZZZZZZZZ!");
}
Зауважте тут, що Вувузелас і Автомобілі - це абсолютно різні речі, але вони мають спільну функціональність: виголошуючи звук. Таким чином, тут є сенс інтерфейсу. Далі це дозволить програмістам згрупувати речі, які видають звуки разом під загальним інтерфейсом - IMakeSound
у цьому випадку. За допомогою цього дизайну ви можете написати наступний код:
List<IMakeSound> soundMakers = new List<ImakeSound>();
soundMakers.Add(new Car());
soundMakers.Add(new Vuvuzela());
soundMakers.Add(new Car());
soundMakers.Add(new Vuvuzela());
soundMakers.Add(new Vuvuzela());
foreach (IMakeSound soundMaker in soundMakers)
{
soundMaker.MakeSound();
}
Чи можете ви сказати, що це може отримати?
Нарешті, ви можете поєднати два.
Комбінований приклад:
public interface IMakeSound
{
void MakeSound();
}
public abstract class BaseAnimal : IMakeSound
{
public int NumberOfLegs { get; set; }
protected BaseAnimal(int numberOfLegs)
{
NumberOfLegs = numberOfLegs;
}
public abstract void MakeSound();
}
public class Cat : BaseAnimal
{
public Cat() : base(4) { }
public override void MakeSound() => Console.WriteLine("Meow!");
}
public class Human : BaseAnimal
{
public Human() : base(2) { }
public override void MakeSound() => Console.WriteLine("Hello, world!");
}
Тут ми вимагаємо, щоб усі BaseAnimal
звучали, але ми ще не знаємо його реалізації. У такому випадку ми можемо абстрагувати реалізацію інтерфейсу та делегувати його реалізацію своїм підкласам.
Останнє, згадайте, як на прикладі абстрактного класу ми змогли оперувати спільними властивостями різних об'єктів, а в прикладі інтерфейсу ми змогли викликати спільну функціональність різних об'єктів? У цьому останньому прикладі ми могли б зробити і те, і інше.
Коли віддати перевагу абстрактному класу над інтерфейсом?
Коли віддати перевагу інтерфейсу над абстрактним класом?
Класи можуть успадковувати лише один базовий клас, тому, якщо ви хочете використовувати абстрактні класи для надання поліморфізму групі класів, всі вони повинні успадковувати цей клас. Анотаційні заняття можуть також надавати учасникам, які вже реалізовані. Отже, ви можете забезпечити певну кількість однакових функціональних можливостей з абстрактним класом, але не можете з інтерфейсом.
Ось кілька рекомендацій, які допоможуть вам вирішити, чи використовувати інтерфейс чи абстрактний клас для надання поліморфізму для своїх компонентів.
Скопійовано з:
http://msdn.microsoft.com/en-us/library/scsyfw1d%28v=vs.71%29.aspx
Подумайте про використання абстрактних класів, якщо якесь із цих тверджень стосується вашої ситуації:
Подумайте про використання інтерфейсів, якщо якесь із цих тверджень стосується вашої ситуації:
Відповіді різняться між мовами. Наприклад, у Java клас може реалізовувати (успадковувати) кілька інтерфейсів, але успадковувати лише один абстрактний клас. Тож інтерфейси дають вам більше гнучкості. Але це не вірно в C ++.
Для мене я б ходив із інтерфейсами у багатьох випадках. Але я віддаю перевагу абстрактним заняттям у деяких випадках.
Класи в ОО загалом відносяться до реалізації. Я використовую абстрактні класи, коли хочу примусити деякі відомості про реалізацію для інших дітей, з якими я йду з інтерфейсами.
Звичайно, абстрактні класи корисні не тільки для примусової реалізації, але й для обміну деякими конкретними деталями між багатьма пов'язаними класами.
Використовуйте абстрактний клас, якщо ви хочете надати деякі основні реалізації.
в Java ви можете успадкувати один (абстрактний) клас, щоб "забезпечити" функціональність, і ви можете реалізувати багато інтерфейсів для "забезпечення" функціональності
Чисто на основі успадкування, ви б використовували Анотацію, де ви визначаєте чітко нащадкові, абстрактні відносини (тобто тварина -> кішка) та / або потребуєте успадкування віртуальних або непублічних властивостей, особливо спільного стану (які інтерфейси не можуть підтримувати ).
Вам слід спробувати надати перевагу складу (через ін'єкцію залежностей) над успадкуванням, де можна, та зазначити, що інтерфейси, що є контрактами, підтримують тестування одиниць, розділення проблем та (різниться мовою) багатократне успадкування таким чином, як конспекти не можуть.
Одне цікаве місце, де інтерфейси працюють краще, ніж абстрактні класи, - коли вам потрібно додати додаткову функціональність до групи (пов'язаних або неспоріднених) об’єктів. Якщо ви не можете дати їм базовий абстрактний клас (наприклад, у них є sealed
або вже є батьківський), ви можете надати їм фіктивний (порожній) інтерфейс, а потім просто написати методи розширення для цього інтерфейсу.
Це може бути дуже важким закликом зробити ...
Один вказівник, який я можу дати: Об'єкт може реалізовувати багато інтерфейсів, тоді як об'єкт може успадковувати лише один базовий клас (у сучасній мові OO, як c #, я знаю, що C ++ має декілька успадкованих - але це не нахмурено?)
Абстрактний клас може мати реалізації.
Інтерфейс не має реалізацій, він просто визначає вид контракту.
Можуть бути і деякі мовнозалежні відмінності: наприклад, C # не має багаторазового успадкування, але в класі можуть бути реалізовані кілька інтерфейсів.
Основне правило великого пальця: Для "Іменників" використовуйте клас "Анотація", а для "Дієслова" - інтерфейс
Наприклад: car
абстрактний клас, і drive
ми можемо зробити його інтерфейсом.
drive
автомобіля - це абстрактний клас.