设计模式全详解——11种行为型模式(下)

本文是学习设计模式后,自己做的笔记。

学习资源有菜鸟教程以及尚硅谷韩顺平图解设计模式

本文是设计模式——11个行为型设计模式的下半篇。

设计模式全详解上半篇


7、备忘录模式

概述:

一种可以存一个对象的某个状态,以便在适当的时候恢复对象的设计模式。

思想就是程序员常用的快捷键:Ctrl + Z

特点:

在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。

通过一个备忘录类专门存储对象状态。

客户不与备忘录类耦合,与备忘录管理类耦合。

角色:

1、原始对象类

2、状态保存类(内置要保存的属性)

3、状态保存类管理者(聚合状态保存类)

4、客户端

代码实现:

  1. 创建原始对象类
//1、创建原始对象类

public class Originator {

	private String state;   //状态信息

	public String getState() {
		return state;
	}

	public void setState(String state) {
		this.state = state;
	}
	
	//获得一个状态保存类
	public Memento savaStateMemento(){
		return new Memento(state);
	}

	//从状态保存类中获取状态信息
	
	public void getStateFromMemento(Memento memento) {
		state = memento.getState();
	}
}
  1. 创建对象保存类
//2、创建对象保存类

public class Memento {

	private String state;

	public Memento(String state) {
		super();
		this.state = state;
	}
	public String  getState() {
		return state;
	}


}
  1. 创建保存管理类

//3、创建保存管理类

public class Caretaker {

	private List<Memento> mementolist = new ArrayList<Memento>();
	
	public void add(Memento memento) {
		mementolist.add(memento);
	}
	
	public Memento get(int index) {
		return mementolist.get(index);
	}

}
  1. 客户端调用
//4、客户端调用

public class Client {

	public static void main(String[] args) {
		Originator originator = new Originator();
		
		Caretaker caretaker = new Caretaker();
		originator.setState("状态1!");
		caretaker.add(originator.savaStateMemento());
		originator.setState("状态2!");
		caretaker.add(originator.savaStateMemento());		
		originator.setState("状态3!");
		caretaker.add(originator.savaStateMemento());
		System.out.println("现在的状态是:"+originator.getState());
		System.out.println("回复状态!");
		originator.setState(caretaker.get(0).getState());
		System.out.println("现在的状态是:"+originator.getState());
	}

}

备忘录模式的注意事项和细节

  1. 给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态
  2. 实现了信息的封装,使得用户不需要关心状态的保存细节
  3. 如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存, 这个需要注意
  4. 适用的应用场景:

1.后悔药
2.打游戏时的存档
3.Windows 里的 ctri + z
4.IE 中的后退
5.数据库的事务管理

  1. 为了节约内存,备忘录模式可以和原型模式配合使用

8、解释器模式

概述:

一种提供了评估语言的语法或表达式的方式的设计模式。

特点:

这种模式实现了一个表达式接口,该接口解释一个特定的上下文。

给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子。

可扩展性比较好,灵活。

增加了新的解释表达式的方式。

易于实现简单文法。

角色:

1、上下文类

2、抽象表达式解析类

3、表达式实现类

类图示例:

解释器模式类图

本例想实现简单的解释器,根据自己的定义来判断输入。

代码实现:

  1. 创建表达式接口
//1、创建表达式接口

public interface Expression {
   public boolean interpret(String context);
}
  1. 实现接口
//2、实现接口

public class TerminalExpression implements Expression {
   
   private String data;
 
   public TerminalExpression(String data){
      this.data = data; 
   }
 
   @Override
   public boolean interpret(String context) {
      if(context.contains(data)){
         return true;
      }
      return false;
   }
}



public class OrExpression implements Expression {
    
   private Expression expr1 = null;
   private Expression expr2 = null;
 
   public OrExpression(Expression expr1, Expression expr2) { 
      this.expr1 = expr1;
      this.expr2 = expr2;
   }
 
   @Override
   public boolean interpret(String context) {      
      return expr1.interpret(context) || expr2.interpret(context);
   }
}


public class AndExpression implements Expression {
    
   private Expression expr1 = null;
   private Expression expr2 = null;
 
   public AndExpression(Expression expr1, Expression expr2) { 
      this.expr1 = expr1;
      this.expr2 = expr2;
   }
 
   @Override
   public boolean interpret(String context) {      
      return expr1.interpret(context) && expr2.interpret(context);
   }
}

  1. 调用
