设计模式

高可复用性代码设计

设计模式

OOP七大原则

单一职责原则

每个类最好只有一个任务或职责比如Controller类就负责接收前端请求,然后向Service层请求结果,而不是直接请求Mapper层或者直接处理数据返回.

开闭原则

对扩展开放,对修改关闭,具体来说是写程序的时候可以多实现接口,让这个接口对应的类有更多功能,而不是删去以前的代码去修改功能

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// 抽象支付接口
interface Payment {
    void pay(BigDecimal amount);
}

// 实现类(扩展时新增类即可)
@Service
class Alipay implements Payment {
    @Override 
    public void pay(BigDecimal amount) {
        System.out.println("支付宝支付:" + amount);
    }
}

@Service 
class WechatPay implements Payment {
    @Override
    public void pay(BigDecimal amount) {
        System.out.println("微信支付:" + amount);
    }
}

// 控制器(对修改关闭)
@RestController
class PaymentController {
    @Autowired
    private List<Payment> payments; // Spring自动注入所有实现

    @PostMapping("/pay")
    public String pay(@RequestParam String type, 
                     @RequestParam BigDecimal amount) {
        payments.stream()
                .filter(p -> p.getClass().getSimpleName()
                              .equalsIgnoreCase(type + "Pay"))
                .findFirst()
                .ifPresent(p -> p.pay(amount));
        return "success";
    }
}

依赖倒置原则

依赖抽象而非实现,多定义很多层接口,最后再对接口进行实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 抽象层
interface UserRepository {
    User findById(Long id);
}

// 高层模块
@Service
class UserService {
    private final UserRepository repository; // 依赖抽象

    @Autowired
    public UserService(UserRepository repository) {
        this.repository = repository;
    }

    public User getUser(Long id) {
        return repository.findById(id);
    }
}

// 低层实现(可以是MySQL/MongoDB等)
@Repository
class JpaUserRepository implements UserRepository {
    @Override
    public User findById(Long id) {
        // 实际数据库操作
    }
}
  • 它不关心具体的数据来源是 MySQL、MongoDB 还是其他方式,只依赖于接口,上层接口只关心数据。

合成复用原则

类最好要组合使用,而不是继承添加特性

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// 错误示范:用继承获取日志能力
class OrderService extends LoggingUtil { ... }

// 正确示范:通过组合引入能力
@Service
class OrderService {
    private final LoggingUtil logger; // 组合

    @Autowired
    public OrderService(LoggingUtil logger) {
        this.logger = logger;
    }

    public void createOrder() {
        logger.log("创建订单");
        // 业务逻辑
    }
}

SpringBoot项目中,多使用@Autowired,组合不同的类,让类之间共享方法

口隔离原则

不应强迫客户端依赖于它们不使用的接口。换句话说,一个类应该只提供给其他类它实际需要的方法,而不是所有可能的方法。

迪米特法则

也被称为最少知识原则,表明一个对象应该尽可能少地了解其他对象。每个单元对于其他的单元只能拥有最少的知识,并且仅仅与那些与之紧密相关的单元进行交互。

比如有Mapper,Service,Controller三层架构,此时我们最好让他们之间一层一层通信,而不是Controller直接去找Mapper层找数据输出.

里氏替换原则

在继承父类的时候最好不要修改父类的方法,可以扩展方法,这样在要使用父类的时候,可以用子类替代.

单例模式

饿汉式单例模式

在项目启动的时候创建出的单例对象的行为就是饿汉式单例模式,比如下列代码

1
2
3
4
5
6
7
8
9
public class Singleton {
    private static final Singleton INSTANCE = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() {
        return INSTANCE;
    }
}

static表示是静态资源,存在与静态资源池里面,final表示这个对象不会再被改变了,因此对象在启动的时候就创建好了.

Spring容器创建的Bean对象默认就是饿汉式单例模式,通过@Autowired实现控制反转与依赖注入.

  • 优点

    • 实现简单,代码清晰。
    • 线程安全。
  • 缺点

    无论是否使用,都会在类加载时创建实例,可能浪费资源。

懒汉式单例模式

