设计模式综述

237 阅读12分钟

设计模式综述

设计模式分为3大类

  • 创建型模式

    1. 工厂方法
    2. 抽象工厂
    3. 单例
    4. 建造者
    5. 原型
  • 结构型模式

    1. 适配器模式
    2. 装饰器模式
    3. 代理模式
    4. 外观模式
    5. 桥接模式
    6. 组合模式
    7. 享元模式
  • 行为型模式

    1. 策略模式
    2. 模板方法模式
    3. 观察者模式
    4. 迭代子模式
    5. 责任链模式
    6. 命令模式
    7. 备忘录模式
    8. 状态模式
    9. 访问者模式
    10. 中介者模式
    11. 解释器模式

如果加上并发性模式以及线程池模式,就是5 + 7+ 11 + 2 = 25种

创建模式

01 工厂方法

在此之前,先说一下简单工厂模式(不属于23种设计模式之一) 举一个例子,工厂生成Xiaomi, Huawei手机。所以存在两条生产线

// 手机接口
public interface Phone {
    void call();
}

// Xiaomi手机
public class XiaomiPhone implements Phone {
    public void call() {
        sout("xiaomi");
    }
}

// HUAWEI手机
public class HuaweiPhone implements Phone {
    public void call() {
        sout("huawei");
    }
}

工厂类

// 工厂1
public class ProduceFactoty1 {
    public Phone produce(int type) {
        if (type == 0) {
            return new XiaomiPhone();
        } else {
            return new HuaweiPhone();
        }
    }
}

// 工厂2
public class ProduceFactory2 {
    public Phone produceXiaomi() {
        return new XiaomiPhone();
    }

    public Phone produceHuawei() {
        return new HuaweiPhone();
    }
}

// 工厂3,仅仅是把上文的方法改成静态的
public class ProduceFactory2 {
    public static Phone produceXiaomi() {
        return new XiaomiPhone();
    }

    public static Phone produceHuawei() {
        return new HuaweiPhone();
    }
}

上文的设计存在一个问题,假设现在想要生产Oppo手机了,就需要修改ProduceFactoty的代码,这违反了闭包原则,是一个很不友好的设计。于是就有了现在的工厂方法模式:创建一个工厂接口和创建多个工厂实现类

首先有一个生产方法接口

public interface Factory {
    Phone produce();
}

// xiaomi 工厂
public XiaomiFactory implements Factory {
    public Phone produce() {
        return new XiaomiPhone();
    }
}

// HUawei 工厂
public HuaweiFactory implements Factory {
    public Phone produce() {
        return new HuaweiPhone();
    }
}

// 如果想添加其他品牌工厂,直接实现Factory接口即可

02抽象工厂模式

很容易与前面的工厂方法混淆,它属于工厂方法更进一步抽象

这里还是以上文的手机生产为例, xiaomi意识到了品牌重要性,于是有xiaomi高端机和低端机,huawei也是如此。于是有了以下类

// xiaomi高端机
class XiaomiHighPhone extends XiaomiPhone {

}
// xiaomi低端机
class XiaomiLowPhone extends XiaomiPhone {

}
// huawei高端机
class HuaweiHighPhone extends XiaomiPhone {

}
// huawei低端机
class HuaweiLowPhone extends XiaomiPhone {

}

显然,对于XiaomiHighPhone和XiaomiLowPhone 属于同一个品牌,而对于XiaomiHighPhone和HuaweiHighPhone是同一个品种,都属于高端机,如果继续用工厂方法,则需要建立4种工厂,我们想要是结果是只需要建立2种工厂,每一种工厂都可以都可以生产两种机型。

// 抽象工厂
interface class ProduceFactory {
    XiaomiPhone produceXiaomiPhone();
    HuaweiPhone produceHuaweiPhone();
}

// 具体工厂
class HighPhoneFactory {
    public XiaomiPhone produceXiaomiPhone() {
        return new XiaomiHighPhone();
    }
    public HuaweiPhone produceHuaweiPhone() {
        return new HuaweiHighPhone();
    }
}
class LowPhoneFactory {
    public XiaomiPhone produceXiaomiPhone() {
        return new XiaomiLowPhone();
    }
    public HuaweiPhone produceHuaweiPhone() {
        return new HuaweiLowPhone();
    }
}

下面我们来测试一下,生产一款小米高端手机

ProduceFactory factory = new HighPhoneFactory();
factory.produceXiaomiPhone();

03单例模式

单例模式就是我们所知的只需要一个单 instance的,经常会用到,这里直接略过, 贴一下经典代码