//、调用
public class InterpreterPatternDemo {
 
   //规则:Robert 和 John 是男性
   public static Expression getMaleExpression(){
      Expression robert = new TerminalExpression("Robert");
      Expression john = new TerminalExpression("John");
      return new OrExpression(robert, john);    
   }
 
   //规则:Julie 是一个已婚的女性
   public static Expression getMarriedWomanExpression(){
      Expression julie = new TerminalExpression("Julie");
      Expression married = new TerminalExpression("Married");
      return new AndExpression(julie, married);    
   }
 
   public static void main(String[] args) {
      Expression isMale = getMaleExpression();
      Expression isMarriedWoman = getMarriedWomanExpression();
 
      System.out.println("John is male? " + isMale.interpret("John"));
      System.out.println("Julie is a married women? " 
      + isMarriedWoman.interpret("Married Julie"));
   }
}

解释器模式的注意事项和细节

  1. 当有一个语言需要解释执行,可将该语言中的句子表示为一个抽象语法树,就可以考虑使用解释器模式,让程序具有良好的扩展性
  2. 应用场景:编译器、运算表达式计算、正则表达式、机器人等
  3. 使用解释器可能带来的问题:解释器模式会引起类膨胀、解释器模式采用递归调用方法,将会导致调试非常复杂、效率可能降低.

9、状态模式

概述:

一种类的行为是基于它的状态改变的设计模式。

主要用来解决对象在多种状态转换时,需要对外输出不同的行为的问题。状态和行为是一一对应的,状态之间可以相互转换

当一个对象的内在状态改变时,允许改变其行为,这个对象看起来像是改变了其类。

特点:

允许对象在内部状态发生改变时改变它的行为。

对象的行为依赖于它的状态(属性),并且可以根据它的状态改变而改变它的相关行为。

可以解决代码中包含大量与对象状态有关的条件语句。

当一个对象的内在状态改变时,允许改变其行为。看起来像是改变了其类。

使用时要先分析出所有状态。

角色:

1、环境角色类:用于维护State实例,这个实例定义当前状态

2、抽象状态角色类:定义一个接口封装与环境

3、实体状态角色类:为每一状态添加具体行为

类图示例:

状态模式类图示例
本类图想实现的是一个抽奖系统,内部含有可抽奖、不能抽奖、有奖品、没奖品等状态。

步骤代码实现:

  1. 创建状态抽象类
//1、创建状态抽象类

public abstract class State {

	
	// 扣除积分 - 50
    public abstract void deductMoney();

    // 是否抽中奖品
    public abstract boolean raffle();

    // 发放奖品
    public abstract  void dispensePrize();

}

  1. 创建状态实体类
//2、创建状态实体类

public class NoRaffleState extends State {

	 // 初始化时传入活动引用,扣除积分后改变其状态
    RaffleActivity activity;

    public NoRaffleState(RaffleActivity activity) {
        this.activity = activity;
    }

    // 当前状态可以扣积分 , 扣除后,将状态设置成可以抽奖状态
    @Override
    public void deductMoney() {
        System.out.println("扣除50积分成功,您可以抽奖了");
        activity.setState(activity.getCanRaffleState());
    }

    // 当前状态不能抽奖
    @Override
    public boolean raffle() {
        System.out.println("扣了积分才能抽奖喔!");
        return false;
    }

    // 当前状态不能发奖品
    @Override
    public void dispensePrize() {
        System.out.println("不能发放奖品");
    }
}



public class DispenseState extends State {

	 // 初始化时传入活动引用,发放奖品后改变其状态
    RaffleActivity activity;

    public DispenseState(RaffleActivity activity) {
        this.activity = activity;
    }
    
    //

    @Override
    public void deductMoney() {
        System.out.println("不能扣除积分");
    }

    @Override
    public boolean raffle() {
        System.out.println("不能抽奖");
        return false;
    }

    //发放奖品
    @Override
    public void dispensePrize() {
        if(activity.getCount() > 0){
            System.out.println("恭喜中奖了");
            // 改变状态为不能抽奖
            activity.setState(activity.getNoRafflleState());
        }else{
            System.out.println("很遗憾,奖品发送完了");
            // 改变状态为奖品发送完毕, 后面我们就不可以抽奖
            activity.setState(activity.getDispensOutState());
            //System.out.println("抽奖活动结束");
            //System.exit(0);
        }

    }
}



