1. Wspólna klasa bazowa

Dzisiaj będziemy cieszyć się szwedzkim stołem ciekawych tematów. Pamiętasz, jak wprowadziliśmy ChessItemklasę bazową, aby uprościć wszystkie klasy reprezentujące figury szachowe? Mam taką nadzieję 🙂

Teraz wyobraź sobie, że każdy kawałek ma draw()metodę, która obsługuje rysowanie go na ekranie. Wywołujesz draw()metodę, a element rysuje się w swoich bieżących współrzędnych. Wygodnie byłoby przenieść tę metodę do klasy bazowej.

Gdyby draw()metoda znajdowała się w klasie bazowej ChessItem, moglibyśmy nadpisać ją w klasach figur i napisać elegancki kod w następujący sposób:

class ChessBoard
{
   public void drawAllChessItems()
   {
      // Add the pieces to the list
      ArrayList<ChessItem> items = new ArrayList<ChessItem>();
      items.add(new King());
      items.add(new Queen());
      items.add(new Bishop());

      // Draw them regardless of their type
      for(ChessItem item: items)
      {
         item.draw();
      }
   }
}

Wprowadzając ChessItemklasę bazową, udało nam się znacznie uprościć kod: nie ma potrzeby wywoływania metod każdej klasy z osobna, możemy łatwo przechowywać wszystkie obiekty w jednej kolekcji itp.

Ale tutaj pojawia się ciekawe pytanie: co powinna narysować na ekranie draw()metoda zadeklarowana bezpośrednio w klasie? ChessItemW końcu w szachach nie ma takiej figury, więc nie ma co rysować.

Dokładnie tak. Co więcej, tworzenie ChessItemobiektów bezpośrednio nie ma sensu. To nie jest figura szachowa, ale raczej abstrakcja klasa, którą stworzyliśmy dla naszej wygody. Tak działa abstrakcja w OOP : przenosimy ważne dane i metody (te wspólne dla wszystkich elementów) do klasy bazowej i zachowujemy ich różnice w oddzielnych klasach potomnych.


2. Zajęcia abstrakcyjne

Klasy abstrakcyjne

W takich sytuacjach Java ma specjalny rodzaj klasy: klasę abstrakcyjną . Zostały zaprojektowane tak, aby ułatwić programistom pracę z podobnymi klasami i zmniejszyć ilość powielanego w nich kodu.

Oto trzy rzeczy, które warto wiedzieć o klasach abstrakcyjnych.

Metoda bez implementacji

Klasa abstrakcyjna może mieć deklarację metody bez implementacji. Właśnie to sprawia, że ​​metoda jest abstrakcyjna. Treść metody jest po prostu zastępowana średnikiem. A przed nazwą metody piszemy słowo abstractkluczowe. Przykład:

public abstract class ChessItem
{
   public int x, y; // Coordinates
   private int value; // The piece's value
   public int getValue() // Ordinary method that returns value field
   {
      return value;
   }

   public abstract void draw(); // Abstract method. The implementation is missing.
}

Klasa abstrakcyjna

Każda metoda bez implementacji jest oznaczona słowem kluczowym abstract. Jeśli klasa ma choć jedną metodę abstrakcyjną, klasa jest również oznaczona słowem abstractkluczowym.

Zakaz tworzenia obiektów

Nie można tworzyć obiektów klasy abstrakcyjnej . Taki kod po prostu się nie skompiluje.

Kod Opis
ChessItem item = new ChessItem();
item.draw();
Ten kod się nie kompiluje :
ChessItem item = new Queen();
item.draw();
Ale możesz to zrobić

Dziedziczenie klasy abstrakcyjnej

Jeśli twoja klasa dziedziczy klasę abstrakcyjną, musisz nadpisać wszystkie odziedziczone metody abstrakcyjne, czyli napisać dla nich implementację. W przeciwnym razie sama twoja klasa również będzie musiała zostać uznana za abstrakcyjną.

Jeśli klasa ma choćby jedną niezaimplementowaną metodę zadeklarowaną bezpośrednio w niej lub odziedziczoną z klasy nadrzędnej, wówczas klasa jest uważana za abstrakcyjną.

I dlaczego to wszystko jest konieczne? Dlaczego potrzebne są klasy abstrakcyjne? Czy zamiast tego nie można użyć zwykłych? Czy zamiast abstrakcyjnych metod nie moglibyśmy po prostu napisać dwóch pustych nawiasów klamrowych jako treści metody?

Moglibyśmy. Ale te ograniczenia są podobne do privatemodyfikatora. Używamy słowa kluczowego private, aby celowo uniemożliwić innym programistom bezpośredni dostęp do danych i zmusić ich do używania tylko naszych publicznych metod podczas pisania klas.

Tak samo jest z klasami abstrakcyjnymi. Autor klasy abstrakcyjnej nie chce, aby obiekty klasy były tworzone. Zamiast tego autor oczekuje, że metody abstrakcyjne będą dziedziczone z klasy abstrakcyjnej, a następnie nadpisywane.

Zaleta tego podejścia jest łatwo widoczna w dużych projektach. Im więcej masz klas, tym wyraźniej musisz określić ich role. Korzyści z takiego podejścia zobaczysz w niedalekiej przyszłości. Wszystko przez to przechodzi.