// 类加载线程互斥机制
public class Singleton {
    private static Singleton instance;

    private Singleton() {

    }

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

    public static class SingletonFactory {
        private static Singleton instance = new Singleton();
    }
}

04建造者模式

个人理解的就是根据具体需求改变实例属性, 还是以手机为例

// 手机类
public class Phone {
    private int size;
    private String color;

    public Phone(PhoneBuilder builder) {
        this.size = builder.size();
        this.color = builder.color();
    }

    public void setSize(int size) {
        this.size = size;
    }

    public void setColor(String color) {
        this.color = color;
    }

    // 默认5寸 红色
    public static class PhoneBuilder {
        public static PhoneBuilder defaultBuilder = new PhoneBuilder();
        private int size = 5;
        private String color = "red";

        public PhoneBuilder setSize(int size) {
            this.size = size;
            return this;
        }

        public PhoneBuilder setColor(String color) {
            this.color = color;
            return this;
        }

        public Phone build() {
            return new Phone(this);
        }
    } 
}
PhoneBuilder.defaultBuilder.setSize(6).setColor("black").build();

05原型模型

基本思想就是将某一个实例作为原型,对其进行clone操作,得到新实例

public class Phone implements Cloneable {
    public Phone clone() throws CloneNotSupportedException {  
        return (Phone) super.clone();   
    }  
}

或许,可以测试下100W对象需要耗时多少,下面是三种生成实例的方法

for (int i = 0; i < 100_0000; i++) {
    Phone p = new Phone();
}

Class<?> clazz = Phone.class;
for (int i = 0; i < 100_0000; i++) {
    Phone p = clazz.newInstance();
}

Phone ori = new Phone();
for (int i = 0; i < 100_0000; i++) {
    Phone p = ori.clone();
}

结构模式

06适配器模式

分为三种,这三种也是其他结构模式的基础

  • 类的适配器模式
  • 对象的适配器模式
  • 接口的适配器模式

类的适配器模式

简而言之,就是存在类C, 需要将其等同于实现了接口 A

class Source {
    void fun1() {

    }
}

interface Target {
    void fun1();
    void fun2();
}

class Adapter extends Source implements Target {
    public void fun2() {

    }
}

例子

Target t = new Adapter();
t.fun1();

对象的适配器模式

思路与前面相同,但不再继承类,而是添加一个类成员变量

class Wrapper implements Target {
    private Source source;

    public Wrapper(Source source) {
        this.source  = source;
    }

    void fun1() {
        source.fun1();
    }

    void fun2() {

    }
}

接口的适配器模式

如果一个接口存在多个方法,而我们又不想全部实现,只想实现部分方法时。Netty有大量代码就是这样设计的

// 接口同上

// 抽象接口
public abstract class AbstractWrapper implements Target {
    void fun1() {}
    void fun2() {}
}

// 
class Wrap1 extends AbstractWrapper {
    void fun1() {

    }
}

class Wrap2 extends AbstractWrapper {
    void fun2() {

    }
}

07装饰模式

当需要扩展一个类功能,同时也能随时撤销该类的功能

// 功能接口
interface Funcable {
    void fun1();
}

// 原有类
class Source implements Funcable {
    void fun1() {

    }
}

// 装饰器
class Decorator implements Funcable {
    private Source source;

    Decorator(Source s) {
        this.source = s;
    }

    void fun1() {
        befor();
        source.fun1();
        after();
    }
}

测试一下

Source source = new Source();
Decorator d = new Decorator(source);
d.fun1();

08代理模式

代理模式分为动态代理和静态代理,这里用静态代理说明,可以看出代码和上文的装饰器模式很像。

// 功能接口
interface Funcable {
    void fun1();
}

// 原有类
class Source implements Funcable {
    void fun1() {

    }
}
// 代理类
class Proxy implements Funcable {
    private Source source;

    public Proxy() {
        this.source = new Source();
    }

    public void fun1() {
        before();
        source.fun1();
        after();
    }
}

可以看到,装饰器模式唯一不同的就在于,Source变成了陌生类

Proxy p = new Proxy();
p.fun1();

09外观模式Facade

主要是为了解决类依赖问题,将他们的依赖关系放到Facade类中 举一个例子,存在一个http server要启动,可能需要初始化缓存模块,session模块, 大文件模块,用户不清楚他们之间的启动顺序,或者说依赖关系。 这时可以建立Facade类

class HttpServer {
    void start() {
        Cache.start();
        Session.Start();
        BigFile.start();
    }
}

