Klasa abstrakcyjna a interfejs

0

Dzień dobry. Spotkałem się z twierdzeniem, że klasa abstrakcyjna to szkielet (rozumiem to jako same definicje metod). Z drugiej strony, czytałem, że jeśli klasa abstrakcyjna posiada same metody abstrakcyjne (definicje) to warto ją zastąpić interfejsem. Jakie jest Wasze zdanie na ten temat?

1

Interfejs to kontrakt - opisuje co klasa go implementująca ma robić, podczas gdy klasa abstrakcyjna dodatkowo może opisywać jak dana rzecz się dzieje (tzn. może zawierać stan oraz implementację).

Zwyczajowo podstawą do komunikacji między modułami aplikacji są interfejsy, choć - jak wszędzie - należy podchodzić do tego z głową ;-)

2

W programowaniu zorientowanym obiektowo chodzi o zamodelowanie w kodzie rzeczywistości.

Interfejs definiuje sposób użycia obiektu, który go implementuje - określa jakie ten posiada metody publiczne i jakie parametry przyjmuje. Jeden interfejs mogą implementować różne klasy, które nie są ze sobą w ogóle powiązane.

Czyli można powiedzieć, ze interfejs, to tylko "instrukcja użycia" obiektu.
Klasa abstrakcyjna sama w sobie już coś reprezentuje, ale na bardzo wysokim poziomie abstrakcji - można by powiedzieć ogólnie. Może ona posiadać właściwości i może posiadać zachowania (metody), tak jak każda inna klasa, jednak utworzenie obiektu tej klasy nie ma sensu.

Kiedy używać interfejsów, a kiedy klas abstrakcyjnych?
Powiedziałbym, że wszędzie powinniśmy się starać użyć interfejsów, a tam gdzie jest to niewystarczające to klas abstrakcyjnych (co nie znaczy, że klasa abstrakcyjna nie może implementować interfejsu).

Odpowiadając wprost na Twoje pytanie: jeśli mamy klasę abstrakcyjną, która posiada tylko metody abstrakcyjne, to jest doskonały kandydat do zmiany na interfejs.

[Adam]

0

Rozumiem. Wydaje Mi się, że takie zastosowanie klas abstrakcyjnych jest prawidłowe.

class Client extends ParserClient
{
  ...Zwrócenie wyników metod z HtmlClient i RestClient, w metodach, które może wykorzystać klient w pliku index.php tworzenie obiektu Client np:
    public function clearNameForUser($param)
    {
        return $this->methodWithParserClientOrRestClient($param);
    }
}

abstract class ParserClient extends RestClient
{
  ...Metody Parsowania i to co mają robić
}

abstract class RestClient
{
  ...Metody REST i to co mają robić
}

Jak to rozumiem. W klasie HtmlClient zawarty jest core całej aplikacji, taki backend backendu. Klasy HtmlClient i RestClient są na tyle ogólne, że nie ma sensu tworzenia ich obiektu. Rozdzielenie na klasy ParserClient i RestClient Moim zdaniem ma taki sens, że programista będzie wiedział, gdzie musi szukać metody, jak chce zmienić na przykład jakąś funkcje restową - czyli czytelność kodu. Dlaczego nie stosuję interfejsów w zamiast klas abstrakcyjnych? gdyż nie chcę wszystkiego pchać do klasy Client.

0

Oczywiście mogę się mylić i proszę o wyprowadzenie z błędu i nakierowanie na prawdiłowe tory @Patryk27 @kchteam

0

Nie do końca rozumiem Twój opis. Gdzie w tym wszystkim jest klasa HtmlClient? Czy ona jest na tym samym poziomie co RestClient? Jaka jest różnica między RestClient i HtmlClient?
Nieczytelne dla mnie jest użycie metody methodWithParserClientOrRestClient() - nie wiem co ona może robić. Dlaczego implementacja metody clearNameForUser() oddelegowuje jakąś akcję do jakiejś tajemniczej metody.

Ponadto zwróć uwagę na nazwy klas - klasy abstrakcyjne to z założenia klasy ogólne, zatem ich nazwy również powinny być ogólne, a te, które z nich dziedziczą powinny być bardziej szczegółowe. U Ciebie klasy abstrakcyjne to RestClient i ParserClient, a konkretna to Client, czyli odwrotnie ;)

