10.19 设计模式

155 阅读12分钟

10.19 设计模式

其实通读完了设计模式,学到的一点是要对谁拿到谁的引用要明白,又隐藏另一个引用的什么东西呢?

1 备忘录模式

速记:后悔药:保存对象的临时状态,以便在需要的时候恢复对象

应用: 游戏存档,ctrl+z,IE中的后退

实现

  1. state存储类,提供getter查询锁包含的 state 状态
  2. 管理state类,维护一个的来管理 state类
  3. 使用者,产生状态,可以调用 sava保存自己状态,get方法获得以前保存的状态

2 观察者模式

速记:一个对象变化,同时所有观察他的人

应用:Vue的js中的watcher

实现

  1. 各种 observe 里面有个 update方法
  2. 被观察者有个notifyAll方法,当被观察者的state发生改变的时候,会notifyAll所有它attach的观察者

3 状态模式

速记:依据上下文来决定动作

应用:状态管理,比如电梯的状态,打篮球的时候运动员可以有正常状态、不正常状态和超常状态。常规的做法是 final 每个状态变量,然后switch case 对每个 final 采取的相应动作

实现

  1. 上下文(contex)持有一个抽象状态变量 state
  2. 具体的state在采取action时会将contex中的state设置为自己

4 策略模式

速记:Context主动选择已定义的不同策略

应用:诸葛亮的锦囊妙计

实现:

  1. context持有一个stragy变量
  2. context可以new出来不同的策略

和状态模式差不多,但是区别在于,策略模式是context主动选择策略而采取相应的行为,而状态模式则是被动状态改变导致context行为改变

5 空对象模式

6 模板模式

7 访问者模式

速记:访问者去帮助类实现某些东西

应用:不改变对象结构,增加新的功能

实现:

  1. 实体的每个零件中 accept 方法接受访问者访问,
  2. 访问者通过visit重载方法去访问每个零件

8 MVC 模式

9 业务代表模式

速记:客户要办事,就交给业务代表去办

应用:

实现:

  1. 抽象服务接口,子类实现不同服务。
  2. 查询服务向上提供接口给业务代表查,向下调用抽象服务接口
  3. 业务代表通过查询服务lookup拿到服务
  4. 客户和业务代表交互

10 组合实体模式

速记:大类包小类

应用:bean持久化存储到数据库,用复合bean去操作小bean

实现:

  1. 小bean
  2. 复合bean

11 数据访问对象模式(DAO模式)

速记:通过 api 操作数据对象

应用:spring service 的增删查改接口 来操作 pojo

实现:

  1. Dao 的 crud 接口,DaoImpl
  2. pojo

12 前端控制器模式

速记:一个 service 负责集中接受所有请求,然后负责分发下去,handler处理后返回 response

应用:springMvc的前端控制器,对 url request 进行解析,分发给对应的 controller,返回response

实现:

  1. FrontController 类接受所有请求,调用dispatch分发下去
  2. 具体的handler收到后处理请求,返回response

13 拦截过滤器模式

速记:一个过滤器链条来对权限日志进行管理,预处理或者后处理拦截

应用:springMvc的拦截器

实现:

  1. 各种拦截过滤器,比如日志拦截器,权限拦截器
  2. 对request执行的handler
  3. 对request执行的handler过滤器链中可以增加一系列拦截,然后启动handler
  4. 过滤器的管理,负责对过滤器链中的过滤器进行增加和删除,然后将request分发到过滤器链中

14 过滤器模式(filter模式,标准模式)

速记:结合多个标准来获得单一标准,对不符合标准的东西进行过滤

应用:springMvc制定了标准过滤

实现:

  1. 通过一个抽象接口来调用多个不同的标准

15 传输对象模式

速记:服务端从数据库种取数据,填充 pojo 类,然后将 pojo 对象通过网络序列化传输

应用:springMvc 的 json返回

实现:

  1. pojo,dao,数据库

16 服务定位器模式

速记:建立缓存。第一次通过JNDI查询数据库,第二次查询缓存。

应用:JNDI缓存cache查询

实现:

  1. JNDI initContext根据名字创建类(工厂模式)
  2. Cache类,对传输进来的对象进行缓存(传输对象模式)
  3. serviceLocator 类,声明一个静态 cache,先查找缓存有没有,没有则新建,然后进入缓存

17 工厂模式

简单工厂模式

速记:交给工厂取 new 对象,调用者只需要知道名字即可

应用:spring的java bean;一类手机产品

