Dlaczego używać interfejsów, dziedziczenie wielokrotne vs Interfejsy, korzyści z interfejsów?

Nadal mam pewne zamieszanie co do tej sprawy. To co do tej pory znalazłem to

(podobne pytania zostały już zadane tutaj, ale miałem kilka innych punktów.)

  1. Interfejs jest zbiorem tylko abstrakcyjnych metod i pól końcowych.

  2. W Javie nie ma dziedziczenia wielokrotnego.

  3. Interfejsy mogą być używane do osiągnięcia wielokrotnego dziedziczenia w Javie.

  4. Jednym z mocnych punktów dziedziczenia jest to, że możemy użyj kodu klasy bazowej w klasie pochodnej, nie zapisując go ponownie. Być może jest to najważniejsza rzecz dla dziedziczenia.

Teraz..

Q1. Skoro interfejsy mają tylko abstrakcyjne metody (bez kodu), to jak możemy powiedzieć, że jeśli implementujemy jakikolwiek interfejs, to jest to dziedziczenie ? Nie używamy jego kodu.

Q2. Jeśli implementacja interfejsu nie jest dziedziczeniem, to jak interfejsy są używane do osiągnięcia wielokrotnego dziedziczenia ?

Q3. Jakie są korzyści z korzystania z interfejsów ? Nie mają żadnego kodu. Musimy pisać kod ponownie i ponownie we wszystkich klasach, które implementujemy.

To po co tworzyć interfejsy ?

Uwaga: znalazłem jeden przypadek, w którym interfejsy są pomocne. Jednym z jej przykładów jest jak w interfejsie Runnable mamy publiczną metodę void run (), w której definiujemy funkcjonalność wątku i tam jest wbudowane w kodowanie, że ta metoda będzie uruchamiana jako osobny wątek. Więc musimy tylko kodować co robić w wątku, reszta jest wstępnie zdefiniowana. Ale to również można osiągnąć za pomocą klas abstrakcyjnych i w ogóle.

Jakie są dokładnie korzyści z korzystania z interfejsów? Czy to naprawdę dziedziczenie wielokrotne, które osiągamy za pomocą interfejsów?

Author: Ravindra babu, 2011-12-16

11 answers

Q1. Skoro interfejsy mają tylko abstrakcyjne metody (bez kodu), to jak możemy powiedzieć, że jeśli implementujemy jakikolwiek interfejs, to jest to dziedziczenie ? Nie używamy jego kodu.

Nie możemy. Interfejsy nie są używane do osiągnięcia wielokrotnego dziedziczenia. Zastępują ją bezpieczniejszą, choć nieco słabszą konstrukcją. Zwróć uwagę na słowo kluczowe implements zamiast extends.

Q2. Jeśli implementacja interfejsu nie jest dziedziczeniem, to jak interfejsy są używane do osiągnąć wielokrotne dziedziczenie ?

Nie są. Z interfejsami jedna klasa może mieć kilka " views ", różne API lub możliwości. Np. klasa może być Runnable i Callable w tym samym czasie, podczas gdy obie metody skutecznie robią to samo.

Q3. Jakie są korzyści z korzystania z interfejsów ? Nie mają żadnego kodu. Musimy pisać kod ponownie i ponownie we wszystkich klasach, które implementujemy.

Interfejsy to rodzaj dziedziczenie wielokrotne bez żadnych problemów, które to ostatnie wprowadza (jak problem z diamentem).

Istnieje kilka przypadków użycia interfejsów:

  1. Obiekt posiada dwie tożsamości: a Tank jest ZARÓWNO a Vehicle jak i a Weapon. Można użyć instancji Tank, w której oczekiwana jest pierwsza lub druga (polimorfizm). Rzadko zdarza się to w prawdziwym życiu i jest to w rzeczywistości ważny przykład, w którym lepsze byłoby wielokrotne dziedziczenie (lub cechy).

  2. Proste zadania: instancją Tank obiektu w grze jest również Runnable, aby umożliwić wykonanie go w wątku i ActionListener, aby odpowiedzieć na zdarzenia myszy.

  3. Interfejsy wywołania zwrotnego: jeśli obiekt implementuje dany interfejs wywołania zwrotnego, jest powiadamiany o jego cyklu życia lub innych zdarzeniach.

  4. Interfejsy znaczników: nie dodawanie żadnych metod, ale łatwo dostępne za pomocą instanceof, aby odkryć możliwości lub życzenia obiektów. Serializable i Cloneable są tego przykładami.

To, czego szukasz, to cecha (jak w Scali), niestety niedostępna w Javie.

 31
Author: Tomasz Nurkiewicz,
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
2011-12-16 09:03:35

Interfejsy są zbiorem końcowych pól statycznych i abstrakcyjnych metod (nowo Java 8 dodała wsparcie posiadania metod statycznych w interfejsie).

