策略模式
环境(Context)角色:这个是要使用策略(算法)的类,持有一个Strategy类的引用。
抽象策略角色(Strategy):定义策略类应该有的接口。
具体策略角色(Concrete):具体实现相关的算法。
Book book = new Comic(12.3);
Strategy strategy = new ComicStrategy();
book.setStrategy(strategy);
book.show();
策略和使用策略者是一个整体,client在创建策略的时候需要事先创建策略适用对象否则策略没意义。
client在使用时必须先了解每个策略的使用场景,然后靠自己根据条件判断使用哪一种策略。
责任链
每个Handler都有下一个Handler的对象引用,每个Handler只关心自己的事情,通过参数判断不属于自己的就传给下一个。
每个处理者都必须严格遵守规定,每个处理者都必须都能处理一类事情,必须知道下一个处理者是谁。
在处理事情之前,每个处理者都必须是创建好并且持有下一个处理者的。
开闭原则:不通过责任链的话,每一个Handler其实就是每一个if else里面的处理代码,通过责任链模式,使得再增加其他需要处理的事情和其他处理者时可以不用修改源代码,而是动态的添加具体的处理者类。
优劣:低耦合的最小封装,执行流程可以自由组合。但处理者类太多,会影响执行效率。
JiaMu jiaMu = new JiaMu(null);
jiaMu.setSuccessor( new JiaShe (
new JiaZheng(
new JiaBaoYu(
new JiaHuan( jiaMu ) ) ) ) );
player = jiaMu;
player.handle(4);
责任链有点类似策略模式,都是为了解决一堆if else判断下的任务,不同在于责任链不把适用条件放在了处理者内部,client端在使用时不必去了解每一个Handler具体是做啥的。另外策略模式一个条件对应一个策略,责任链的一个事情可能会被多个Handler处理过。
命令
将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请 求排队或者记录请求日志,可以提供命令的撤销和恢复功能。
更松散的耦合: 命令模式使得发起命令的对象——客户端,和具体实现命令的对象——接收者对象完全解耦,也就是说发起命令的对象完全不知道具体实现对象是谁,也不知道如何实现。
更动态的控制:命令模式把请求封装起来,可以动态地对它进行参数化、队列化和日志化等操作,从而使得系统更灵活。
很自然的复合命令:命令模式中的命令对象能够很容易地组合成复合命令,如宏命令,从而使系统操作更简单,功能更强大。
更好的扩展性: 由于发起命令的对象和具体的实现完全解耦,因此扩展新的命令就很容易,只需要实现新的命令对象,然后在装配的时候,把具体的实现对象设置到命令对象中,然后就可以使用这个命令对象,已有的实现完全不用变化。
Receiver receiver = new Receiver();
Command command = new ConcreteCommand(receiver);
Invoker invoker = new Invoker();
invoker.setCommand(command);
invoker.invoke();
封装命令,可动态扩展命令,需要增加命令时只需再实现一个命令对象。命令和命令之前一个的修改不影响一个。
解耦命令的请求者client和接收者,client创建各种对象之后只需通过invoker.invoke()就能实现自己的功能。
调用者(Invoker)角色:要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。
Invoker 作为调用者,可以自由安排命令的预处理:直接执行单个命令,或者把命令放入队列后批量执行,或者把命令放到异步线程去执行。可以缓存命令作为历史记录,可以为每个命令记录执行日志,可以撤销执行等等。
状态
首先要先找出主体拥有的所有状态和每个状态对应的操作,然后画出状态图
状态接口声明所有的主体动作,每个具体状态实现每个动作的反应。
所有的具体状态全部预先创建在主体里面,并在主体里面设置一个当前状态,主体的所有动作都由当前状态来执行。在执行过程中状态根据执行情况自动发生转移,这样主体只需要执行业务不用关系内部状态,执行后的结果由状态自动反馈回来。
ATM atm;
atm = new ATM(totalAmount, balance, amount, pwd); //所有的状态都已预置在ATM里面
System.out.println(atm.toString());
atm.insertCard();
atm.submitPwd();
atm.getCash();//操作之后内部状态会根据改变后的情况自动状态转移
适用于状态比较复杂的场景
解释器
用来解释某些内容的含义,适用的比较少的一种模式。
public class Context { private String[] shuzis={"1","2","3","4","5","6","7","8","9","0"}; private String[] xiaoxiezimus={"a","b","c","d","e","f","g","h","i","j","k","l"}; private String[] daxiezimus={"A","B","C","D","E","F","G"};
private AbstractExpression infomation;
public Context(){
AbstractExpression shuzi=new TerminalExpression(shuzis); AbstractExpression xiaoxiezimu=new TerminalExpression(xiaoxiezimus); AbstractExpression daxiezimu=new TerminalExpression(daxiezimus);
infomation=new NonTerminalExpression(shuzi,xiaoxiezimu,daxiezimu); }
public void jieshi(String info) { boolean ok=infomation.interpret(info);
if(ok) System.out.println("正确! ["+info+"] 满足 [单个数字-单个小写-单个大写] 的条件");
else System.out.println("错误! ["+info+"] 不满足 [单个数字-单个小写-单个大写] 的条件"); }}
Context people=new Context();
people.jieshi("2-a-A"); people.jieshi("11-A-5"); people.jieshi("你-好-吖"); people.jieshi("2aA");
中介者
中介者模式通过中介者对象来封装一系列的对象交互,将对象间复杂的关系网状结构变成结构简单的以中介者为核心的星形结构,对象间一对多的关联转变为一对一的关联,简化对象间的关系,便于理解;各个对象之间的关系被解耦,每个对象不再和它关联的对象直接发生相互作用,而是通过中介者对象来与关联的对象进行通讯,使得对象可以相对独立地使用,提高了对象的可复用和系统的可扩展性。
在中介者模式中,中介者类处于核心地位,它封装了系统中所有对象类之间的关系,除了简化对象间的关系,还可以对对象间的交互进行进一步的控制。
迪米特法:不要和"陌生人"说话、只与你的直接朋友通信。尽量减少对象之间的交互,如果两个对象不必直接通信,那么这两个对象就不应该发生任何直接的相互作用,如果其中一个对象需要调用另外一个对象的某个方法时,可以通过第三者转发这个调用。就是通过引入一个合理的第三者来降低先有对象之间的耦合度。
MediatorStructure mediator = new MediatorStructure(); //中介里面持有所有人的引用
HouseOwner houseOwner = new HouseOwner("张三", mediator);//房东和租户都依赖于中介
Tenant tenant = new Tenant("李四", mediator);
mediator.setHouseOwner(houseOwner);
mediator.setTenant(tenant);
tenant.constact("需要租三室的房子"); //租户和房东的交流信息最终都汇聚到中介那
houseOwner.constact("我这有三室的房子,你需要租吗?");
适用于关系比较复杂的系统,关键点在中介类封装,中介类持有所有联系人,每个联系人也都持有中介。
备忘录
备忘录模式的本质:保存和恢复内部状态。保存是手段,恢复才是目的。
发起人(Originator)角色:记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里的所有信息。
备忘录(Memento)角色:负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人。
管理者(Caretaker)角色:对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录的内容进行访问与修改。
Originator originator = new Originator();
Caretaker caretaker = new Caretaker();
originator.setState("老的状态");
caretaker.setMemento(originator.createMemento());//保存状态
originator.setState("新的状态");//新的状态
System.out.println(originator.getState());
originator.restoreMemento(caretaker.getMemento());//恢复状态
System.out.println(originator.getState());
迭代器
Java库中的容器本身没有迭代器所以搞出来的
观察者
关键点:分离观察者和被观察者。解耦之后增加新观察者或者增加被观察者时不需要改动原有的代码,冲新继承实现新的观察者或者被观察者就行。
发布-订阅的广播模型
1对多的 request 和 response
访问者
关键点:分离操作和数据元素,然后通过双向分派来完成访问。
主要是针对一些结构比较稳定的数据,操作方式多样且易变的情况进行的设计。
如对各种列表中的元素,需要定义一些不同的遍历操作。
client手上有访问者,也有数据列表,如果不用访问者模式,client对列表中的每个元素都有很多种不同的操作或者访问时,那每次都需要同样的遍历方式去调不同的操作。而这些访问除了对每个元素的操作不同之外,遍历的代码都是一样的(变的是对每个元素的操作,不变的是对数据的遍历),所以从设计角度需要对变和不变进行分离,访问者模式就是一种分离方法。
把操作(访问者)和数据分为2个模块,数据在接受访问的时候把访问者分派给数据,数据在接受访问者之后,数据把自己分派给访问者,由访问者执行自己内部的方法来操作数据(双向分派)。
client 只知道调函数访问数据,不关心具体的数据结构是列表还是什么其他的,所以需要ObjectStructure包装具体的数据结构,client只需通过ObjectStructure调用操作即可。
ObjectStructure 在通过访问者模式访问数据时,只关心数据列表,不关心访问者的操作。ObjectStructure 在遍历列表要做的只是接受访问,至于接受访问之后是怎么访问的它并不关心。ObjectStructure接受访问完成数据访问也就完成,
对数据的操作放在访问者中,由访问者实现。
结构对象(ObjectStructure)角色:有如下的责任,可以遍历结构中的所有元素;如果需要,提供一个高层次的接口让访问者对象可以访问每一个元素;如果需要,可以设计成一个复合对象或者一个聚集,如List或Set。
public class ObjectStructure {
private List<Node> nodes = new ArrayList<Node>();
public void action(Visitor visitor){
for(Node node : nodes) { node.accept(visitor); }
}
public void add(Node node){ nodes.add(node); }
}
ObjectStructure os = new ObjectStructure();
os.add(new NodeA());
os.add(new NodeB());
Visitor visitor = new VisitorA();
os.action(visitor);