Ktoś kiedyś powiedział, że przestrzeganie zasad SOLID jest tym, co czyni z klepaczy kodu prawdziwych programistów. Wiedzeni zatem poczuciem obowiązku i oczywiście ciekawością, przyjrzymy się każdej z nich po kolei, by przekonać się czy i jak wpływają na jakość naszego kodu. Dziś sprawdzimy co kryje się pod literką S :)

Pierwsza z zasad SOLID głosi, że każda klasa powinna mieć tylko jedną odpowiedzialność. W kontekście programowania obiektowego odpowiedzialność powinna być rozumiana jako domniemany powód do zmiany w przyszłości. Zilustrujemy to przykładem prostej klasy odpowiedzialnej za konto bankowe:

public class BankAccount
{
    public string Number { get; set; }

    public decimal Balance { get; set; }

    public decimal CalculateInterests()
    {
        throw new NotImplementedException();
    }
}

 

Wyobraźmy sobie teraz, że otrzymujemy od klienta change request, który przewiduje dodanie do klasy pola zawierającego numer karty przypisanej do konta oraz zmianę sposobu obliczeń odsetek. Intuicyjnie wyczuwamy, że to dwie różne osie zmian - dotyczą zupełnie różnych funkcjonalności konta bankowego. Klasa odpowiedzialna za konto nie powinna zajmować się naliczaniem odsetek, to odpowiedzialność innej klasy, np. kalkulatora odsetek. Zróbmy więc kilka zmian:

public interface IBankAccount
{
    string Number { get; set; }

    decimal Balance { get; set; }
}

public class BankAccount : IBankAccount
{
    public string Number { get; set; }

    public decimal Balance { get; set; }
}

 

Teraz nasza klasa jest odpowiedzialna wyłącznie za właściwości konta. Jak wspominałem, miejsce na metody związane z odsetkami znajdzie się w klasie kalkulatora odsetek. W przypadku kolejnych zmian w obliczeniach, nie będziemy zmuszeni modyfikować klasy konta. Dodajmy do projektu następujący interfejs i dziedziczącą po nim klasę kalkulatora:

public interface ICalculator
{
    decimal CalculateInterests(IBankAccount bankAccount);
}

public class Calculator : ICalculator
{
    public decimal CalculateInterests(IBankAccount bankAccount)
    {
        throw new NotImplementedException();
    }
}

 

Jak należy zatem rozumieć Zasadę Pojedynczej Odpowiedzialności? Mniej-więcej tak, że wszystkie pola i metody, które tworzą klasę powinny mieć ścisły związek z jej przeznaczeniem. Można jednak sformułować to nieco inaczej: w klasie nie powinny znajdować się żadne pola i metody, które nie mają z nią ścisłego związku - czyli dotyczą innego podmiotu. Chroni nas to przed kruchością, tzn. “tendencją oprogramowania do psucia się w wielu miejscach po wprowadzeniu pojedynczej zmiany” - cytując Uncle Boba, który w swojej książce “Agile Software Development: Principles, Patterns and Practices” zdefiniował SOLID. Wspomnę o niej jeszcze nie raz, a na dziś to tyle, nie pozostało nic prócz ćwiczeń! ;)