Interfejsy powstają w sytuacjach, gdy wiemy, że pewne zadanie musi być wykonane, ale sposób, w jaki powinno być wykonane, może być różny. Innymi słowy możemy powiedzieć, że implementujemy interfejsy tak, że nasza klasa zaczyna zachowywać się w określony sposób.

Pozwól, że wyjaśnię na przykładzie, wszyscy wiemy, czym są zwierzęta. Jak lew jest zwierzęciem, małpa jest zwierzęciem, słoń jest zwierzęciem, krowa jest zwierzęciem i tak dalej. Teraz wiemy, że wszystkie zwierzęta coś jedzą i śpią. Ale sposób, w jaki każde zwierzę może coś zjeść lub spać, może się różnić. Jak lew je polując na inne zwierzęta, gdzie jak krowa je trawę. Ale oboje jedzą. Więc możemy mieć jakiś pseudo kod jak ten,
interface Animal {
    public void eat();
    public void sleep();   
}

class Lion implements Animal {
    public void eat() {
        // Lion's way to eat
    }

    public void sleep(){
         // Lion's way to sleep
    }
}

class Monkey implements Animal {
    public void eat() {
        // Monkey's way to eat
    }

    public void sleep() {
        // Monkey's way to sleep
    }
}

Zgodnie z wyżej wspomnianym pseudo kodem, wszystko, co jest w stanie jeść lub spać, będzie nazywane zwierzęciem lub możemy powiedzieć, że wszystkie zwierzęta muszą jeść i spać, ale sposób jedzenia i sen zależy od zwierzęcia.

W przypadku interfejsów dziedziczymy tylko zachowanie, a nie rzeczywisty kod, jak w przypadku dziedziczenia klas.

Q1. Skoro interfejsy mają tylko abstrakcyjne metody (bez kodu), to jak możemy powiedzieć, że jeśli implementujemy jakikolwiek interfejs, to jest to dziedziczenie ? Nie używamy jego kodu.

Implementowanie interfejsów to inny rodzaj dziedziczenia. Nie jest podobny do dziedziczenia klas jak w tej klasie dziedziczenia dzieci pobiera prawdziwy kod do ponownego użycia z klasy bazowej.

Q2. Jeśli implementacja interfejsu nie jest dziedziczeniem, to w jaki sposób interfejsy są używane do osiągnięcia dziedziczenia wielokrotnego ?

Mówi się, że jedna klasa może zaimplementować więcej niż jeden interfejs. Musimy jednak zrozumieć, że to dziedziczenie jest INNE niż dziedziczenie klasowe.

Q3. Jakie są korzyści z korzystania z interfejsów ? Nie mają żadnego kodu. Musimy jeszcze raz napisać kod i ponownie we wszystkich klasach wdrażamy go.

Implementacja interfejsu powoduje, że klasa musi nadpisać wszystkie jej abstrakcyjne metody.

Czytaj więcej w mojej książce tutaj i tutaj

 16
Author: gprathour,
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
2017-06-09 07:10:44

KISS

Szukałem dni, tygodni próbując zrozumieć interfejsy i wydaje mi się, że czytam tę samą ogólną pomoc; nie próbuję dyskredytować wkładu, ale myślę, że żarówka właśnie kliknęła, więc się czepiam :))

Wolę, żeby to było proste, więc zaproponuję mój nowy znaleziony widok interfejsów.

Jestem dorywczym programistą, ale chcę dodać ten kod, który napisałem w VB.NET (zasada jest taka sama dla innych języków), aby pomóc innym zrozumieć interfejsy.

Jeśli się mylę, daj znać innym w komentarzach.

Explanation

Trzy przyciski na formularzu, kliknięcie każdego z nich zapisuje odwołanie do innej klasy do zmiennej interfejsu (_data). Cały punkt różnych odwołań klas do zmiennej interfejsu, jest tym, czego nie rozumiałem, ponieważ wydawało się to zbędne, wtedy jego moc staje się widoczna z msgbox, muszę tylko wywołać tę samą metodę, aby wykonać zadanie, którego potrzebuję, w tym case' GetData ()', który używa metody w klasie, która jest obecnie przechowywana przez zmienną referencyjną interfejsu (_data).

Więc jakkolwiek chcę uzyskać moje dane (z bazy danych, internetu lub pliku tekstowego), zawsze robi się to przy użyciu tej samej metody nazwa ; kod stojący za tą implementacją...nie obchodzi mnie to.

Łatwo jest zmienić kod każdej klasy za pomocą interfejsu bez żadnych zależności...jest to kluczowy cel w OO i enkapsulacji.

Kiedy do użyj

Klasy kodu i jeśli zauważysz ten sam czasownik używany dla metod ,jak ' GetData ()', to dobrym kandydatem jest zaimplementowanie interfejsu na tej klasie i użycie nazwy metody jako abstrakcji / interfejsu.

