# 一、六大设计原则

# 开闭原则(对修改)

一个软件实体应当对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。

本质:扩展新业务新代码时,可以调用(重利用)原来的代码,但是不要去修改。

# 单一原则(高内聚)

一个类只负责一个功能领域中的相应职责。

本质:一个类承担的职责越少,划分越细,则其可读性、复用性更高。

# 迪米特法则(最少知道原则)(低耦合)

一个软件实体应当尽可能少地与其他实体发生相互作用。

本质:降低类与类之间的耦合度,降低类的修改对其他类的影响,提高稳定性与复用性。

# 里氏替换原则(面向抽象)

里氏代换原则告诉我们,在软件中将一个基类对象替换成它的子类对象,程序将不会产生任何错误和异常,反过来则不成立,如果一个软件实体使用的是一个子类对象的话,那么它不一定能够使用基类对象。

本质:在继承模式下,尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象。

# 依赖倒置原则(面对抽象)

抽象不应该依赖于具体类,具体类应当依赖于抽象。换言之,要针对接口编程,而不是针对实现编程。

//todo 待理解

例如:HashMap为什么要用Map接口?优点是什么

# 接口分离原则(高内聚)

使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口。

本质:可以认为是单一职责对接口的要求,接口粒度太大会导致其实现类职责不单一。

# 二、常见设计模式

img

# 2.1 创建型

# 单例模式

目的:减少类的频繁创建和销毁,降低内存的使用

应用:

在Spring中,单例的应用是比较多的,我们通常是通过@Component注解来声明Bean,并将其注入到IOC容器中,而IOC容器中的Bean默认为单例。

饿汉模式

public class Singleton {
 // 首先,将 new Singleton() 堵死
 private Singleton() {};
 // 创建私有静态实例,意味着这个类第一次使用的时候就会进行创建
 private static Singleton instance = new Singleton();

 public static Singleton getInstance() {
     return instance;
 }

}
1
2
3
4
5
6
7
8
9
10
11

懒汉模式

懒汉模式下的单例会有线程安全问题,有两种解决方式:

  1. 直接在方法加锁,这种实现简单,但是性能低
  2. 采用双重校验锁,当有多个线程进入第一层if后,通过加锁创建保证单例。(推荐)
// 双重校验锁

public class Singleton {
    // 首先,也是先堵死 new Singleton() 这条路
    private Singleton() {}
    // volatile保证多线程的可见性
    private static volatile Singleton instance = null;

