Як я дізнаюся, коли створити інтерфейс?


196

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

Я часто читаю про них, але мені здається, що я не можу їх зрозуміти.

Я читав такі приклади, як: базовий клас Animal, з інтерфейсом IAnimal для таких речей, як 'Walk', 'Run', 'GetLegs' і т. Д. - але я ніколи не працював над чимось і відчував себе так: "Ей, я повинен використовувати інтерфейс тут! "

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


1
Ви повинні перевірити stackoverflow.com/q/8531292/1055241
gprathour

Відповіді:


151

це вирішує цю конкретну проблему:

у вас є a, b, c, d 4-х різних типів. у всьому коді у вас є щось на кшталт:

a.Process();
b.Process();
c.Process();
d.Process();

чому б їм не реалізувати IProcessable, а потім зробити

List<IProcessable> list;

foreach(IProcessable p in list)
    p.Process();

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


Ще одна конкретна проблема:

Ви коли-небудь заглядали на System.Linq.Eumerable? Він визначає тонну методів розширення, які працюють на будь-якому типі, що реалізує IEnumerable. Оскільки все, що реалізує IEnumerable, в основному говорить: "Я підтримую ітерацію за невпорядкованим шаблоном типу foreach", ви можете визначити складну поведінку (Count, Max, Where, Select тощо) для будь-якого численного типу.


2
Це допомагає. Яка перевага в наявності інтерфейсу, а не в тому, що типи просто мають реалізацію для методу Process ()?
користувач53885

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

Вам не доведеться додавати, на відміну від 50 різних класів методом Process. C # не використовує "набирання качок", тому тільки тому, що в A є Process (), а B - Process (), це не означає, що немає жодного загального способу викликати. Для цього вам потрібен інтерфейс.
користувач7116

правильно. Я просто змінив "var" на "IProcessable", щоб приклад мав більше сенсу.
Джиммі

2
@Rogerio: Я намагався бути загальним. Справа не в тому, що "коли у вас є речі, які мають функцію Process ()" це ", коли у вас є речі, які мають спільний набір методів". приклад можна легко змінити наforeach(IMyCompanyWidgetFrobber a in list) a.Frob(widget, context);
Джиммі

133

