设计模式和原则

一、设计原则

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

  1. 定义
    • 一个类应该只有一个引起它变化的原因。这意味着每个类应该专注于一项特定的职责,不要将多种不同的功能或业务逻辑混合在一个类中。例如,一个 "用户信息管理类" 应该只负责用户信息的操作,如添加用户、删除用户、查询用户信息等,而不应该同时承担订单处理、库存管理等其他职责。
  2. 优点
    • 易于理解和维护:当一个类的职责单一,代码的可读性会大大提高。开发人员可以很容易地理解这个类的功能,并且在需要修改或扩展功能时,只需要关注这个特定的职责相关的代码部分,降低了维护成本。
    • 降低耦合度:单一职责的类与其他类的关联通常更简单,减少了因为一个职责的变化而影响到其他不相关部分的可能性,从而降低了系统的耦合度。
  3. 应用场景
    • 在各种软件系统的模块划分和类设计中广泛应用。例如,在企业资源规划(ERP)系统中,"财务管理模块"、"人力资源管理模块"、"供应链管理模块" 等都应该遵循单一职责原则,各自独立负责财务管理、人力资源管理和供应链管理相关的业务逻辑。

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

  1. 定义
    • 软件实体(如类、模块、函数等)应该对扩展开放,对修改关闭。即当需要添加新功能时,应该通过扩展已有的代码来实现,而不是直接修改现有的稳定代码。例如,在一个图形绘制系统中,已经有绘制圆形和矩形的功能,若要添加绘制三角形的功能,应该通过新增一个绘制三角形的类或者方法来扩展系统,而不是去修改原来绘制圆形和矩形的代码。
  2. 优点
    • 提高可维护性:遵循开闭原则可以减少对已有代码的修改,降低了引入新错误的风险。因为稳定的代码部分不需要频繁改动,系统的稳定性得以保持。
    • 增强可扩展性:方便在不破坏原有系统结构的基础上添加新功能,使软件系统能够更好地适应业务需求的变化。
  3. 应用场景
    • 在框架设计和插件式系统开发中尤为重要。例如,在一个游戏开发框架中,游戏开发者可以通过实现框架提供的接口来添加新的游戏角色、道具等功能,而框架的核心代码不需要修改,从而保证了框架的稳定性和扩展性。

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

  1. 定义
    • 所有引用基类(父类)的地方必须能透明地使用其子类的对象。也就是说,子类对象能够替换父类对象,并且程序的行为不会发生改变。例如,在一个动物叫声模拟系统中,有一个动物类(Animal),其中有一个 "叫(makeSound)" 的方法。猫类(Cat)和狗类(Dog)继承自动物类,当使用动物类的引用指向猫类或狗类对象时,调用 "makeSound" 方法应该能够正确地发出猫叫或狗叫的声音,而不会出现错误。
  2. 优点
    • 保证继承关系的正确性:确保了继承体系的合理性,使得子类在继承父类的同时,不会破坏父类原有的功能和行为约束。
    • 提高代码的可维护性和可复用性:在遵循里氏替换原则的继承体系中,代码的逻辑更加清晰,父类的代码可以被多个子类复用,并且在替换子类时不会引发意外的错误。
  3. 应用场景
    • 在面向对象编程的继承体系设计中非常关键。例如,在一个图形处理库中,各种图形类(如圆形类、矩形类等)继承自一个抽象图形类,当在一些图形处理算法中使用抽象图形类的引用时,应该可以用任何具体的图形子类对象来替换,而算法仍然能够正确执行。

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

  1. 定义
    • 高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。例如,在一个电商系统中,订单处理模块(高层模块)不应该直接依赖于具体的数据库存储模块(低层模块),而是两者都应该依赖于一个抽象的数据访问接口。这样,当数据库存储方式发生变化(如从关系型数据库改为非关系型数据库)时,只需要实现新的符合接口的存储模块,而订单处理模块不需要修改。
  2. 优点
    • 降低耦合度:通过依赖抽象,减少了高层模块和低层模块之间的直接依赖关系,使得系统各模块之间的耦合更加松散,提高了系统的灵活性和可维护性。
    • 便于独立开发和测试:各个模块可以基于抽象接口进行独立开发和测试,只要遵循相同的抽象规范,模块之间就可以相互协作,而不需要关注其他模块的具体实现细节。
  3. 应用场景
    • 在分层架构和模块划分中经常使用。比如在企业级应用开发中,业务逻辑层和数据访问层之间通过抽象的数据访问接口进行交互,避免了业务逻辑层直接依赖于具体的数据存储方式,便于对不同的数据存储技术进行替换和升级。

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

  1. 定义
    • 客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。例如,在一个办公软件系统中,有一个打印设备接口,它包含了打印文档、扫描文档、传真文档等方法。但对于一个只需要打印功能的简单打印机设备来说,它不应该被迫实现扫描和传真等不需要的方法。应该将打印接口、扫描接口、传真接口进行分离,让设备类只实现它们需要的接口。
  2. 优点
    • 提高接口的内聚性:使得接口的职责更加单一,每个接口只包含相关的方法,避免了接口的臃肿和混乱。
    • 减少实现类的负担:实现类只需要实现自己真正需要的接口方法,降低了实现类的复杂度,提高了代码的可读性和可维护性。
  3. 应用场景
    • 在接口设计和模块之间的交互设计中广泛应用。比如在一个智能家居系统中,智能灯设备只需要实现控制灯光开关、亮度调节等接口,而智能摄像头设备只需要实现视频拍摄、视频传输等接口,避免设备类实现不必要的接口,使系统的接口设计更加合理。

