SOLID- SINGLE RESPONSIBILITY PRINCIPLE

SOLID jest to skrót od pięciu głównych założeń programowania obiektowego. Przestrzeganie tych zasad pomaga w pisaniu czystego kodu, który łatwo można modyfikować i utrzymać. W skrócie można te zasady przedstawić w ten sposób:

  • Single responsibility principle (zasada jednej odpowiedzialności)- klasa powinna mieć tylko jedną odpowiedzialność,
  • Open-closed principle (zasada otwarte-zamknięte)- elementy naszego programu tj. klasy, moduły, funkcje itp. powinny być otwarte na rozszerzenie, ale zamknięte na modyfikacje,
  • Liskov substitution principle(Zasada podstawienia Liskov)- jeżeli klasa P dziedziczy po klasie T, to obiekt klasy T powinien być zastępowalny przez obiekt klasy P, bez wpływu na prawidłowe działanie programu
  • Interface segregation principle(zasada segregacji interfejsów)- kilka wyspecjalizowanych interfejsów jest lepszych niż jeden ogólny
  • Dependency inversion principle(zasada odwrócenia zależności)- moduły wyższego poziomu nie powinny zależeć od modułów niższego poziomu, oba powinny zależeć od abstrakcji np. interfejsów. Abstrakcje nie powinny zależeć od detali (konkretnych implementacji). Detale powinny zależeć od abstrakcji.

Single responsibility principle

By program był prosty w zrozumieniu i łatwy do czytania przez innych programistów (po czasie też nas samych), każda klasa powinna być odpowiedzialna tylko za jedną rzecz. Tak samo funkcje lub metody, powinny robić jedną rzecz i mieć jak najmniej kodu by były przejrzyste. Należy starać się unikać w takich funkcjach długich drabinek if/else.

Przykład złego kodu na przykładzie funkcji

    private void setWeatherTemperature(int hour, WeatherForecast weatherForecast){
        String temperatureWithUnit;
        String temperature;
        for (weatherForecast :weatherList) {
            int temperatureAsInt = (int)Math.round(weatherForecast.getTemperature().getValue());
            temperatureWithUnit = temperatureAsInt + "°C";
            temperature = temperatureWithUnit;
            if (hour == 8){
                temperatures.add(temperature);
            }
        }
    }

Poprawiony kod- wydzielenie zwrócenia poprawnego formatowania temperatury do osobnej metody.

 private String returnFormattedTemperature(WeatherForecast weatherForecast){
        String temperatureWithUnit;
        int temperatureAsInt = (int)Math.round(weatherForecast.getTemperature().getValue());
        temperatureWithUnit = temperatureAsInt + "°C";

        return temperatureWithUnit;
    }

    private void setWeatherTemperature(int hour, WeatherForecast weatherForecast){
        String temperature;
        for (weatherForecast :weatherList) {
            temperature = returnFormattedTemperature(weatherForecast);
            if (hour == 8){
                temperatures.add(temperature);
            }
        }
    }

W powyższym przykładzie wydzieliliśmy z funkcji setWeatherTemperature funkcję, która zwracała nam temperaturę w wymaganym formacie, dzięki czemu kod jest bardziej dla nas czytelny i gdy będzie taka potrzeba będziemy mogli użyć funkcji returnFormattedTemperature również w innym miejscu naszej aplikacji.

Przykład tej samej zasady na przykładzie klasy

Poniżej stworzyliśmy klasę Account, której zadaniem oprócz tworzenia konta jest też zapis do pliku. Jest to złamanie zasady pojedynczej odpowiedzialności.

public class Account{
    private String username;
    private String password;
    private String email;
    private String error = "";

    Account(String username, String password, String email){
        this.username = username;
        this.password = password;
        this.email = email;
    }

    public getUsername{
        return username;
    }

     public getEmail{
        return email;
    }

    public void writeToFile(){
        if(ifFileExists()) {
            try {
                Path path = Paths.get("C:\\documents\\data");
                BufferedWriter writer = Files.newBufferedWriter(path, StandardCharsets.UTF_8);
                writer.append(username + "\n");
                writer.append(password + "\n");
                writer.append(email + "\n");
                writer.flush();
            } catch (IOException e) {
                e.printStackTrace();
                error = "Saving error";
            }
        }
    }

    private boolean ifFileExists(){
        File f = new File("C:\\documents\\data\\userData.txt");
        return f.exists() && !f.isDirectory();
    }
}

Poprawną wersją tego programu powinno być stworzenie osobnej klasy, której zadaniem jest zapis danych do pliku. Może to wyglądać w następujący sposób:

public class Account{
    private String username;
    private String password;
    private String email;

    Account(String username, String password, String email){
        this.username = username;
        this.password = password;
        this.email = email;
    }

    public getUsername{
        return username;
    }

     public getEmail{
        return email;
    }

}

public Class DataAccess{
    private String error = "";
    public void writeToFile(List<String> dataToWrite){
        if(ifFileExists()) {
            try {
                Path path = Paths.get("C:\\documents\\data");
                BufferedWriter writer = Files.newBufferedWriter(path, StandardCharsets.UTF_8);
               for (String arg : dataToWrite) {
                    writer.append(arg + "\n");
                    writer.flush();
                }
            } catch (IOException e) {
                e.printStackTrace();
                error = "Saving error";
            }
        }
    }

    private boolean ifFileExists(){
        File f = new File("C:\\documents\\data\\userData.txt");
        return f.exists() && !f.isDirectory();
    }

}

W ten sposób mamy 2 klasy, gdzie każda ma swoje konkretne zadanie, kod naszego programu dzięki temu jest dużo bardziej czytelny i łatwiejszy w utrzymaniu.

W kolejnych wpisach omówione zostaną pozostałe zasady.

Dodaj komentarz