Six Design Principles: Building Elegant, Maintainable, and Scalable Software

Six design principles: Build elegant, maintainable and scalable software

  • Single Responsibility Principle
  • Open-Closed Principle
  • Liskov Substitution Principle
  • Dependency Inversion Principle
  • Interface Segregation Principle
  • Composite Reuse Principle

Design principles are basic guidelines used in software engineering to guide and evaluate design decisions. Following these principles helps create software systems that are highly cohesive, low coupling, and easy to maintain. In this article, we will introduce six commonly used design principles, namely the single responsibility principle, the open and closed principle, the Liskov substitution principle, the dependency inversion principle, the interface isolation principle and the composition reuse principle, and explain their application in detail through examples and benefits.

Single Responsibility Principle

Definition: The single responsibility principle requires that a class should have only one reason for change. In other words, a class should have only one responsibility.

Application scenario: The single responsibility principle helps ensure the high cohesion of a class and reduces the complexity of modifying a class. Good for breaking down complex classes into smaller, more manageable parts.

Example: A logging class can be responsible for logging, but should not be responsible for handling configuration.

class Logger {<!-- -->
    public void log(String message) {<!-- -->
        //Record log to file
    }
}

class ConfigurationManager {<!-- -->
    public void loadConfig() {<!-- -->
        //Load configuration
    }

    public void saveConfig() {<!-- -->
        //Save configuration
    }
}

Open-Closed Principle

Definition: The open-closed principle requires that software entities (classes, modules, functions, etc.) should be open to extension but closed to modification. That is, new functionality should be introduced by extending existing code rather than modifying it.

Application scenarios: The open and closed principle encourages the use of abstraction, interfaces and polymorphism to achieve scalability and reduce modifications to existing code. Suitable for systems with frequently changing requirements.

Example: Use the Abstract Factory pattern to create extensible graphical user interfaces.

interface Button {<!-- -->
    void click();
}

class WindowsButton implements Button {<!-- -->
    public void click() {<!-- -->
        System.out.println("Windows button clicked.");
    }
}

class MacButton implements Button {<!-- -->
    public void click() {<!-- -->
        System.out.println("Mac button clicked.");
    }
}

Liskov Substitution Principle

Definition: The Liskov Substitution Principle requires that a subclass can replace its base class without affecting the correctness of the program. That is, subclasses should inherit the behavior of the base class but can extend or modify that behavior.

Application scenario: The Liskov substitution principle helps ensure code consistency and can be achieved through inheritance and polymorphism. Suitable for creating abstract base classes and concrete subclasses.

Example: Create shapes of different shapes using the Liskov substitution principle.

class Shape {<!-- -->
    public void draw() {<!-- -->
        // draw graphics
    }
}

class Circle extends Shape {<!-- -->
    public void draw() {<!-- -->
        // draw a circle
    }
}

class Rectangle extends Shape {<!-- -->
    public void draw() {<!-- -->
        // draw rectangle
    }
}

Dependency Inversion Principle

Definition: The dependency inversion principle requires that high-level modules should not depend on low-level modules, but both should depend on abstractions. Abstractions should not depend on details, details should depend on abstractions.

Application scenarios: The dependency inversion principle achieves loose coupling through interface-oriented programming, reducing direct dependencies between modules. Suitable for implementation using interfaces, abstract classes and dependency injection.

Example: Create a notification system using the dependency inversion principle so that both the sender and receiver depend on the notification interface.

interface Notification {<!-- -->
    void sendNotification(String message);
}

class EmailNotification implements Notification {<!-- -->
    public void sendNotification(String message) {<!-- -->
        // Send email notification
    }
}

class SMSNotification implements Notification {<!-- -->
    public void sendNotification(String message) {<!-- -->
        //Send SMS notification
    }
}

Interface Segregation Principle

Definition: The interface isolation principle states that clients should not be forced to rely on interfaces they do not use. A class should not be forced to implement an interface it does not need.

Application scenarios: The principle of interface isolation helps avoid huge interfaces and improve the maintainability and readability of the code.

. Suitable for decomposing large interfaces into multiple small interfaces.

Example: Split the large interface Worker into multiple small interfaces, such as Worker, Eater and Sleeper .

interface Worker {<!-- -->
    void work();
    void eat();
    void sleep();
}

class Employee implements Worker {<!-- -->
    public void work() {<!-- -->
        // employee work
    }

    public void eat() {<!-- -->
        // Employees eat
    }

    public void sleep() {<!-- -->
        // Employees sleep
    }
}

class Robot implements Worker {<!-- -->
    public void work() {<!-- -->
        // robot work
    }

    public void eat() {<!-- -->
        //The robot does not need to eat
    }

    public void sleep() {<!-- -->
        //The robot does not need to sleep
    }
}

Composite Reuse Principle

Definition: The principle of composition and reuse requires that new functions be implemented by combining or aggregating existing classes, rather than by inheriting existing classes.

Application scenarios: The principle of composition and reuse helps reduce the complexity and coupling of inheritance and build new functionality by combining existing classes. Suitable for creating reusable modules.

Example: Use the compositional reuse principle to create a universal logger that can log to a file, database, or console.

interface Logger {<!-- -->
    void log(String message);
}

class FileLogger implements Logger {<!-- -->
    public void log(String message) {<!-- -->
        //Record log to file
    }
}

class DatabaseLogger implements Logger {<!-- -->
    public void log(String message) {<!-- -->
        //Record log to database
    }
}

class ConsoleLogger implements Logger {<!-- -->
    public void log(String message) {<!-- -->
        //Record logs to the console
    }
}

These six design principles are key guiding principles for building high-quality, maintainable, and scalable software. By understanding and following these principles, developers can more easily create robust software systems. When these principles are combined, they provide a powerful design framework that helps solve complex programming problems.

Copyright Statement:
Original blogger: Conan the Niu coo coo
Blogger’s original link: https://keafmd.blog.csdn.net/
Personal blog link: https://www.keafmd.top/

If this helps you, please click the like button below to support!
[Haha][Hold fist]


Come on!

Work together!

Keafmd

Thank you for your supportNiuxiao Conan, looking forward to you Three consecutive + follow~~

keep accumulate for my dream【share encouragement】

↓ ↓ ↓ ↓ ↓ ↓