Zadano mi pytanie, chciałem tutaj sprawdzić swoją odpowiedź.
P: W którym scenariuszu lepiej jest rozszerzyć klasę abstrakcyjną niż implementować interfejs (y)?
O: Jeśli używamy wzorca projektowego metody szablonowej.
Mam rację ?
Przepraszam, że nie mogłem jasno sformułować pytania.
Znam podstawową różnicę między klasą abstrakcyjną a interfejsem.
1) używaj klasy abstrakcyjnej, gdy wymaganie jest takie, że musimy zaimplementować tę samą funkcjonalność w każdej podklasie dla określonej operacji (zaimplementuj metodę) i inną funkcjonalność dla niektórych innych operacji (tylko sygnatury metod)
2) użyj interfejsu, jeśli chcesz złożyć podpis, aby był taki sam (i inna implementacja), abyś mógł zachować zgodność z implementacją interfejsu
3) możemy rozszerzyć maksymalnie jedną klasę abstrakcyjną, ale możemy zaimplementować więcej niż jeden interfejs
Powtarzając pytanie: czy są jakieś inne scenariusze poza wymienionymi powyżej, w których konkretnie wymagamy użycia klasy abstrakcyjnej (widać, czy wzorzec projektowania metody szablonowej jest koncepcyjnie oparty tylko na tym)?
Interfejs a klasa abstrakcyjna
Wybór między tymi dwoma naprawdę zależy od tego, co chcesz zrobić, ale na szczęście dla nas Erich Gamma może nam trochę pomóc.
Jak zawsze istnieje kompromis, interfejs daje swobodę w odniesieniu do klasy bazowej, klasa abstrakcyjna daje swobodę dodawania nowych metod później . - Erich Gamma
Nie możesz przejść i zmienić interfejsu bez konieczności zmiany wielu innych rzeczy w kodzie, więc jedynym sposobem na uniknięcie tego byłoby utworzenie zupełnie nowego interfejsu, co nie zawsze może być dobrą rzeczą.
Abstract classes
powinien być używany przede wszystkim dla obiektów, które są blisko spokrewnione. Interfaces
lepiej zapewniają wspólną funkcjonalność dla niepowiązanych klas.
Odpowiedzi:
Kiedy używać interfejsów
Interfejs pozwala komuś zacząć od zera, aby zaimplementować Twój interfejs lub zaimplementować go w innym kodzie, którego pierwotne lub podstawowe przeznaczenie różniło się od interfejsu. Dla nich twój interfejs jest tylko przypadkowy, coś, co musi dodać do ich kodu, aby móc używać twojego pakietu. Wadą jest to, że każda metoda w interfejsie musi być publiczna. Możesz nie chcieć ujawniać wszystkiego.
Kiedy używać klas abstrakcyjnych
Natomiast klasa abstrakcyjna zapewnia większą strukturę. Zwykle definiuje pewne domyślne implementacje i dostarcza narzędzi przydatnych do pełnej implementacji. Haczyk polega na tym, że wykorzystujący go kod musi używać Twojej klasy jako podstawy. Może to być bardzo niewygodne, jeśli inni programiści, którzy chcą używać twojego pakietu, już samodzielnie opracowali własną hierarchię klas. W Javie klasa może dziedziczyć tylko z jednej klasy bazowej.
Kiedy używać obu
Możesz zaoferować to, co najlepsze z obu światów, interfejs i klasę abstrakcyjną. Implementatorzy mogą zignorować twoją klasę abstrakcyjną, jeśli zechcą. Jedyną wadą jest wywoływanie metod przez ich nazwy interfejsów, jest nieco wolniejsze niż wywoływanie ich przez ich abstrakcyjną nazwę klasy.
źródło
If we are using template method design pattern
nie możemy powiedziećYES
lubNO
Tak, jeśli używasz JAXB. Nie lubi interfejsów. Należy użyć klas abstrakcyjnych lub obejść to ograniczenie za pomocą typów ogólnych.
Z osobistego posta na blogu :
Berło:
Ogólnie rzecz biorąc, interfejsy powinny służyć do definiowania kontraktów (co ma zostać osiągnięte, a nie jak to osiągnąć).
Klasa abstrakcyjna:
Do (częściowej) implementacji należy używać klas abstrakcyjnych. Mogą być sposobem na ograniczenie sposobu implementacji kontraktów API.
źródło
default
istatic
metody.Interfejs jest używany, gdy masz scenariusz, w którym wszystkie klasy mają taką samą strukturę, ale całkowicie mają różne funkcje.
Klasa abstrakcyjna jest używana, gdy istnieje scenariusz, w którym wszystkie klasy mają taką samą strukturę, ale taką samą i inną funkcjonalność.
Zobacz artykuł: http://shoaibmk.blogspot.com/2011/09/abstract-class-is-class-which-cannot-be.html
źródło
Jest tu wiele świetnych odpowiedzi, ale często uważam, że używanie OBU interfejsów i klas abstrakcyjnych jest najlepszą drogą. Rozważmy ten wymyślony przykład:
Jesteś programistą w banku inwestycyjnym i musisz zbudować system, który będzie składał zamówienia na rynek. Twój interfejs zawiera najbardziej ogólne pojęcie o tym, co robi system transakcyjny ,
1) Trading system places orders 2) Trading system receives acknowledgements
i można je przechwycić w interfejsie,
ITradeSystem
public interface ITradeSystem{ public void placeOrder(IOrder order); public void ackOrder(IOrder order); }
Teraz inżynierowie pracujący w dziale sprzedaży i na innych liniach biznesowych mogą zacząć łączyć się z systemem, aby dodać funkcjonalność składania zamówień do swoich istniejących aplikacji. I jeszcze nawet nie zacząłeś budować! To jest siła interfejsów.
Więc idź dalej i zbuduj system dla inwestorów giełdowych ; słyszeli, że Twój system ma funkcję wyszukiwania tanich akcji i bardzo chętnie ją wypróbowują! Ujmujesz to zachowanie za pomocą metody zwanej
findGoodDeals()
, ale zdajesz sobie sprawę, że jest wiele niechlujnych rzeczy związanych z łączeniem się z rynkami. Na przykład musisz otworzyćSocketChannel
,public class StockTradeSystem implements ITradeSystem{ @Override public void placeOrder(IOrder order); getMarket().place(order); @Override public void ackOrder(IOrder order); System.out.println("Order received" + order); private void connectToMarket(); SocketChannel sock = Socket.open(); sock.bind(marketAddress); <LOTS MORE MESSY CODE> } public void findGoodDeals(); deals = <apply magic wizardry> System.out.println("The best stocks to buy are: " + deals); }
Konkretne implementacje będą miały wiele takich niechlujnych metod
connectToMarket()
, ale na tymfindGoodDeals()
wszystkim zależy traderom.Teraz w grę wchodzą klasy abstrakcyjne. Twój szef poinformuje Cię, że traderzy walutowi również chcą korzystać z Twojego systemu. Patrząc na rynki walutowe, widać, że kanalizacja jest prawie identyczna jak na giełdach. W rzeczywistości
connectToMarket()
może być ponownie użyty dosłownie do łączenia się z rynkami walutowymi. JednakfindGoodDeals()
na arenie walutowej jest to zupełnie inna koncepcja. Zanim więc przekażesz kod źródłowy czarodziejowi zajmującemu się handlem walutami po drugiej stronie oceanu, najpierw zmienisz kod naabstract
klasę, pozostawiającfindGoodDeals()
nieskomplikowanąpublic abstract class ABCTradeSystem implements ITradeSystem{ public abstract void findGoodDeals(); @Override public void placeOrder(IOrder order); getMarket().place(order); @Override public void ackOrder(IOrder order); System.out.println("Order received" + order); private void connectToMarket(); SocketChannel sock = Socket.open(); sock.bind(marketAddress); <LOTS MORE MESSY CODE> }
Twój system handlu akcjami wdraża się
findGoodDeals()
tak, jak już zdefiniowałeś,public class StockTradeSystem extends ABCTradeSystem{ public void findGoodDeals(); deals = <apply magic wizardry> System.out.println("The best stocks to buy are: " + deals); }
ale teraz dzieciak, który specjalizuje się w FX, może zbudować swój system, po prostu dostarczając implementację
findGoodDeals()
dla walut; nie musi ponownie implementować połączeń gniazd ani nawet metod interfejsu!public class CurrencyTradeSystem extends ABCTradeSystem{ public void findGoodDeals(); ccys = <Genius stuff to find undervalued currencies> System.out.println("The best FX spot rates are: " + ccys); }
Programowanie w interfejsie jest potężne, ale podobne aplikacje często ponownie implementują metody w prawie identyczny sposób. Użycie klasy abstrakcyjnej pozwala uniknąć powtórzeń, przy jednoczesnym zachowaniu mocy interfejsu.
Uwaga: można się zastanawiać, dlaczego
findGreatDeals()
nie jest częścią interfejsu. Pamiętaj, że interfejs definiuje najbardziej ogólne elementy systemu handlowego. Inny inżynier może opracować ZUPEŁNIE RÓŻNY system transakcyjny, w którym nie zależy mu na znalezieniu dobrych transakcji. Interfejs gwarantuje, że dział sprzedaży może również połączyć się ze swoim systemem, dlatego lepiej nie mieszać interfejsu z koncepcjami aplikacji, takimi jak „świetne okazje”.źródło
Których należy używać, klas abstrakcyjnych lub interfejsów?
Rozważ użycie klas abstrakcyjnych, jeśli którekolwiek z poniższych instrukcji mają zastosowanie do Twojego przypadku użycia:
Chcesz udostępnić kod kilku blisko spokrewnionym klasom.
Oczekujesz, że klasy, które rozszerzają twoją klasę abstrakcyjną, mają wiele typowych metod lub pól albo wymagają modyfikatorów dostępu innych niż public (takich jak protected i private).
Chcesz zadeklarować pola niestatyczne lub niekońcowe. Umożliwia to definiowanie metod, które mogą uzyskiwać dostęp i modyfikować stan obiektu, do którego należą.
Rozważ użycie interfejsów, jeśli którekolwiek z poniższych stwierdzeń ma zastosowanie do twojego przypadku użycia:
Oczekujesz, że niepowiązane klasy zaimplementują Twój interfejs. Na przykład interfejsy Comparable i Cloneable są implementowane przez wiele niepowiązanych klas.
Chcesz określić zachowanie określonego typu danych, ale nie musisz przejmować się tym, kto zaimplementuje jego zachowanie.
Chcesz skorzystać z wielokrotnego dziedziczenia typu.
http://docs.oracle.com/javase/tutorial/java/IandI/abstract.html
źródło
W ciągu ostatnich trzech lat wiele się zmieniło dzięki dodaniu nowych możliwości do interfejsu z wydaniem Java 8.
Ze strony dokumentacji Oracle w interfejsie:
Jak zacytowałeś w swoim pytaniu, klasa abstrakcyjna najlepiej pasuje do wzorca metody szablonowej, w której musisz utworzyć szkielet. Nie można tutaj użyć interfejsu.
Jeszcze jedna kwestia, aby preferować klasę abstrakcyjną od interfejsu:
Nie masz implementacji w klasie bazowej i tylko podklasy muszą zdefiniować własną implementację. Potrzebujesz klasy abstrakcyjnej zamiast interfejsu, ponieważ chcesz dzielić stan z podklasami.
Klasa abstrakcyjna ustanawia relację „jest” między powiązanymi klasami, a interfejs zapewnia „ma” możliwość między klasami niepowiązanymi .
Odnośnie drugiej części pytania, która jest ważna dla większości języków programowania w tym języku Java przed java-8 wydaniu
Jeśli wolisz, aby klasa abstrakcyjna łączyła się wcześniej z powyższymi dwoma względami, musisz teraz przemyśleć to ponownie, ponieważ metody domyślne dodały potężne możliwości do interfejsów.
Aby wybrać jeden z nich między interfejsem a klasą abstrakcyjną, na stronie dokumentacji Oracle zacytuj:
Aby uzyskać więcej informacji, zapoznaj się z tymi powiązanymi pytaniami:
Interfejs a klasa abstrakcyjna (ogólne OO)
Jak powinienem był wyjaśnić różnicę między klasą Interface a abstrakcyjną?
Podsumowując: równowaga przechyla się teraz bardziej w kierunku interfejsów .
Niektóre wzorce projektowe używają klas abstrakcyjnych (przez interfejsy) oprócz wzorca metody Template.
Wzorce kreacyjne:
Abstract_factory_pattern
Wzory strukturalne:
Decorator_pattern
Wzorce zachowań:
Mediator_pattern
źródło
Nie masz racji. Scenariuszy jest wiele. Po prostu nie można sprowadzić go do jednej reguły złożonej z 8 słów.
źródło
Najkrótszą odpowiedzią jest rozszerzenie klasy abstrakcyjnej, gdy niektóre poszukiwane funkcjonalności są już w niej zaimplementowane.
Jeśli implementujesz interfejs, musisz zaimplementować całą metodę. Jednak w przypadku klas abstrakcyjnych liczba metod, które trzeba zaimplementować, może być mniejsza.
We wzorcu projektowym szablonu musi być zdefiniowane zachowanie. To zachowanie zależy od innych metod, które są abstrakcyjne. Tworząc podklasy i definiując te metody, w rzeczywistości definiujesz główne zachowanie. Podstawowe zachowanie nie może występować w interfejsie, ponieważ interfejs niczego nie definiuje, po prostu deklaruje. Dlatego wzorzec projektowy szablonu zawsze zawiera klasę abstrakcyjną. Jeśli chcesz zachować ciągłość zachowania, musisz rozszerzyć klasę abstrakcyjną, ale nie nadpisywać głównego zachowania.
źródło
Pure virtual functions can also be used where the method declarations are being used to define an interface - similar to what the interface keyword in Java explicitly specifies. In such a use, derived classes will supply all implementations. In such a design pattern, the abstract class which serves as an interface will contain only pure virtual functions, but no data members or ordinary methods.
część (1/2)no data members or ordinary methods
[w klasie abstrakcyjnej].Moim zdaniem podstawowa różnica polega na tym
an interface can't contain non abstract methods while an abstract class can
. Więc jeśli podklasy mają wspólne zachowanie, to zachowanie można zaimplementować w superklasie, a tym samym odziedziczyć w podklasachZacytowałem również następujący fragment z książki „Wzorce projektowania architektury oprogramowania w java”
„W języku programowania Java nie ma obsługi dziedziczenia wielokrotnego. Oznacza to, że klasa może dziedziczyć tylko z jednej klasy. Dlatego dziedziczenie powinno być używane tylko wtedy, gdy jest to absolutnie konieczne. Jeśli to możliwe, metody określające typowe zachowanie należy zadeklarować w formę interfejsu Java, który ma być implementowany przez różne klasy implementatorów. Jednak interfejsy cierpią z powodu ograniczenia polegającego na tym, że nie mogą zapewnić implementacji metod. Oznacza to, że każdy użytkownik implementujący interfejs musi jawnie implementować wszystkie metody zadeklarowane w interfejsie, nawet jeśli niektóre z nich metody reprezentują niezmienną część funkcjonalności i mają dokładnie taką samą implementację we wszystkich klasach implementujących, co prowadzi do redundantnego kodu.Poniższy przykład ilustruje, jak wzorzec abstrakcyjnej klasy nadrzędnej może być używany w takich przypadkach bez konieczności stosowania redundantnych implementacji metod. "
źródło
Klasy abstrakcyjne różnią się od interfejsów w dwóch ważnych aspektach
źródło
To dobre pytanie. Te dwa elementy nie są podobne, ale można ich użyć z tego samego powodu, na przykład przepisania. Podczas tworzenia najlepiej jest skorzystać z Interface. Jeśli chodzi o zajęcia, jest dobry do debugowania.
źródło
Abstract classes should be extended when you want to some common behavior to get extended
. Klasa abstrakcyjna będzie miała wspólne zachowanie i będzie definiować abstrakcyjną metodę / określone zachowanie, które podklasy powinny implementować.Interfaces allows you to change the implementation anytime allowing the interface to be intact
.źródło
To jest moje zrozumienie, mam nadzieję, że to pomoże
Zajęcia abstrakcyjne:
Interfejsy:
źródło
Wykorzystanie abstraktu i interfejsu:
Jeden ma „związek”, a drugi „związek”
Właściwości domyślne zostały ustawione w sposób abstrakcyjny, a dodatkowe właściwości można wyrazić za pomocą interfejsu.
Przykład: -> W przypadku istot ludzkich mamy pewne domyślne właściwości, takie jak jedzenie, spanie itp., Ale jeśli ktoś ma jakieś inne zajęcia w programie, takie jak pływanie, zabawa itp., Można je wyrazić za pomocą interfejsu.
źródło