Dlaczego nie mogę zrozumieć interfejsów?

Czy ktoś mógłby mi przedstawić jakiś dobry przykład? Ciągle widzę interfejsy wyskakujące tu i ówdzie, ale nigdy tak naprawdę nie byłem narażony na dobre wyjaśnienie interfejsów i kiedy ich używać.

Mówię o interfejsach w kontekście interfejsów a klas abstrakcyjnych.

Author: Peter Mortensen, 2008-09-23

26 answers

Interfejsy pozwalają na programowanie według "opisu" zamiast typu, co pozwala na bardziej luźne kojarzenie elementów oprogramowania.

Pomyśl o tym w ten sposób: chcesz udostępnić dane komuś w kostce obok ciebie, więc wyciągasz swój flash stick i kopiujesz / wklejasz. Idziesz obok i facet mówi: "czy to USB?"a ty mówisz tak - wszystko gotowe. Nie ma znaczenia wielkość pamięci flash, ani producenta - liczy się tylko to, że jest to USB.

W tym samym sposób, interfejsy pozwalają generalizować swój rozwój. Używając innej analogii-wyobraź sobie, że chciałeś stworzyć aplikację, która praktycznie malowała samochody. Możesz mieć taki podpis:

public void Paint(Car car, System.Drawing.Color color)...

To działało, dopóki twój Klient nie powiedział "Teraz chcę malować ciężarówki", więc możesz to zrobić: {]}

public void Paint (Vehicle vehicle, System.Drawing.Color color)...
To rozszerzy Twoją aplikację... aż twój klient powiedział: "Teraz chcę malować domy!"To, co mogłeś zrobić od samego początku, jest stworzone interfejs:
public interface IPaintable{
   void Paint(System.Drawing.Color color);
}

...i przekazałem to Twojej rutynie:

public void Paint(IPaintable item, System.Drawing.Color color){
   item.Paint(color);
}

Mam nadzieję, że to ma sens - to dość uproszczone wyjaśnienie, ale mam nadzieję, że trafi do sedna.

 78
Author: Zaid,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-05 15:56:14

Interfejsy ustanawiają umowę między klasą A kodem, który ją wywołuje. Pozwalają również na posiadanie podobnych klas, które implementują ten sam interfejs, ale wykonują różne akcje lub zdarzenia i nie muszą wiedzieć, z którymi faktycznie pracujesz. To może mieć więcej sensu jako przykład, więc pozwól mi spróbować tutaj.

Powiedzmy, że masz kilka zajęć o nazwie Pies, Kot i mysz. Każda z tych klas jest zwierzakiem i teoretycznie można je odziedziczyć z innej klasy o nazwie Pet, ale w tym problem. Zwierzęta same w sobie nic nie robią. Nie możesz iść do sklepu i kupić zwierzaka. Można iść i kupić psa lub kota, ale zwierzę jest pojęciem abstrakcyjnym, a nie konkretnym. Więc wiesz, że zwierzęta potrafią robić pewne rzeczy. Mogą spać, jeść itp. Więc definiujesz interfejs o nazwie IPet i wygląda on mniej więcej tak (składnia C#)
public interface IPet
{
    void Eat(object food);
    void Sleep(int duration);
}

Każda z klas Psów, Kotów i myszy implementuje IPet.

public class Dog : IPet
Więc teraz każda z tych klas musi mieć własna realizacja jeść i spać. Yay you have a contract... O co chodzi?

Następnie powiedzmy, że chcesz zrobić nowy obiekt o nazwie PetStore. A to nie jest bardzo dobry sklep zoologiczny, więc w zasadzie sprzedają ci losowego zwierzaka (tak Wiem, że to wymyślony przykład).

public class PetStore
{
     public static IPet GetRandomPet()
     {    
          //Code to return a random Dog, Cat, or Mouse
     } 
}

IPet myNewRandomPet = PetStore.GetRandomPet();
myNewRandomPet.Sleep(10);
Problem w tym, że nie wiesz, jaki to będzie rodzaj zwierzaka. Dzięki interfejsowi, choć wiesz, co to jest, będzie jeść i spać.

Więc ta odpowiedź może w ogóle nie być pomocna ale ogólna idea jest taka, że interfejsy pozwalają ci robić schludne rzeczy, takie jak iniekcja zależności i Inwersja sterowania, gdzie możesz uzyskać obiekt, mieć dobrze zdefiniowaną listę rzeczy, które obiekt może zrobić, nie wiedząc nawet, jaki jest konkretny typ tego obiektu.

 45
Author: JoshReedSchramm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-23 19:06:04

Najprostszą odpowiedzią jest to, że interfejsy definiują co twoja klasa może zrobić. To "kontrakt", który mówi, że twoja klasa będzie w stanie to zrobić.

Public Interface IRollOver
    Sub RollOver()
End Interface

Public Class Dog Implements IRollOver
    Public Sub RollOver() Implements IRollOver.RollOver
        Console.WriteLine("Rolling Over!")
    End Sub
End Class

Public Sub Main()
    Dim d as New Dog()
    Dim ro as IRollOver = TryCast(d, IRollOver)
    If ro isNot Nothing Then
        ro.RollOver()
    End If
End Sub

Zasadniczo gwarantujesz, że Klasa psów zawsze ma możliwość przewrócenia się tak długo, jak będzie nadal implementować ten interfejs. Jeśli koty kiedykolwiek zyskają zdolność do RollOver (), one również mogą zaimplementować ten interfejs i możesz traktować zarówno psy, jak i koty jednorodnie, prosząc je o RollOver().

 33
Author: Bob King,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-23 18:56:35

Kiedy prowadzisz samochód przyjaciela, mniej lub bardziej wiesz, jak to zrobić. Dzieje się tak dlatego, że konwencjonalne samochody mają bardzo podobny interfejs: kierownicę, pedały i tak dalej. Pomyśl o tym interfejsie jako o umowie między producentami samochodów a kierowcami. Jako kierowca (użytkownik/klient interfejsu w kategoriach oprogramowania), nie musisz uczyć się szczegółów różnych samochodów, aby móc je prowadzić: na przykład, wszystko, co musisz wiedzieć, to to, że obracanie kierownicy sprawia, że samochód skręca. Jako Producent samochodów (dostawca implementacji interfejsu w zakresie oprogramowania) masz jasne pojęcie, co powinien mieć twój nowy samochód i jak powinien się zachowywać, aby kierowcy mogli z nich korzystać bez większego dodatkowego szkolenia. Ta umowa jest tym, co ludzie w projektowaniu oprogramowania określają jako oddzielenie (użytkownik od dostawcy) - kod klienta jest pod względem korzystania z interfejsu, a nie konkretnej jego implementacji i dlatego nie musi znać szczegółów obiektów implementujących interfejs.

 16
Author: Alexander,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-23 18:51:34

Interfejsy są mechanizmem zmniejszającym sprzężenie między różnymi, prawdopodobnie różnymi częściami systemu.

From a . NET perspective

  • definicja interfejsu jest listą operacji i / lub właściwości.
  • metody interfejsu są zawsze publiczne.
  • [21]}sam interfejs nie musi być publiczny.