Powiedz coś więcej o całości problemu, który masz do rozwiązania - wtedy łatwiej będzie powiedzieć coś więcej i poukładać odpowiedzialność klas.

[Adam]

0
class Client extends ParserClient
{
  ...Zwrócenie wyników metod z ParserClient i RestClient, w metodach, które może wykorzystać klient w pliku index.php tworzenie obiektu Client np:
    public function clearNameForUser($param)
    {
        .metoda zdefuniowana w jednej z klas ParserClient lub RestClient.
        return $this->methodWithParserClientOrRestClient($param);
    }
}
 
abstract class ParserClient extends RestClient
{
  ...Metody Parsowania i to co mają robić
}
 
abstract class RestClient
{
  ...Metody REST i to co mają robić
}

Chciałem zrobić tak, aby klasy RestClient i ParserClient zawierały wszystkie metody do zarządzania aplikacją czyli taki jakby Core systemu, mógłbym te wszystkie metody umieścić w jednej klasie o nazwie Core.php i tą klasą rozszerzać klasę Client, wyszłoby na to samo co jest teraz ale chciałem także klasę Core.php podzielić na dwie inne (celem czytelności), zależnie od tego, co robią, w tym przypadku jedna klasa byłaby odpowiedzialna za parsowanie tekstu(ParserClient) i zwracanie wyniku, a druga klasa za wyciąganie danych ze strony w postaci JSON(RestClient). Klasa Client, zawiera metody, które zwracają wyniki metod z klas ParserClient i RestClient.
Założeniem jest, aby użytkownik w pliku index.php, mógł wywołać wybraną metodę przez

$show = new Client();
$show->clearNameForUser($param);

Ale faktycznie, z tego co piszesz, klasa Client powinna być tą abstrakcyjną, a nie tamte. Muszę całą strukturę jeszcze przemyśleć. Bo chciałbym, aby użytkownik musiał tworzyć obiekt tylko jednej klasy(w tym przypadku Client) z której może wywoływać metody zarówno z klas ParserClient jak i RestClient.

0

W Twoim opisie jest kilka rzeczy, które mnie niepokoją ;)

Chciałem zrobić tak, aby klasy RestClient i ParserClient zawierały wszystkie metody do zarządzania aplikacją czyli taki jakby Core systemu, mógłbym te wszystkie metody umieścić w jednej klasie o nazwie Core.php i tą klasą rozszerzać klasę Client

Nie wiem, co ma robić cała Twoja aplikacja, ale niemal na pewno umieszczenie "wszystkich metod do zarządzania" w jednej lub dwóch klasach jest złym pomysłem. Klasy nie powinny być uniwersalne i zawierać wszystkiego - powinny mieć w miarę wąski zakres zadań - jedną odpowiedzialność. Jednocześnie należy dbać o to, żeby klasy nie były zbyt długie - maksymalnie kilkaset linii i parę metod publicznych. Po czym poznać, że klasa "robi za dużo"? Zwykle ma mnóstwo publicznych metod, zupełnie ze sobą nie powiązanych.

Kolejna rzecz - dziedziczenie zazwyczaj nie jest dobrym pomysłem na rozdzielenie odpowiedzialności klas - znacznie lepiej sprawdzi się skomponowanie obiektów, które muszą coś wykonać (kompozycja) - czyli korzystanie z innych klas w danej klasie, zamiast dziedziczenia po nich.

Jeśli już dziedziczysz, to w Twoim przykładzie metoda clearNameForUser() zupełnie niepotrzebnie deleguje zadanie do innej metody z tej samej klasy. Domyślam się, że chodziło Ci o to, żeby metoda nazywała się tak samo, ale miała różną implementację w zależności od... no własnie czego? W takiej sytuacji są dwa wyjścia: albo możesz metodę clearNameForUser() zrobić jako abstrakcyjną w klasie nadrzędnej i ją implementować w podrzędnych, albo jeśli operacja, którą ta metoda ma wykonać jest złożona, zaimplementować ją w klasie nadrzędnej i poskładać wynik jej działania z kilku abstrakcyjnych metod chronionych (wzorzec metoda szablonowa

Podsumowując - być może w ogóle nie potrzebujesz klas abstrakcyjnych, a zamiast tego szereg klas, z których każda będzie odpowiadała za swój kawałek roboty.

[Adam]

1 użytkowników online, w tym zalogowanych: 0, gości: 1