Mam szczerą nadzieję, że to pomoże koledze noobowi z tą trudną zasadą.
Public Class Form1

Private _data As IData = Nothing

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    _data = New DataText()
    MsgBox(_data.GetData())
End Sub

Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
    _data = New DataDB()
    MsgBox(_data.GetData())
End Sub

Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
    _data = New DataWeb()
    MsgBox(_data.GetData())
End Sub

End Class

Public Interface IData
Function GetData() As String
End Interface

Friend Class DataText : Implements IData

Friend Function GetData() As String Implements IData.GetData
    Return "DataText"
End Function

End Class

Friend Class DataDB : Implements IData

Friend Function GetData() As String Implements IData.GetData
    Return "DataDB"
End Function

End Class

Friend Class DataWeb : Implements IData

Friend Function GetData() As String Implements IData.GetData
    Return "DataWeb"
End Function

End Class
 9
Author: Data,
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-11-20 02:23:35

jest to bardzo stare pytanie, a wersja java-8 dodała więcej funkcji i mocy do interfejsu.

Deklaracja interfejsu może zawierać

  1. podpisy metody
  2. metody domyślne
  3. metody statyczne
  4. stałe definicje.

jedynymi metodami, które mają implementacje w interfejsie są metody default i static .

Zastosowanie interfejsu:

  1. aby zdefiniować kontrakt
  2. aby połączyć niepowiązane klasy z ma możliwości (np. klasy implementujące interfejs Serializowalny mogą lub nie muszą mieć żadnych relacji między nimi z wyjątkiem implementacji tego interfejsu
  3. aby zapewnić wymienne wdrożenie np. Strategy_pattern
  4. domyślne metody umożliwiają dodawanie nowych funkcjonalności do interfejsów bibliotek i zapewniają Kompatybilność binarną z kodem napisanym dla starszych wersje tych interfejsów
  5. organizuj metody pomocnicze w swoich bibliotekach za pomocą metod static (możesz trzymać statyczne metody specyficzne dla interfejsu w tym samym interfejsie, a nie w oddzielnej klasie)

Spójrz na to powiązane pytanie SE Dla przykładu kodu, aby lepiej zrozumieć pojęcia:

Jak powinienem wyjaśnić różnicę między interfejsem a klasą abstrakcyjną?

Wracając do Twoich zapytań:

Q1. Skoro interfejsy mają tylko abstrakcyjne metody (bez kodu), to jak możemy powiedzieć, że jeśli implementujemy jakikolwiek interfejs, to jest to dziedziczenie ? Nie używamy jego kodu.

Q2. Jeśli implementacja interfejsu nie jest dziedziczeniem, to w jaki sposób interfejsy są używane do osiągnięcia dziedziczenia wielokrotnego ?

Interfejs może zawierać kod dla statycznych i domyślnych metod . Te domyślne metody zapewniają kompatybilność wsteczną, a metody statyczne zapewniają helper/utility functions.

Nie można mieć prawdziwego dziedziczenia Wielokrotnego w Javie i interfejs nie jest sposobem, aby go uzyskać. Interfejs może zawierać tylko stałe. Więc nie możesz dziedziczyć stanu, ale możesz zaimplementować zachowanie.

Możesz zamienić dziedziczenie na Możliwość . interfejs zapewnia wiele możliwości implementacji klas.

Q3. Jakie są korzyści z korzystania z interfejsów ? Nie mają żadnego kodu. My trzeba pisać kod ponownie i ponownie we wszystkich klasach, które implementujemy.

Zobacz sekcję" uses of interface " w mojej odpowiedzi.

 5
Author: Ravindra babu,
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
2017-05-23 12:32:17

Q1. Skoro interfejsy mają tylko abstrakcyjne metody (bez kodu), to jak możemy powiedzieć, że jeśli implementujemy jakikolwiek interfejs, to jest to dziedziczenie ? Nie używamy jego kodu.

Niestety, w potocznym użyciu, słowo inheritance jest nadal często używane, gdy klasa implementuje interfejs, chociaż interface implementation byłoby lepszym terminem - IMO, termin inheritance powinien być ściśle używany z dziedziczeniem konkretnej lub abstrakcyjnej klasy. W językach takich jak C++ i C#, ta sama składnia (tj. Subclass : Superclass i Class : Interface) jest używana zarówno do dziedziczenia klas, jak i implementacji interfejsu, co mogło przyczynić się do rozpowszechnienia niewłaściwego użycia słowa inheritance z interfejsami. Java ma inną składnię dla extend w klasie w przeciwieństwie do implementw interfejsie, co jest dobrą rzeczą.

Q2 jeśli implementacja interfejsu nie jest dziedziczeniem, to jak interfejsy są używane do osiągnięcia dziedziczenia wielokrotnego ?

Można osiągnąć "efekt" wielu dziedziczenie przez kompozycję - poprzez implementację wielu interfejsów na klasie, a następnie dostarczenie implementacji dla wszystkich metod, właściwości i zdarzeń wymaganych od wszystkich interfejsów na klasie. Jedną z powszechnych technik robienia tego z konkretnymi klasami jest wykonywanie relacji "has-a" (kompozycja) z klasami, które implementują zewnętrzne interfejsy poprzez "okablowanie" implementacji do każdej z wewnętrznych implementacji klas. (Języki takie jak C++ obsługują wiele konkretnych dziedziczenie bezpośrednio, ale co stwarza inne potencjalne problemy, takie jak problem diamentowy).

Q3 w każdym razie jakie są korzyści z używania interfejsów ? Nie mają żadnego kodu. Musimy pisać kod ponownie i ponownie we wszystkich klasach, które implementujemy.

Interfejsy pozwalają istniejącym klasom (np. frameworkom) na interakcję z nowymi klasami bez ich "widziania" wcześniej, ze względu na możliwość komunikowania się przez znany interfejs. Pomyśl o interfejsie jako kontrakt. Implementując ten interfejs na klasie, jesteś umownie zobowiązany do spełnienia wymaganych od niego zobowiązań, a gdy ten kontrakt zostanie wdrożony, twoja klasa powinna być w stanie być używana zamiennie z dowolnym innym kodem, który zużywa interfejs.

Przykład Świata Rzeczywistego

Przykładem "prawdziwego świata" byłoby prawodawstwo i konwencja (interfejs) otaczająca gniazdko elektryczne w danym kraju. Każde urządzenie elektryczne podłączony do gniazda musi spełniać specyfikacje (kontrakt), które władze zdefiniowały dla gniazda, np. położenie linii, przewodów neutralnych i uziemiających, położenie i wybarwienie włącznika / wyłącznika oraz zgodność napięcia elektrycznego, częstotliwości i maksymalnego prądu, które będą dostarczane przez[8]} Po włączeniu. [[12]}korzyści z odsprzęgnięcia interfejsu (tj. standardowego gniazda ściennego), a nie tylko lutowania przewodów razem jest że można do niego podłączyć (i odłączyć) wentylator, czajnik, podwójny adapter lub jakieś nowe urządzenie, które zostanie wynalezione w przyszłym roku, mimo że to urządzenie nie istniało, gdy interfejs został zaprojektowany. Dlaczego? Ponieważ będzie on zgodny {[37] } z wymaganiami interfejsu.