public class DispenseOutState extends State {

	// 初始化时传入活动引用
    RaffleActivity activity;

    public DispenseOutState(RaffleActivity activity) {
        this.activity = activity;
    }
    @Override
    public void deductMoney() {
        System.out.println("奖品发送完了,请下次再参加");
    }

    @Override
    public boolean raffle() {
        System.out.println("奖品发送完了,请下次再参加");
        return false;
    }

    @Override
    public void dispensePrize() {
        System.out.println("奖品发送完了,请下次再参加");
    }
}



public class CanRaffleState extends State {

    RaffleActivity activity;

    public CanRaffleState(RaffleActivity activity) {
        this.activity = activity;
    }

    //已经扣除了积分,不能再扣
    @Override
    public void deductMoney() {
        System.out.println("已经扣取过了积分");
    }

    //可以抽奖, 抽完奖后,根据实际情况,改成新的状态
    @Override
    public boolean raffle() {
        System.out.println("正在抽奖,请稍等!");
        Random r = new Random();
        int num = r.nextInt(10);
        // 10%中奖机会
        if(num == 0){
            // 改变活动状态为发放奖品 context
            activity.setState(activity.getDispenseState());
            return true;
        }else{
            System.out.println("很遗憾没有抽中奖品!");
            // 改变状态为不能抽奖
            activity.setState(activity.getNoRafflleState());
            return false;
        }
    }

    // 不能发放奖品
    @Override
    public void dispensePrize() {
        System.out.println("没中奖,不能发放奖品");
    }
}

  1. 创建环境类,用于储存各个状态以及状态行为,这里需要持有所有的状态类,这样子才可以实现针对不同的状态表现出不同的行为


//3、创建主题活动

public class RaffleActivity {

	// state 表示活动当前的状态,是变化
    State state = null;
    // 奖品数量
    int count = 0;
    
    // 四个属性,表示四种状态
    State noRafflleState = new NoRaffleState(this);
    State canRaffleState = new CanRaffleState(this);
    
    State dispenseState =   new DispenseState(this);
    State dispensOutState = new DispenseOutState(this);

    //构造器
    //1. 初始化当前的状态为 noRafflleState(即不能抽奖的状态)
    //2. 初始化奖品的数量 
    public RaffleActivity( int count) {
        this.state = getNoRafflleState();
        this.count = count;
    }

    //扣分, 调用当前状态的 deductMoney
    public void debuctMoney(){
        state.deductMoney();
    }

    //抽奖 
    public void raffle(){
    	// 如果当前的状态是抽奖成功
        if(state.raffle()){
        	//领取奖品
            state.dispensePrize();
        }
    }

    public State getState() {
        return state;
    }

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

    //这里请大家注意,每领取一次奖品,count--
    public int getCount() {
    	int curCount = count; 
    	count--;
        return curCount;
    }

    public void setCount(int count) {
        this.count = count;
    }

    public State getNoRafflleState() {
        return noRafflleState;
    }

    public void setNoRafflleState(State noRafflleState) {
        this.noRafflleState = noRafflleState;
    }

    public State getCanRaffleState() {
        return canRaffleState;
    }

    public void setCanRaffleState(State canRaffleState) {
        this.canRaffleState = canRaffleState;
    }

    public State getDispenseState() {
        return dispenseState;
    }

    public void setDispenseState(State dispenseState) {
        this.dispenseState = dispenseState;
    }

    public State getDispensOutState() {
        return dispensOutState;
    }

    public void setDispensOutState(State dispensOutState) {
        this.dispensOutState = dispensOutState;
    }
}

  1. 客户端调用
//4、创建客户端

public class ClientTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		// 创建活动对象,奖品有1个奖品
        RaffleActivity activity = new RaffleActivity(1);

        // 我们连续抽300次奖
        for (int i = 0; i < 30; i++) {
            System.out.println("--------第" + (i + 1) + "次抽奖----------");
            // 参加抽奖,第一步点击扣除积分
            activity.debuctMoney();

            // 第二步抽奖
            activity.raffle();
        }
	}

}

