设计模式七大原则

在软件开发的广袤领域中,设计模式如同熠熠生辉的灯塔,为开发者指引着通往高效、可维护、可扩展软件系统的方向。而设计模式背后的七大原则,更是这些灯塔的核心支撑,它们是软件开发过程中必须遵循的黄金法则,为我们构建高质量的软件架构奠定了坚实基础。本文将深入剖析这七大原则,带您领略其深刻内涵与实际应用。

一、单一职责原则(Single Responsibility Principle, SRP)

2.1 原则定义

一个类应该仅有一个引起它变化的原因,即一个类应该只负责一项职责。

2.2 示例分析

以一个简单的用户管理系统为例,假设我们有一个 UserService 类,它既负责用户信息的存储(如将用户数据写入数据库),又负责用户信息的验证(如验证用户名是否合法、密码强度是否符合要求)。当数据库存储方式发生变化时,UserService 类需要修改;当用户验证规则改变时,UserService 类同样需要修改。这就违背了单一职责原则。

按照单一职责原则,我们应该将其拆分为两个类:UserStorageService 负责用户信息的存储操作,UserValidationService 负责用户信息的验证操作。这样,当某一项职责发生变化时,只需要修改对应的类,而不会影响到其他职责相关的代码。

2.3 优势

  • 提高可维护性:当一个类只负责一项职责时,代码逻辑更加清晰,修改某个功能时不容易影响到其他功能,从而降低维护成本。
  • 增强可扩展性:如果需要增加新的职责,只需要创建新的类来承担该职责,而不需要对现有类进行大规模修改。

二、开闭原则(Open - Closed Principle, OCP)

3.1 原则定义

软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。即当软件需要变化时,我们应该通过扩展代码来实现变化,而不是修改现有的代码。

3.2 示例分析

假设我们正在开发一个图形绘制系统,目前有一个 Shape 类和一个 DrawShapes 类。Shape 类是所有图形类的基类,DrawShapes 类负责绘制各种形状。最初系统只有圆形(Circle)和矩形(Rectangle)两种图形。

java 复制代码
// 基类Shape
class Shape {
    public void draw() {
        // 空实现,具体图形类实现
    }
}

// 圆形类
class Circle extends Shape {
    @Override
    public void draw() {
        System.out.println("绘制圆形");
    }
}

// 矩形类
class Rectangle extends Shape {
    @Override
    public void draw() {
        System.out.println("绘制矩形");
    }
}

// 绘制图形类
class DrawShapes {
    public void drawAllShapes(Shape[] shapes) {
        for (Shape shape : shapes) {
            shape.draw();
        }
    }
}

当我们需要添加新的图形(如三角形 Triangle)时,按照开闭原则,我们不需要修改 DrawShapes 类的代码,只需要创建 Triangle 类继承自 Shape 类,并实现 draw 方法即可。

java 复制代码
// 三角形类
class Triangle extends Shape {
    @Override
    public void draw() {
        System.out.println("绘制三角形");
    }
}

3.3 优势

  • 降低风险:避免修改现有代码可能引入的新 bug,因为修改现有代码可能会影响到已经测试通过的功能。
  • 提高可维护性和可扩展性:通过扩展新的类来实现功能的增加,使得软件系统更加灵活,易于维护和扩展。

三、里氏替换原则(Liskov Substitution Principle, LSP)

4.1 原则定义

所有引用基类(父类)的地方必须能透明地使用其子类的对象。这意味着子类对象必须能够替换掉它们的父类对象,而程序的行为不会发生任何异常或错误。

4.2 示例分析

假设有一个鸟类 Bird,它有一个 fly 方法表示飞行。现在有两个子类 Sparrow(麻雀)和 Ostrich(鸵鸟),如果简单地继承 Bird 类,鸵鸟虽然继承了 fly 方法,但实际上鸵鸟并不会飞。这就违背了里氏替换原则。

正确的做法是,重新审视类的设计,将 Bird 类中 fly 方法提取到一个接口 Flyable 中,让 Sparrow 类实现 Flyable 接口,而 Ostrich 类不实现 Flyable 接口,这样就符合里氏替换原则。

java 复制代码
// 飞行接口
interface Flyable {
    void fly();
}

// 鸟类
class Bird {
    // 鸟类的其他通用属性和方法
}

// 麻雀类
class Sparrow extends Bird implements Flyable {
    @Override
    public void fly() {
        System.out.println("麻雀飞行");
    }
}

// 鸵鸟类
class Ostrich extends Bird {
    // 鸵鸟的特有属性和方法
}

4.3 优势

  • 保证继承体系的稳定性:遵循里氏替换原则,能确保子类在替换父类时不会破坏原有程序的正确性,使得继承体系更加健壮。
  • 提高代码的可复用性和可维护性:基于里氏替换原则设计的代码,父类和子类之间的关系更加清晰,代码复用性更高,维护起来也更加容易。

四、依赖倒置原则(Dependency Inversion Principle, DIP)

5.1 原则定义

  • 高层模块不应该依赖低层模块,二者都应该依赖其抽象。
  • 抽象不应该依赖细节,细节应该依赖抽象。

5.2 示例分析

假设我们有一个高层模块 CustomerService(客户服务类),它需要调用低层模块 Database(数据库操作类)来获取客户信息。如果 CustomerService 直接依赖于 Database 类,那么当数据库操作方式发生变化时,CustomerService 类也需要进行修改。

按照依赖倒置原则,我们可以定义一个抽象接口 ICustomerDataAccessDatabase 类实现这个接口,CustomerService 类依赖于 ICustomerDataAccess 接口。

java 复制代码
// 客户数据访问接口
interface ICustomerDataAccess {
    String getCustomerInfo();
}

