In this article I will try to introduce two principles of good programming, namely SOLID and DRY. On the Internet you can find many technical descriptions and examples of the use of SOLID and DRY. I will present these concepts from a less technical perspective so that they are understandable to people who are just starting their adventure with programming or are wondering whether to learn programming, especially in Java.

Why are the principles of good programming so important? Is the integrated development environment IDE e.g. IntelliJ IDEA shouldn't do this automatically for the developer? Unfortunately, or perhaps fortunately, this is not the case, when writing code, a programmer must follow the set rules and standards and at the same time be creative. The rules of good programming allow you to create good, readable and easy-to-modify code.

Code written in any programming language should read like a good book. A person reading a book would not want to be bored with exact copies of book fragments placed in many places in the same book (DRY - Don't Repeat Yourself). It would be difficult to read a book that has mixed chapters, the action is not coherent and you get the impression that the author probably didn't know what he was writing (SOLID).

It's not about a creative form of presenting the book or a new type of novel. Let's assume that the book has five chapters and each of them, read in sequence, creates a cause and effect sequence. Now imagine a situation where the book publisher mixed up the chapters and put together the book with the chapters in random order. Reading such a book won't be impossible, but it definitely won't be fun.

It's the same with source code, when a programmer reads someone else's code, he or she must first arrange the code he or she is reading chronologically in his head (code sections), then find the characters in the book (variables) and understand how they interact (methods) with each other ( dependence). Correctly constructed A Java class according to Oracle should have members in the following order:
– constants,
– variables,
– constructors,
– methods,
– first constants, variables and public methods, then private ones.

public class GoodBook {
    public static final int pages = 120;

    private int currentPage = 1;
    private int progress = 0;

    public GoodBook() {
    }

    public void nextPage() {
        currentPage++;
    }

    private void saveProgress() {
        // calculate reading progress...
    }
}
public class BadBook {

    // prywatna metoda
    private void saveProgress() {
        // calculate reading progress...
    }
 
   // publiczna stała
    public static final int pages = 120;

    // konstruktor
    public GoodBook() {
    }

    // prywatne zmienne
    private int currentPage = 1;
    private int progress = 0;

    // publiczna metoda
    public void nextPage() {
        currentPage++;
    }
}

DRY, czyli ang. Don’t Repeat Yourself, pol. Nie powtarzaj się, dotyczy zarówno samego kodu jak i czynności wykonywanych przez programistów. Wiele czynności wykonywanych przez programistę można zautomatyzować, czyli np. napisać niewielki program, skrypt, który wykona automatycznie powtarzalne czynności. Skrypty dla Windows to batch file, a dla Linux shell script. Jeżeli chodzi o sam kod, to podstawowym sposobem na uniknięcie duplikowania kodu jest wydzielanie powtarzających się fragmentów kodu do metod, przez metodę rozumiemy blok kodu, który dla wprowadzanych danych zwraca określony wynik.

SOLID określa podstawowe założenia programowania obiektowego, gdzie każda litera oznacza konkretną zasadę programowania. Tak jak pisałem w Internecie można znaleźć bardzo dużo przykładów zastosowania SOLID, np. w języku Java – Baeldung.com, java2blog.com, howtodoinjava.com. Ja skupię się na wyjaśnieniu tych reguł dla osób zaczynających przygodę z programowaniem w języku Java.
S – Single responsibility principle,
O – Open-closed principle,
L – Liskov substitution principle,
I – Interface segregation principle,
D – Dependency inversion principle.

S jak zasada jednej odpowiedzialności – wyobraźmy sobie sytuację, w której jedna osoba (klasa Java) prowadzi biuro rachunkowe, ale jednocześnie jest jego właścicielem. Czynności które wykonuje (metody w klasie Java), to m.in. obsługa księgowa firm, zatrudnianie pracowników, marketing i pozyskiwanie nowych klientów. Ciężko jest zweryfikować, co i kiedy tak na prawdę robi (testy jednostkowe dla klasy Java, np. JUnit) ta osoba. W sytuacji, gdy właściciel zrezygnuje z prowadzenia swojego biura rachunkowego (zmienia kod metody w klasie Java), zmiany należy wprowadzić we wszystkich firmach (klasach Java), które korzystały z jego usług, czyli znaleźć nowe biuro rachunkowe. Podobnie jest z klasami w kodzie Java, jeżeli jedna klasa robi za dużo, to zmiana jednej jej funkcji wpływa na wszystkie klasy korzystające z tej funkcji.

O jak zasada otwarte-zamknięte – bardzo dobrze obrazują to elementy, które można modyfikować, rozszerzać bez zmiany samego elementu. Dla przykładu firma obsługująca paczkomaty (klasa języka Java) chce obsługiwać nowe większe paczki (metoda w klasie Java), dla których nie ma miejsca w obecnych paczkomatach (algorytm obsługi paczek w klasie Java). Zmiana wszystkich istniejących paczkomatów byłaby bardzo kosztowna, a czasem niemożliwa. Dlatego dobrym rozwiązanie jest takie zaprojektowanie paczkomatu (klasy języka Java), aby można było bez zmiany samego paczkomatu dostawić kolejny element, moduł, który będzie obsługiwał większe paczki (algorytm obsługi paczek w klasie Java pozostanie ten sam). Tak samo jest z klasami języka Java, projektujemy je tak, aby można było je wykorzystywać dla nowo pojawiających się elementów, bez konieczności modyfikacji kodu samej klasy.

L jak zasada podstawienia Liskov – przykładem może być koło rowerowe (klasa języka Java), które jeżeli ma odpowiednie wymiary (atrybuty w klasie języka Java), średnicę, grubość i wysokość bieżnika można zamontować w dowolnym rowerze, do którego koło będzie pasować. Oczywiście koła mogą mieć inne dodatkowe cechy (atrybuty w klasie języka Java) takie jak, np. rodzaj i kolor bieżnika, dodatkowe szprychy, odblaski. Ale nadal będą spełniać swoją funkcję jako koła. Podobnie jest z klasami języka Java dobrze przemyślana klasa może wymieniać jeden element (atrybuty w klasie języka Java) na inny pochodzący (dziedziczący) z tej samej grupy elementów.

I jak zasada segregacji interfejsów – krawiec (interfejs w języku Java) biorąc miarę na marynarkę dla mężczyzny lub na żakiet dla kobiety w inny sposób obsługuje (różne metody w różnych interfejsach w języku Java) każdą osobę. Mężczyzna (klasa implementująca interfejs języka Java) nie musi wiedzieć jakie czynności wykonuje krawiec dla kobiety (klasa implementująca intrfejs języka Java) i odwrotnie, ich interesuje usługa (metody w interfejsie w języku Java) jaką wykonuje krawiec. To samo dotyczy segregacji interfejsów. Każdy interfejs musi być uszyty na miarę konkretnego klienta, czyli klasy, która będzie go obsługiwać.

D jak zasada odwrócenia zależności – zasadę tę dobrze obrazuje roczny przegląd samochodu i sprawy z tym związane. Jadąc na przegląd do serwisu (framework z Inversion of Control, IoC) nie musimy znać wszystkich czynności, które wchodzą w skład przeglądu oraz wiedzieć która osoba (zależności między klasami języka Java), co i kiedy będzie wykonywać. Takie rzeczy wie serwis (IoC), do którego jedziemy na przegląd, to w serwisie wiedzą jakie czynności, w jakiej kolejności należy wykonać i kto ma wykonać (klasy w języku Java). My tylko przyjeżdżamy do serwisu (korzystamy z framework’u z Inversion of Control, IoC) i podajemy numery rejestracyjne, markę, model oraz przebieg samochodu. W kodzie języka Java sprawa wygląd tak samo, korzystamy z framewokr’a np. Spring Framework, który implementuje mechanizm odwrócenia zależności (ang. Inversion of Control) i to on za nas wie kiedy i jak stworzyć obiekty, przekazać innym obiektom oraz kiedy je usunąć.