状态模式的注意事项和细节

  1. 代码有很强的可读性。状态模式将每个状态的行为封装到对应的一个类中
  2. 方便维护。将容易产生问题的 if-else 语句删除了,如果把每个状态的行为都放到一个类中,每次调用方法时都要判断当前是什么状态,不但会产出很多 if-else 语句,而且容易出错
  3. 符合“开闭原则”。容易增删状态
  4. 会产生很多类。每个状态都要一个对应的类,当状态过多时会产生很多类,加大维护难度
  5. 应用场景:当一个事件或者对象有很多种状态,状态之间会相互转换,对不同的状态要求有不同的行为的时候,可以考虑使用状态模式

10、策略者模式

概述:

一种个类的行为或其算法可以在运行时更改的设计模式。

跟状态模式有明显区别的是,他是基于自己对类进行改变的,而不是状态模式中的基于状态来改变。

策略模式(Strategy Pattern)中,定义算法族(策略组),分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户

这算法体现了几个设计原则,第一、把变化的代码从不变的代码中分离出来;第二、针对接口编程而不是具体类(定义了策略接口);第三、多用组合/聚合,少用继承(客户通过组合方式使用策略)。

跟组合模式的显著区别是他是对类的算法进行组合改变,组合模式是对类的结构进行组合改变

特点:

创建表示各种策略的对象和一个行为随着策略对象改变而改变的 context 对象。策略对象改变 context 对象的执行算法。

定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。

让算法变化独立与使用算法的用户。

把变化的代码从不变的代码中分离开来,针对接口编程而不是具体类,多用组合聚合,少用继承。

角色:

1、策略接口

2、具体策略实现类

3、客户端:聚合策略接口

类图示例:

策略类图示例

代码实现:

  1. 创建策略接口

//1、策略接口

public interface FlyBehavior {
	void fly();
}

  1. 创建策略实现类
//2、策略实现类

public class NoFlyBehavior implements FlyBehavior{

	public NoFlyBehavior() {
		// TODO Auto-generated constructor stub
	}

	@Override
	public void fly() {
		System.out.println("没有飞行能力!");
		
	}

}



public class GoodFlyBehavior implements FlyBehavior{

	public GoodFlyBehavior() {
	
	}

	@Override
	public void fly() {
		System.out.println("飞行能力好!");
		
	}

}
  1. 创建对象类,在这里给其定义对应的策略

//3、对象类

public abstract class Duck {

	private FlyBehavior flybehavior;

	public Duck(FlyBehavior flybehavior) {
		super();
		this.flybehavior = flybehavior;
	}	
	public abstract void disply();
	public void fly() {
		if (flybehavior!=null)
			flybehavior.fly();
	}
}

客户端调用

//4、客户端调用

public class client {

	public static void main(String[] args) {
		Duck duck = new WildDuck(new GoodFlyBehavior());
		duck.fly();
	}

}



策略模式的注意事项和细节优点:

优点:

1、算法可以自由切换。

2、避免使用多重条件判断。

3、扩展性良好。

缺点:

1、策略类会增多。

2、所有策略类都需要对外暴露。

  1. 策略模式的关键是:分析项目中变化部分与不变部分
  2. 策略模式的核心思想是:多用组合/聚合 少用继承;用行为类组合,而不是行为的继承。更有弹性
  3. 体现了**“对修改关闭,对扩展开放”**原则,客户端增加行为不用修改原有代码,只要添加一种策略(或者行为)即可,避免了使用多重转移语句(if…else if…else)
  4. 提供了可以替换继承关系的办法: 策略模式将算法封装在独立的 Strategy 类中使得你可以独立于其 Context 改变它,使它易于切换、易于理解、易于扩展
  5. 需要注意的是:每添加一个策略就要增加一个类,当策略过多是会导致类数目庞

11、职责链模式

概述:

一种为请求创建了一个接收者对象的链的设计模式。

  • 职责链模式(Chain of Responsibility Pattern), 又叫 责任链模式,为请求创建了一个接收者对象的链。这种模式对请求的发送者和接收者进行解耦。
  • 职责链模式通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。
  1. 这种类型的设计模式属于行为型模式

特点:

给予请求的类型,对请求的发送者和接收者进行解耦。

每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。

避免请求发送者与接收者耦合在一起,让多个对象都有可能接收请求,将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。

角色:

1、抽象请求接收/处理者类(内部聚合自己)

2、实体请求接收/处理者类

3、请求发送类

类图示例:

职责链模式类图示例

代码实现:

实现一个学校 OA 系统的采购审批项目,要求审批逐层进行。

  1. 创建请求类
