Polimorfizm część 2 – but why?


Jest taki Brytyjczyk Simon Sinek, który propaguje odszukanie swojego “why?“, gdzie kluczowa jest właśnie zawsze odpowiedź na to pytanie. No bo każdy wie co robi, no i wie lepiej lub gorzej jak to robić – skoro robi, jednak sens całości zawsze nadaje odpowiedź na pytanie, dlaczego właściwie to robi? – wyczuwam głębię 😉 . Nawiązując do jego koncepcji, myślę że z nauką programowania może wyglądać podobnie. Odpowiedź na pytania dlaczego się coś stosuje? w jakich konkretnie przypadkach najlepiej? wymaga lepszego jakościowo oglądu sytuacji, wynikającego z doświadczenia.

Przyznaję więc, że brakuje mi tego doświadczenia – piszę jak rozumiem i przy tym się uczę.

Czemu służy polimorfizm?

Polimorfizm jest jednym z czterech filarów OOP, w izolacji więc od pozostałych, jest troszkę niezręcznie uzasadniać powody jego zastosowania. W szczególności polimorfizm zależy od abstrakcji, czyli występującego w kodzie uogólnienia. To właśnie na pewnym poziomie odejścia od szczegółu, możliwe jest aby potraktować obiekty stojące poniżej tego uogólnienia, w jednakowy sposób.

Powiedzmy, że mam mały sklepik z ilomaś rodzajami produktów. Rodzaje produktów, to klasy, a konkretne produkty, to ich obiekty. Chciałbym teraz wiele obiektów, różnego rodzaju, na pewnym wyższym poziomie uogólnienia potraktować tak samo – nadać im rabat.

Przykład:

 class Product {
    String name;
    BigDecimal price;

    void discount() {
        price = price.subtract(price.multiply(new BigDecimal("0.20"))).
                setScale(2, RoundingMode.HALF_UP);
    }
}

class Book extends Product {
    private String author;

    public Book(String author, String name, BigDecimal price) {
        this.name = name;
        this.author = author;
        this.price = price;
    }
    @Override
    public String toString() {
        return "Book{" +
                "author='" + author + '\'' +
                ", name='" + name + '\'' +
                ", price=" + price + " zł" +
                '}';
    }
}

class Movie extends Product {
    private String director;

    public Movie(String director, String name, BigDecimal price) {
        this.name = name;
        this.director = director;
        this.price = price;
    }
    @Override
    public String toString() {
        return "Movie{" +
                "director='" + director + '\'' +
                ", name='" + name + '\'' +
                ", price=" + price + " zł" +
                '}';
    }
}

public class App {
    public static void main(String[] args) {
        Product book, movie;
        
        book = new Book("Gregor Clegane","Java in 3 days", new BigDecimal("35.50"));
        movie = new Movie("Someone good", "Java in action", new BigDecimal("45.00"));
        App app = new App();

        app.doDiscount(book);
        app.doDiscount(movie);

        System.out.println("\u001B[33m" +
                "Books and movies with 20% discount!!! \n" +
                "\u001B[0m");
        System.out.println(book);
        System.out.println(movie);
    }
    void doDiscount(Product product) {
        product.discount();
    }
}

Chciałem żeby przykład wyszedł trafnie, zgrabnie i poprawnie, nie jestem pewien czy się w pełni udało – czasem chcesz tak, wychodzi śmak.

Użyłem klasy BigDecimal do zakodowania ceny. Przy tej okazji mały szczególik dotyczący jej stosowania – używaj typu String, nie double do zapisu ceny. No a jeżeli coś tu potrzebujesz zrozumieć, to najlepiej wklej kod do swojego IDE i zobacz na spokojnie co się dzieje. No chyba, że takie podstawy to dawno opanowane, ale czytasz bo Cię post zainteresował, to super! – dodaj komentarz co myślisz 😉 .

Programik składa się z klasy bazowej Produkty oraz dwóch klas po niej dziedziczących – Movie i Book. W klasie bazowej definiuję metodę discount(), która będzie robiła 20% zniżki na polach price zawierających się w obiektach książek i filmów. Pola pricename są automatycznie, przez dziedziczenie dostępne dla obiektów klas rozszerzających. W klasie App poprzez użycie jednej pomocniczej metody doDiscount(), korzystającej z napisanej w klasie bazowej discount(), mogę obniżać cenę dla wszystkich obiektów dowolnego typu rozszerzającego klasę Product. Mogę również dołożyć następne klasy produktów i wszystkie je potraktować tak samo, jak już istniejące. Implementacja natomiast dołożonych klas, których instancjami będą obiekty – produkty, nie powinna mieć wpływu na pozostałe klasy oraz cały program.

Polimorfizm pozwala na pewnym wyższym poziomie uogólnienia, traktować obiekty różnego typu, w jednakowy sposób.

Dokładanie czegoś nowego z jego bardziej szczegółową implementacją na niższym poziomie uogólnienia, nie powinno mieć wpływu na resztę, ale powinno zostać potraktowane jak reszta.

Rozumiem więc, że takie podejście służy większej swobodzie w modelowaniu rzeczywistości za po mocą kodu. Celem natomiast jest zwiększenie zdolności adaptowania się oprogramowania do zachodzących zmian w prawdziwym życiu – lepsza rozszerzalność.

Warning! Tego nie pisał programista, ale dev-wannabe. Jak coś poknociłem, chętnie się o tym dowiem.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *