Nie SOLID-nie #04: Interface Segregation Principle

N

Seria zainspirowana bardzo dobrym kursem SOLID od Jarka Stadnickiego, dostępnym na platformie Udemy – SOLID praktyczny kurs

Nie jest to reklama, ani żadna afiliacja. Wyrażam swoje zdanie 🙂 . Polecam zerknąć. Jarek za pomocą obrazowych, trafnych porównań tłumaczy poszczególne zasady.

 


Spis postów z serii Nie SOLID-nie:

  1. Nie SOLID-nie #01: Single Responsibility Principle
  2. Nie SOLID-nie #02: Open Close Principle
  3. Nie SOLID-nie #03: Liskov Substitution Principle
  4. Nie SOLID-nie #04: Interface Segregation Principle
  5. Nie SOLID-nie #05: Dependency Inversion Principle

Krótko o

Dzisiaj, bez oficjalnej definicji. Jedynie wspomnę o tym, o czym – w kontekście Interface Segregation Principle – należy wspomnieć.

Zasadniczo ISP mówi o „rozczłonkowaniu” dużych, wielozadaniowych kontraktów i interfejsów na mniejsze, posiadające jedną konkretną odpowiedzialność. Dzięki czemu, każdy element który konsumuje taki interfejs, ma dostęp tylko do określonej funkcjonalnośći. Intencją ISP jest utrzymanie systemu luźnych powiązań między obiektami, ułatwienie konserwacji systemu, testowania, czy refaktoringu.

Reguła ta została sformułowana i użyta po raz pierwszy przez Roberta C. Martina, który zaproponował tego typu rozwiązanie pracując nad produktem dla Xeroxa. W skrócie – podczas pracy nad modyfikowaniem i utrzymaniem systemu dla nowej drukarki Xeroxa, „Uncle Bob” Martin zauważył, że nawet wprowadzenie małej zmiany jest to coraz trudniejsze. Powodem była klasa Bóg, job Bóg…(zwał jak zwał), która wykonywała dosłownie wszystko. Rozwiązaniem tej sytuacji, zaproponowanym przez „Uncle Boba”, było odseparowanie poszczególnych funkcjonalności tej wielkiej, głównej klasy, za pomocą interfejsów które ta klasa implementowała. Dzięki czemu, korzystając z określonego interfejsu (kontraktu) nie korzystał on z całej klasy.

 

Zalety

Zalety stosowania ISP?

  • sposób na enkapsulację, ponieważ udowstępniamy kontrakt/interfejs, nie więcej
  • rozbijanie potrzebnej funkcjonalności na mniejsze interfejsy powoduje, że możemy działać w ramach jednej odpowiedzialności przewidzianej przez kontrakt/interfejs
  • zmniejszamy tzw. coupling, czyli „sztywne” zależności  między klasami, tzn. klasa korzysta z innego obiektu, który implementuje dany kontrakt, jednocześnie, można w jako parametr podać  (wstrzyknąć) inny obiekt tego samego kontraktu
  • kod jest łatwiejszy w utrzymaniu i rozszerzaniu
  • jeśli klasa „BÓG” nie jest naszym tworem, to pisząc adapter, możemy sam adapter ładnie „ointerfejsować” i skorzystać z zalet segregacji interfejsów

 

Anty-przykład

Typowym anty-przykładem – jaki mi przychodzi do głowy w tej chwili – kodu który nadaje się do tego aby użyć na nim ISP są często widywane, zwłaszcza w monolitach, konstrukcje rodzaju – CośTamService które używa CośTamRepository. Tych CośTamService mamy kilka, w zależności od funkcjonalności, czy kontrolera który do nich „sięga”, natomiast CośTamRepository jest jedno i każdy CośTamService korzysta z tej samej klasy.

 

Tym razem bardzo pseudo-kod. Nie chciałem wrzucać niczego produkcyjnego.

Klasa PaymentService „używa” GodRepository

public class PaymentService : IPaymentService
{
    private GodRepository _godRepository;

    public PaymentService(string dataBaseHost)
    {
        InitializeDatabase(dataBaseHost);
    }

    private void InitializeDatabase(string dataBaseHost)
    {
        var db = server.GetDatabase(Constants.DatabaseName);
        _godRepository = new GodRepository(db, Constants.PaymentDatabaseTableName);
    }

    public async Task<Payment> AddPayment(Payment payment)
    {
        await _godRepository.AddPayment(payment);
        return payment;
    }

    public async Task<Payment> GetPayment(string sigleUseCode, string userId)
    {
        return await _godRepository.GetPayment(sigleUseCode, userId);
    }

    public async Task<IEnumerable<Payment>> GetPayments(PaymentStatus paymentStatus)
    {
        return await _godRepository.GetPayments(paymentStatus);
    }
    
    public async Task<IEnumerable<Payment>> GetPayments()
    {
        return await _godRepository.GetPayments();
    }
}

 

Druga klasa, UsersService która również używa tego samego GodRepository

public class UsersService : IUsersService
{
    private GodRepository _godRepository;

    public UsersService(string dataBaseHost)
    {
        InitializeDatabase(dataBaseHost);
    }

    private void InitializeDatabase(string dataBaseHost)
    {
        var db = server.GetDatabase(Constants.DatabaseName);
        _godRepository = new GodRepository(db, Constants.UsersDatabaseTableName);
    }

    public async Task<bool> RegisterUser(string userId)
    {
        return await _godRepository.RegisterUser(userId);
    }

    public async Task<bool> UserExist(string userId)
    {
        return await _godRepository.CheckIfUserExist(userId);
    }

    public async Task<List<User>> GetUserList()
    {
        return _godRepository.GetList();
    }
}

 

Pojawiają się pytania

Co, jeśli chcemy dla każdego CośTamService wywołać metodę z GodRepository, o takiej samej sygnaturze, ale innej logice? Zaczyna się kombinowanie typu tworzenie sztucznych metod które wołają inne metody, ale z jakąś dodatkową logiką. Przekazywanie Typu Action do metody jako parametr, itp.

Code smell

 

Co, jeśli dwa różne CośTamService używają tej samej metody z GodRepository, ale tylko dla jednego z nich trzeba ją zmienić? No to co…dodajemy kolejną? Więcej kodu = większa klasa = klasa BÓG!

Code smell

 

Co, jeśli przychodzi PO do naszego teamu i mówi – „Od dzisiaj udostępniamy nasz GodRepository innemu zespołowi” – a my? My nie chcemy udostępniać wszystkiego. Modyfikatory dostępu? Mają wpływ również na nasz kod przecież. No i kupa…

Code smell

 

 

Stosowanie się do Interface Segregation Principle nie jest remedium na każde zło Świata, ale potrafi ułatwić prace nad kodem nowym i utrzymanie już istniejącego. Samo to, powinno nas skłaniać do wdrażania na codzień ISP. Do zatrzymania się i refleksji – „Czy robię to dobrze”?

Powodzenia!

 

Masz jakieś uwagi? Chcesz coś skomentować?

A zatem do dzieła! Napisz coś od siebie w komentarzach poniżej!

Czołem!

 



 

About the author

Add comment

By Patryk

Autor serwisu

Patryk

Społecznościowe

Instagram

Newsletter



Historycznie

Tagi