实现:

  1. 工厂类 return new xx();

工厂模式(抽象工厂的单产品族)

速记:不同类型的工厂继承工厂,new工厂的时候就决定了产品类型

应用:

实现:

  1. 工厂模板类
  2. 具体工厂类
  3. 工厂下的产品

抽象工厂模式

速记:抽象工厂生产工厂,并且定义工厂所生产的产品

应用:一套pc,键盘,鼠标等来自一个工厂

实现:

  1. 一个抽象工厂,定义了要生产的产品
  2. 具体的工厂生产自己类型的产品
  3. 具体的产品

18 单例模式

饿汉式

饿汉饿在不管要不要new对象,都给你一个new对象。就像不管一个人想不想吃东西都把吃的先买好,如同饿怕了一样

  • 线程安全

速记与实现:利用类加载时就static,来直接 new 对象

懒汉式

懒汉懒在调用实例化方法时才new对象,类加载时没有new对象

  • 线程不安全

速记与实现:提供一个static引用给外部,私有化构造方法,暴露实例化方法,实例化方法中生产对象

  • 线程安全

速记与实现:将实例化方法 synchronized 化

双重校验锁

速记与实现:第一重判断加快速度,第二重判断保证原子性。volatile保证禁止指令重排。

重点:

能不能不使用 valatile?不能!

  1. volatile关键字的重要性,禁止 new 指令重排序
  2. 一个new过程由三个原子操作:① 分配内存,② 调用构造器方法,执行初始化,③ 变量赋值。其中②,③可能发生指令重排序
  3. 如果一个线程在新建内存时优先执行了 ③ ,而另一个线程是可以发现为null是可以这个变量的,由于变量还未初始化,所以出错了。

能不能不使用 synchronized?不能!

  1. 保证大量线程进入方法时,互斥

synchronized 保证了有序性吗?没有

  1. synchronized保证了内部代码块,单线程的有序性。

  2. 但是外部多线程环境在第一次判断为 null 时,不会进入锁,所以没有保证多线程的有序性。

     public class Singleton {
    
     	private volatile static Singleton singleton;
    
     	private Singleton() {}
    
     	public static Singleton getSingleton() {
    
     		if(singleton == null) {
     			synchronized (Singleton.class) {
     				if(singleton == null) {
     					singleton = new Singleton(); // volatile 保证这一行优先执行
     				}
     			}
     		}
    
     		return singleton;
     	}
    
     }
    

静态内部类

速记与实现:静态内部类也是利用了类加载机制,但是由于是类,所以没有实例化,节约了内存

枚举类

速记与实现:枚举类中定义枚举变量,因为枚举变量在java中是唯一的

破坏以上单例模式

要破坏单例模式,肯定要创建新的对象,创建对象的方式有四种

  • new,无法破坏
  • clone:实现cloneable接口,主动破坏
  • 反序列化:实现servlized接口,原理也是反射机制破坏,通过反射拿到Object的构造函数,反射出单例类对象,从而创建了新的实例。解决办法:可以通过readResolve返回对象来防止破坏
  • 反射:知道类结构,通过反射去拿构造器,改变构造器权限,就可以破坏。解决办法:可以通过构造器中抛出异常来破坏

19 建造者模式

速记:链式调用,一直返回当前对象

应用:当一个类的构造函数参数个数超过4个,而且这些参数有些是可选的参数,考虑使用构造者模式

实现:

  1. 在Computer 中创建一个静态内部类 Builder,然后将Computer 中的参数都复制到Builder类中。

  2. 在Computer中创建一个private的构造函数,参数为Builder类型

  3. 在Builder中创建一个public的构造函数,参数为Computer中必填的那些参数,cpu 和ram

  4. 在Builder中创建设置函数,对Computer中那些可选参数进行赋值,返回值为Builder类型的实例

  5. 在Builder中创建一个build()方法,在其中构建Computer的实例并返回

     public class Computer {
         private final String cpu;//必须
         private final String ram;//必须
         private final int usbCount;//可选
         private final String keyboard;//可选
         private final String display;//可选
    
         private Computer(Builder builder){
             this.cpu=builder.cpu;
             this.ram=builder.ram;
             this.usbCount=builder.usbCount;
             this.keyboard=builder.keyboard;
             this.display=builder.display;
         }
         public static class Builder{
             private String cpu;//必须
             private String ram;//必须
             private int usbCount;//可选
             private String keyboard;//可选
             private String display;//可选
    
             public Builder(String cup,String ram){
                 this.cpu=cup;
                 this.ram=ram;
             }
    
             public Builder setUsbCount(int usbCount) {
                 this.usbCount = usbCount;
                 return this;
             }
             public Builder setKeyboard(String keyboard) {
                 this.keyboard = keyboard;
                 return this;
             }
             public Builder setDisplay(String display) {
                 this.display = display;
                 return this;
             }
             public Computer build(){
                 return new Computer(this);
             }
         }
       //省略getter方法
     }
    