Мені подобається відповідь Джиммі, але я відчуваю, що потрібно щось додати до неї. Ключ до всього - "здатний" в IProcess здатний. Він вказує на здатність (або властивість, але означає "внутрішню якість", а не в сенсі C # властивостей) об'єкта, що реалізує інтерфейс. IAnimal, мабуть, не є хорошим прикладом для інтерфейсу, але IWalkable може бути хорошим інтерфейсом, якщо у вашій системі є багато речей, які можуть ходити. У вас можуть бути заняття, отримані від тварин, таких як Собака, Корова, Риба, Змія. Перші два, ймовірно, реалізують IWalkable, другі два не ходять, тому вони не будуть. Тепер ви запитуєте: "чому б просто не мати іншого суперкласу, WalkingAnimal, з якого походять Собака та Корова?". Відповідь, коли у вас є щось зовсім поза деревом спадку, яке також може ходити, наприклад, робот. Робот би реалізував IWalkable, але, ймовірно, не походив би від Animal. Якщо ви хочете список речей, по яких можна ходити,

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


9
Мені подобається те, що ти придумав - "Це вказує на здатність". Інтерфейси справді потрібні, коли потрібно визначити можливості, тому що основи повинні дотримуватися класу "База".
Раміз Уддін

72

Використовуйте інтерфейси, коли реалізація одного і того ж функціоналу буде відрізнятися.

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


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

@ArnisLapsa Я не зовсім розумію, що ви маєте на увазі під "субклассом, який не є базовим". Коли підклас не буде "базовим класом"? (як у isключовому
слові

32

Подумайте про інтерфейс, як контракт. Це спосіб сказати: "Ці класи повинні дотримуватися цього набору правил".

Отже, в прикладі IAnimal - це спосіб сказати: "Я ПОВИНЕН бути в змозі викликати Run, Walk тощо" на класах, які реалізують IAnimal. "

Чому це корисно? Можливо, ви хочете побудувати функцію, яка спирається на те, що ви повинні мати можливість викликати Run та Walk, наприклад, на об'єкті. Ви можете мати наступне:

public void RunThenWalk(Monkey m) {
    m.Run();
    m.Walk();
}

public void RunThenWalk(Dog d) {
    d.Run();
    d.Walk();
}

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

public void RunThenWalk(IAnimal a) {
    a.Run();
    a.Walk();
}

Програмуючи проти інтерфейсу, ви по суті довіряєте класам для реалізації намірів інтерфейсу. Тож у нашому прикладі думка така: «Мені все одно, як вони бігають і гуляють, доки вони бігають і гуляють. Мій RunThenWalk буде дійсним до тих пір, поки вони виконають цю угоду. Він прекрасно функціонує, не знаючи нічого іншого про клас."

У цьому пов'язаному питанні також є хороша дискусія .


1
Не забувайте про незалежність впровадження. Інтерфейс дозволяє не байдужесь, як реалізована основна функція, а лише те, що вона робить те, що говорить інтерфейс.
Метью Брубакер

18

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

Приклад, який я завжди даю комусь, це якщо у вас є клас «Вітрильник» та «Війпер». Вони успадковують клас Човен та Клас Авто відповідно. Тепер скажіть, що вам потрібно проглянути всі ці об’єкти і назвати їх Drive()метод. Хоча ви могли написати якийсь код, наприклад:

if(myObject is Boat)
    ((Boat)myObject).Drive()
else
    if (myObject is Car)
        ((Car)myObject).Drive()

Буде набагато простіше написати:

((IDrivable)myObject).Drive()

16

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

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


15

Мені подобається армійська аналогія.

Сержанта не хвилює, чи ви розробник програмного забезпечення , музикант чи юрист .
До вас ставляться як до солдата .

uml

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

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

Інтерфейси - це програмні конструкції, які допомагають досягти поліморфізму.

Необхідність абстрактних деталей для досягнення простоти - це відповідь на Ваше запитання.

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

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


14

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

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

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


Правильна справа з неправильних причин. У вашому випадку - ви отримуєте так звані "інтерфейси заголовків" і додаєте складності надмірних ваг, досягнутих простоти.
Арніс Лапса

@Arnis - тестування одиниць - це "неправильна причина", легко знущатися з класів, щоб зняти залежності в тестах, є "неправильною причиною". Вибачте, але я не згоден.
tvanfosson

1
тести повинні впливати на дизайн опосередковано , надаючи зворотній зв'язок, якщо код є або не перевіряється. додавання балів розширюваності для поліпшення тестабельності саме як обман. Я думаю, що Марк Семан підсумовує це найкраще bit.ly/esi8Wp
Арніс Лапса

1
@Arnis - тож ти взагалі використовуєш знущання у своїх одиничних тестах? Якщо ні, то як зняти залежність від залежностей. Ви взагалі використовуєте DI? Блок тестування змусив мене використовувати глузування та DI; глузування та Д.І. довели цінність належної практики використання інтерфейсів для визначення контрактів таким чином, що жодне академічне розуміння ніколи не могло. Через те, що я прийняв TDD, мій код набагато менший, ніж був би інакше. Я думаю, що це гарна річ.
tvanfosson

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

10

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

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

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

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

Приклади з натури: Я не надто захоплююсь прикладами eats (), makeSound (), move () тощо. Вони описують поведінку, що є правильним, але не описують взаємодії та те, як вони ввімкнуті . Очевидні приклади інтерфейсів, які дозволяють взаємодіяти в природі, пов'язані з розмноженням, наприклад, квітка забезпечує певний інтерфейс для бджоли, щоб було можливе запилення.


5

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

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

  1. У нашій моделі плагінів ми завантажуємо модулі за допомогою інтерфейсу і надаємо цей інтерфейс для записуючих плагінів для відповідності.

  2. У нашій системі міжміських повідомлень класи класів повідомлень реалізують певний інтерфейс і "розкручуються" за допомогою інтерфейсу.

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

  4. У нас є один інтерфейс, який ми використовуємо, щоб уникнути неприємної циркулярної проблеми. (Не робіть цього, якщо не потрібно.)

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


5

Приклад коду (комбінація Ендрю з додатковим моїм інтерфейсом що є, що є ціль ), який також робить випадок, чому інтерфейс замість абстрактного класу на мовах, що не підтримують багатократне успадкування (c # і java):

interface ILogger
{
    void Log();
}
class FileLogger : ILogger
{
    public void Log() { }
}
class DataBaseLogger : ILogger
{
    public void Log() { }
}
public class MySpecialLogger : SpecialLoggerBase, ILogger
{
    public void Log() { }
}

Зауважте, що FileLogger та DataBaseLogger не потрібен інтерфейс (це може бути абстрактний базовий клас Logger). Але врахуйте, що вам потрібно використовувати сторонній реєстратор, який змушує вас використовувати базовий клас (припустимо, він відкриває захищені методи, які вам потрібно використовувати). Оскільки мова не підтримує багатократне успадкування, ви не зможете використовувати абстрактний підхід базового класу.

Підсумок: використання інтерфейсу, коли можливо, для отримання додаткової гнучкості у вашому коді. Ваша реалізація менш зав'язана, тому вона краще підходить для змін.


4

Я використовував інтерфейси час від часу і ось моє останнє використання (імена узагальнено):

У мене є маса спеціальних елементів управління на WinForm, які потребують збереження даних для мого бізнес-об’єкта. Один із підходів - викликати кожен елемент управління окремо:

myBusinessObject.Save(controlA.Data);
myBusinessObject.Save(controlB.Data);
myBusinessObject.Save(controlC.Data);

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

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

foreach(Control c in Controls)
{
  ISaveable s = c as ISaveable;

  if( s != null )
      s.SaveToBusinessObject(myBusinessObject);
}

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

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


4

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

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

Ще один практичний приклад - інтерфейс ActionListener (або Runnable). Ви реалізовували б їх, коли вам потрібно відстежувати певну подію. Тому вам потрібно надати реалізацію actionPerformed(Event e)методу у своєму класі (або підкласі). Аналогічно, для інтерфейсу Runnable ви надаєте реалізацію public void run()методу.

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

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


3
Попросіть Бога, щоб вони перестали говорити такі речі, як багаторазове успадкування щодо інтерфейсів. Ви не успадковуєте інтерфейс у класі. Ви РЕАЛІЗОВАТИ це.
Андрій Ронеа

4

Припустимо, що ви хочете сформулювати роздратування, які можуть статися під час сну.

Модель перед інтерфейсами

введіть тут опис зображення

class Mosquito {
    void flyAroundYourHead(){}
}

class Neighbour{
    void startScreaming(){}
}

class LampJustOutsideYourWindow(){
    void shineJustThroughYourWindow() {}
}

Як ви чітко бачите, що багато «речей» можуть дратувати, коли ви намагаєтесь спати.

Використання класів без інтерфейсів

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

class TestAnnoyingThings{
    void testAnnoyingThinks(Mosquito mosquito, Neighbour neighbour, LampJustOutsideYourWindow lamp){
         if(mosquito != null){
             mosquito.flyAroundYourHead();
         }
         if(neighbour!= null){
             neighbour.startScreaming();
         }
         if(lamp!= null){
             lamp.shineJustThroughYourWindow();
         }
    }
}

Модель з інтерфейсами

Щоб подолати цю проблему, ми можемо ввести її поверхнювведіть тут опис зображення

interface Annoying{
   public void annoy();

}

І реалізувати це всередині класів

class Mosquito implements Annoying {
    void flyAroundYourHead(){}

    void annoy(){
        flyAroundYourHead();
    }
}

class Neighbour implements Annoying{
    void startScreaming(){}

    void annoy(){
        startScreaming();
    }
}

class LampJustOutsideYourWindow implements Annoying{
    void shineJustThroughYourWindow() {}

    void annoy(){
        shineJustThroughYourWindow();
    }
}

Використання інтерфейсів

Що значно полегшить використання цих класів

class TestAnnoyingThings{
    void testAnnoyingThinks(Annoying annoying){
        annoying.annoy();
    }
}

Гаразд, але це не потрібно Neighbourі LampJustOutsideYourWindowтакож потрібно реалізувати Annoying?
Stardust

Так, дякую, що вказали на це. Я змінив цю зміну
Marcin Szymczak

2

Найпростіший приклад - щось на зразок процесорів платежів (Paypal, PDS тощо).

Скажімо, ви створюєте інтерфейс IPaymentProcessor, який має методи ProcessACH та ProcessCreditCard.

Тепер ви можете реалізувати конкретну реалізацію Paypal. За допомогою цих методів виклик певних функцій PayPal.

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


2

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

http://www.nmock.org/


2

Якщо ви переглянете збірки .NET Framework і перегляньте основні класи для будь-якого зі стандартних об'єктів, ви помітите багато інтерфейсів (учасників, названих як ISomeName).

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

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

КЛАСИЧНИЙ ПРИКЛАД: не реалізуючи інтерфейс IDisposable, ви не можете використовувати структуру ключових слів "using ()" у C #, оскільки це вимагає, щоб будь-який об'єкт, вказаний у якості параметра, можна неявно передати в IDisposable.

ПРИКЛАД КОМПЛЕКСУ: Більш складним прикладом може бути клас System.ComponentModel.Component. Цей клас реалізує як IDisposable, так і IComponent. Більшість, якщо не всіх, об’єктів .NET, з якими пов’язаний візуальний дизайнер, реалізують IComponent, щоб IDE змогла взаємодіяти з компонентом.

ВИСНОВОК: Коли ви більше ознайомитесь з .NET Framework, перше, що ви зробите, зіткнувшись з новим класом у браузері об’єктів або в інструменті .NET Reflector (безкоштовно) ( http://www.red-gate.com / products / reflector / ) має перевірити, від якого класу він успадковує, а також інтерфейси, які він реалізує. .NET Reflector навіть кращий, ніж браузер об’єктів, тому що він також дозволяє переглядати класи похідних. Це дозволяє дізнатися про всі об'єкти, що походять з певного класу, тим самим потенційно дізнавшись про функціональність фреймворку, про який ви не знали, що існує. Це особливо важливо, коли оновлені або нові простори імен додаються до .NET Framework.


2

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

У нас може бути інтерфейс, Gunякий визначає функцію shoot().

Нам потрібні різні підкласи Gunкласу, а саме ShotGun Sniperтощо.

ShotGun implements Gun{
    public void shoot(){
       \\shotgun implementation of shoot.
    } 
}

Sniper implements Gun{
    public void shoot(){
       \\sniper implementation of shoot.
    } 
}

Клас шутера

Стрілець має всі гармати у своєму Броні. Давайте створимо, Listщоб його представити.

List<Gun> listOfGuns = new ArrayList<Gun>();

Стрілець перебирає свої гармати, як і коли потрібно, використовуючи функцію switchGun()

public void switchGun(){
    //code to cycle through the guns from the list of guns.
    currentGun = //the next gun in the list.
}

Ми можемо встановити поточну зброю, використовуючи вищевказану функцію та просто викликаючи shoot()функцію, коли fire()викликається.

public void fire(){
    currentGun.shoot();
}

Поведінка функції зйомки змінюватиметься залежно від різних реалізацій Gunінтерфейсу.

Висновок

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

наприклад, fire()функція від Shooterкласу очікує, що зброю ( Sniper, ShotGun) реалізувати shoot()функцію. Тож якщо ми переключимо пістолет і вогонь.

shooter.switchGun();
shooter.fire();

Ми змінили поведінку fire()функції.


1

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


1

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

Хорошим прикладом цього в світі .NET є інтерфейс IDisposable , який використовується в будь-яких класах Microsoft, які використовують системні ресурси, які потрібно звільнити вручну. Він вимагає, щоб клас, що реалізує його, мав метод Dispose ().

(Метод Dispose () викликається також використанням мовної конструкції для VB.NET і C # , яка працює тільки на IDisposables)

Майте на увазі, що ви можете перевірити, чи об’єкт реалізує певний інтерфейс, використовуючи такі конструкції, як TypeOf ... Is(VB.NET), is(C #), instanceof(Java) тощо ...


1

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

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

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

Я рекомендую прочитати розділ про інтерфейси в Java Design від Coad, Mayfield та Kern. Вони пояснюють це трохи краще, ніж середній вступний текст. Якщо ви не використовуєте Java, ви можете просто прочитати початок глави, яка є лише переважно поняттями.


1

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

Тут, як завжди, є компроміс: гнучкість щодо ремонту. Який із них важливіший? Відповіді немає - це залежить від проекту. Але пам’ятайте лише, що кожен програмний продукт доведеться підтримувати ...

Тому моя порада: не використовуйте інтерфейси, поки вони вам справді не потрібні. (За допомогою Visual Studio ви можете витягти інтерфейс з існуючого класу за 2 секунди - тому не поспішайте.)

Сказавши це, коли вам потрібно створити інтерфейс?

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

І це працює: o)

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

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

З повагою, Сільвейн.


1

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

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

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

Також дивіться це пов'язане питання щодо кодування до інтерфейсу .


1

Існує так багато цілей для використання інтерфейсу.

  1. Використання в поліморфній поведінці. Там, де ви хочете викликати конкретні методи дочірнього класу з інтерфейсом, що має посилання на дочірній клас.

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

  3. Зменшити використання пам'яті, завантажуючи конкретні методи в класі. Як якщо у вас є три бізнес-об’єкти і вони реалізовані в одному класі, ви можете використовувати три інтерфейси.

Наприклад, IUser, IOrder, IOrderItem

public interface IUser()
{

void AddUser(string name ,string fname);

}

// Same for IOrder and IOrderItem
//


public class  BusinessLayer: IUser, IOrder, IOrderItem

{    
    public void AddUser(string name ,string fname)
    {
        // Do stuffs here.
    }

    // All methods from all interfaces must be implemented.

}

Якщо ви хочете лише додати користувача, зробіть це так:

IUser user = new (IUser)BusinessLayer();

// It will load  all methods into memory which are declared in the IUser interface.

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