(六)迪米特法则(Law of Demeter,LoD),也叫最少知识原则

  1. 定义
    • 一个对象应该对其他对象有最少的了解,即一个模块或对象尽量减少与其他模块或对象的交互,只和直接的朋友进行交互。例如,在一个学校管理系统中,学生类(Student)不应该直接访问教师类(Teacher)的私人信息(如教师的工资信息),学生类只应该和与自己相关的课程类(Course)、成绩类(Grade)等进行交互。
  2. 优点
    • 降低耦合度:减少了对象之间不必要的关联,使得每个对象的职责更加独立,系统的耦合度降低,提高了系统的可维护性和可扩展性。
    • 提高可维护性和可复用性:当一个对象的内部实现发生变化时,由于它与其他对象的交互较少,对其他对象的影响也较小,从而提高了代码的可维护性和可复用性。
  3. 应用场景
    • 在各种复杂系统的对象关系设计中应用。比如在一个社交网络系统中,用户类只应该和自己的好友列表、动态消息等直接相关的模块进行交互,而不应该访问其他用户的隐私设置等无关信息,这样可以使系统的结构更加清晰,易于维护和扩展。

(七)组合 / 聚合复用原则(Composite/Aggregate Reuse Principle,CARP)

  1. 定义
    • 尽量使用组合 / 聚合关系来实现复用,而不是继承关系。组合是指一个对象包含另一个对象作为其成员变量;聚合是指一个对象包含另一个对象的引用。例如,在一个汽车制造系统中,汽车类(Car)可以包含发动机类(Engine)、轮胎类(Tire)等对象作为成员变量(组合关系),当需要复用发动机或轮胎的功能时,通过这种组合关系来实现,而不是让汽车类直接继承发动机类或轮胎类。
  2. 优点
    • 灵活性高:组合 / 聚合关系是一种比较松散的关系,当被包含的对象发生变化时,对包含它的对象的影响相对较小,而且可以方便地在运行时动态地改变组合 / 聚合的对象。
    • 降低耦合度:相比于继承,组合 / 聚合关系使得类之间的耦合度更低,因为对象之间的依赖关系更加灵活,有助于提高系统的可维护性和可扩展性。
  3. 应用场景
    • 在软件系统的对象关系设计和代码复用中广泛应用。比如在一个图形绘制系统中,一个复杂图形可以由多个简单图形组合而成,通过组合关系来复用简单图形的绘制功能,而不是通过继承关系,这样可以更加灵活地构建和修改图形对象。

二、设计模式