使用方法:链式调用

	Computer computer=new Computer.Builder("因特尔","三星")
	                .setDisplay("三星24寸")
	                .setKeyboard("罗技")
	                .setUsbCount(2)
	                .build();

20 原型模式(克隆模式)

速记:就是克隆模式,java 实现 cloneable 接口

应用:实现克隆操作,在 JAVA 继承 Cloneable,重写 clone()

实现:

  1. 实现cloneable接口嘛

21 适配器模式

速记:两个不相容接口的桥梁,使其能很好的工作

应用:转接头

实现:

  1. 适配器继承其中一个接口,并将另一个接口作为成员
  2. 使用者将适配器作为成员来使用

22 桥接模式

速记:抽象类中有一个接口成员,具体子类要实现这个接口成员,避免了继承

应用:抽象类和具体类之间进行桥接;减少子类的继承

实现:

  1. Abstraction:定义抽象接口,拥有一个Implementor类型的对象引用,比如shape
  2. RefinedAbstraction:子类中可以扩展Abstraction中的接口定义,比如红圈,绿圈
  3. ConcreteImplementor:实现Implementor接口,给出具体实现

23 组合模式

速记:用树形结构来表示一个组织

应用:树形结构的组织,文件夹管理

实现:

  1. 成员中有一个可以装本类成员的list即可、

24 装饰器模式

速记:动态的给一个对象添加职责,把一个对象包起来

应用:python @ 装饰器

实现:

  1. 抽象装饰器持有一个被装饰类的接口作为成员变量,
  2. 被装饰类接口变量,在构造方法中初始化
  3. 调用装饰器的时候,将对象传入构造方法即可

25 外观模式

速记:通过一个类,就可以new出来其他类,或者释放其他类,很方便

应用:电脑是一个外观,开机的时候把cpu,内存这些都启动了

实现:

  1. 在一个类中包含多个类的成员变量,构造方法中,new 出来多个成员变量即可
  2. 类中有多个方法可以操作成员变量的方法

26 享元模式(共享元对象模式)

速记:让对象不重复创建,共享元对象

应用:JAVA 中的 String,如果有则返回,如果没有则创建一个字符串保存在字符串缓存池里面

实现:

  1. 一个创建对象的工厂,维护一个hashmap容器
  2. 将对象放进hashmap中,查询hashmap,如果有,则返回,没有则新建

27 代理模式

速记: 代理模式有很多种,多是为了更好的控制另外一个对象,比如更好的缓存,更好的安全控制

应用:买火车票不一定要去火车站买,也可以去代售点买;windows里面的快捷模式;spring aop代理模式;vpn代理

实现:

  1. 代理类中拿到被代理类的引用,然后对引用private化,暴露自己的public接口来控制引用

28 责任链模式

速记:

应用:js中的事件冒泡机制;jsp servlet中的filter;卧槽,日志记录器也是吗

实现:

  1. 抽象类中设置下一个抽象类的引用
  2. 抽象类中对级别进行检验,如果某个当前抽象类级别够了,就调用具体类的实现方法

29 命令模式

速记:不同的命令封装成类,该类将请求类作为成员变量,并在构造方法中初始化

应用:命令行 command

实现:

  1. 请求者
  2. 命令类,该类将请求类作为成员变量,并在构造方法中初始化,提供调用请求者
  3. 调用者调用命令类,来处理请求

30 解释器模式

速记:定义新的规则来表示原来的东西

应用:编译器、运算表达式计算。

实现:

  1. 定义新的规则来表示原来的东西

31 迭代器模式

速记:iterator迭代器

应用:java迭代器

实现:

  1. 实现 hasnext(),next()方法,其实也是内部维护一个index++实现的
  2. 迭代器一般用于集合中,所以有个container

32 中介者模式

速记:中介类封装一系列对象的交互,接触交互的相互引用

应用:聊天室中一系列对象在相互交互,qq群

实现:

  1. 对象只需要和中介交互就行了