    public static Singleton getInstance() {
        if (instance == null) {
            // 加锁
            synchronized (Singleton.class) {
                // 这一次判断也是必须的,不然会有并发问题
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 原型模式

有一个原型实例,基于这个原型实例产生新的实例,也就是“克隆”了。

应用:在Java中,Object类定义了clone方法,但是要调用该方法,是要实现cloneable接口。

# 工厂模式

目的:解耦客户端代码和大量对象创建的代码

简单工厂模式

将客户端代码中的通过if判断创建不同对象的语句抽离出来,单独维护到工厂类中,使得创建过程与客户端代码解耦。

优点:实现简单,客户端代码与创建代码解耦,提高代码复用性。

缺点:

  • 当产品的种类非常多的时候,if-else的语句会很多
  • 不符合开闭原则,添加和删除产品都需要修改工厂的代码

img

工厂方法模式

父类工厂不再提供具体实现,由子类继承工厂并实现具体过程。

优点:

  • 符合开闭原则,程序更容易拓展

缺点:

  • 对于有多种分类的产品,或具有二级分类的产品,工厂方法模式并不适用

多种分类:如我们有一个电子白板白板程序,可以绘制各种图形。那么画笔的绘制功能可以理解为是一个工厂,而图形可以理解为一种产品;图形可以根据形状分为直线、矩形、椭圆等,也可以根据颜色分为红色图形、绿色图形、蓝色图形等。

二级分类: 如一个家电工厂,它可能同时生产冰箱、空调和洗衣机,那么冰箱、空调、洗衣机属于一级分类;而洗衣机又可能分为高效型的和节能型的,那么高效型洗衣机和节能型洗衣机就属于二级分类。

enter image description here

抽象工厂模式

工厂不再只限于生产一种产品,而是可生产多种产品,产品的特性则被抽象到具体工厂上,让每个工厂生产不同特性的产品。

优点:

  • 解决了具有二级分类的产品的创建

缺点:

  • 如果产品的分类超过二级,如三级甚至更多的级,抽象工厂模式将会变得非常臃肿
  • 不能解决产品有多种分类多种组合的问题

enter image description here

enter image description here

AppliancesFactory 是一个抽象的工厂类,定义了三个方法,分别用来生产冰箱(Refrigerator)、空调(Air-conditioner)、洗衣机(WashingMachine)。EfficientFactory 和 EnergySavingFactory 是两个具体的工厂类,分别用来生产高效型的家电和节能弄的家电。

如果产品有多种分类多种组合怎么办?

如果产品有多种分类,就不能单独使用工厂模式了,需要结合其他的设计模式进行优化。如上面提到的白板程序,可以结合中介模式和工厂方法模式进行实现。

# 2.2 结构型

# 代理模式

代理模式可以用于代码增强作用。代理类通过调用真实类的方法,并在其调用前后做增强,客户端调用代理类间接实现对真实类的调用。

目的:对原有的类添加新的特性

应用:Spring AOP:日志、权限

enter image description here

public interface Image {
    void showImage();
}
1
2
3
public class HighResolutionImage implements Image {

    private URL imageURL;
    private long startTime;
    private int height;
    private int width;

    public int getHeight() {
        return height;
    }

    public int getWidth() {
        return width;
    }

    public HighResolutionImage(URL imageURL) {
        this.imageURL = imageURL;
        this.startTime = System.currentTimeMillis();
        this.width = 600;
        this.height = 600;
    }

    public boolean isLoad() {
        // 模拟图片加载,延迟 3s 加载完成
        long endTime = System.currentTimeMillis();
        return endTime - startTime > 3000;
    }

    @Override
    public void showImage() {
        System.out.println("Real Image: " + imageURL);
    }
}
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
public class ImageProxy implements Image {
    private HighResolutionImage highResolutionImage;

    public ImageProxy(HighResolutionImage highResolutionImage) {
        this.highResolutionImage = highResolutionImage;
    }

    @Override
    public void showImage() {
        while (!highResolutionImage.isLoad()) {
            try {
                System.out.println("Temp Image: " + highResolutionImage.getWidth() + " " + highResolutionImage.getHeight());
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        highResolutionImage.showImage();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ImageViewer {
    public static void main(String[] args) throws Exception {
        String image = "http://image.jpg";
        URL url = new URL(image);
        HighResolutionImage highResolutionImage = new HighResolutionImage(url);
        Image imageProxy = new ImageProxy(highResolutionImage);
        imageProxy.showImage();
    }
}
1
2
3
4
5
6
7
8
9

# 装饰器模式

待补充

# 适配器模式

目的:把一个类接口转换成另一个用户需要的接口。

enter image description here

应用场景:

  • 系统需要使用现有的类,而这些类的接口不符合现有系统的要求。
  • 对已有的系统拓展新功能时,尤其适用于在设计良好的系统框架下增加接入第三方的接口或第三方的 SDK 时。例如原来的项目Txt阅读和Epub阅读两种模式,后来业务扩展,需要加入PDF阅读模式,但是PDF引用了第三方SDK,无法直接兼容系统,故采用适配器模式进行适配。

enter image description here

# 桥接模式

待补充

# 2.3 行为型

# 责任链模式

责任链就是一种解耦请求发送者与接收者的设计模式,通过创建多个对象,并将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。应用场景有:

  • 分级审批
  • Netty的pipelineHandler
  • Java的Filter

# 观察者模式

# 策略模式

# 模版方法

Last Updated: 2023-2-7 4:20:28 ├F10: PM┤