(一)创建型设计模式

  1. 单例模式(Singleton Pattern)

    • 意图:确保一个类只有一个实例,并提供一个全局访问点来访问这个实例。例如,在一个操作系统中,系统的任务管理器通常是单例的,因为整个系统只需要一个实例来管理任务。

    • 实现方式

      • 懒汉式:在第一次被调用时才创建实例。需要考虑线程安全问题,通常使用双重检查锁定(Double - Checked Locking)来实现。例如:

        public class Singleton {
        private static volatile Singleton instance;
        private Singleton() {}
        public static Singleton getInstance() {
        if (instance == null) {
        synchronized (Singleton.class) {
        if (instance == null) {
        instance = new Singleton();
        }
        }
        }
        return instance;
        }
        }

      • 饿汉式:在类加载时就创建实例,这种方式简单直接,但可能会提前占用资源。例如:

        public class Singleton {
        private static final Singleton instance = new Singleton();
        private Singleton() {}
        public static Singleton getInstance() {
        return instance;
        }
        }

    • 应用场景:适用于资源管理器(如数据库连接池、线程池)、日志系统等场景。例如,在数据库连接池场景中,只需要一个连接池实例来管理数据库连接,通过单例模式可以有效地控制连接池的创建和访问。

  2. 工厂模式(Factory Pattern)

    • 意图:将对象的创建和使用分离,通过一个工厂类来负责创建对象,而不是在客户端代码中直接使用 "new" 关键字来创建对象。例如,在一个游戏开发中,游戏中的各种角色可以通过角色工厂来创建,根据不同的角色类型参数,工厂返回不同的角色对象。

    • 实现方式

      • 简单工厂模式:一个工厂类创建多种产品对象。例如,有一个简单的图形工厂类,可以创建圆形、矩形等不同形状的图形:

        class ShapeFactory {
        public Shape createShape(String shapeType) {
        if (shapeType.equals("Circle")) {
        return new Circle();
        } else if (shapeType.equals("Rectangle")) {
        return new Rectangle();
        }
        return null;
        }
        }

      • 工厂方法模式:每个产品对象有对应的工厂子类来创建。例如,对于圆形和矩形分别有对应的工厂子类来创建:

        abstract class ShapeFactory {
        abstract Shape createShape();
        }
        class CircleFactory extends ShapeFactory {
        @Override
        Shape createShape() {
        return new Circle();
        }
        }
        class RectangleFactory extends ShapeFactory {
        @Override
        Shape createShape() {
        return new Rectangle();
        }
        }

      • 抽象工厂模式:工厂类创建一组相关的产品对象。例如,在一个家具工厂中,可以创建一组相关的家具产品,如桌子和椅子:

        interface FurnitureFactory {
        Table createTable();
        Chair createChair();
        }
        class ModernFurnitureFactory implements FurnitureFactory {
        @Override
        public Table createTable() {
        return new ModernTable();
        }
        @Override
        public Chair createChair() {
        return new ModernChair();
        }
        }

    • 应用场景:适用于对象创建过程复杂(如需要读取配置文件、进行数据库查询等)或者需要根据不同条件创建不同类型对象的场景。例如,在图形绘制系统中,根据用户选择创建不同形状的图形;在软件系统的插件加载过程中,通过工厂模式创建不同类型的插件对象。

  3. 建造者模式(Builder Pattern)

    • 意图:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。例如,在构建一个电脑对象时,电脑有 CPU、内存、硬盘等多个部件,建造者模式可以通过不同的建造者来构建不同配置的电脑。

    • 实现方式

      • 通常有一个抽象建造者类定义建造方法,具体建造者类实现这些方法来构建对象的各个部分,还有一个指挥者类来控制建造过程,最后通过建造者得到构建好的对象。例如,构建一个电脑的建造者模式实现:

        // 电脑部件类
        class CPU {
        private String model;
        public CPU(String model) {
        this.model = model;
        }
        public String getModel() {
        return model;
        }
        }
        class Memory {
        private int capacity;
        public Memory(int capacity) {
        this.capacity = capacity;
        }
        public int getCapacity() {
        return capacity;
        }
        }
        class Computer {
        private CPU cpu;
        private Memory memory;
        public void setCPU(CPU cpu) {
        this.cpu = cpu;
        }
        public void setMemory(Memory memory) {
        this.memory = memory;
        }
        @Override
        public String toString() {
        return "Computer [cpu=" + cpu.getModel() + ", memory=" + memory.getCapacity() + "GB]";
        }
        }
        // 抽象建造者类
        abstract class ComputerBuilder {
        protected Computer computer = new Computer();
        public abstract void buildCPU();
        public abstract void buildMemory();
        public Computer getComputer() {
        return computer;
        }
        }
        // 具体建造者类
        class HighEndComputerBuilder extends ComputerBuilder {
        @Override
        public void buildCPU() {
        computer.setCPU(new CPU("Intel Core i9"));
        }
        @Override
        public void buildMemory() {
        computer.setMemory(new Memory(32));
        }
        }
        // 指挥者类
        class ComputerDirector {
        public Computer construct(ComputerBuilder builder) {
        builder.buildCPU();
        builder.buildMemory();
        return builder.getComputer();
        }
        }

    • 应用场景:适用于创建复杂对象,特别是当对象的构建过程比较复杂,且可能有多种不同的构建方式或配置组合时。如汽车制造过程、房屋建筑过程等,在这些场景中,可以通过不同的建造者来构建不同配置或风格的产品。