Dlaczego używać interfejsów?

Interfejsy są świetne do luźnego łączenia klas i są jednym z filarów paradygmatu SOLID, zwłaszcza Dependency Inversion Principle i Interface Segregation Principles.

Mówiąc najprościej, zapewniając, że zależności między klasami są sprzężone tylko na interfejsach (abstrakcjach), a nie na innych konkretnych klasach, pozwala to na zastąpienie zależności dowolną inną implementacją klasy, która spełnia wymagania interfejsu.

W testowaniu, stuby i mocki zależności mogą być używane do testowania jednostkowego każdej klasy, a interakcja klasy z zależnością może być 'spyed'na.

 5
Author: StuartLC,
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-04-20 15:40:06

Dziedziczenie jest wtedy, gdy jedna klasa wywodzi się z innej klasy (która może być abstrakcyjna) lub interfejsu. Najsilniejszym punktem zorientowania obiektowego (dziedziczenia) nie jest ponowne użycie kodu (jest na to wiele sposobów), ale polimorfizm.

Polimorfizm jest wtedy, gdy mamy kod, który używa interfejsu, którego obiekt instancyjny może należeć do dowolnej klasy wywodzącej się z tego interfejsu. Na przykład mogę mieć taką metodę: public void Pet (ianimal animal) i ta metoda otrzyma obiekt, który jest przykład psa lub kota, które dziedziczą z IAnimal. albo mogę mieć taki kod: IAnimal animal i wtedy mogę wywołać metodę tego interfejsu: zwierzę.Eat (), które pies lub kot może zaimplementować w inny sposób.

Główną zaletą interfejsów jest to, że możesz dziedziczyć z niektórych z nich, ale jeśli chcesz dziedziczyć tylko z jednego, Możesz również użyć klasy abstrakcyjnej. Oto artykuł, który wyjaśnia więcej na temat różnic między klasą abstrakcyjną a interfejs: http://www.codeproject.com/KB/cs/abstractsvsinterfaces.aspx

 3
Author: Ben,
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
2011-12-16 08:18:46

Obie metody działają (Interfejsy i dziedziczenie wielokrotne).

Szybka Praktyczna Krótka Odpowiedź

Interfejsy są lepsze, gdy masz kilkuletnie doświadczenie w używaniu wielu Dziedziczeń, które mają Super klasy z tylko definicją metody i bez kodu w ogóle.

Pytanie uzupełniające może brzmieć: "jak i dlaczego przenieść kod z klas abstrakcyjnych do interfejsów".

