【菜鸡进阶之路】----二十三种设计模式及六大设计原则


共包含二十三种设计模式,本文将随着笔者的学习逐渐进行完善,下面一起进入主题吧。

设计原则

  • 开闭原则:软件实体对修改关闭,对扩展开发(总纲)
    • 如上生产环境后,新增需求需要修改原先代码逻辑,则破坏了开闭原则。。
  • 单一职责原则:一个类只负责一个功能领域的相应职责 (控制类的粒度大小
  • 里氏代换原则:所有引用基类对象的地方都能透明使用其子类的对象 (不要破坏继承体系
    • 父类所有公开方法,子类都必须实现,否则调用父类方法时,如果替换为子类会出问题。
    • 父类已实现的方法,子类不可去修改(可super调用后添加自己的逻辑代码)。
  • 依赖倒置原则:抽象不依赖于细节,细节因依赖于抽象 (面向接口编程
    • 类的成员变量,方法参数,方法返回值在使用时,能使用抽象层或接口的,不要去依赖具体的实现。
  • 接口隔离原则:使用多个专门的接口,而不是单一的总接口
  • 合成复用原则:尽量使用对象组合,而不是继承达到服用目的 (优先对象组合或聚合关系复用,少用继承)
  • 迪米特法则:一个软件实体应尽可能少的与其他实体发生相互作用 (降低耦合)

设计模型类型

创建型(5种):工厂、抽象工厂、单例、原型、构建者

行为型(7种):适配器、装饰、代理、外观、桥接、组合、享元

结构型(11种):模板方法、策略、观察者、中介者、状态、责任链、命令、迭代器、访问者、解释器、备忘录

一、单例模式

  1. 什么是单例?
  • 含义:保证内存中的某一实例是独一无二的。

共8种单例写法,展示如下几种

  • 饿汉模式:正如字面意思,即类加载到容器后就会实例化一个单例,JVM保证线程安全
    • 优点:简单实用
    • 缺点:无论用到与否,类加载是就会完成实例化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* @author truly
* @date 2023/2/28 24:24
* @Description: 饿汉式
*/
public class Singleton {
//static修饰的变量在new对象中,不存在多线程问题
private static Singleton singleton=new Singleton();
private Singleton(){

}
public static Singleton getInstance(String[] args) {
return singleton;
}
}
//jvm通过类加载器加载一个类时是加锁的,也就是线程安全的。类加载时会初始化静态成员,其实就是调用cinit()方法。
    • 较为完美的单例写法:静态内部类的方式
      • 优点:加载外部类的时候不会加载内部类,可以实现懒加载,JVM保证单例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* @author truly
* @date 2023/2/28 24:24
* @Description: 懒加载
*/
public class Singleton {
private Singleton(){

}
public static class SingletonHolder{
private final static Singleton INSTANCE=new Singleton();;
}
public static Singleton getInstance(String[] args) {
return SingletonHolder.INSTANCE;
}
}
  • 枚举单例–最为完美

      • 不仅解决线程同步,还能防止反序列化
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    /**
    * @author truly
    * @date 2023/2/28 24:24
    * @Description: 枚举单例
    */
    public enum Singleton {
    INSTANCE;
    public void Singleton(){

    }
    //后续写业务方法
    }

    -

  • 懒汉模式:正如字面意思,即当需要用到实例时才会实例化一个单例

    • 优点:可以按需实例化
    • 缺点:虽然达到了按需初始化的目的,但带来了线程安全问题。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* @author truly
* @date 2023/2/28 21:24
* @Description: 懒汉式
*/
public class Singleton {
private static volatile Singleton singleton; //涉及语句重排序
//第一步将构造函数私有化
private Singleton(){

}
//第二步提供获取实例的静态方法
public static Singleton getInstance(String[] args) {
if (singleton==null){
synchronized (Singleton.class){
if (singleton==null){
singleton= new Singleton();
}
}
}
return singleton;
}
}

二、策略模式

  1. 什么是策略模式?
  • 为什么要用?:当我们定义了一个int类型的排序算法后,相对float类型进行排序,那是不是要进行很多改变呢……。

  • 含义:定义一系列算法类,将每一个算法封装起来,并让它们可以相互替换,策略模式让算法独立于使用它的客户而变化。策略模式是一种对象行为型模式。

  • 优点:

    • 策略模式可以在不修改代码的情况下,可以任意切换算法。增加新的算法也很方便。符合了“开闭原则”。

    • 策略模式所有的算法都继承自一个基类,便于对具体算法进行统一管理。继承可以在基类中抽象出所有算法的公共方法。

    • 客户端可以不需要知道算法的复杂度。只需要知道使用哪个算法。

  • 缺点:

    • 策略模式中客户端需要知道所有的算法,才能知道具体使用哪一个算法。
    • 策略模式可能会使系统中产生多个具体的算法类。
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
/**
* @author truly
* @date 2023/2/28 24:24
* @Description: 策略模式实现
*/
//作为一个排序的接口
@FunctionalInterface //表明是个函数式接口
public interface Compartor<T> {
int compareto(T o1,T o2);
default void m(){
System.out.println("JDK1.8后接口可以实现写方法实现!!!");
}
}

//实现排序接口的具体算法类如下
public class dogCompartor implements Compartor<Dog> {
@Override
public int compareto(T o1,T o2){
//实现狗排序的具体算法
}
}

//如果一个比较方法如下
public void sort(T[] arr,Compartor<T> compartor){
//传入一个具体的算法实现,即可使用对应算法
}


三四、工厂模式及抽象工厂模式

  1. 什么是工厂模式?
  • 为什么有了new 还要用工厂?:灵活控制生成过程、权限、修饰、日志……。

  • 含义:任何可以产生对象的方法或类,都可称之为工厂,单例也是种工厂。

  • 具体实现如下

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
public interface Moveable{
void go();
}

public class Car() implements Moveable{
public void go(){
System.println.out("Car go wuwuwuuwuwwuwu.....")
}
}

/**
* @author truly
* @date 2023/3/01 24:24
* @Description: 简单工厂模式实现 但可扩展性并不好,违反开闭原则
*/
//存在的问题--解决方法
//1.违反开闭原则-------将对象的名称和对象通过配置文件(xml)进行配置,建立映射关系
//2.入参不明确---------将入参设置成要查找对象的类名
//3.实现不方便---------通过对象名称,去map中查找只需要一行代码。spring源码中getBean
//可任意定制交通工具---------继承Moveable
public class simpleVehicleFactory {
public Car createCar(){
retrun new Car();
}
public Plane createPlane(){
retrun new Plane();
}
}


/**
* @author truly
* @date 2023/3/01 24:24
* @Description: 工厂方法实现 解决简单工厂存在的问题
*/
//返回一个共同接口
//可任意定制生成过程--------moveable xxxFactory.create()
public class CarFactory {
public Moveable createCar(){
retrun new Car();
}
}

--------------------------------------------------------------------------------------------------------------------------------

//任意定义产品族
//食物
public abstract class Food {
abstract void eat();
}
//出行
public abstract class Vehicle {
abstract void go();
}
//---------------------------抽象工厂------------------------------
public abstract class AbstractFactory {
//可以生成一系列的产品族
abstract Food createMike();
abstract Vehicle createVehicle();
}
public class Car extends Vehicle{
go(){
xxxxxxxxxxxxxx实现
}
}
//如现代工厂继承该抽象工厂,就生成现代的食物和交通工具
public class modernFactory extends AbstractFactory(){
@Override
Food createMike(){
retrun new Bread();
}
@Override
Vehicle createVehicle(){
retrun new Car()
}
}
  • 简单工厂方便于产品的扩展,只需要加新产品就可以了,
  • 抽象工厂方便于产品族扩展,但是对产品扩展不方便(产品抽象方法得增加,实现方法也得增加)

更好的解决方案

  • bean工厂
1

五、Facade门面者模式

  • 含义:例如你去政府机构办事,需要从A部门后再去B部门,然后再去C部门,D部门,这时候就需要一个门面模式提供一个统一接口,我只要去找门面,他就能帮我去协调各个部门内部关系(相当于代办人)。
1
 

-

六、调停者Mediator模式

  • 含义:相当于一个系统内部,有多个部门需要相互协调,如果新增部门,则需要和很多系统打交道,那就可以抽出一个专门部门,所有部门都和该部门打交道。

MQ中间件就是使用的门面-调停者模式

1

  • 待更新….

六、Decorator装饰器模式

  • 含义:顾名思义就是起一个装饰的作用。

  • 理解:例如一个坦克游戏,你想给坦克加个外壳显示、加个血条、加个尾巴、子弹加外壳、子弹加尾巴等……

    ​ 就可以有如下装饰器

1
2
3
4
5
6
7
8
9
			GameObject
|
|
bullet------------------------GoDecorator
|
|
|-------------------|
| |
RectDecorator TailDecorator
  • 这样子弹想要加外壳和尾巴就可以将new一个RectDecorator(GameObejct bullet),然后再将处理后的对象放入new的尾巴装饰器中
  • 待更新….

七、责任链模式

  • 问题场景:论坛中发帖,后台需要经过信息处理才能发表或进入数据库。

  • 理解:但是每个处理规则和处理方式都有可能不同,使用过滤器也需要new很多个特定功能的过滤器,那怎么做到多个过滤器逐个执行呢……

    ​ 就可以有如下

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
public class FilterChain(){
Filter filters=new ArrayList();
public void add(Filter f){
filters.add(f);
}
public void dofilter(String str){
for(Filter:f:filters){
f.doFilter(str);
}
}
}

//可以进一步优化,在add时可以实现链式编程
public class FilterChain(){
Filter filters=new ArrayList();

public FilterChain add(Filter f){
filters.add(f);
retrun this;
}
public void dofilter(String str){
for(Filter:f:filters){
f.doFilter(str);
}
}
}


//继续完善,实现同一个接口,在add时可以再传入一个FilterChain
public class FilterChain() implements Filter{
Filter filters=new ArrayList();

public FilterChain add(Filter f){
filters.add(f);
retrun this;
}
public void dofilter(String str){
for(Filter:f:filters){
f.doFilter(str);
}
}
}
  • 场景模拟:由FilterChain中的某一个Filter决定链条是否继续该怎么做呢??

    • 理解:只要将dofilter的返回值改为Boolean值.

      ​ 然后将责任链中的逻辑改为如下

1
2
3
4
5
6
7
8
public Bolean dofilter(String str){
for(Filter:f:filters){
if(!f.doFilter(str)){
retrun false;
}
}
retrun true;
}
  • 场景模拟:如有request和response两个对象传入过滤器中,怎么保证request按顺序处理后(1、2、3顺序),response再处理(3、2、1顺序),该怎么办?

    • 理解:将dofilter传入参数加上FilterChain链条本身

      ​ 然后将责任链中的逻辑改为如下

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
public class FilterChain(){
Filter filters=new ArrayList();
//加入一个索引,标明当前是哪一个filter
int index=0;
public void add(Filter f){
filters.add(f);
}
public boolean dofilter(Request resquest,Response respone,FilterChain chain){
//已经执行完该链条,返回false
if(index==filters.size()) retrun false;

//拿到当前过滤器
filter=filters.get(index);
index++;

//调用当前过滤器的dofilter
retrun filter.doFilter(resquest,response,chain);
}
}
}


class HTMLFilter implements Filter{
public void dofilter(Request resquest,Response respone,FilterChain chain){
//request逻辑处理

//调用链条的dofilter
chain.dofilter(request,response,chain);

//response的处理-----这样顺序就倒过来了
}
}

八、Observe观察者模式

  • 理解:观察者模式通常包含以下三个类…..

    • 事件源:被观察者,发出事件的对象
    • 观察者:等待事件的对象
    • 事件:被发出的对象(Event基类中有个getResource方法,获取事件源)

    大多数情况下,我们观察者处理事件时,需要事件源对象,那就可以传入事件对象,使用具体事件实现的getResource方法

1

  • 主要有如下应用

    • Listener
    • Hook
    • callBack

    在很多系统中,Observer模式往往和责任链模式共同负责事件的处理,其中的某个Observer负责是否将事件进一步传递

九、Composite组合模式

PS:树状结构专用模式

  • 理解:demo代码如下
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
package compositeMode;

import java.util.ArrayList;
import java.util.List;

/**
* @author truly
* @date 2023/3/5 22:16
* @Description: Composite组合模式demo
*/
public class CompositeModeDemo {
public static void main(String[] args) {
BranchNode root = new BranchNode("root");
BranchNode chapter1 = new BranchNode("chapter1");
BranchNode chapter2 = new BranchNode("chapter2");
LeafNode c11 = new LeafNode("c11");
LeafNode c12 = new LeafNode("c12");
BranchNode selection21 = new BranchNode("selection21");
LeafNode c211 = new LeafNode("c211");
LeafNode c212 = new LeafNode("c212");

root.add(chapter1);
root.add(chapter2);
chapter1.add(c11);
chapter1.add(c12);
chapter2.add(selection21);
selection21.add(c211);
selection21.add(c212);

tree(root,0);
}
static void tree(Node n,int depth){
for (int i=0;i<depth;i++) {
System.out.print("--");
}
n.p();
if (n instanceof BranchNode){
for (Node node :((BranchNode) n).nodes){
tree(node,depth+1);
}
}
}
}

abstract class Node {
abstract public void p();
}

class LeafNode extends Node {
String content;

public LeafNode(String content) {
this.content = content;
}

@Override
public void p() {
System.out.println(content);
}
}

class BranchNode extends Node {
List<Node> nodes = new ArrayList();
String name;

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

@Override
public void p() {
System.out.println(name);
}

public void add(Node node) {
nodes.add(node);
}
}


十、Flyweight享元模式

PS:重复利用对象(共享元数据,类似活字印刷)

  • 理解:就如线程池,池化思想,JAVA中的Stirng就是使用享元模式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package compositeMode;

import java.util.ArrayList;
import java.util.List;

/**
* @author truly
* @date 2023/3/5 22:16
* @Description: Flyweight享元模式demo
*/
public class FlyweightDemo {
String s1="abc";
String s2="abc"; //放入常量池,没有则放入,有则直接拿来使用
String s3=new String("abc");
Strinh s4=new String("abc");
s1==s2 //true
s1==s3 //false
s3.intern() //true intern方法为s3内部指向常量池的应用

}


  • 主要有如下应用

    • 结合Compsite模式,就如ABABABABA就可以由A和B两个元组合而来

十、Proxy代理模式

  • 理解:静态代理:基于接口实现,会产生较多代理类
  • 动态代理:
    • cglib字节码方式:基于子类实现代理
    • JDK代理:基于实现同一接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package compositeMode;

import java.util.ArrayList;
import java.util.List;

/**
* @author truly
* @date 2023/3/5 22:16
* @Description: Flyweight享元模式demo
*/
public class FlyweightDemo {
String s1="abc";
String s2="abc"; //放入常量池,没有则放入,有则直接拿来使用
String s3=new String("abc");
Strinh s4=new String("abc");
s1==s2 //true
s1==s3 //false
s3.intern() //true intern方法为s3内部指向常量池的应用

}


  • 主要有如下应用

    • 结合Compsite模式,就如ABABABABA就可以由A和B两个元组合而来

十一、原型模式

理解:其实就是通过复制、克隆去创建一个一样的对象

包括深浅复制两种方法:

  • 浅复制:仅对基本数据类型进行拷贝为新的,没有对引用类型进行拷贝。实现coloneable接口(引用类型地址还是之前的)

    1
    Object shallowClone=super().clone;     //调用object的本地方法
  • 深复制:所有都拷贝。实现serializable接口(引用地址不一样)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    //将对象序列化成二进制流
    ByteArrayOutputStream bos=new ByteOutputStream();
    ObjectOutputStream oos=new ObjectOutputStream(bos);
    oos.wirteObject(this);

    //将二进制流反序列化成对象
    ByteArrayIntputStream bis=new ByteIntputStream();
    ObjectIntputStream oos=new ObjectIntputStream(bos.toByteArray());
    bis.readObject(this);

十二、构建者模式

理解:如果工厂模式是批量生产产品,那构建者就是私人定制化产品。

一般来说,读取配置文件产生对象时,最好选择构建者模式,因为配置文件中的配置信息可有可无。

  • 角色
    • 产品角色:要构建的具体对象
    • 导演角色:导演该产品如何构建,使用builder的。
    • 构建者角色:提供构建表示(属性外露)

  目录