大牛分享的几种设计模式及知识要点(二)
一、代理模式(Proxy Pattern )
定义:Provide a surrogate or placeholder for another object to control access to it.
(为其他对象提供一种代理以控制对这个对象的访问。)
● Subject 抽象主题角色
抽象主题类可以是抽象类也可以是接口,是一个最普通的业务类型定义,无特殊要
求。
● RealSubject 具体主题角色
也叫做被委托角色、被代理角色。它才是冤大头,是业务逻辑的具体执行者。
● Proxy 代理主题角色
也叫做委托类、代理类。它负责对真实角色的应用,把所有抽象主题类定义的方法
限制委托给真实主题角色实现,并且在真实主题角色处理完毕前后做预处理和善后
处理工作。
普通代理和强制代理:
普通代理就是我们要知道代理的存在,也就是类似的 GamePlayerProxy 这个类的
存在,然后才能访问;
强制代理则是调用者直接调用真实角色,而不用关心代理是否存在,其代理的产生
是由真实角色决定的。
普通代理:
在该模式下,调用者只知代理而不用知道真实的角色是谁,屏蔽了真实角色的变更
对高层模块的影响,真实的主题角色想怎么修改就怎么修改,对高层次的模块没有
任何的影响,只要你实现了接口所对应的方法,该模式非常适合对扩展性要求较高
的场合。
强制代理:
强制代理的概念就是要从真实角色查找到代理角色,不允许直接访问真实角色。高
层模块只要调用 getProxy 就可以访问真实角色的所有方法,它根本就不需要产生
一个代理出来,代理的管理已经由真实角色自己完成。
动态代理:
根据被代理的接口生成所有的方法,也就是说 给定一个接口,动态代理会宣称 “ 我
已经实现该接口下的所有方法了 ” 。
两条独立发展的线路。动态代理实现代理的职责,业务逻辑 Subject 实现相关的逻
辑功能,两者之间没有必然的相互耦合的关系。通知 Advice 从另一个切面切入,
最终在高层模块也就是 Client 进行耦合,完成逻辑的封装任务。
动态代理调用过程示意图:
动态代理的意图:横切面编程,在不改变我们已有代码结构的情况下增强或控制对
象的行为。
首要条件:被代理的类必须要实现一个接口。
二、原型模式(Prototype Pattern )
定义:Specify the kinds of objects to create using a prototypical instance,and
create new objects by copying this prototype.(用原型实例指定创建对象的种类,
并且通过拷贝这些原型创建新的对象。)
原型模式通用代码:
public class PrototypeClass implements Cloneable{
//覆写父类 Object 方法
@Override
public PrototypeClass clone(){
PrototypeClass prototypeClass = null;
try {
prototypeClass = (PrototypeClass)super.clone();
} catch (CloneNotSupportedException e) {
//异常处理
}
return prototypeClass;
}
}
原型模式实际上就是实现 Cloneable 接口,重写 clone ()方法。
使用原型模式的优点:
● 性能优良
原型模式是在内存二进制流的拷贝,要比直接 new 一个对象性能好很多,特别是
要在一个循环体内产生大量的对象时,原型模式可以更好地体现其优点。
● 逃避构造函数的约束
这既是它的优点也是缺点,直接在内存中拷贝,构造函数是不会执行的(参见
13.4 节)。
使用场景:
● 资源优化场景
类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。
● 性能和安全要求的场景
通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模
式。
● 一个对象多个修改者的场景
一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以
考虑使用原型模式拷贝多个对象供调用者使用。
浅拷贝和深拷贝:
浅拷贝:Object 类提供的方法 clone 只是拷贝本对象,其对象 内部的数组、引用
对象 等都不拷贝,还是指向原生对象的内部元素地址,这种拷贝就叫做 浅拷贝 ,其
他的原始类型比如 int、long、char、string(当做是原始类型)等都会被拷贝。
注意: 使用原型模式时,引用的成员变量必须满足两个条件才不会被拷贝:一是
类的成员变量 ,而不是方法内变量;二是必须是一个 可变的引用对象 ,而不是一个
原始类型或不可变对象。
深拷贝:对私有的类变量进行独立的拷贝
如:thing.arrayList = (ArrayList<String>)this.arrayList.clone();
kx33389,加她就能免费领取分布式、微服务、源码分析、性能优化、高并发高可用等技术的资料
三、中介者模式
定义:Define an object that encapsulates how a set of objects interact.Mediator
promotes loose coupling by keeping objects from referring to each other
explicitly,and it lets you vary their interaction independently.(用一个中介对象封装
一系列的对象交互,中介者使各对象不需要显示地相互作用,从而使其耦合松散,
而且可以独立地改变它们之间的交互。)
● Mediator 抽象中介者角色
抽象中介者角色定义统一的接口,用于各同事角色之间的通信。
● Concrete Mediator 具体中介者角色
具体中介者角色通过协调各同事角色实现协作行为,因此它必须依赖于各个同事角
色。
● Colleague 同事角色
每一个同事角色都知道中介者角色,而且与其他的同事角色通信的时候,一定要通
过中介者角色协作。每个同事类的行为分为两种:一种是同事本身的行为,比如改
变对象本身的状态,处理自己的行为等,这种行为叫做自发行为(Self-
Method),与其他的同事类或中介者没有任何的依赖;第二种是必须依赖中介者
才能完成的行为,叫做依赖方法(Dep-Method)。
通用抽象中介者代码:
public abstract class Mediator {
//定义同事类
protected ConcreteColleague1 c1;
protected ConcreteColleague2 c2;
//通过 getter/setter 方法把同事类注入进来
public ConcreteColleague1 getC1() {
return c1;
}
public void setC1(ConcreteColleague1 c1) {
this.c1 = c1;
}
public ConcreteColleague2 getC2() {
return c2;
}
public void setC2(ConcreteColleague2 c2) {
this.c2 = c2;
}
//中介者模式的业务逻辑
public abstract void doSomething1();
public abstract void doSomething2();
}
ps :使用同事类注入而不使用抽象注入的原因是因为抽象类中不具有每个同事类
必须要完成的方法。即每个同事类中的方法各不相同。
问:为什么同事类要使用构造函数注入中介者,而中介者使用 getter/setter 方式注
入同事类呢?
这是因为同事类必须有中介者,而中介者却可以只有部分同事类。
使用场景:
中介者模式适用于多个对象之间紧密耦合的情况,紧密耦合的标准是:在类图中出
现了蜘蛛网状结构,即每个类都与其他的类有直接的联系。
四、命令模式
定义:Encapsulate a request as an object,thereby letting you parameterize clients
with different requests,queue or log requests,and support undoable operations.
(将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求
排队或者记录请求日志,可以提供命令的撤销和恢复功能。)
● Receive 接收者角色
该角色就是干活的角色,命令传递到这里是应该被执行的,具体到我们上面的例子
中就是 Group 的三个实现类(需求组,美工组,代码组)。
● Command 命令角色
需要执行的所有命令都在这里声明。
● Invoker 调用者角色
接收到命令,并执行命令。在例子中,我(项目经理)就是这个角色。
使用场景:
认为是命令的地方就可以采用命令模式,例如,在 GUI 开发中,一个按钮的点击
是一个命令,可以采用命令模式;模拟 DOS 命令的时候,当然也要采用命令模
式;触发-反馈机制的处理等。