Kiedy tworzysz klasę, która implementuje interfejs, musisz podać jawne lub niejawne implementacja wszystkich metod i właściwości zdefiniowanych przez interfejs.

Co więcej,. NET ma tylko pojedyncze dziedziczenie, a interfejsy są koniecznością, aby obiekt ujawniał metody innym obiektom, które nie są świadome lub leżą poza jego hierarchią klas. Jest to również znane jako ujawnianie zachowań.

Przykład, który jest trochę bardziej konkretny:

Rozważmy, że mamy wiele DTO (data transfer objects), które mają właściwości dla tego, kto ostatnio zaktualizował i kiedy to było. Na problem w tym, że nie wszystkie DTO mają tę właściwość, ponieważ nie zawsze jest to istotne.

Jednocześnie chcemy, aby ogólny mechanizm gwarantował, że te właściwości są ustawione, jeśli są dostępne podczas przesyłania do obiegu pracy, ale obiekt workflow powinien być luźno powiązany z przesyłanymi obiektami. tzn. metoda submit workflow nie powinna znać wszystkich subtelności każdego obiektu, a wszystkie obiekty w obiegu pracy niekoniecznie są obiektami DTO.

// First pass - not maintainable
void SubmitToWorkflow(object o, User u)
{
  if (o is StreetMap)
  {
     var map = (StreetMap)o;
     map.LastUpdated = DateTime.UtcNow;
     map.UpdatedByUser = u.UserID;
  }
  else if (o is Person)
  {
     var person = (Person)o;
     person.LastUpdated = DateTime.Now; // Whoops .. should be UtcNow
     person.UpdatedByUser = u.UserID;
  }
  // Whoa - very unmaintainable.

W kodzie powyżej {[10] } musi wiedzieć o każdym przedmiocie. Dodatkowo, kod jest bałaganem z jednym potężnym if / else / switch, narusza zasadę don ' t repeat yourself (DRY) i wymaga od programistów zapamiętania zmian kopiuj/wklej za każdym razem, gdy nowy obiekt jest dodawany do systemu.

// Second pass - brittle
void SubmitToWorkflow(object o, User u)
{
  if (o is DTOBase)
  {
     DTOBase dto = (DTOBase)o;
     dto.LastUpdated = DateTime.UtcNow;
     dto.UpdatedByUser = u.UserID;
  }
Jest nieco lepszy, ale wciąż kruchy. Jeśli chcemy przesłać inne typy obiektów, potrzebujemy jeszcze więcej poleceń case. itd.
// Third pass pass - also brittle
void SubmitToWorkflow(DTOBase dto, User u)
{
  dto.LastUpdated = DateTime.UtcNow;
  dto.UpdatedByUser = u.UserID;

Jest nadal kruchy, a oba metody nakładają ograniczenie, że wszystkie Dto muszą zaimplementować tę właściwość, która nie była powszechnie stosowana. Niektórzy programiści mogą pokusić się o pisanie metod do-nothing, ale to źle pachnie. Nie chcemy, aby klasy udawały, że wspierają Śledzenie aktualizacji, ale tego nie robią.]}

Interfejsy, jak mogą pomóc?

Jeśli zdefiniujemy bardzo prosty interfejs:

public interface IUpdateTracked
{
  DateTime LastUpdated { get; set; }
  int UpdatedByUser { get; set; }
}

Każda klasa, która potrzebuje automatycznego śledzenia aktualizacji, może zaimplementować interfejs.

public class SomeDTO : IUpdateTracked
{
  // IUpdateTracked implementation as well as other methods for SomeDTO
}

Metoda workflow może być o wiele bardziej ogólna, mniejsza i łatwiejsza do utrzymania i będzie nadal działać bez względu na to, ile klas implementuje interfejs (DTOs lub w inny sposób), ponieważ zajmuje się tylko interfejsem.

void SubmitToWorkflow(object o, User u)
{
  IUpdateTracked updateTracked = o as IUpdateTracked;
  if (updateTracked != null)
  {
     updateTracked.LastUpdated = DateTime.UtcNow;
     updateTracked.UpdatedByUser = u.UserID;
  }
  // ...
  • możemy zauważyć, że odmiana void SubmitToWorkflow(IUpdateTracked updateTracked, User u) gwarantowałaby bezpieczeństwo typu, jednak nie wydaje się to istotne w tych okolicznościach.

W niektórych kodach produkcyjnych, których używamy, mamy generowanie kodu do tworzenia te klasy DTO z definicji bazy danych. Jedyne, co robi programista, to musi poprawnie utworzyć nazwę pola i udekorować klasę interfejsem. Tak długo, jak właściwości są nazywane LastUpdated i UpdatedByUser, to po prostu działa.

Może pytasz co się stanie, jeśli moja baza danych jest dziedziczona, a to nie jest możliwe? po prostu trzeba trochę więcej pisać; kolejną świetną cechą interfejsów jest to, że mogą one pozwolić na stworzenie pomostu między klasy.

W poniższym kodzie mamy fikcyjny LegacyDTO, istniejący wcześniej obiekt o podobnych nazwach pól. Implementuje interfejs IUpdateTracked, aby pomostować istniejące, ale inaczej nazwane właściwości.

// Using an interface to bridge properties
public class LegacyDTO : IUpdateTracked
{
    public int LegacyUserID { get; set; }
    public DateTime LastSaved { get; set; }

    public int UpdatedByUser
    {
        get { return LegacyUserID; }
        set { LegacyUserID = value; }
    }
    public DateTime LastUpdated
    {
        get { return LastSaved; }
        set { LastSaved = value; }
    }
}

Możesz coś fajne, ale czy nie jest mylące posiadanie wielu właściwości? lub co się stanie, jeśli te właściwości już istnieją, ale oznaczają coś innego?. Net daje możliwość jawnego wdrożenia interfejs.

Oznacza to, że właściwości IUpdateTracked będą widoczne tylko wtedy, gdy używamy odniesienia do IUpdateTracked. Zauważ, że w deklaracji nie ma publicznego modyfikatora, a deklaracja zawiera nazwę interfejsu.

// Explicit implementation of an interface
public class YetAnotherObject : IUpdatable
{
    int IUpdatable.UpdatedByUser
    { ... }
    DateTime IUpdatable.LastUpdated
    { ... }

Posiadanie tak dużej elastyczności w definiowaniu, w jaki sposób klasa implementuje interfejs, daje deweloperowi dużo swobody w oddzielaniu obiektu od metod, które go wykorzystują. Interfejsy to świetny sposób na złamanie sprzęgło.

W interfejsach jest o wiele więcej niż tylko to. Jest to tylko uproszczony przykład, który wykorzystuje jeden z aspektów programowania opartego na interfejsach.

Jak wspomniałem wcześniej, i przez innych respondentów, można tworzyć metody, które przyjmują i / lub zwracają odwołania do interfejsu, a nie konkretne odniesienia do klasy. Jeśli muszę znaleźć duplikaty na liście, mogę napisać metodę, która pobiera i zwraca IList (interfejs definiujący operacje działające na listach) i nie jestem ograniczony do konkretnych zajęć z kolekcji.

// Decouples the caller and the code as both
// operate only on IList, and are free to swap
// out the concrete collection.
public IList<T> FindDuplicates( IList<T> list )
{
    var duplicates = new List<T>()
    // TODO - write some code to detect duplicate items
    return duplicates;
}

Zastrzeżenie wersji

Jeśli jest to publiczny interfejs, deklarujesz gwarantuję, że interfejs x wygląda tak! i po wysłaniu kodu i opublikowaniu interfejsu, nigdy nie powinieneś go zmieniać. Jak tylko kod konsumenta zaczyna polegać na tym interfejsie, nie chcesz łamać ich kodu w polu.

Zobacz ten Haacked post {[18] } dla dobrej dyskusji.

Interfejsy kontra klasy abstrakcyjne (bazowe)

Klasy abstrakcyjne mogą zapewnić implementację, podczas gdy Interfejsy nie mogą. Klasy abstrakcyjne są pod pewnymi względami bardziej elastyczne w aspekcie wersjonowania, jeśli zastosujesz się do pewnych wytycznych, takich jak wzorzec Nvpi (Non-Virtual Public Interface).

Warto przypomnieć, że w. NET klasa może dziedziczyć tylko z jednej klasy, ale klasa może zaimplementować tyle interfejsów, ile chce.

Dependency Injection

Szybkie podsumowanie interfaces and dependency injection (DI) polega na tym, że użycie interfejsów umożliwia programistom pisanie kodu, który jest zaprogramowany na interfejsie w celu świadczenia usług. W praktyce można skończyć z wieloma małymi interfejsami i małymi klasami, a jedną z idei jest to, że małe klasy, które robią jedną rzecz i tylko jedną rzecz, są znacznie łatwiejsze w kodowaniu i utrzymaniu.

class AnnualRaiseAdjuster
   : ISalaryAdjuster
{
   AnnualRaiseAdjuster(IPayGradeDetermination payGradeDetermination) { ...  }

   void AdjustSalary(Staff s)
   {
      var payGrade = payGradeDetermination.Determine(s);
      s.Salary = s.Salary * 1.01 + payGrade.Bonus;
   }
}

W skrócie, korzyść pokazana w powyższym fragmencie polega na tym, że określenie klasy płac jest właśnie dodawane do rocznej podwyżki / align = "left" / To, w jaki sposób ustalana jest klasa wynagrodzenia, nie ma znaczenia dla tej klasy. Podczas testowania programista może naśmiewać się z wyników określania poziomu wynagrodzeń, aby zapewnić, że regulator Wynagrodzeń działa zgodnie z życzeniem. Testy są również szybkie, ponieważ test testuje tylko klasę, A nie wszystko inne.

Nie jest to jednak podkład DI, ponieważ są całe książki poświęcone temu tematowi; powyższy przykład jest bardzo uproszczony.

 14
Author: Robert Paulson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-12-13 22:39:45

Jest to raczej "długi" temat, ale spróbuję to uprościć.

[4]}interfejs jest - jak to" nazywają " - kontraktem. Ale zapomnij o tym słowie.

Najlepszym sposobem na ich zrozumienie jest jakiś pseudokodowy przykład. Tak rozumiałem ich dawno temu.

Załóżmy, że masz aplikację, która przetwarza wiadomości. Wiadomość zawiera pewne rzeczy, takie jak temat, tekst itp.

Więc piszesz swój MessageController, aby odczytać bazę danych i rozpakowujesz wiadomości. To bardzo miłe, dopóki nagle nie usłyszysz, że Faksy zostaną wkrótce wdrożone. Więc teraz będziesz musiał przeczytać "Faksy" i przetwarzać je jako wiadomości!

To może łatwo zmienić się w kod Spagettiego. Więc to, co robisz zamiast mieć MessageController niż steruje tylko "wiadomościami", sprawiasz, że jest on w stanie pracować z interfejsem o nazwie IMessage (I jest po prostu powszechnym użyciem, ale nie jest wymagane).

Twój interfejs IMessage zawiera podstawowe dane, których potrzebujesz aby upewnić się, że jesteś w stanie przetworzyć wiadomość jako taką.

Więc kiedy tworzysz swoje klasy E-mail, Fax, PhoneCall, sprawiasz, że zaimplementujesz interfejs o nazwie IMessage .

Więc w MessageController możesz mieć metodę o nazwie Tak:

private void ProcessMessage(IMessage oneMessage)
{
    DoSomething();
}

Gdybyś nie używał interfejsów, musiałbyś mieć:

private void ProcessEmail(Email someEmail);
private void ProcessFax(Fax someFax);
etc.

Tak więc, używając wspólnego interfejsu , upewniłeś się, że metoda ProcessMessage będzie możliwość pracy z nim, bez względu na to, czy był to Faks, E-mail, telefon itp.

Dlaczego lub jak ?

Ponieważ interfejs jest kontrakt, który określa pewne rzeczy, które musisz przestrzegać (lub wdrożyć), aby móc z niego korzystać. Pomyśl o tym jak o odznace . Jeśli twój obiekt " Fax " nie ma interfejsu IMessage, wtedy twoja metoda ProcessMessage nie będzie mogła z tym pracować, spowoduje to wyświetlenie nieprawidłowego typu, ponieważ przekazujesz Faks do metody, która oczekuje obiektu IMessage.

Widzisz w czym rzecz?

Pomyśl o interfejsie jako o "podzbiorze" dostępnych metod i właściwości, pomimo rzeczywistego typu obiektu. Jeśli oryginalny obiekt (Faks, E-mail, telefon, itp.) implementuje ten interfejs, można bezpiecznie przekazać go metodom, które tego potrzebują.

Jest tam więcej magii, możesz rzucić interfejsy z powrotem do ich oryginalnych obiektów:

Fax myFax = (Fax)SomeIMessageThatIReceive;

ArrayList() w. NET 1.1 miał ładny interfejs o nazwie IList. Jeśli masz IList (bardzo "ogólny"), możesz przekształcić go w ArrayList:

ArrayList ar = (ArrayList)SomeIList;
I są tysiące próbek na wolności.

Interfejsy takie jak ISortable, IComparable, itp., zdefiniuj metody i właściwości, które musisz zaimplementować w swojej klasie, aby osiągnąć tę funkcjonalność.

Aby rozszerzyć naszą próbkę, możesz mieć listę z E-maile, Faks, Telefon, wszystko na tej samej liście, jeśli typ jest IMessage, ale nie można mieć je wszystkie razem, jeśli obiekty były po prostu e-mail, faks, itp.

Jeśli chcesz posortować (lub wyliczyć na przykład) swoje obiekty, potrzebujesz ich do zaimplementowania odpowiedniego interfejsu. W próbce.NET, jeśli masz listę obiektów "Fax" i chcesz być w stanie sortować za pomocą MyList.Sort (), you need to make your fax class like this:

public class Fax : ISorteable 
{
   //implement the ISorteable stuff here.
}

Mam nadzieję, że to to podpowiedź. Inni użytkownicy będą ewentualnie publikować inne dobre przykłady. Powodzenia! i wykorzystaj moc interfejsów.

Warning : nie wszystko jest dobre w interfejsach, są niektóre problemy z nimi, puryści OOP zaczną wojnę w tej sprawie. Zostanę z boku. Jedną z wad interfejsu (przynajmniej w. NET 2.0) jest to, że nie można mieć prywatnych członków, lub chronione, to musi {[20] } być publiczne. To ma jakiś sens, ale czasami chciałbyś po prostu zadeklaruj rzeczy jako prywatne lub chronione.

 6
Author: Martin Marconcini,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-23 18:57:13

Oprócz funkcji, jakie interfejsy mają w językach programowania, są również potężnym narzędziem semantycznym przy wyrażaniu pomysłów projektowych innym osobom.

Baza kodu z dobrze zaprojektowanymi interfejsami jest nagle o wiele łatwiejsza do omówienia. "Tak, potrzebujesz CredentialsManager, aby zarejestrować nowe zdalne serwery.""Przekaż mapę właściwości do ThingFactory, aby uzyskać działającą instancję."

Umiejętność rozwiązywania złożonych rzeczy jednym słowem jest bardzo przydatna.

 5
Author: Internet Friend,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-23 19:01:44

Interfejsy pozwalają na kodowanie obiektów w sposób ogólny. Na przykład, powiedzmy, że masz metodę, która wysyła raporty. Teraz powiedz, że masz nowy wymóg, który pojawia się tam, gdzie musisz napisać nowy raport. Byłoby miło, gdybyś mógł ponownie użyć metody, którą już napisałeś, prawda? Interfejsy ułatwiają:

interface IReport
{
    string RenderReport();
}

class MyNewReport : IReport
{
    public string RenderReport()
    {
        return "Hello World Report!";

    }
}

class AnotherReport : IReport
{
    public string RenderReport()
    {
        return "Another Report!";

    }
}

//This class can process any report that implements IReport!
class ReportEmailer()
{
     public void EmailReport(IReport report)
     {
         Email(report.RenderReport());
     }
}

class MyApp()
{
    void Main()
    {
        //create specific "MyNewReport" report using interface
        IReport newReport = new MyNewReport();

        //create specific "AnotherReport" report using interface
        IReport anotherReport = new AnotherReport();

        ReportEmailer reportEmailer = new ReportEmailer();

        //emailer expects interface
        reportEmailer.EmailReport(newReport);
        reportEmailer.EmailReport(anotherReport);



    }

}
 4
Author: Giovanni Galbo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-23 19:00:49

Interfejsy są również kluczowe dla polimorfizmu, jednego z"trzech filarów OOD".

Niektórzy poruszyli to powyżej, polimorfizm oznacza po prostu, że dana klasa może przybierać różne "formy". Oznacza to, że jeśli mamy dwie klasy, "pies" i " Kot "i obie implementują interfejs "INeedFreshFoodAndWater" (hehe) - Twój kod może zrobić coś takiego (pseudocode): {]}

INeedFreshFoodAndWater[] array = new INeedFreshFoodAndWater[];
array.Add(new Dog());
array.Add(new Cat());

foreach(INeedFreshFoodAndWater item in array)
{
   item.Feed();
   item.Water();
}

Jest to potężne, ponieważ pozwala na abstrakcyjne traktowanie różnych klas obiektów i pozwala robić rzeczy jak zrobić swoje obiekty luźniej połączone, itp.

 4
Author: Sam Schutte,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-23 19:09:28

OK, więc chodzi o klasy abstrakcyjne vs. interfejsy...

Koncepcyjnie klasy abstrakcyjne są używane jako klasy bazowe. Często same dostarczają już jakąś podstawową funkcjonalność, a podklasy muszą zapewnić własną implementację metod abstrakcyjnych (są to metody, które nie są implementowane w klasie abstrakcyjnej bazowej).

Interfejsy są najczęściej używane do oddzielenia kodu klienta od szczegółów konkretnej implementacji. Również, czasami możliwość zmiany implementacji bez zmiany kodu klienta sprawia, że kod klienta jest bardziej ogólny.

Na poziomie technicznym trudniej jest wyznaczyć granicę między klasami abstrakcyjnymi a interfejsami, ponieważ w niektórych językach (np. C++) nie ma różnicy w składni, lub dlatego, że można również użyć klas abstrakcyjnych w celu oddzielenia lub uogólnienia. Użycie klasy abstrakcyjnej jako interfejsu jest możliwe, ponieważ każda klasa bazowa z definicji definiuje interfejs, który wszystkie jego podklasy powinny honorować (tzn. powinno być możliwe użycie podklasy zamiast klasy bazowej).

 3
Author: Alexander,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-23 19:06:54

Interfejsy są sposobem wymuszania, że obiekt implementuje pewną ilość funkcjonalności, bez konieczności stosowania dziedziczenia (co prowadzi do silnie sprzężonego kodu, zamiast luźno sprzężonego, co można osiągnąć poprzez użycie interfejsów).

Interfejsy opisują funkcjonalność, a nie implementację.

 2
Author: Darren Kopp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-23 19:02:37

Większość interfejsów, z którymi się spotykasz, to zbiór podpisów metod i właściwości. Każdy, kto implementuje interfejs musi podać definicje tego, co kiedykolwiek jest w interfejsie.

 2
Author: Arcturus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-22 22:50:08

Mówiąc najprościej: interfejs jest klasą, która definiuje metody, ale nie ma w nich implementacji. W przeciwieństwie do klasy abstrakcyjnej niektóre metody zaimplementowane, ale nie wszystkie.

 1
Author: Spoike,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-23 18:50:14

Pomyśl o interfejsie jak o kontrakcie. Kiedy klasa implementuje interfejs, zasadniczo zgadza się przestrzegać warunków tej umowy. Jako konsument dbasz tylko o to, aby przedmioty, które posiadasz, mogły wykonywać swoje obowiązki umowne. Ich wewnętrzne funkcjonowanie i szczegóły nie są ważne.

 1
Author: Kevin Pang,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-23 18:55:24

Jednym z dobrych powodów używania interfejsu A klasy abstrakcyjnej w Javie jest to, że podklasa nie może rozszerzyć wielu klas bazowych, ale może zaimplementować wiele interfejsów.

 1
Author: Eric Asberry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-23 19:30:28

Java nie pozwala na wielokrotne dziedziczenie (z bardzo dobrych powodów, poszukaj Diamond), ale co jeśli chcesz, aby Twoja klasa dostarczała kilka zestawów zachowań? Powiedz, że chcesz, aby każdy, kto go używa, wiedział, że może być serializowany, a także, że może malować się na ekranie. odpowiedzią jest zaimplementowanie dwóch różnych interfejsów.

Ponieważ interfejsy nie zawierają implementacji własnych i żadnych członków instancji, można bezpiecznie zaimplementować kilka z nich w tej samej klasie bez niejasności.

Minusem jest to, że będziesz musiał mieć implementację w każdej klasie osobno. Więc jeśli twoja hierarchia jest prosta i istnieją części implementacji, które powinny być takie same dla wszystkich klas dziedziczących, Użyj klasy abstrakcyjnej.

 1
Author: user21334,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-23 19:39:40

Zakładając, że odnosisz się do interfejsów w statycznie typowanych językach obiektowych, głównym zastosowaniem jest twierdzenie, że twoja klasa przestrzega określonego kontraktu lub protokołu.

Powiedz, że masz:

public interface ICommand
{
    void Execute();
}
public class PrintSomething : ICommand
{
    OutputStream Stream { get; set; }
    String Content {get; set;}
    void Execute()
    { 
        Stream.Write(content);
    }
}

Teraz masz zastępowalną strukturę poleceń. Każda instancja klasy, która implementuje IExecute może być przechowywana na liście jakiegoś rodzaju, powiedzieć coś, co implementuje IEnumerable i można ją zapętlić i wykonać każdą z nich, wiedząc, że każdy obiekt po prostu zrobi Słusznie. Możesz utworzyć polecenie kompozytowe, implementując CompositeCommand, które będzie miało własną listę poleceń do uruchomienia, lub LoopingCommand, aby wielokrotnie uruchamiać zestaw poleceń, a następnie będziesz mieć większość prostego interpretera.

Kiedy możesz zredukować zestaw obiektów do zachowania, które wszystkie mają ze sobą wspólnego, możesz mieć powód do wyodrębnienia interfejsu. Również czasami można użyć interfejsów, aby zapobiec przypadkowemu wtargnięciu obiektów na obawy danej klasy; dla na przykład można zaimplementować interfejs, który pozwala klientom tylko na pobieranie, a nie na zmianę danych w obiekcie, a większość obiektów otrzymuje tylko odniesienie do interfejsu pobierania.

Interfejsy działają najlepiej, gdy interfejsy są stosunkowo proste i mają niewiele założeń.

Przyjrzyj się zasadzie substytucji Liskowa, aby nadać temu większy sens.

Niektóre języki statycznie typowane, takie jak C++, nie obsługują interfejsów jako koncepcji pierwszej klasy, więc tworzysz interfejsy wykorzystujące Czyste klasy abstrakcyjne.

Update Ponieważ wydaje ci się, że pytasz o klasy abstrakcyjne vs. interfejsy, oto moje preferowane uproszczenie:

  • interfejsy definiują możliwości i funkcje.
  • klasy abstrakcyjne definiują podstawową funkcjonalność.

Zazwyczaj wykonuję refaktoryzację interfejsu wyodrębniania, zanim zbuduję klasę abstrakcyjną. Jestem bardziej skłonny zbudować klasę abstrakcyjną, jeśli uważam, że powinien być kontrakt Twórczy (w szczególności, że określony typ konstruktora powinien być zawsze obsługiwany przez podklasy). Jednak rzadko używam "czystych" klas abstrakcyjnych w C# / java. Jestem o wiele bardziej skłonny zaimplementować klasę z co najmniej jedną metodą zawierającą znaczące zachowanie i używać abstrakcyjnych metod do wspierania metod szablonów wywoływanych przez tę metodę. Wtedy klasa abstrakcyjna jest podstawową implementacją zachowania, z której wszystkie konkretne podklasy mogą skorzystać bez konieczności ponownego implementacji.

 1
Author: JasonTrue,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-23 20:29:04

Prosta odpowiedź: interfejs jest zbiorem podpisów metod (+Typ return). Kiedy obiekt mówi, że implementuje interfejs, wiesz, że ujawnia ten zestaw metod.

 1
Author: David,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-23 20:42:59

Interfejsy są sposobem implementacji konwencji w sposób, który jest nadal silnie typowany i polimorficzny.

Dobrym prawdziwym przykładem może być IDisposable w. NET. A klasa, która implementuje interfejs IDisposable zmusza tę klasę do zaimplementowania metody Dispose (). Jeśli Klasa Nie zaimplementuje metody Dispose (), podczas próby budowania pojawi się błąd kompilatora. Dodatkowo ten wzór kodu:

using (DisposableClass myClass = new DisposableClass())
  {
  // code goes here
  }

Spowoduje myClass.Dispose() do wykonania automatycznie po zakończeniu wykonania wewnętrzny blok.

Jednakże, i to jest ważne, nie ma żadnego egzekwowania, co powinna zrobić twoja metoda Dispose (). Możesz poprosić swoją metodę Dispose() o wybranie losowych przepisów z pliku i wysłanie ich na listę dystrybucyjną, kompilator ma to gdzieś. Intencją wzorca IDisposable jest ułatwienie czyszczenia zasobów. Jeśli instancje klasy będą trzymać się uchwytów plików, IDisposable bardzo ułatwia centralizację kodu dealokacji i czyszczenia w jednym miejscu i promowanie stylu użytkowania, który zapewnia, że dealokacja zawsze występuje.

I to jest klucz do interfejsów. Są one sposobem na usprawnienie konwencji programowania i wzorców projektowych. Który, jeśli jest używany poprawnie, Promuje prostszy, samodokumentujący się kod, który jest łatwiejszy w użyciu, łatwiejszy w utrzymaniu i bardziej poprawny.

 1
Author: Wedge,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-23 21:04:08

Oto przykład związany z db, którego często używam. Powiedzmy, że masz obiekt i obiekt kontenera, taki jak lista. Załóżmy, że kiedyś będziesz chciał przechowywać obiekty w określonej sekwencji. Załóżmy, że sekwencja nie jest związana z pozycją w tablicy, ale zamiast tego, że obiekty są podzbiorem większego zestawu obiektów, a pozycja sekwencji jest związana z filtrowaniem bazy danych SQL.

Aby śledzić swoje niestandardowe pozycje sekwencji, które możesz zrobić Twój obiekt implementuje niestandardowy interfejs. Niestandardowy interfejs może pośredniczyć w wysiłku organizacyjnym wymaganym do utrzymania takich sekwencji.

Na przykład Sekwencja, którą jesteś zainteresowany, nie ma nic wspólnego z kluczami głównymi w rekordach. Dzięki obiektowi implementującemu interfejs można powiedzieć myObject.next() lub myObject.prev ().

 1
Author: fooledbyprimes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-25 05:43:35

Miałem ten sam problem co ty i uważam Wyjaśnienie "umowy" za nieco mylące.

Jeśli określisz, że metoda przyjmuje interfejs IEnumerable jako parametr in, możesz powiedzieć, że jest to umowa określająca, że parametr musi być typu, który dziedziczy z interfejsu IEnumerable i dlatego obsługuje wszystkie metody określone w interfejsie IEnumerable. To samo byłoby prawdą, gdybyśmy użyli klasy abstrakcyjnej lub klasy normalnej. Każdy obiekt, który dziedziczy z te klasy byłyby w porządku, aby przekazać jako parametr. W każdym razie można powiedzieć, że dziedziczony obiekt obsługuje wszystkie publiczne metody w klasie bazowej, niezależnie od tego, czy klasa bazowa jest klasą normalną, klasą abstrakcyjną czy interfejsem.

Klasa abstrakcyjna ze wszystkimi abstrakcyjnymi metodami jest zasadniczo taka sama jak interfejs, więc można powiedzieć, że interfejs jest po prostu klasą bez zaimplementowanych metod. Można faktycznie zrzucić interfejsy z języka i po prostu użyć klasy abstrakcyjnej z zamiast tego tylko metody abstrakcyjne. Myślę, że powodem, dla którego je oddzielamy, są powody semantyczne, ale z powodów kodowania nie widzę powodu i uważam, że jest to po prostu mylące.

Inną sugestią może być zmiana nazwy interfejsu na interface class, ponieważ interfejs jest tylko kolejną odmianą klasy.

W niektórych językach istnieją subtelne różnice, które pozwalają klasie dziedziczyć tylko 1 klasę, ale wiele interfejsów, podczas gdy w innych można mieć wiele obu, ale to jest inny problem i nie bezpośrednio związane myślę

 1
Author: terjetyl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-05 16:47:32

Najprostszym sposobem na zrozumienie interfejsów jest rozważenie, co oznacza dziedziczenie klas. Obejmuje dwa aspekty:

  1. Członkowie klasy pochodnej mogą używać publicznych lub chronionych członków klasy bazowej jako własnych.
  2. Członkowie klasy pochodnej mogą być używane przez kod, który oczekuje członka klasy bazowej (co oznacza, że są zastępowalne).

Obie te funkcje są przydatne, ale dlatego, że trudno jest pozwolić klasie na używanie członków więcej wiele języków i frameworków pozwala klasom dziedziczyć tylko z jednej klasy bazowej. Z drugiej strony, nie ma szczególnych trudności z posiadaniem klasy zastępowalnej dla wielu innych niepowiązanych rzeczy.

Ponadto, ponieważ pierwsza korzyść dziedziczenia może być w dużej mierze osiągnięta poprzez enkapsulację, względna korzyść z zezwolenia na dziedziczenie wielokrotne pierwszego typu jest nieco ograniczona. Z drugiej strony, możliwość zastąpienia przedmiotu za wiele niepowiązanych typów rzeczy jest przydatną umiejętnością, której nie można łatwo osiągnąć bez wsparcia językowego.

Interfejsy zapewniają sposób, za pomocą którego język / framework może pozwolić programom korzystać z drugiego aspektu dziedziczenia dla wielu typów bazowych, bez konieczności dostarczania pierwszego.

 1
Author: supercat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-06 15:16:21

Interfejs jest jak w pełni abstrakcyjna klasa. Oznacza to, że klasa abstrakcyjna ma tylko elementy abstrakcyjne. Możesz również zaimplementować wiele interfejsów, to jak dziedziczenie z wielu w pełni abstrakcyjnych klas. W każdym razie.. to wyjaśnienie pomaga tylko wtedy, gdy rozumiesz, czym jest klasa abstrakcyjna.

 1
Author: Raz Megrelidze,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-07 05:01:56

Tak jak inni tutaj mówili, interfejsy definiują umowę (jak Klasy korzystające z interfejsu będą "wyglądały"), A klasy abstrakcyjne definiują współdzieloną funkcjonalność.

Zobaczmy, czy kod pomoże:

public interface IReport
{
    void RenderReport(); // This just defines the method prototype
}

public abstract class Reporter
{
    protected void DoSomething()
    {
        // This method is the same for every class that inherits from this class
    }
}

public class ReportViolators : Reporter, IReport
{
    public void RenderReport()
    {
        // Some kind of implementation specific to this class
    }
}

public class ClientApp
{
    var violatorsReport = new ReportViolators();

    // The interface method
    violatorsReport.RenderReport();

    // The abstract class method
    violatorsReport.DoSomething();
}
 1
Author: Robert S.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-12-13 22:31:05

Interfejsy wymagają, aby każda klasa, która je implementuje, zawierała metody zdefiniowane w interfejsie.

Celem jest to, że bez konieczności oglądania kodu w klasie, możesz wiedzieć, czy można go użyć do określonego zadania. Na przykład, Klasa Integer w Javie implementuje porównywalny interfejs, więc jeśli widziałeś tylko nagłówek metody (public class String implementuje porównywalne), wiedziałbyś, że zawiera ona metodę compareTo ().

 0
Author: cblades,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-23 16:09:31

W Twoim prostym przypadku, możesz osiągnąć coś podobnego do tego, co otrzymujesz z interfejsami, używając wspólnej klasy bazowej, która implementuje show() (lub może definiuje ją jako abstrakcję). Pozwól, że zmienię Twoje nazwy rodzajowe na bardziej konkretne, Eagle i Hawk zamiast MyClass1 i MyClass2 . W takim przypadku możesz napisać kod w stylu

Bird bird = GetMeAnInstanceOfABird(someCriteriaForSelectingASpecificKindOfBird);
bird.Fly(Direction.South, Speed.CruisingSpeed);

, który pozwala pisać kod, który może obsłużyć wszystko, co jest ptak . Możesz wtedy napisać kod, który powoduje, że ptak robi swoje (lata, je, składa jaja, itd.), który działa na instancję, którą traktuje jako ptak. Ten kod zadziała, czy Bird jest naprawdę Eagle, Hawk , lub cokolwiek innego, co wywodzi się z Bird .

Ten paradygmat zaczyna się jednak bałagan, gdy nie masz prawdziwegojest związek. Powiedz, że chcesz napisać kod, który lata po niebie. Jeśli napiszesz ten kod aby zaakceptować Bird base class, nagle staje się trudne, aby ewoluować ten kod do pracy najumbojet instancja, ponieważ o ileBird i JumboJet mogą z pewnością latać, o tyle {3]} JumboJet {4]}z pewnością nie jest {3]} Bird {4]}.

Wprowadź interfejs.

What Bird (and Eagle , and Hawk) do mają wspólnego jest to, że wszystkie mogą latać. Jeśli napiszesz powyższy kod zamiast działać na interfejsie, IFly , to kod może być stosowany do wszystkiego, co zapewnia implementację do tego interfejsu.

 0
Author: Eric J.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-12-13 22:43:22