10桥接模式

我理解成类的具体实现可以单独分开。这里用数据库举一个例子

// 先统一定义数据库操作接口
public interface DBDriver {
    void update();
}

// 数据库Mysql
public class MysqlDriver implements DBDriver {
    void update() {

    }
}

// 数据库Oracle
public class OracleDriver implements DBDriver {
    void update() {
        
    }
}

// 桥
public abstract class Bridge {
    private DBDriver driver;

    void update() {}
    // getter
    // setter

} 

// 具体实现
public MyBridge extends Bridge {
    void update() {
        getDriver().update();
    }
}
// 使用mysql
MyBridge myb = new MyBridge();
myb.setDriver(new MysqlDriver());
// 使用Oracle
myb.setDriver(new OracleDriver())

11组合模式

处理部分整体,典型就是树节点

class TreeNode {
    int value;
    TreeNode left;
    TreeNode right;
}

class Tree {
    private TreeNode root;
}

12享元模式

主要目的是实现对象的共享,这里以数据库连接池为例

public class DBConnPool {
    List<DBConn> pool = new ArrayList<>();
    public DBConnPool() {
        for (int i = 0; i < size; i++) {
            DBConn conn = new DBConn(...);
            pool.add(pool);
        }
    }

    public DBConn getConn() {
        if (pool.size() > 0) {
            DBConn conn = pool.get(0);
            pool.remove(conn);
            return conn;
        }
        return 0;
    }
}

关系模式

  1. 父子类关系
    1. 策略模式
    2. 模板方法模式
  2. 两个独立类之间
    1. 观察者模式
    2. 迭代子模式
    3. 责任链模式
  3. 类的状态
    1. 备忘录模式
    2. 状态模式
  4. 通过中间件
    1. 访问者模式
    2. 中介者模式
    3. 解释器模式

13策略模式

策略模式定义了各种策略,用户自己选择用哪一个策略。或者应该说,就是interface的具体应用

interface Calculator {
    int calculate(int num);
}

class Add implements Calculator {
    public int calculate(int num) {
        return num + 1;
    }
}

class Reduce implements Calculator {
    public int calculate(int num) {
        return num - 1;
    }
}

测试一下

Calculator cal = new Add();
cal.calculate(7);

14模板方法

这个写过框架的基本都会用到,netty中大量的也是这样的用法。这里以http server为例. 可以说,就是abstract类具体应用

abstract class AbstractController {
    public void service(Request req, Response resp) {
        doGet(Request req, Response resp);
    }

    abstrac void doGet();
}

class Test1Controller extend AbstractController {
    public void doGet(Request req, Response resp) {

    }
}

class Test2Controller extend AbstractController {
    public void doGet(Request req, Response resp) {

    }
}

测试代码

AbstractController t = new Test1Controller();
t.service();

观察者模式

不解释了,直接上代码

// 定义一个监狱犯罪人接口
interface Eviler {
    // 收到消息
    void rcv(String s);
}

// 定义偷窃罪人
class TouqieEviler implements Eviler {
    public void rcv (String s) {
        sout("touqie" + s);
    }
} 
// 定义打劫罪犯罪人
class DajieEviler implements Eviler {
    public void rcv (String s) {
        sout("dajie" + s);
    }
} 

//定义一个警察
class Police {
    //增加监控的犯罪人
    Set<Eviler> set = new HashSet<>();
    void addEviler(Eviler e) {
        set.add(e);
    }
    void deleteEvile(Eviler e) {
        set.remove(e);
    }
    // 通知所有被监控人
    void notifyAll() {
        for (Eviler e : set) {
            e.rcv();
        }
    }
}

迭代子模式

简而言之,就是实现一个迭代器,java中的集合对象都用到了

// 先定义一个迭代器接口
interface Iterator {
    Object previous(); //前一个元素
    Object next(); // 后一个元素
    boolean hasNext(); // 是否有后面的元素
    Object first(); // 第一个元素
}

// 再定义一个集合接口
interface Collection {
    Iterator iterator();
    Object get(int i); // 得到集合内的元素
    int size(); // 集合大小
}

//定义一个自己的迭代器
class MyIterator implements Iterator {
    private Collection col;
    private int pos = -1;

    public MyIterator(Collletion c) {
        this.col = c;
    }

    public Object previous() {
        if (pos > 0) {
            pos--;
        }
        return col.get(pos);
    }

    public Object next() {
        if (pos < col.size() - 1) {
            pos++;
        }
        return col.get(pos);
    }

