soliD…ne programowanie!

Odwracanie kota ogonem??

Dziś ostatni wpis z serii o solid…nym programowaniu. Na warsztat bieżemy piątą zasadę „D”, czyli Dependency Inversion.

Jak już wcześniej zauważyłeś kolejne zasady SOLID są jakoś ze sobą powiązane. Nie inaczjej jest z dependency inversion. Ostatnia zasada jest bezpośrednim następstwem zasad, Open/Closed oraz Liskov Substitution.

Co zawiera treść omawianej zasady?

Moduły wysokiego poziomu nie powinny być zależne od modułów niskiego poziomu. Oba powinny być zależne od abstrakcji.

Abstrakcje nie powinny być zależne od detali. Detale powinny być zależne od abstrakcji.

Tło i przykład soliD

Wracamy do aplikacji finansowej. Posiadamy dwie klasy typu Account. Klasa BasicAccount pozwala tylko wypłacać środki finansowe. Natomiast klasa PremiumAccount pozwala dodatkowo wykonywać operację związaną z oszczędzaniem.

class BasicAccount {
   private UUID id;
   private BigDecimal balance;

  //konstruktor
  //getter dla pola balance

    public void withdraw(BigDecimal amount) {
        //wykonanie operacji 
    }

class PremiumAccount{
    private UUID id;
    private BigDecimal balance;
    private BigDecimal savings;

  //konstruktor
  //getter dla pola balance
  //getter dla pola savings

    public void withdraw(BigDecimal amount) {
       //wykonanie operacji 
    }

    public void saveMoney(BigDecimal savingAmount) {
	//wykonaj operację oszczędzania
    }

}

Chcemy stworzyć klasę managera, który mógłby wykonywać operację wypłacania funduszy na dowolnym koncie użytkownika.

class  AccountManager {
    private BasicAccount basicAccount;
    private PremiumAccount premiumAccount;

    //konstruktor
    //medoty

}

Trachhhhh…. wpadliśmy w sidła solid!

Właśnie powiązaliśmy zależnościami klasę AccountManager z klasami niższego rzędu!

Aby nasz WithdrawalManager był bardziej “uniwersalny” musi dojść do sytuacji, że nie wie jakie system, ma rodzaje kont. Załóżmy, że nowe wymagania dodają nowy rodzaj konta InvestmentAccount. W sytuacji powyższej, musimy zmienić kod WithdrawalManager’a (i jaką zasadę SOLID, tutaj naruszamy???)

Rozwiązanie

Rozwiązaniem jest stworzenie abstrakcji, która będzie łączyła klasy częścią wspólną.

Stwórzmy interfejs Withdrawal:

inteface Withdrawal {
	void withdraw(BigDecimal amount); 
}

Implementacja interfejsu w klasach wygląda następująco:

class BasicAccount implements Withdrawal {
   private UUID id;
   private BigDecimal balance;

  //konstruktor
  //getter dla pola balance

    @Override
    public void withdraw(BigDecimal amount) {
        //wykonanie operacji 
    }

class PremiumAccount implement WIthdrawal {
    private UUID id;
    private BigDecimal balance;
    private BigDecimal savings;

  //konstruktor
  //getter dla pola balance
  //getter dla pola savings

    @Override
    public void withdraw(BigDecimal amount) {
       //wykonanie operacji 
    }

    public void saveMoney(BigDecimal savingAmount) {
	//wykonaj operację oszczędzania
    }

}

Po implementacji interfejsu Withdrawal WithrdawalManager wygląda następująco:

class  WithdrawalManager {
    private Withdrawal withdrawal;

    public WithdrawalManager(Withdrawal withdrawal) {
	  this.withdrawal= withdrawal;
    }

    public void withdrawMoneyFromAccount(BigDecimal amount) {
	  withdrawal.withdraw(amount);
    }

}

Co się tak naprawdę stało?

Tworzymy więc klasę WithdrawalManager. Wstrzykując abstrakcję, która łączy obydwie klasy kont dochodzimy do  “uniwersalności” klasy WithdrawalManager. Teraz nie przejmujemy się, które zależnościami. Każde konto implementujące interfejs, będzie mogło być obsłużone, przez menadżera wypłat!

public class BankApp {
	public static void main(String[] args) {
		PremiumAccount premiumAccount = new PremiumAccount(id, balance);
		
		WithdrawalManager manager = new WithdrawalManager(premiumAccount);
		manager.withdrawMoneyFromAccount(someMoney);
    }
}

To już jest koniec…. z solid…em!

To już wszystkie zasady SOLID. Poniżej linki do poprzednich wpisów. Mam nadzieję, żę przyniołsy Ci wartość i udało mi się pomóc Tobie w zrozumieniu tych niezmiernie ważnych zasad programowania.

Single Responsibility
Open/Closed
Liskov Substitution
Interface Segregation

Już za tydzień kolejny wpis. Do zobaczenia na Lekcjach Kodu.

Dodaj komentarz

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