Jeśli nie masz zbyt wielu abstrakcyjnych klas, możesz pominąć interfejsy.

Dont pośpiech do korzystania z interfejsów.

Długa Nudna ODPOWIEDŹ

Interfejsy są bardzo podobne, a nawet równoważne klasom abstrakcyjnym.

Jeśli Twój kod ma wiele abstrakcyjnych klas, to czas zacząć myśleć w kategoriach interfejsów.

Następujący kod z klasami abstrakcyjnymi:


MyStreamsClasses.java

/* File name : MyStreamsClasses.java */
import java.lang.*;
// Any number of import statements

public abstract class InputStream {
  public void ReadObject(Object MyObject);
}

public abstract class OutputStream {
  public void WriteObject(Object MyObject);
}

public abstract class InputOutputStream 
    imnplements InputStream, OutputStream {
  public void DoSomethingElse();
}

Można zastąpić:


MyStreamsInterfaces.java

/* File name : MyStreamsInterfaces.java */
import java.lang.*;
// Any number of import statements

public interface InputStream {
  public void ReadObject(Object MyObject);
}

public interface OutputStream {
  public void WriteObject(Object MyObject);
}

public interface InputOutputStream 
    extends InputStream, OutputStream {
  public void DoSomethingElse();
}

Zdrówko.
 1
Author: umlcat,
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-04-03 00:05:40

Q1. Ponieważ interfejsy mają tylko abstrakcyjne metody (bez kodu), więc jak możemy powiedzieć, że jeśli implementujemy interfejs, to jest to dziedziczenie? Nie używamy jego kodu.

To nie jest równe dziedziczenie. To jest po prostu podobne. Pozwól mi wyjaśnić:

VolvoV3 extends VolvoV2, and VolvoV2 extends    Volvo (Class)
VolvoV3 extends VolvoV2, and VolvoV2 implements Volvo (Interface)

line1: Volvo v = new VolvoV2(); 
line2: Volvo v = new VolvoV3(); 

Jeśli widzisz tylko line1 i line2 możesz wywnioskować, że VolvoV2 i VolvoV3 mają ten sam typ. Nie można wywnioskować, czy Volvo jest superklasą, czy Volvo jest interfejsem.

Q2. Jeśli implementacja interfejsu nie jest dziedziczenie to jak interfejsy są używane do osiągnięcia wielu dziedziczeń?

Teraz za pomocą interfejsów:

VolvoXC90 implements XCModel and Volvo (Interface)
VolvoXC95 implements XCModel and Volvo (Interface)

line1: Volvo   a = new VolvoXC90();
line2: Volvo   a = new VolvoXC95();
line3: XCModel a = new VolvoXC95();

Jeśli widzisz tylko line1 i line2 możesz wywnioskować, że VolvoXC90 i VolvoXC95 mają ten sam typ (Volvo). Nie można wywnioskować, że Volvo jest klasą nadrzędną lub Volvo jest interfejsem.

Jeśli widzisz tylko line2 i line3 możesz wywnioskować, że Volvo95 implementuje dwa typy, XCModel i Volvo, w Javie wiesz, że przynajmniej jeden musi być interfejsem. Jeśli ten kod został napisany na przykład w C++ mogą to być obie klasy. Dlatego wiele spadków.

Q3. Jakie są korzyści z używania interfejsów? Nie mają żadnego kodu. Musimy pisać kod ponownie i ponownie we wszystkich klasach, które implementujemy.

Wyobraź sobie system, w którym używasz klasy VolvoXC90 w 200 innych klasach.

VolvoXC90 v = new VolvoXC90();

Jeśli musisz rozwinąć swój system, aby uruchomić VolvoXC95, musisz zmienić 200 innych klas.

Teraz wyobraź sobie system, w którym używasz interfejs Volvo w 10 000 000 klasach.

// Create VolvoXC90 but now we need to create VolvoXC95
Volvo v = new VolvoFactory().newCurrentVolvoModel(); 

Teraz, jeśli chcesz ewoluować swój system do tworzenia modeli VolvoXC95 musisz zmienić tylko jedną klasę, fabrykę.

To kwestia zdrowego rozsądku. Jeśli Twój system składa się tylko z kilku klas i ma kilka aktualizacji użyj interfejsów wszędzie jest przeciwny do zamierzonego. W przypadku dużych systemów może to zaoszczędzić wiele bólu i uniknąć ryzyka przyjęcia interfejsów. Polecam przeczytać więcej o zasadach S. O. L. I. D i przeczytać książkę Skuteczna Java. Ma dobre lekcje od doświadczonych inżynierów oprogramowania.
 0
Author: Anderson Marques,
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-04-02 23:37:17

Interfejsy są tworzone tak, aby Klasa zaimplementowała funkcjonalność w interfejsie i zachowywała się zgodnie z tym interfejsem.

 0
