Sobes.tech
Middle+
143

Өз тәжірибеңіз негізінде SOLID қағидаларын қалай түсінесіз?

AI-дан жауап

sobes.tech AI

SOLID — объектті бағдарлама дизайнының бес принципінен тұратын жиынтығы (ООД), олар икемді, кеңейтілген және оңай қолдау көрсетілетін жүйелер құруға көмектеседі. Өз тәжірибеме сүйене отырып, әр принципті осылай түсінемін:

  1. S - Бір жауапкершілік принципі (Single Responsibility Principle): Әр класс тек бір өзгерту себебіне ие болуы керек. Бұл класс нақты бір функцияға немесе жүйенің бір бөлігіне жауапты болуы керек дегенді білдіреді. Бұл класс түсінуді, тестілеуді және өзгерістерді жеңілдетеді, жанама әсерлерді азайтады.

    class ReportGenerator {
        // Негізгі жауапкершілік: есепті құрастыру
        public void generateReport(Data data) {
            // Есепті құрастыру логикасы
        }
    
        // SRP бұзу: егер бұл класс есепті сақтауымен де айналысса
        // public void saveReport(Report report) { /* ... */ }
    }
    
  2. O - Ашық/жабық принципі (Open/Closed Principle): Бағдарламалық объектілер (кластар, модульдер, функциялар және т.б.) кеңейту үшін ашық, бірақ өзгерту үшін жабық болуы керек. Бұл абстракциялар (интерфейстер, абстрактты кластар) және полиморфизм арқылы жүзеге асырылады. Жаңа функционал бұрынғы кодты өзгерместен жаңа жүзеге асырулар арқылы қосылады.

    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 {
        // Кеңейтуге ашық: жаңа фигураларды қосуға болады, бұл әдісті өзгерту қажет емес
        public double calculateTotalArea(Shape[] shapes) {
            double totalArea = 0;
            for (Shape shape : shapes) {
                totalArea += shape.calculateArea();
            }
            return totalArea;
        }
    }
    
  3. L - Барбара Лисковтің ауыстыру принципі (Liskov Substitution Principle): Төменгі типтер толықтай өздерінің негізгі типтерімен алмастырылуы керек. Бұл клиенттік код негізгі типпен жұмыс істеген кезде, ол оның кез келген төменгі типімен де дұрыс жұмыс істеуі керек дегенді білдіреді. Практикада бұл мұрагерлік кластар негізгі класс немесе интерфейс анықтаған шарттарды бұзбауы керек дегенді білдіреді.

    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; // LSP бұзу, егер клиент ені мен биіктігін тәуелсіз орнатуды күтсе
        }
    
        @Override
        public void setHeight(int height) {
            this.width = height;
            this.height = height; // LSP бұзу
        }
    }
    
    // Дұрыс LSP тәсілі әдетте иерархияны қайта ойластыруды талап етеді
    // Мысалы, Rectangle және Square үшін бөлек кластар жасау
    
  4. I - Интерфейс бөліну принципі (Interface Segregation Principle): Клиенттер қолданбайтын интерфейстерге тәуелді болмауы керек. Кішкентай, нақты интерфейстерді көп жасау жақсырақ, олар бір функцияны ғана орындауы керек. Бұл байланысты азайтады және кластарға тек қажетті функцияларды жүзеге асыруға мүмкіндік береді.

    interface Worker { // "жирный" интерфейс
        void work();
        void eat();
        void sleep();
    }
    
    interface Workable { // бөлінген интерфейстер
        void work();
    }
    
    interface Feedable {
        void eat();
    }
    
    interface Sleepable {
        void sleep();
    }
    
    class HumanWorker implements Workable, Feedable, Sleepable {
        // Барлық қажетті интерфейстерді жүзеге асырады
        // ...
    }
    
    class RobotWorker implements Workable {
        // Тек Workable-ды жүзеге асырады, Feedable және Sleepable-ға тәуелді емес
        // ...
    }
    
  5. D - Тәуелділікті инверсиялау принципі (Dependency Inversion Principle):

    • Жоғары деңгейлі модульдер төмен деңгейлі модульдерге тәуелді болмауы керек. Екеуі де абстракцияларға тәуелді болуы керек.
    • Абстракциялар детальдарға тәуелді болмауы керек. Детальдар абстракцияларға тәуелді болуы керек. Бұл принцип жиі тәуелділікті енгізу (Dependency Injection) арқылы жүзеге асырылады, мұнда класс тәуелділіктері сырттан беріледі, ал өздігінен емес. Бұл байланысты азайтады және тестілеуді жеңілдетеді.
    // DIP бұзу: HighLevelModule тікелей LowLevelModule-ге тәуелді
    class LowLevelModule {
        public void doLowLevelStuff() { /* ... */ }
    }
    
    class HighLevelModule {
        private LowLevelModule lowLevel = new LowLevelModule(); // Тікелей тәуелділік
    
        public void doHighLevelStuff() {
            lowLevel.doLowLevelStuff();
        }
    }
    
    // DIP қолдану: Екеуі де абстракцияға тәуелді
    interface Service {
        void execute();
    }
    
    class ConcreteService implements Service {
        @Override
        public void execute() { /* Іске асыру детальдары */ }
    }
    
    class ClientModule {
        private final Service service; // Абстракцияға тәуелділік
    
        // Тәуелділік енгізіледі (мысалы, конструктор арқылы)
        public ClientModule(Service service) {
            this.service = service;
        }
    
        public void doSomething() {
            service.execute(); // Абстракциямен жұмыс істейді
        }
    }
    

SOLID принциптерін сақтау менің тәжірибемде таза, кеңейтілген және қолдауға оңай код құруға әкелді, бұл ұзақ мерзімді бағдарламалық өнімдерді дамыту үшін өте маңызды.