Sobes.tech
Middle+

Ako chápete princípy SOLID na základe vašich skúseností?

sobes.tech AI

Odpoveď od AI

SOLID — súbor piatich princípov objektovo orientovaného návrhu (OOD), ktoré pomáhajú vytvárať flexibilné, rozšíriteľné a ľahko udržiavateľné systémy. Na základe svojich skúseností chápem každý princíp takto:

  1. S - Single Responsibility Principle (Princíp jedinej zodpovednosti): Každá trieda by mala mať len jeden dôvod na zmenu. To znamená, že trieda by mala byť zodpovedná len za jednu konkrétnu funkciu alebo časť systému. To uľahčuje pochopenie triedy, jej testovanie a modifikáciu, minimalizujúc vedľajšie efekty pri zmenách.

    class ReportGenerator {
        // Hlavná zodpovednosť: generovanie správy
        public void generateReport(Data data) {
            // Logika generovania správy
        }
    
        // Porušenie SRP: ak sa táto trieda zaoberá aj ukladaním správy
        // public void saveReport(Report report) { /* ... */ }
    }
    
  2. O - Open/Closed Principle (Princíp otvorenosti/zatvorenosti): Programové entity (triedy, moduly, funkcie atď.) by mali byť otvorené na rozšírenie, ale zatvorené na zmenu. Toho sa dosahuje pomocou využívania abstrakcií (rozhraní, abstraktných tried) a polymorfizmu. Nová funkcionalita sa pridáva vytváraním nových implementácií existujúcich abstrakcií, bez zmeny ich kódu.

    interface Shape {
        double calculateArea();
    }
    
    class Circle implements Shape {
        private double radius;
        public Circle(double radius) { this.radius = radius; }
        @Override
        public double calculateArea() { return Math.PI * radius * radius; }
    }
    
    class Square implements Shape {
        private double side;
        public Square(double side) { this.side = side; }
        @Override
        public double calculateArea() { return side * side; }
    }
    
    class AreaCalculator {
        // Otvorený na rozšírenie: môžeme pridať nové tvary, bez zmeny tejto metódy
        public double calculateTotalArea(Shape[] shapes) {
            double totalArea = 0;
            for (Shape shape : shapes) {
                totalArea += shape.calculateArea();
            }
            return totalArea;
        }
    }
    
  3. L - Liskov Substitution Principle (Princíp substitúcie Liskov): Podtypy by mali byť úplne zameniteľné za svoje základné typy. To znamená, že kód klienta, ktorý pracuje so základným typom, by mal správne fungovať aj s akýmkoľvek jeho podtypom, bez potreby vedieť o konkrétnej implementácii podtypu. V praxi to často znamená, že triedy dediace by nemali porušovať kontrakty stanovené základnou triedou alebo rozhraním.

    class Rectangle {
        protected int width;
        protected int height;
    
        public void setWidth(int width) { this.width = width; }
        public void setHeight(int height) { this.height = height; }
    
        public int getArea() { return width * height; }
    }
    
    class SquareLSP extends Rectangle {
        @Override
        public void setWidth(int width) {
            this.width = width;
            this.height = width; // Porušenie Liskov, ak klient očakáva nezávislosť
        }
    
        @Override
        public void setHeight(int height) {
            this.width = height;
            this.height = height; // Porušenie Liskov
        }
    }
    
  4. I - Interface Segregation Principle (Princíp rozdeľovania rozhraní): Klienti by nemali závisieť od rozhraní, ktoré nepoužívajú. Je lepšie mať veľa malých, špecifických rozhraní, než jedno veľké "tučné" rozhranie. To znižuje väzbu a umožňuje triedam implementovať len tie funkcie, ktoré naozaj potrebujú.

    interface Worker { // "Tučné" rozhranie
        void work();
        void eat();
        void sleep();
    }
    
    interface Workable { // Rozdelené rozhrania
        void work();
    }
    
    interface Feedable {
        void eat();
    }
    
    interface Sleepable {
        void sleep();
    }
    
    class HumanWorker implements Workable, Feedable, Sleepable {
        // Implementuje všetky potrebné rozhrania
        // ...
    }
    
    class RobotWorker implements Workable {
        // Implementuje len Workable, nezávisí od Feedable a Sleepable
        // ...
    }
    
  5. D - Dependency Inversion Principle (Princíp obrátenia závislostí):

    • Moduly vyššej úrovne by nemali závisieť od modulov nižšej úrovne. Oba by mali závisieť od abstrakcií.
    • Abstrakcie by nemali závisieť od detailov. Detaily by mali závisieť od abstrakcií. Tento princíp sa často realizuje pomocou dependency injection, keď závislosti triedy sú jej poskytované zvonku, nie vlastnou triedou. To znižuje väzbu a uľahčuje testovanie.
    // Porušenie DIP: HighLevelModule priamo závisí od LowLevelModule
    class LowLevelModule {
        public void doLowLevelStuff() { /* ... */ }
    }
    
    class HighLevelModule {
        private LowLevelModule lowLevel = new LowLevelModule(); // priama závislosť
    
        public void doHighLevelStuff() {
            lowLevel.doLowLevelStuff();
        }
    }
    
    // Uplatnenie DIP: obidva závisia od abstrakcie
    interface Service {
        void execute();
    }
    
    class ConcreteService implements Service {
        @Override
        public void execute() { /* detaily implementácie */ }
    }
    
    class ClientModule {
        private final Service service; // závislosť od abstrakcie
    
        // závislosť je injektovaná (napríklad cez konštruktor)
        public ClientModule(Service service) {
            this.service = service;
        }
    
        public void doSomething() {
            service.execute(); // pracuje s abstrakciou
        }
    }