Author: Dark Drake,
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-04-04 10:22:11

Stare pytanie. Dziwi mnie, że nikt nie cytował kanonicznych źródeł: Java: an Overview autorstwa Jamesa Goslinga, Design Patterns: Elements of reusable Object-Oriented Software autorstwa Gang of Four czy Effective Java autorstwa Joshuy Blocha (między innymi).

Zacznę od cytatu:

Interfejs jest po prostu specyfikacją zestawu metod, na które obiekt reaguje. Nie zawiera żadnych zmiennych instancji ani wdrożenie. Interfejsy mogą być dziedziczone wielokrotnie (w przeciwieństwie do klas) i mogą być używane w bardziej elastyczny sposób niż zwykła sztywna Klasa struktura dziedziczenia. (Gosling, str. 8)

Teraz przyjmijmy twoje założenia i pytania jeden po drugim(dobrowolnie zignoruję funkcje Java 8).

Założenia

Interfejs jest zbiorem tylko abstrakcyjnych metod i końcowych pól.

Czy widziałeś słowo kluczowe abstract w interfejsach Java? Nie. Więc nie powinieneś rozważmy interfejs jako zbiór abstrakcyjnych metod. Być może wprowadzają cię w błąd tzw. interfejsy C++, które są klasami z tylko czystymi metodami wirtualnymi. C++ z założenia nie posiada (i nie musi mieć) interfejsów, ponieważ posiada dziedziczenie mutliple.

Jak wyjaśnia Gosling, powinieneś raczej rozważyć interfejs jako "zbiór metod, na które obiekt reaguje". Lubię widzieć interfejs i powiązaną dokumentację jako umowę serwisową. Informatyka opisuje, czego można oczekiwać od obiektu, który implementuje ten interfejs. Dokumentacja powinna określać warunki wstępne i końcowe (np. parametry NIE POWINNY być null, wynik zawsze jest dodatni, ...) oraz niezmienniki (metoda, która nie modyfikuje stanu wewnętrznego obiektu). Ten kontrakt jest sercem OOP.

W Javie nie ma dziedziczenia wielokrotnego.

W rzeczy samej.

JAVA pomija wiele rzadko używanych, słabo rozumianych, mylących cechy C++, które z naszego doświadczenia przynoszą więcej żalu niż korzyści. Składa się głównie z przeciążenia operatorów (chociaż ma przeciążenie metody), wielokrotnego dziedziczenia i rozległych automatycznych przymusów. (Gosling, str. 2)

Nic dodać.

Interfejsy mogą być używane do osiągnięcia wielu dziedziczeń w Javie.

Nie, simlpy, ponieważ w Javie nie ma dziedziczenia wielokrotnego. Patrz wyżej.

Jednym z mocnych punktów dziedziczenia jest to, że możemy użyć Kod klasy bazowej w klasie pochodnej nie zapisując go ponownie. Być może jest to najważniejsza rzecz dla dziedziczenia.

To się nazywa "dziedziczenie implementacji". Jak napisałeś, jest to wygodny sposób na ponowne użycie kodu.

Ale ma ważny odpowiednik:

Klasy nadrzędne często definiują przynajmniej część fizycznej reprezentacji swoich podklas. Ponieważ dziedziczenie naraża podklasę na szczegóły implementacji rodzica, często powiedział, że " dziedziczenie łamie enkapsulację "[Sny86]. Implementacja podklasy jest tak związana z implementacją jej klasy macierzystej, że jakakolwiek zmiana w implementacji rodzica zmusi podklasę do zmiany. (GOF, 1.6)

[[27]}(jest podobny cytat w Bloch, poz. 16.) W rzeczywistości dziedziczenie służy również innemu celowi:]}

Dziedziczenie klas łączy dziedziczenie interfejsu i dziedziczenie implementacji. Dziedziczenie interfejsu definiuje nowy interfejs w kategoriach lub więcej istniejących interfejsów. Dziedziczenie implementacji definiuje nową implementację w kategoriach jednej lub więcej istniejących implementacji. (GOF, Dodatek A)

Oba używają słowa kluczowego extends w Javie. Możesz mieć hierarchie klas i hierarchie interfejsów. Pierwsze z nich mają wspólne wdrożenie, drugie mają wspólne zobowiązanie.

Pytania

Q1. Ponieważ interfejsy mają tylko metody abstrakcyjne (brak kodu), więc jak można mówimy, że jeśli implementujemy jakikolwiek interfejs, to jest to dziedziczenie ? Nie używamy jego kodu.**

Implementacja interfejsu nie jest dziedziczeniem. To implementacja. Tak więc słowo kluczowe implements.

Q2. Jeśli implementacja interfejsu nie jest dziedziczeniem, to w jaki sposób interfejsy są używane do osiągnięcia dziedziczenia wielokrotnego ?**

Brak wielokrotnego dziedziczenia w Javie. Patrz wyżej.

