Wczoraj mówiliśmy o tym, że funkcja powinna wykonywać jedną rzecz. Świetnym papierkiem lakmusowym jest nazwa funkcji. Jeśli można łatwo nadać jej nazwę i nie zawiera spójników typu i, lub, albo, oraz (lub ich odpowiedników w j. angielskim ) to jesteśmy na dobrej drodze. Funkcje, które wykonują kilka czynności są wprowadzają po prostu w błąd.
A co z klasami?
Single Responsibility Principle mówi, że klasa powinna mieć jedną odpowiedzialność, powinna mieć jeden powód do zmiany. Takie podejście powoduje, że powstaje bardzo dużo za to bardzo małych klas. Małe klasy łatwiej się czyta i łatwiej się je “ogarnia” to nie podlega dyskusji. Co więcej nie budujemy potężnych klas posiadających setki czy nawet tysiące (o zgrozo) linii kodu.
Popatrzmy na taki kawałek kodu
[csharp]
public class Employee
{
private string Name;
private string Surname;
private string SomeOtherData;
private string AboutThisEmployee;
private string fileName;
//…
//…
//…
public void SetFileName()
{
// zapytaj użytkownika o nazwę pliku
fileName = fileNameGivenByTheUser();
}
public void Save()
{
var stream = new XmlStream(fileName);
//..
// ..
// ..
}
public void SerializeToFile(string xmlFileName)
{
fileName = xmlFileName;
var xml = new XDocument();
var xmlStream = new XmlTextWriter(fileName, Encoding.UTF8);
xml.Save(xmlStream);
}
}
[/csharp]
Kod taki jak powyżej powstaje nieintencjonalnie. Powstaje ponieważ klasy robią wiele rzeczy i wielokrotnie wykorzystujemy te same zmienne do różnych rzeczy. Problem w powyższym przypadku jest taki, że wywołując metodę SetFileName a potem Save i ponownie Zapis kod zachowa się tak jak przewidujemy, jednak jeśli w między czasie ktoś skorzysta z metody SerializeToFile to zmieni się zmienna fileName i kolejne wywołanie metody Save zapisze już do innego miejsca niż do tego, które ustawiliśmy w metodzie SetFileName.
Jest jeszcze jeden problem.
Takie pisanie klas powoduje, że często dodając nowy sposób zapisu po prostu dodajemy nową metodę: SerializeToDocx, SerializeToOdt, SerializeToCloud SerializeToWhethever. Dopisujemy kolejne metody a tymczasem klasa puchnie. Przy osiągnięciu masy krytycznej, wprowadzenie jakiejkolwiek zmiany, nawet tej najmniejszej powoduje, że spędzamy godziny na debugowaniu kodu i sprawdzaniu dlaczego nie zawsze działa tak jak byśmy tego chcieli.
Rozwiązaniem jest Single Responsibility Principle – pojedyncza odpowiedzialność. Klasa posiada jedną odpowiedzialność. W powyższym przykładzie aż prosi się aby powstała klasa Pracownik i dwie klasy do zapisu. Jeśli pracownik zamiast wiązać się z konkretną klasą do zapisu, wykorzystywał będzie jedynie interfejs (IZapisz) i metodę zapisz z tego interfejsu, to wówczas będziemy mogli dowolnie dodawać kolejne klasy do zapisywania i wymiennie je stosować. Co więcej dodanie nowego sposobu zapisu czy też zmodyfikowanie istniejącego nie będzie wymagało nawet otwierania klasy Employee. Możemy taką pracę delegować na kogoś innego.
Single Responsibility Principle (SRP) uważam za jedną z nabardziej znaczących dlatego jutro dalej, będziemy wałkować ten temat.