    public boolean hasNext() {
        return pos < col.size() - 1;
    }

    public Object first() {
        pos = 0;
        return col.get(pos);
    }
}

// 最后定义一个自己的集合类
class MyCollection implements Collection {
    public Obejct[] objs = {"zhuj", 11, 'h', 12.3}
    Iterator iterator;
    public Iterator iterator() {
        /* 特别注意,
        这个是完全错误的写法。
        如果thread1 执行next, thread2执行previous, 那还怎么遍历?
        遍历有个基本条件,每次都是是从开头到结尾,而不是只有部分元素
        if (null == iterator) {
            iterator = new MyIterator(this);
        }
        return iterator;
        */
        return new MyIterator(this);
    }

    public Object get(int i) {
        return objs[i];
    }

    public int size() {
        return objs.length;
    }
}

定义一个测试

Collection col = new MyCollection();
Iterator it = col.iterator();
while (it.hasNext()) {
    sout(it.next());
}

我不是很清楚迭代器在集合内部有什么特殊优势,这里从网上看到一个例子,说一下在某些情况下的为什么要用迭代器实现效果非常好。例如,我们要得到前100个斐波那契数列的值。所以有以下写法

    main() {
        for (int i = 1; i < 100; i++) {
            sout(fibonacci(i));
        }
    }

    private int fibonacci(int num) {
        if (num <= 0) {
            throw new UnsupportedOperationException("斐波那契数列不允许输入非自然数");
        }
        
        int ret;
        if (num == 1) {
            ret = 1;
        } else if (num == 2) {
            ret = 1;
        } else {
            ret = fibonacci(num - 1) + fibonacci(num - 2);
        }
        return ret;
    }

很显然,这样输出的话,量会很大,基本把栈搞崩,所以,改成循环的用法

    main() {
        for (int i = 1; i < 100; i++) {
            sout(fibonacciLoop(i));
        }
    }

    private int fibonacciLoop(int num) {
        if (num <= 0) {
            throw new UnsupportedOperationException("斐波那契数列不允许输入非自然数");
        }
        if (num == 1) {
            return 1;
        }
        if (num == 2) {
            return 1;
        }

        int f1 = 1;
        int f2 = 1;
        int f3 = f1 + f2;
        int i = 2;
        while (i++ < num) {
            f3 = f2 + f1;
            f1 = f2;
            f2 = f3;
        }
        return f3;
    }

这个方法也存在一个问题,因为每一次计算都要循环计算之前已经计算过的值,这在计算机中,是很忌讳的用法。所以,接下来,用一个迭代器。这样就存储的之前的结果,也避免了大量的重复计算。

main() {
    Iterator it = new FibonacciIterator(100);
    while(it.hasNext()) {
        sout(it.next())
    }
}

class FibonacciIterator implements Iterator {
    int f1 = 1;
    int f2 = 1;
    int f3 = 0;

    int count;
    
    int index = 1;

    public FibonacciIterator(int val) {
        this.count = val;
    }    

    public boolean hasNext() {
        return index < count;
    }
    
    public int next() {
        index++;
        if (index == 1) {
            return f1;
        }
        if (index == 2) {
            return f2;
        }
        f3 = f2 + f1;
        f1 = f2;
        f2 = f3;
        return f3;
    }
}

17责任链模式

字面意思,存在多个对象,每个对象有对下一个对象的引用,这样看起来就像是一条链子,一直到存在某个对象(实例)处理请求。

class Handler {
    private Handler handler;
    private String name;

    public Handler(String s) {
        this.name = s;
    }

    public void setHandler(Handler h) {
        this.handler = h;
    }  

    public void handle() {
        sout(name + "handle it");
        if (handler != null) {
            handler.handle();
        }
    }
}

void main() {
    Handler h1 = new Handler("h1");
    Handler h2 = new Handler("h2");
    Handler h3 = new Handler("h3");

    h1.setHandler(h2);
    h2.setHandler(h3);
    h1.handle();
}

18命令模式

// 新建一个命令接口
interface Cmd {
    void exec();
}

// 具体的命令, 关机命令
class ShutdwonCmd implements Cmd {
    private Pc pc;

    public ShutdwonCmd(Pc pc) {
        this.pc = pc;
    }

    public void exec() {
        pc.act_shutdown();
    }
}

// 执行者,执行命令的类
class Pc {
    public void act_startup() {

    }

    public void act_shutdown() {
        sout("执行关机。。。");
    }
}

// 调用命令的类,或者可以分配命令的类
public class Invoker {
    private Cmd cmd;
    