Q3. Jakie są korzyści z korzystania z interfejsów ? Nie mają żadnego kodu. My trzeba pisać kod ponownie i ponownie we wszystkich klasach, które implementujemy./ To po co tworzyć interfejsy ?/ Jakie są dokładne korzyści z używania interfejsów? Czy to naprawdę dziedziczenie wielokrotne, które osiągamy za pomocą interfejsów?

Najważniejsze pytanie brzmi: dlaczego chcesz mieć dziedziczenie wielokrotne? myślę o dwóch odpowiedziach: 1. aby nadać obiektowi typy mutliple; 2. aby ponownie użyć kodu.

Nadawanie typów mutliple obiektowi

W OOP jeden obiekt może mieć różne typy . Na przykład w Javie ArrayList<E> ma następujące typy: Serializable, Cloneable, Iterable<E>, Collection<E>, List<E>, RandomAccess, AbstractList<E>, AbstractCollection<E> i Object (mam nadzieję, że o nikim nie zapomniałem). Jeśli obiekt ma różne typy, różni konsumenci będą mogli z niego korzystać bez świadomości jego specyfiki. Potrzebuję Iterable<E>, a Ty dajesz mi ArrayList<E>? W porządku. Ale jeśli potrzebuję teraz List<E>, a Ty dasz mi ArrayList<E>, to też jest w porządku. Itd.

Jak wpisać obiekt w OOP? Zabrałeś interfejs Runnable jako przykład, a ten przykład jest doskonały do zilustrowania odpowiedzi na to pytanie. Cytuję oficjalny dokument Java:

Dodatkowo, Runnable zapewnia środki dla klasy, aby była aktywna, a nie podklasyfikowała wątek.

Oto chodzi: Dziedziczenie jest wygodnym sposobem wpisywania obiektów. chcesz utworzyć wątek? Podklasujmy klasę Thread. Jeśli chcesz, aby obiekt miał różne typy, użyjmy dziedziczenia mutliple. Argh. Informatyka nie istnieje w Javie. (W C++, jeśli chcesz, aby obiekt miał różne typy, dobrym rozwiązaniem jest dziedziczenie wielokrotne.)

Jak zatem nadać obiektowi typy mutliple? W Javie możesz wpisać swój obiekt bezpośrednio . To właśnie robisz, gdy twoja klasa implements Runnable interfejs. Po co używać Runnable jeśli jesteś fanem dziedziczenia? Może dlatego, że twoja klasa jest już podklasą innej klasy, powiedzmy A. Teraz twoja klasa ma dwa typy: A i Runnable.

Z wiele interfejsów, można nadać obiektowi wiele typów. Wystarczy utworzyć klasę, która implements ma wiele interfejsów. Tak długo, jak jesteś zgodny z umowami, jest ok.

Kod ponownego użycia

To trudny temat; już zacytowałem GOF na temat łamania enkapsulacji. Inna odpowiedź wspominała o problemie diamentów. Można również pomyśleć o zasadzie jednej odpowiedzialności:

Klasa powinna mieć tylko jeden powód do zmiany. (Robert C. Martin, Agile Software Development, Principles, Patterns, and Practices) {]}

W przeciwieństwie do innych klas, w których rodzic jest rodzicem, klasa może mieć powód do zmiany, poza własnymi obowiązkami:]}

Implementacja superklasy może się zmieniać z wydania na wydanie, a jeśli tak się stanie, podklasa może się zepsuć, nawet jeśli jej kod nie został dotknięty. W związku z tym podklasa musi ewoluować równolegle z klasą nadrzędną (Bloch, poz. 16).

Dodałbym bardziej prozaiczny problem: zawsze mam dziwne uczucie, gdy próbuję znaleźć kod źródłowy metody w klasie i nie mogę go znaleźć. Potem pamiętam: musi być zdefiniowany gdzieś w klasie rodzica. Albo w klasie dziadków. A może nawet wyżej. Dobre IDE jest cennym atutem w tym przypadku, ale pozostaje w moim umyśle czymś magicznym. Nic podobnego do hierarchii interfejsów, bo javadoc to jedyne czego mi potrzeba: jeden skrót klawiaturowy w IDE i dostaję to.

Dziedziczenie ma zalety:

Bezpiecznie jest używać dziedziczenia wewnątrz pakietu, gdzie implementacje podklasy i klasy nadrzędnej są pod kontrolą tych samych programistów. Bezpieczne jest również stosowanie dziedziczenia przy rozszerzaniu klas specjalnie zaprojektowanych i udokumentowanych do rozszerzenia (pozycja 17: projekt i dokument do dziedziczenia lub inaczej tego zabronić). (Bloch, poz. 16)

Przykład klasy " specjalnie zaprojektowanej i documented for extension " w języku Java jest AbstractList.

