对象可能经常需要使用多种不同的算法,但是如果变化频繁,会将类型变得脆弱。
在软件构建过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都编码到对象中,将会使得对象变得异常复杂;而且有时候支持不适用的算法也是一个性能负担。
如何在运行时根据需要透明地更改对象的算法?将算法与对象本身解耦,从而避免上述问题?
定义一系列算法,把他们一个个封装起来,并且使它们可互相替换。该模式使得算法可独立于使用它的客户而变化。
Context:封装角色 它也叫上下文角色,起承上启下封装作用,屏蔽高层模块对策略、算法的直接访问,封装可能存在的变化。
Strategy:抽象策略角色 策略。算法家族的抽象,通常为接口。
ConcreteStrategy:具体策略角色 实现抽象策略中的操作,该类含有具体的算法。
理解锦囊妙计例子,锦囊对应Context ,妙计对应ConcreteStrategy 。
Strategy及其子类为组件提供了一系列可重用的算法,从而可以使得类型在运行时方便地根据需要在各个算法之间进行切换。所谓封装算法,支持算法的变化。
Strategy模式提供了用条件判断语句以外的另一种选择,消除条件判断语句,就是在解耦合。含有许多条件判断语句的代码通常都需要Strategy模式。
与State模式类似,如果Strategy对象没有实力变量,那么各个上下文可以共享同一个Strategy对象,从而节省对象开销。
缺点: 策略类数量增多,每一个策略都是一个类,复用的可能性很小。 所有的策略类都需要对外暴露。
策略模式和代理模式的差别就是:策略模式的封装角色和被封装的策略类不用是同一个接口,如果是同一个接口那就成为了代理模式。
抽象的策略角色:
public interface Strategy{
//策略模式的运算法则
public void doSomething();
}
具体策略角色:
public class ConcreteStrategy1 implements Strategy{
public void doSomething(){
System.out.println("具体策略1的运算法则");
}
}
public class ConcreteStrategy2 implements Strategy{
public void doSomething(){
System.out.println("具体策略2的运算法则");
}
}
封装角色:
public class Context{
//抽象策略
private Strategy strategy = null;
//构造函数设置具体策略
public Context(Strategy strategy){
this.strategy = strategy;
}
//封装后的策略方法
public void doAnything(){
this.strategy.doSomething();
}
}
场景类:
public class Client{
public static void mian(String[] args){
//声明一个具体的策略
Strategy strategy = new ConcreteStrategy1();
//声明上下文对象
Context context = new Context(strategy);
//执行封装后的方法
context.doAnything();
}
}
在软件构建过程中,某些对象的状态如果改变,其行为也会随之而发生改变,比如电梯在运行和停止状态可以进行哪些操作,其支持的行为和运行/停止状态支持的行为就可能完全不同。
如何在运行时根据对象的状态来透明地更改对象的行为?而不会为对象操作和状态转化之间引入紧耦合?
允许一个对象在其内部状态改变时改变它的行为。从而使对象看起来似乎修改了其行为。
State:抽象状态角色 负责对象状态定义,并且封装环境角色以实现状态的切换
ConcreteState:具体状态角色 每个具体状态必须完成两个职责:本状态的行为管理以及趋向状态处理
State模式将所有与一个特定状态相关的行为都放入一个State的子类对象中,在对象状态切换时,切换相应的对象;但同时维持State的接口,这样实现了具体操作与状态转换之间的解耦。
为不同的状态引入不同的对象使得状态转换变得更加明确,而且可以保证不会出现状态不一致的情况,因为转换是原子性的——即要么彻底转换,要么不转换。
某一角色在在执行行为时,通过环境角色知道自己目前的状态,然后执行此状态下的行为或通过状态下的过渡方法转到其他状态。
抽象状态角色:
public abstract class State{
//定义一个环境角色,提供在子类访问
protected Context context;
//设置环境角色
public void setContext(Context context){
this.context = context;
}
//行为1
public abstract void handle1();
//行为2
public abstract void handle2();
}
具体状态角色:
public class ConcreteState1 extends State{
public void handle1(){
//本状态下必须处理的逻辑
}
public void handle2(){
//设置当前状态为state2
super.context.setState(Context.STATE2);
//过渡到state2状态,由handle2();
}
}
public class ConcreteState2 extends State{
public void handle1(){
//设置当前状态为state1
super.context.setState(Context.STATE1);
//过渡到state1状态,由handle1();
}
public void handle2(){
//本状态下必须处理的逻辑
}
}
具体环境角色:
public class Context{
//定义状态
public final static State STATE1 = new ConcreteState1();
public final static State STATE2 = new ConcreteState2();
//当前状态
private State CurrentState;
//获得当前状态
public State getCurrentState(){
return CurrentState;
}
//设置当前状态
public void setCurrentState(State currentState){
this.CurrentState = currentState;
//把当前状态通知给各实现类
this.CurrentState = setContext(this);
}
//行为委托
public void handle1(){
this.CurrentState.handle1();
}
public void handle2(){
this.CurrentState.handle2();
}
}
场景类:
public class Client{
public static void main(String[] args){
//定义环境角色
Context context = new Context();
//初始化状态
context.setCurrentState(new ConcreteState1);
//行为执行
context.handle1();
context.handle2();
}
}
在软件构建过程中,某些对象的状态在转换过程中,可能由于某种需要,要求程序能够回溯到对象之前处于某个点时的状态。如果使用一些公有接口来让其他对象得到对象的状态,便会暴露对象的细节实现。 如何实现对象状态的良好保存与恢复?但同时又不会因此而破坏对象本身的封装性。
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将对象恢复到原先保存的状态。
备忘录(Memento)存储原发器(Originator)对象的内部状态,在需要时恢复原发器状态。Memento模式适用于“由原发器管理,却又必须存储在原发器之外的信息”。
在实现Memento模式中,要防止原发器以外的对象访问备忘录对象。备忘录对象有两个接口,一个为原发器使用的宽街口;一个为其他对象使用的窄接口。
在实现Memento模式时,要考虑拷贝对象状态的效率问题,如果对象开销比较大,可以采用某种增量式改变来改变Memento模式。
发起人角色:
public class Originator{
//内部状态
private String state = "";
public String getState(){
return state;
}
public void setState(String state){
this.state = state;
}
//创建一个备忘录
public Memento createMemento(){
return new Memento(this.state);
}
//恢复一个备忘录
public void restoreMemento(Memento memento){
this.setState(memento.getState());
}
}
备忘录角色:
public class Memento{
//发起人的内部状态
private String state = "";
//构造函数传递参数
public Memento(String state){
this.state = state;
}
public String getState(){
return state;
}
public void setState(String state){
this.state = state;
}
}
备忘录管理员角色:
public class Caretaker{
//备忘录对象
private Memento memento;
public Memento getMemento(){
return memento;
}
public void setMemento(Memento memento){
this.memento = memento;
}
}
场景类:
public class Client{
public static void main(String[] args){
//定义出发起人
Originator originator = new Originator();
//定义出备忘录管理员
Caretaker caretaker = new Caretaker();
//创建一个备忘录
caretaker.setMemento(originator.createMemento());
//恢复一个备忘录
originator.restoreMemento(caretaker.getMemento());
}
}
public class Originator implements Cloneable{
private Originator backup;
//内部状态
private String state = "";
public String getState(){
return state;
}
public void setState(String state){
this.state = state;
}
//恢复一个备忘录
public void restoreMemento(){
//在进行恢复前应该进行断言,防止空指针
this.setState(this.backup.getState());
}
//克隆当前对象
protected Originator clone(){
try{
return (Originator)super.clone();
} catch(CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
}
在软件构件过程中,一个请求可能被多个对象处理,但是每个请求在运行时只能有一个接受者,如果显式指定,将必不可少地带来请求发送者与接受者的紧耦合。
如何使请求的发送者不需要指定具体的接受者?让请求的接受者自己在运行时决定来处理请求,从而使两者解耦。
使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。
抽象的处理者实现三个职责:
一是定义一个请求的处理方法handleMessage,唯一对外开放的方法;
二是定义一个链的编排方法setNext,设置下一个处理者;
三是定义了具体的请求者必须实现的两个方法:定义自己能够处理的级别getHandlerLever和具体的处理任务echo。
Chain of Responsibility模式的应用场合在于“一个请求可能有多个接受者,但是最后整整的接受者只有一个”,只有这时候请求发送者与接受者的耦合才有可能常出现“变化脆弱”的症状,责任链的目的就是将二者解耦,从而更好地应对变化。
应用了Chain of Responsibility模式后,对象的职责分派将更具有灵活性。我们可以在运行时动态添加/修改请求的处理职责。
如果请求传递到责任链的末尾仍得不到处理,应该有一个合理的缺省机制。这也是每一个接受对象的责任,而不是发出请求的对象的责任。
实例:古代女子三从四德,结婚之前听从父亲,结婚之后听从丈夫,丧偶之后听从儿子。 抽象处理者:
public abstract class Handler{
private Handler nextHandler;
//每个处理者都必须对请求做出处理
public final Request handleMessage(Request request)【
Response response = null;
//判断是否是自己的处理级别
if(this.getHandlerLevel().equals(request.getReqeustLevel())){
response = this.echo(request);
}else{//不属于自己的处理级别
//判断是否有下一个处理者
if(this.nextHandler != null){
response = this.nextHandler.handleMessage(request);
}else{
//没有适当的处理者,业务自行处理
}
}
return response;
}
//设置下一个处理者是谁
public void setNext(Handler handler){
this.nextHandler = handler;
}
//每个处理者都有一个处理级别
protected abstract Level getHandlerLevel();
//每个处理者都必须实现处理任务
protected abstract Response echo(Request request);
}
具体处理者:
public class ConcreteHandlerl extends Handler{
//定义自己的处理逻辑
protected Response echo(Reuqest request){
return null;
}
//设置自己的处理级别
protected Level getHandlerLevel(){
return null;
}
}
public class ConcreteHandler2 extends Handler{
//定义自己的处理逻辑
protected Response echo(Reuqest request){
return null;
}
//设置自己的处理级别
protected Level getHandlerLevel(){
return null;
}
}
public class ConcreteHandler3 extends Handler{
//定义自己的处理逻辑
protected Response echo(Reuqest request){
return null;
}
//设置自己的处理级别
protected Level getHandlerLevel(){
return null;
}
}
模式中有关框架代码:
public class Level{
//定义一个请求和处理等级
}
public class Request{
//请求的等级
public Level getRequestLevel(){
return null;
}
}
public class Response{
//处理返回的数据
}
场景类:
public class Client{
publci static void main(String[] args){
//声明所有的处理节点
Handler handler1 = new ConcreteHandler1();
Handler handler2 = new ConcreteHandler2();
Handler handler3 = new ConcreteHandler3();
//设置链这种的阶段顺序1-->2-->3
handler1.setNext(handler2);
handler2.setNext(handler3);
//提交请求,返回结果
Response response = handler1.handlerMessage(new Request());
}
}
在软件构件过程中,我们需要为某些对象建立一种“通知依赖关系”——一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知。如果这样的依赖关系过于紧密,将使软件不能很好地抵御变化。
使用面向对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系。从而实现软件体系结构的松耦合。
定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。
使用面向对象的抽象,Observer模式使得我们可以独立地改变目标与观察者,从而使二者之间的依赖关系达至松耦合。
目标发送通知时,无需指出观察者,通知(可以携带通知信息作为参数)会自动传播。观察者自己决定是否需要订阅通知,目标对象对此一无所知。
被观察者:
public abstract class Subject{
//定义一个观察者数组
private Vector<Observer> obsVector = new Vector<Observer>();
//增加一个观察者
public void addObserver(Observer o){
this.obsVector.add(o);
}
//删除一个观察者
public void delObserver(Observer o){
this.obsVector.remove(o);
}
//通知所有观察者
public void notifyObservers(){
for(Observer o : this.obsVector){
o.update();
}
}
}
具体被观察者:
public class ConcreteSubject extends Subject【
//具体的业务
public void doSomething(){
...
super.notifyObservers();
}
}
观察者:
public interface Observer{
//更新方法
public void update();
}
具体观察者:
public class ConcreteObserver implements Observer{
//实现更新方法
public void update(){
System.out.println("接收到信息,并进行处理!");
}
}
场景类: public class Client{ public static void main(String[] args){ //创建一个被观察者 ConcreteObserver subject = new ConcreteObserver(); //定义一个观察者 Observer } }
在软件构件过程中,集合对象内部结构常常变化各异。但对于这些集合对象,我们希望在不暴露其内部结构的同时,可以让外部客户代码透明地访问其中包含的元素;同时这种“透明遍历”也为“同一种算法在多种集合对象上进行操作”提供了可能。
使用面向对象技术将这种遍历机制抽象为“迭代器对象”为“应对变化中的集合对象”提供了一种优雅的方式。
提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。
迭代抽象:访问一个聚合对象的内容而无需暴露它的内部表示。
迭代多态:为遍历不同的集合结构提供一个统一的接口,从而支持同样的算法在不同的集合结构上进行操作。
迭代器的健壮性考虑:遍历的同时更改迭代器所在的集合结构,会导致问题。
抽象迭代器:
public interface Iterator{
//遍历到下一个元素
public Object next();
//是否已经遍历到尾部
public boolean hasNext();
//删除当前指向的元素
public boolean remove();
}
具体迭代器:
public class ConcreteIterator implements Iterator{
private Vector vector = new Verctor();
//定义当前游标
public int cursor = 0;
public ConcreteIterator(Vector vector){
this.vector = vector;
}
//判断是否到达尾部
public boolean hasNext(){
if(this.cursor == this.vector.size()){
return false;
}else{
return true;
}
}
//返回下一个元素
public Object next(){
Object result = null;
if(this.hasNext()){
result = this.vector.get(this.cursor++);
}else{
result = null;
}
return result;
}
//删除当前元素
public boolean remove(){
this.vector.remove(this.cursor);
return true;
}
}
抽象容器:
public interface Aggregate{
//是容器必然有元素的增加
public void add(Object object);
//减少元素
public void remove(Object object);
//由迭代器来遍历所有的元素
public Iterator iterator();
}
具体容器:
public class ConcreteAggregate implements Aggregate{
//容纳对象的容器
private Vector vector = new Vector();
//增加一个元素
public void add(Object object){
this.vector.add(Obect);
}
//删除一个元素
public void remove(Object object){
this.remove(object);
}
//返回迭代器对象
public Iterator iterator(){
return new ConcreteIterator(this.vector);
}
}
场景类:
public class Client{
public static void main(String[] args){
//声明出容器
Aggregate agg = new ConcreteAggregate();
//产生对象数据放进去
agg.add("abc");
agg.add("aaa");
agg.add("1234");
//遍历一下
Iterator iterator = agg.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
}