Skip to content

单一职责原则(Single-Responsibility Principle)

内容:一个类最好只做一件事

提高可维护性:当一个类只负责一个功能时,其实现通常更简单、更直接,这使得理解和维护变得更容易。

减少代码修改的影响:更改影响较小的部分,因此减少了对系统其他部分的潜在破坏。

开放封闭原则(Open-Closed principle)

内容:对扩展开放、对修改封闭

促进可扩展性:可以在不修改现有代码的情况下扩展功能,这意味着新的功能可以添加,而不会影响旧的功能。

降低风险:由于不需要修改现有代码,因此引入新错误的风险较低。

Liskov替换原则(Liskov-Substituion Principle)

内容:子类必须能够替换其基类

提高代码的可互换性:能够用派生类的实例替换基类的实例,使得代码更加模块化,提高了其灵活性。

增加代码的可重用性:遵循LSP的类和组件更容易被重用于不同的上下文。

依赖倒置原则(Dependency-Inversion Principle)

内容:程序要依赖于抽象接口,而不是具体的实现

提高代码的可测试性:通过依赖于抽象而不是具体实现,可以轻松地对代码进行单元测试。

减少系统耦合:系统的高层模块不依赖于低层模块的具体实现,从而使得系统更加灵活和可维护。

接口隔离原则(Interface-Segregation Principle)。

内容:使用多个小的专门的接口,而不要使用一个大的总接口

减少系统耦合:通过使用专门的接口而不是一个大而全的接口,系统中的不同部分之间的依赖性减少了。

提升灵活性和稳定性:更改一个小接口比更改一个大接口风险更低,更容易管理。

示例

以下是一些示例,通过代码的方式给大家介绍一下这几个原则具体的应用和实践。

单一职责原则:一个类最好只做一件事

假如有一个类用于日志消息的处理,但是这个类不仅仅负责创建日志消息,还负责将其写入文件。根据单一职责原则,我们应该将这两个职责分开,让一个类专注于创建日志消息,而另一个类专注于日志消息的存储。

java
// 负责日志消息的创建
class LogMessageCreator {
    public String createLogMessage(String message, LogLevel level) {
        // 创建和格式化日志消息
        LocalDateTime now = LocalDateTime.now();
        return now.toString() + " [" + level.toString() + "] " + message;
    }
}

// 日志级别枚举
enum LogLevel {
    INFO, WARNING, ERROR;
}

// 负责日志消息的存储
class LogFileWriter {
    public void writeToFile(String message, String filename) {
        // 将日志消息写入指定的文件
        try {
            Files.write(Paths.get(filename), message.getBytes(), StandardOpenOption.APPEND);
        } catch (IOException e) {
            // 处理文件写入异常
        }
    }
}

// 使用例子
public class Logger {
    private LogMessageCreator messageCreator;
    private LogFileWriter fileWriter;

    public Logger() {
        messageCreator = new LogMessageCreator();
        fileWriter = new LogFileWriter();
    }

    public void log(String message, LogLevel level, String filename) {
        String logMessage = messageCreator.createLogMessage(message, level);
        fileWriter.writeToFile(logMessage, filename);
    }
}

LogMessageCreator类只负责创建和格式化日志消息,而LogFileWriter类只负责将日志消息写入文件。这种分离确保了每个类只有一个改变的原因,遵循了单一职责原则。

开放封闭原则:对扩展开放、对修改封闭

假设有一个图形绘制应用程序,其中有一个Shape类。

在遵守开闭原则的情况下,如果要添加新的形状类型,应该能够扩展Shape类而无需修改现有代码。这可以通过创建继承自Shape的新类来实现,如Circle和Rectangle。

java

// 形状接口
interface Shape {
    void draw();
}

// 圆形类
class Circle implements Shape {
    public void draw() {
        // 绘制圆形
    }
}

// 矩形类
class Rectangle implements Shape {
    public void draw() {
        // 绘制矩形
    }
}

// 图形绘制类
class GraphicEditor {
    public void drawShape(Shape shape) {
        shape.draw();
    }
}

这样,当我们想要修改Circle的时候不会对Rectangle有任何影响。

里氏替换原则:子类必须能够替换其基类

假设有一个函数接受Bird对象作为参数。根据里氏替换原则,这个函数应该能够接受一个Bird的子类对象(如Sparrow或Penguin)而不影响程序运行。

java
// 鸟类
class Bird {
    public void fly() {
        // 实现飞行
    }
}

// 麻雀类
class Sparrow extends Bird {
    // 重写飞行行为
}

// 企鹅类
class Penguin extends Bird {
    @Override
    public void fly() {
        throw new UnsupportedOperationException("Penguin can't fly");
    }
}



public static void main(String[] args){
    
    Penguin penguin = new Penguin();

    makeItFly(penguin);

    Sparrow sparrow = new Sparrow();
    makeItFly(sparrow);
}


// 使用鸟类的函数
public static void makeItFly(Bird bird) {
    bird.fly();
}

我们可以把任意一个Bird的实现传入到makeItFly方法中,实现了用子类替换父类

依赖倒置原则:程序要依赖于抽象接口,而不是具体的实现

在构建一个电商应用程序时,一个高层的“订单处理”模块不应该直接依赖于一个低层的“数据访问”模块。相反,它们应该依赖于抽象,例如一个接口。这样,数据访问的具体实现可以随时改变,而不会影响订单处理模块。

java
// 数据访问接口
interface DataAccess {
    void saveOrder(Order order);
}

// 高层模块:订单处理
class OrderProcessingService {
    private DataAccess dataAccess;

    public OrderProcessingService(DataAccess dataAccess) {
        this.dataAccess = dataAccess;
    }

    public void processOrder(Order order) {
        // 订单处理逻辑
        dataAccess.saveOrder(order);
    }
}

// 低层模块:数据访问实现
class DatabaseDataAccess implements DataAccess {
    public void saveOrder(Order order) {
        // 数据库保存逻辑
    }
}

这样底层的数据存储我们就可以任意更换,可以用MySQL,可以用Redis,可以用达梦,也可以用OceanBase,因为我们做到了依赖接口,而不是具体实现。

接口隔离原则:使用多个小的专门的接口,而不要使用一个大的总接口

如果有一个多功能打印机接口包含打印、扫描和复制功能,那么只需要打印功能的客户端应该不必实现扫描和复制的接口。这可以通过将大接口分解为更小且更具体的接口来实现。

java
// 打印接口
interface Printer {
    void print();
}

// 扫描接口
interface Scanner {
    void scan();
}

// 多功能打印机类
class MultiFunctionPrinter implements Printer, Scanner {
    public void print() {
        // 打印实现
    }

    public void scan() {
        // 扫描实现
    }
}

// 仅打印类
class SimplePrinter implements Printer {
    public void print() {
        // 打印实现
    }
}