Bloch i GOF nalegają na to: "Faworyzuj kompozycję ponad dziedziczenie".]}

Delegacja jest sposobem na uczynienie kompozycji tak potężną do ponownego użycia, jak dziedziczenie [Lie86, JZ91]. W delegowaniu dwa obiekty są zaangażowane w obsługę żądania: obiekt otrzymujący przekazuje operacje swojemu delegatowi. Jest to analogiczne do podklas odkładających żądania do klas nadrzędnych. (GOF str. 32)

Jeśli używasz kompozycji, nie będziesz musiał pisać tego samego kodu wielokrotnie. po prostu tworzysz klasę, która obsługuje duplikacje i przekazujesz instancję tej klasy do klas, które implementują interfejs. Jest to bardzo prosty sposób na ponowne użycie kodu. A to pomaga przestrzegać zasady jednej odpowiedzialności i uczynić kod bardziej testowalnym. Rust I Go nie mają dziedziczenia( nie mają też klas), ale nie sądzę, aby Kod był bardziej zbędny niż w innych OOP języki.

Ponadto, jeśli używasz kompozycji, w naturalny sposób będziesz używać interfejsów, aby nadać swojemu kodowi strukturę i elastyczność, której potrzebuje (zobacz inne odpowiedzi na temat przypadków użycia interfejsów).

Uwaga: możesz współdzielić kod z interfejsami Java 8

I na koniec ostatni cytat:

[27]}podczas pamiętnej sesji pytań i odpowiedzi, ktoś zapytał Go [James Gosling]: "gdybyś mógł zrobić Javę jeszcze raz, co byś zmienił?""Pominąłbym klasy " (gdziekolwiek w sieci, Nie wiem, czy to prawda)
 0
Author: jferard,
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-04-05 23:28:09

Interfejsy

[[15]}interfejs jest umową określającą sposób interakcji z obiektem. Są one przydatne, aby wyrazić, w jaki sposób twoje wewnętrzne elementy zamierzają wchodzić w interakcję z obiektem. Po odwróceniu zależności Twoje publiczne API będzie miało wszystkie parametry wyrażone za pomocą interfejsów. Nie obchodzi cię, jak robi to, czego potrzebujesz, tylko to, że robi dokładnie to, czego potrzebujesz.

Przykład: możesz po prostu potrzebować Vehicle do transportu towarów, nie dbasz o szczególny rodzaj transportu.

Dziedziczenie

Dziedziczenie jest rozszerzeniem konkretnej implementacji. Wdrożenie to może, ale nie musi, spełniać wymagania określonego interfejsu. Powinieneś oczekiwać przodka konkretnej implementacji tylko wtedy, gdy zależy ci na how.

Przykład: możesz potrzebować Plane implementacji pojazdu do szybkiego transportu.

Skład

Skład może być stosowany jako alternatywa dla dziedziczenia. Zamiast klasy rozszerzającej klasę bazową, jest ona tworzona z obiektami, które implementują mniejsze części odpowiedzialności klasy głównej. Skład stosowany jest w facade pattern i decorator pattern.

Przykład: możesz utworzyć DuckBoat (Klasa DUKW) implementująca LandVehicle i WaterVehicle, które implementują Vehicle złożone z implementacji Truck i Boat.

Odpowiedzi

Q1. Skoro interfejsy mają tylko metody abstrakcyjne (bez kodu) to jak możemy powiedzieć, że jeśli implementujemy jakiś interfejs to jest to dziedziczenie ? Nie używamy jego kodu.

Interfejsy nie są dziedziczeniem. Implementacja interfejsu wyraża zamiar, aby Twoja klasa działała w sposób zdefiniowany przez interfejs. Dziedziczenie jest wtedy, gdy masz wspólnego przodka i otrzymujesz to samo zachowanie (inherit) co przodek, więc nie musisz go definiować.

Q2. Jeśli implementacja interfejsu nie jest dziedziczeniem, to jak interfejsy są używane do osiągnięcia wielokrotnego dziedziczenia ?

Interfejsy nie uzyskują dziedziczenia wielokrotnego. Wyrażają one, że klasa może być odpowiednia dla wielu ról.

Q3. Jakie są korzyści z korzystania z interfejsów ? Nie mają żadnego kodu. Musimy pisać kod ponownie i ponownie we wszystkich klasach, które implementujemy.

Jedną z głównych zalet interfejsów jest rozdzielenie problemów:]}
  • możesz napisać klasę, która robi coś z inną klasą bez dbania o to, jak ta klasa jest zaimplementowana.
  • każdy przyszły rozwój może być zgodny z Twoją implementacją bez potrzeby rozszerzania określonej klasy bazowej.

W duchu DRY możesz napisać implementację, która spełnia interfejs i zmienić go przy jednoczesnym poszanowaniu open/closed principal, jeśli wykorzystasz kompozycję.

 -1
Author: Steve Buzonas,
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-30 19:22:02