// 数据库操作类实现接口
class Database implements ICustomerDataAccess {
    @Override
    public String getCustomerInfo() {
        // 从数据库获取客户信息的具体实现
        return "客户信息";
    }
}

// 客户服务类依赖接口
class CustomerService {
    private ICustomerDataAccess dataAccess;

    public CustomerService(ICustomerDataAccess dataAccess) {
        this.dataAccess = dataAccess;
    }

    public String getCustomer() {
        return dataAccess.getCustomerInfo();
    }
}

5.3 优势

  • 提高系统的灵活性和可维护性:通过依赖抽象,当底层实现发生变化时,只需要替换实现抽象接口的类,而不需要修改高层模块的代码,从而提高了系统的灵活性和可维护性。
  • 降低模块间的耦合度:高层模块和低层模块之间通过抽象接口进行交互,降低了它们之间的直接耦合,使得各个模块可以独立开发、测试和维护。

五、接口隔离原则(Interface Segregation Principle, ISP)

6.1 原则定义

客户端不应该被迫依赖于它不使用的接口。即一个类对另一个类的依赖应该建立在最小的接口上。

6.2 示例分析

假设我们有一个接口 AnimalActions,它包含了 eat(吃)、fly(飞)、swim(游泳)等方法。现在有两个类 DogEagleDog 类只需要实现 eat 方法,Eagle 类需要实现 eatfly 方法。如果 DogEagle 类都实现 AnimalActions 接口,那么 Dog 类就被迫依赖于它不需要的 flyswim 方法,这违背了接口隔离原则。

我们应该将 AnimalActions 接口拆分为多个小接口,如 IEatIFlyISwim,让 Dog 类实现 IEat 接口,Eagle 类实现 IEatIFly 接口。

java 复制代码
// 吃接口
interface IEat {
    void eat();
}

// 飞接口
interface IFly {
    void fly();
}

// 狗类实现吃接口
class Dog implements IEat {
    @Override
    public void eat() {
        System.out.println("狗吃东西");
    }
}

// 鹰类实现吃和飞接口
class Eagle implements IEat, IFly {
    @Override
    public void eat() {
        System.out.println("鹰吃东西");
    }

    @Override
    public void fly() {
        System.out.println("鹰飞行");
    }
}

6.3 优势

  • 降低类的复杂度:避免类实现过多不需要的接口方法,使得类的职责更加明确,代码更加简洁。
  • 提高系统的灵活性和可维护性:当接口需求发生变化时,只需要修改相关的小接口,而不会影响到其他不相关的类,从而提高了系统的灵活性和可维护性。

六、迪米特法则(Law of Demeter, LoD)

7.1 原则定义

一个对象应该对其他对象有尽可能少的了解,即 "只和你的直接朋友交谈,不跟'陌生人'说话"。这里的 "直接朋友" 是指:出现在成员变量、方法的输入输出参数中的类。

7.2 示例分析

假设我们有一个学校系统,有 School 类、Class 类和 Student 类。School 类包含多个 Class 类对象,Class 类包含多个 Student 类对象。如果 School 类直接获取某个 Class 类中的某个 Student 类对象的信息,这就违背了迪米特法则,因为 Student 类对于 School 类来说是 "陌生人"。

正确的做法是,School 类通过 Class 类来间接获取 Student 类的信息,Class 类是 School 类的 "直接朋友"。

java 复制代码
class Student {
    private String name;

    public Student(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

class Class {
    private Student[] students;

    public Class(Student[] students) {
        this.students = students;
    }

    public Student getStudent(int index) {
        return students[index];
    }
}

class School {
    private Class[] classes;

    public School(Class[] classes) {
        this.classes = classes;
    }

    public String getStudentName(int classIndex, int studentIndex) {
        Class classObj = classes[classIndex];
        Student student = classObj.getStudent(studentIndex);
        return student.getName();
    }
}

7.3 优势

  • 降低系统的耦合度:减少对象之间不必要的依赖,使得各个模块之间的联系更加松散,从而降低系统的耦合度。
  • 提高系统的可维护性和可扩展性:当某个模块发生变化时,由于其与其他模块的耦合度低,对其他模块的影响也较小,便于系统的维护和扩展。

结语

掌握设计模式七大原则,能帮你构建稳健、易维护的软件架构,提升开发效率与软件质量。希望本文能助你更好运用这些原则。若有疑问或想深入探讨,欢迎留言!

相关推荐
水宝的滚动歌词6 小时前
设计模式之建造者模式
java·设计模式·建造者模式
Upuping8 小时前
「全网最细 + 实战源码案例」设计模式——外观模式
java·后端·设计模式
渊渟岳10 小时前
掌握设计模式--抽象工厂模式
设计模式
玉面小君13 小时前
C# 设计模式(行为型模式):访问者模式
设计模式·c#·访问者模式
玉面小君14 小时前
C# 设计模式(行为型模式):模板方法模式
设计模式·c#·模板方法模式
工一木子14 小时前
【HeadFirst系列之HeadFirst设计模式】第1天之HeadFirst设计模式开胃菜
java·设计模式·headfirst设计模式
水宝的滚动歌词1 天前
设计模式之适配器模式
设计模式·适配器模式
冷雨夜中漫步1 天前
领域驱动设计(4)—绑定模型与实现
java·开发语言·笔记·后端·设计模式
JINGWHALE11 天前
设计模式 结构型 外观模式(Facade Pattern)与 常见技术框架应用 解析
前端·人工智能·后端·设计模式·性能优化·系统架构·外观模式
JINGWHALE11 天前
设计模式 结构型 代理模式(Proxy Pattern)与 常见技术框架应用 解析
前端·人工智能·后端·设计模式·性能优化·系统架构·代理模式