//1、创建请求类

public class PurchaseRequest {

	private int type = 0; //请求类型
	private float price = 0.0f;
	private int id = 0;
	public PurchaseRequest(int type, float price, int id) {
		super();
		this.type = type;
		this.price = price;
		this.id = id;
	}
	public int getType() {
		return type;
	}
	public void setType(int type) {
		this.type = type;
	}
	public float getPrice() {
		return price;
	}
	public void setPrice(float price) {
		this.price = price;
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	
}
  1. 创建请求接收类(抽象),值得注意的是,每一层接受类都需要持有下一层接受类,这样子才能实现逐层抽象的传递
//2、创建抽象请求接受类


public abstract class Approver {

	Approver approver;  //下一个处理者
	String name;
	
	public Approver(String name) {
		this.name = name;
	}
	
	//下一个处理者
	public void setApprover(Approver approver) {
		this.approver = approver;
	}
	
	//处理审批请求的方法,得到一个请求,处理是子类完成,因此本方法做成抽象
	public abstract void processRequest(PurchaseRequest purchaserequest);
}

  1. 创建请求实体类
//3、创建实体请求类

public class CollegeApprover extends Approver{

	public CollegeApprover(String name) {
		super(name);
	}

	@Override
	public void processRequest(PurchaseRequest purchaserequest) {
		if(purchaserequest.getPrice()>5000&&purchaserequest.getPrice()<=10000) {
			System.out.println("请求编号:"+purchaserequest.getId()+"被"+this.name+"处理!");
			
		}else {
			approver.processRequest(purchaserequest);
		}
		
	}
	

}


public class DepartmentApprover extends Approver {

	public DepartmentApprover(String name) {
		super(name);
	}

	@Override
	public void processRequest(PurchaseRequest purchaserequest) {

		if(purchaserequest.getPrice()<=5000) {
			System.out.println("请求编号:"+purchaserequest.getId()+"被"+this.name+"处理!");
			
		}else {
			approver.processRequest(purchaserequest);
		}
	}

	
}


public class SchoolMasterApprover extends Approver{

	public SchoolMasterApprover(String name) {
		super(name);
	}

	@Override
	public void processRequest(PurchaseRequest purchaserequest) {
		if(purchaserequest.getPrice()>=10000) {
			System.out.println("请求编号:"+purchaserequest.getId()+"被"+this.name+"处理!");
			
		}else {
			approver.processRequest(purchaserequest);
		}	
		
	}

}
  1. 客户端调用
//4、客户端调用

public class Client {
	public static void main(String[] args) {
		//创建请求类
		PurchaseRequest purchaserequest = new PurchaseRequest(1, 9999, 1);
		
		//创建审批人
		DepartmentApprover departmentapprover  = new DepartmentApprover("张主任");
		CollegeApprover collegeapprover = new CollegeApprover("胡院长");
		SchoolMasterApprover master = new SchoolMasterApprover("陈校长");
		
		//设置链状处理对象
		departmentapprover.setApprover(collegeapprover);
		collegeapprover.setApprover(master);
		
		//开始处理
		departmentapprover.processRequest(purchaserequest);
	}
}


优点:
1、降低耦合度。它将请求的发送者和接收者解耦。

2、简化了对象。使得对象不需要知道链的结构。

3、增强给对象指派职责的灵活性。通过改变链内的成员或者调动它们的次序,允许动态地新增或者删除责任。

4、增加新的请求处理类很方便。

缺点:

1、不能保证请求一定被接收。

2、系统性能将受到一定影响,而且在进行代码调试时不太方便,可能会造成循环调用。

3、可能不容易观察运行时的特征,有碍于除错。

职责链模式注意事项和细节

  1. 请求和处理分开,实现解耦,提高系统的灵活性
  2. 简化了对象,使对象不需要知道链的结构
  3. 性能会受到影响,特别是在链比较长的时候,因此需控制链中最大节点数量,一般通过在 Handler 中设置一个最大节点数量,在 setNext()方法中判断是否已经超过阀值,超过则不允许该链建立,避免出现超长链无意识地破坏系统性能
  4. 调试不方便。采用了类似递归的方式,调试时逻辑可能比较复杂
  5. 最佳应用场景:有多个对象可以处理同一个请求时,比如:多级请求、请假/加薪等审批流程、Java Web 中 Tomcat对 Encoding 的处理、拦截器
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值