    public Invoker(Cmd cmd) {
        this.cmd = cmd;
    }

    public void act() {
        cmd.exec();
    }
}

void main() {
    // 最终执行命令的对象
    Pc pc  = new Pc();
    // 要求被执行的命令
    Cmd cmd = new ShutdownCmd(pc);
    // 发布命令
    Invoker invoker = new Invoker(cmd));
    invoker.act();
}

19备忘录模式

主要目的就是备忘录,也就是保存某对象的状态,必要时恢复。也就是说明,本质就是备份。 我们以一本书为例

// 先创建一个快照类,这个类用来保存当前书籍的内容,只读不写
class BookSnapshot {
    static int CountFlag = 1;
    int id;
    String val;

    public BookSnapshot(String val) {
        this.id = CountFlag++;
        this.val = val;
    }
    
    public getValue() {
        return this.val;
    }

    public int getId() {
        return id;
    }
}

// 一本书
class Book {
    private String content = "";

    // 书本写入新的内容
    public void writeContent(String s) {
        this.content += s;
    }

    // 得到当前书本内容
    public String getContent() {
        return this.content;
    }

    // 创建快照
    public BookSnapshot createSnapshot() {
        return new BookSnapshot(content);
    }

    // 根据快照恢复书籍的内容
    public restore(BookSnapshot shot) {
        this.content = shot.getValue();
    }
}

// 快照管理类,也就是备忘录
class BookManager {
    Map<Integer, BookSnapshot> map = new HashMap();

    //存储新的快照
    public void addBookSnapshot(BookSnapshot shot) {
        map.put(shot.getId(). shot);
    }

    //拿出旧的快照
    public BookSnapshot getBookSnapshot(int id) {
        return map.get(id);
    }
}

到了这一步,可以测试一下

//先写一本书
Book book = new Book();
BookManager bm = new BookManager();
// 写入内容
book.writeContent("123");
// 创建快照
BookSnapshot shot1 = book.createSnapshot();
bm.addBookSnapshot(shot1);

// 再次写入内容
book.writeContent("1we");
// 不想写了,恢复过去的内容
book.restore(bm.getBookSnapshot(1));

20状态模式

简而言之,不同的状态得到不同的行为, 这里直接以QQ为例

class QQ {
    static int online = 1;
    static int offline = 2;

    private int state;

    public void setState(int s) {
        this.state = s;
    }

    public int getState() {
        return this.state;
    }

    public void online() {
        sout("我在线");
    }

    public voud offline() {
        sout("我离线了");
    }
}

class Pc {
    private QQ qq;

    public Pc(QQ qq) {
        this.qq = qq;
    }

    // 打开qq
    public openQQ() {
        if (qq.getState == QQ.online) {
            qq.online();
        } else {
            qq.offline();
        }
    }
}

void main() {
    QQ qq = new QQ();
    Pc  pc = new Pc(qq);
    qq.setState(1);
    pc.openQQ();
    qq.setState(2);
    pc.openQQ();
}

21访问者模式

这个有点看不明白使用场景了.

// 先定义一个访问者接口
interface Visitor {
    void visit(Subject sub);
}

// 具体的访问者,假设是记者
class ReportVisitor implements VIsitor {

    public void visite(Subject sub) {
        sout("ReportVisitor 访问了 " + sub);
        sout("得到了许多信息:" + sub.getInfo());
    }
}

// 被访问者接口
interface Subject {
    void accept(Visitor vistor);
    String getInfo();
}

// 具体的被访问者,假设是一名作家
class WriterSubject implements Subject {
    public void accept(Visitor visite) {
        visite.visite(this);
    }

    public String getInfo() {
        return "我的最新书籍是《》";
    }
}

测试代码

Visitor vistor = new ReportVisitor();
WriterSubject writer = new WriterSubject();
writer.accept(vistor); //接受采访

22中介者模式

中介模式相对好理解一些,存在两个类相互关联,也就是相互包含,改变其中任何一个都可以可能改动全部,这个时候引入中介会降低耦合,以二手房地产为例

// 先建立客户接口
interface Buziness {
    void action();
}

// 二手房卖家
class Seller implements Buziness {
    public void action() {
        
    }
}

// 买家
class Buyer implements Buziness {
    public void action() {

    }
}

// 中介
class Mediator {
    private Seller seller;
    private Buyer buyer;
    ...
    public void work() {
        seller.action();
        buyer.action();
    }
} 

23解释器模式

一般用的很少,除非编译器开发, pass