(二)结构型设计模式

  1. 代理模式(Proxy Pattern)
    • 意图:为其他对象提供一种代理以控制对这个对象的访问。例如,在网络访问控制中,代理服务器可以作为真实服务器的代理,客户端请求先到达代理服务器,代理服务器可以进行权限验证、缓存等操作后再将请求转发给真实服务器。
    • 实现方式
      • 静态代理:在编译时期就确定代理类和被代理类的关系。例如,一个简单的接口和其实现类以及代理类的示例:

        interface Subject {
        void request();
        }
        class RealSubject implements Subject {
        @Override
        public void request() {
        System.out.println("RealSubject is handling the request.");
        }
        }
        class ProxySubject implements Subject {
        private RealSubject realSubject;
        public ProxySubject() {
        this.realSubject = new RealSubject();
        }
        @Override
        public void request() {
        System.out.println("ProxySubject is pre - handling the request.");
        realSubject.request();
        System.out.println("ProxySubject is post - handling the request.");
        }
        }

      • 动态代理 :在运行时期通过反射机制动态生成代理类。在 Java 中可以使用java.lang.reflect.Proxy类来实现。例如:

        import java.lang.reflect.InvocationHandler;
        import java.lang.reflect.Method;
        import java.lang.reflect.Proxy;
        interface Subject {
        void request();
        }
        class RealSubject implements Subject {
        @Override
        public void request() {
        System.out.println("RealSubject is handling the request.");
        }
        }
        class DynamicProxyHandler implements InvocationHandler {
        private Object realSubject;
        public DynamicProxyHandler(Object realSubject) {
        this.realSubject = realSubject;
        }
        @Override

相关推荐
B站计算机毕业设计超人17 分钟前
计算机毕业设计制造业MES生产管理平台 MES 生产制造源码+文档+运行视频+讲解视频)
java·spring boot·mysql·eclipse·tomcat·maven·web
技术咖啡馆C1 小时前
二、通义灵码插件保姆级教学-IDEA(使用篇)
java·intellij-idea·通义灵码·ai助手·idea-plugin
文城5211 小时前
HTML-day1(学习自用)
前端·学习·html
星星点点洲1 小时前
【SpringBoot实现全局API限频】 最佳实践
java·spring boot·后端
阿珊和她的猫1 小时前
Vue 和 React 的生态系统有哪些主要区别
前端·vue.js·react.js
偷光1 小时前
深度剖析 React 的 useReducer Hook:从基础到高级用法
前端·javascript·react.js
linwq82 小时前
Java网络编程学习(一)
java·网络·学习
lllsure2 小时前
【快速入门】SpringMVC
java·后端·spring·mvc
翻晒时光2 小时前
24、深入理解与使用 Netty:Java 高性能网络编程的利器
java·网络
The_era_achievs_hero2 小时前
动态表格html
前端·javascript·html