懒汉式单例模式是指单例对象在需要使用的时候才会创建,样例代码如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public class Singleton {
    private static Singleton instance;

    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

这种情况下可能导致线程安全问题,高并发的时候并不能保证单例,因此要在创建对象的时候加上锁

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public class Singleton {
    private static Singleton instance;

    private Singleton() {}

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

工厂模式

简单工厂

简单工厂创建对象的时候要考虑对象的类型,然后用if-else语句来判断是要创建哪个对象

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// 工厂类
public class ShapeFactory {

    // 根据传入的类型创建对应的对象
    public Shape getShape(String shapeType) {
        if (shapeType == null || shapeType.isEmpty()) {
            return null;
        }

        if (shapeType.equalsIgnoreCase("CIRCLE")) {
            return new Circle();
        } else if (shapeType.equalsIgnoreCase("SQUARE")) {
            return new Square();
        } else if (shapeType.equalsIgnoreCase("RECTANGLE")) {
            return new Rectangle();
        }

        return null;
    }
}

这样局限性还是很高,判断很多if else语句也会导致代码效率不高

工厂方法模式

一个类只负责创建一种产品,通过继承和多态性,可以方便地扩展新的产品类型。

代码示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// 定义产品接口
public interface Product {
    void use();
}

// 具体产品A
public class ConcreteProductA implements Product {
    @Override
    public void use() {
        System.out.println("使用产品A");
    }
}

// 具体产品B
public class ConcreteProductB implements Product {
    @Override
    public void use() {
        System.out.println("使用产品B");
    }
}

// 抽象工厂接口
public abstract class Creator {
    // 工厂方法,由子类实现
    public abstract Product factoryMethod();
}

// 具体工厂A
public class ConcreteCreatorA extends Creator {
    @Override
    public Product factoryMethod() {
        return new ConcreteProductA();
    }
}

// 具体工厂B
public class ConcreteCreatorB extends Creator {
    @Override
    public Product factoryMethod() {
        return new ConcreteProductB();
    }
}

// 测试类
public class FactoryMethodTest {
    public static void main(String[] args) {
        // 使用具体工厂A创建产品A
        Creator creatorA = new ConcreteCreatorA();
        Product productA = creatorA.factoryMethod();
        productA.use();

        // 使用具体工厂B创建产品B
        Creator creatorB = new ConcreteCreatorB();
        Product productB = creatorB.factoryMethod();
        productB.use();
    }
}

一个工厂只负责创建一种对象

抽象工厂

抽象工厂模式提供了一组用于创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。能够创建一系列相关的对象,而不是单一的产品。

代码示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
// 定义产品族接口
public interface AbstractProductA {
    void useA();
}

public interface AbstractProductB {
    void useB();
}

// 具体产品A1
public class ConcreteProductA1 implements AbstractProductA {
    @Override
    public void useA() {
        System.out.println("使用产品A1");
    }
}

// 具体产品A2
public class ConcreteProductA2 implements AbstractProductA {
    @Override
    public void useA() {
        System.out.println("使用产品A2");
    }
}

// 具体产品B1
public class ConcreteProductB1 implements AbstractProductB {
    @Override
    public void useB() {
        System.out.println("使用产品B1");
    }
}

// 具体产品B2
public class ConcreteProductB2 implements AbstractProductB {
    @Override
    public void useB() {
        System.out.println("使用产品B2");
    }
}

// 抽象工厂接口
public interface AbstractFactory {
    AbstractProductA createProductA();
    AbstractProductB createProductB();
}

// 具体工厂1
public class ConcreteFactory1 implements AbstractFactory {
    @Override
    public AbstractProductA createProductA() {
        return new ConcreteProductA1();
    }

    @Override
    public AbstractProductB createProductB() {
        return new ConcreteProductB1();
    }
}

// 具体工厂2
public class ConcreteFactory2 implements AbstractFactory {
    @Override
    public AbstractProductA createProductA() {
        return new ConcreteProductA2();
    }

    @Override
    public AbstractProductB createProductB() {
        return new ConcreteProductB2();
    }
}

// 测试类
public class AbstractFactoryTest {
    public static void main(String[] args) {
        // 使用具体工厂1创建产品族1
        AbstractFactory factory1 = new ConcreteFactory1();
        AbstractProductA productA1 = factory1.createProductA();
        AbstractProductB productB1 = factory1.createProductB();
        productA1.useA();
        productB1.useB();

        // 使用具体工厂2创建产品族2
        AbstractFactory factory2 = new ConcreteFactory2();
        AbstractProductA productA2 = factory2.createProductA();
        AbstractProductB productB2 = factory2.createProductB();
        productA2.useA();
        productB2.useB();
    }
}

每个具体工厂可以创建不同的对象

在Spring中,配置类就使用了工厂模式

1
2
3
4
5
6
7
8
9
@Bean
    public ChatClient DesignPattern(OpenAiChatModel model, ChatMemory chatMemory) {
        return ChatClient.builder(model)
                .defaultSystem(AIConstant.DESIGN_PATTERN)
                .defaultAdvisors(
                        new MessageChatMemoryAdvisor(chatMemory),
                        new SimpleLoggerAdvisor())
                .build();
    }

而一个配置类里面有多个@Bean注解下的工厂方法,可以实现抽象工厂和工厂方法模式

Licensed under CC BY-NC-SA 4.0