SOLID

SOLID cz. I – Zasada pojedynczej odpowiedzialności

By on 7 lutego 2017

SOLID opisuje pięć podstawowych założeń którym powinien kierować się programista pisząc obiektowo, a więc także w Javie.

Pierwszą jest zasada pojedynczej odpowiedzialności (Single Responsibility Principle). To jest nasze S w słowie SOLID. Każdy obiekt powinien mieć tylko jeden cel. Najlepiej tę zasadę opisuje zdanie:

Nie powinien być więcej niż jeden powód, żeby edytować klasę.

Zasada ta powinna tyczyć się również interfejsów i metod.

Podstawowe pytanie jakie się pojawia, to czym jest pojedyncza odpowiedzialność. Odpowiedź na to pytanie wcale nie jest takie proste i wymaga doświadczenia u programisty. Wszystko zależy od założonego stopnia abstrakcji. Jeden programista powie, że elementarną czynnością klasy będzie obsługa pliku, a drugi stwierdzi że obsługę pliku można rozłożyć na dwie klasy, zapis i odczyt. Trzeci programista stwierdzi, że jednak potrzebny jest trzeci poziom abstrakcji, bo zapis może odbywać się do pdf i doc, a odczyt może odbywać się lokalnie jak i zdalnie.

Który z nich ma rację? Nie jestem w stanie tego jednoznacznie stwierdzić. Przy zbyt wielu poziomach abstrakcji możemy utonąć w plikach i znacznie zwiększyć złożoność projektu, a przy niewielu dochodzi do sytuacji że klasy są tzw. boskimi obiektami (god objects) odpowiadającymi za zbyt wiele funkcjonalności w jednej klasie, co utrudnia utrzymanie kodu. Tworzenie zbyt zasobnych klas może powodować, że zmiana jednej funkcjonalności w klasie wymusi zmianę kodu innych  funkcjonalności. Dobrym jest taki podział w którym staramy się zapobiec temu, że jeśli dwóch programistów otrzyma dwa różne taski do wykonania, to na tyle na ile to możliwe nie będą musieli edytować tych samych klas. Warto także zwrócić uwagę, że dobrze skonstruowane moduły o pojedynczej funkcjonalności dają się łatwiej  ponownie wykorzystać w innych fragmentach programu.

Rozważmy prostą klasę o następującej budowie:

class Person{
  String name;
  String surname;
  String email;
  String city;
  String street;
  String numberOfHouse;
  
  public Person(...){
    ...
  }
  
  public bool isValidEmail(String email){
    ...
  }
  
  public void printData(){
    ...
  }
}

Skoro jesteś już po przeczytaniu powyższego wstępu zastanów się co jest z nią nie tak.

Wyraźnie widać trzy odrębne funkcjonalności, klasa przechowuje dane, wyświetla dane oraz weryfikuje ich poprawność.

Zgodnie z zasadą pojedynczej odpowiedzialności powinniśmy te funkcje rozdzielić. Pierwszą wyodrębnijmy funkcjonalność weryfikacji poprawności emaila:

class EmailValidator{
  public bool isValidEmail(String email){
    ...
  }
}

Drugą niech będzie wyświetlenie danych:

class UserInterface{
  public bool printPersonData(Person person){
    ...
  }
}

A trzecią przechowywanie danych:

class Person{
  String name;
  String surname;
  String email;
  String city;
  String street;
  String numberOfHouse;

  public Person(...){
    ...
  }
}

Taki podział ułatwił nam możliwość wielokrotnego wykorzystania metod. EmailValidator tym razem może zostać wykorzystany w dowolnej klasie do sprawdzenia poprawności emaila, a nie tylko poprawności emaila w klasie Person. Podział na mniejsze i prostsze klasy również wspomaga proces testowania, a czasem wręcz sprawia że kod w ogóle jest testowalny.

W klasie Person po poprawkach można zauważyć, że również trzy składowe mogą być wyodrębnione do osobnej klasy. Składowe te to city, street, numberOfHouse, które składają się na adres osoby.

class Address{
  String city;
  String street;
  String numberOfHouse;

  public Address(...){
    ...
  }
}
class Person{
  String name;
  String surname;
  String email;
  Address address;

  public Person(){
    ...
  }
}

Takie wyodrębnienie klasy może w przyszłości ułatwić sprawę kiedy za task dostaniesz wyświetlenie samego adresu, a nie wszystkich danych osoby.

class UserInterface{
  public bool printPersonData(Person person){
    ...
  }

  public bool printAddressData(Address address){
    ...
  }
}

Jako zadanie domowe znajdź na dysku jakiś swój kod mający co najmniej sto linii i spróbuj go przerobić w taki sposób aby spełniał wymogi pojedynczej odpowiedzialności klas.

TAGS
RELATED POSTS

LEAVE A COMMENT