Dobry wieczór. Właśnie piszę klasę implementującą kolejkę cykliczną. Klasa ta implementuje interfejs BlockingQueue<E>
, a przez to jeszcze kilka innych. Problem w tym, że nadpisałem kilka potrzebnych mi metod, jednak kompilator wyrzuca, że mam nadpisywać jeszcze kolejne. Wychodzi na to, że potrzebne czy nie (nie potrzebuję np. remove(Object o)
z BlockingQueue<E>
, bo wystarczy mi remove()
z Queue<E>
), ale musiałbym nadpisać metody ze wszystkich interfejsów. A to, delikatnie mówiąc, jest bez sensu. Czy istnieje jakiś sposób, by to obejść?
Nie, co najwyzej mozesz te metody zostawic puste
Jeśli implementujesz jakiś interfejs to MUSISZ implementować wszystkie jego metody. Inaczej nie miałoby to sensu! Przecież ktoś teraz może wziąć tą twoja klasę TwojaKlasa i zrobić:
BlockingQueue<String> queue = new TwojaKlasa<>();
queue.remove(cośtam);
I co teraz? Możesz zostawić puste implementacje, albo lepiej rzucać jakies NotImplementedError
czy coś, ale to też bardzo biedne rozwiązanie.
Niemniej normalnie to się robi jednak jakąś delegacje -> masz swoją klasę która ma w sobie obiekt jakiejś standardowej kolejki. Nadpisujesz metody które cię interesuję, a resztę metod delegujesz do tej kolejki którą masz pod spodem:
class MojaKolejka<T> implements Queue<T>{
private final queue = new LinkedList<>();
@Override
public boolean add(T element){
// jakaś nasza specjalna logika
return queue.add(element);
}
@Override
public boolean offer(T element){
return queue.offer(element); // goła delegacja
}
}
Rozumiem. Zostawienie tych metod nie wydaje mi się bezpiecznym rozwiązaniem, ale chyba tak zrobię (lub wewnątrz nich wywołam ich przeciążone odpowiedniki). Pytanie zadałem, bo wspomniane przez @Shalom zastosowanie metody remove(Object) wydaje mi się absolutnie bez sensu. W kolejce cyklicznej mogę usunąć tylko element będący głową, a nie wybrany przeze mnie.
To nie ma znaczenia, bo implementujesz jakiś OGÓLNY interfejs! Czyli mówisz ze twoja klasa dostarcza taką funkcjonalność. Zasada L z SOLID się kłania. Jeśli chcesz mieć KolejkęCykliczną to zrób interfejs CyclicQueue<T>
który ma tylko te metody które mają sens dla tej kolejki. Tutaj znowu kłania się Interface Segregation Principle. Po co chcesz implementować BlockingQueue<E>
skoro twoja klasa w ogóle do tego interfejsu nie pasuje? o_O
Problem w tym, że muszę dziedziczyć po BlockingQueue<E>
, więc stworzenie dodatkowego interfejsu nie zmieni mojej sytuacji, bo i tak muszę przesłonić wszystko. Prawdopodobnie zostanę przy wypisaniu wszystkich metod, wywoływaniu metod mających sens wewnątrz metod "bezsensownych" i oznaczeniu tych drugich jako "depracated".
@niepamietamloginu: na pewno musisz implementowac BlockingQueue? Czemu tak?
Niestety. Zostało to narzucone w treści zadania.
Ale właściwie czemu uważasz że np. usuwanie losowego elementu "nie ma sensu"? Bo nie da sie tego wykonać w O(1)? LinkedList też na to pozwala, mimo że też w praktyce tylko head i tail można usunać w O(1), a losowe usuwanie to O(n).
Jeśli masz w treści zadania ze to ma być BlockingQueue to musisz zaimplementować wszystkie metody.
Możesz też spróbować dziedziczyć z klasy AbstractQueue<T>
i wtedy nie musisz nadpisywać metody remove(Object)
i paru innych.
Słowo klucz – delegacja. Zamiast samodzielnie implementować wszystkie metody, wydeleguj je do istniejącej implementacji:
class MyCyclicQueue<T> implements BlockinigQueue<T>, CyclicQueue<T> {
private final BlockinigQueue<T> delegate;
public CyclicQueue(BlockinigQueue<T> delegate){
this.delegate = delegate;
}
public boolean add(T t){
return delegate.add(t);
}
}
Do tego zdefiniuj interfejs CyclicQueue
, który będzie zawierał tylko metody, które masz zamiar zaimplementować samodzielnie – z uwzględnieniem odpowiednich nazw. Nadal nie będzie to 100% SOLID, ale już będzie lepiej.