深入理解设计模式!详细解析11种行为型模式

举报
攻城狮Chova 发表于 2022/05/12 22:12:11 2022/05/12
【摘要】 本篇文章主要介绍了程序设计中设计模式的行为型模式。行为型模式包括策略模式,模板方法模式,观察者模式,迭代子模式,责任链模式,命令模式,备忘录模式,状态模式,访问者模式,中介者模式和解释器模式。详细说明了这11种行为型模式的使用方式和使用场景。

行为型模式

  • 11种行为模式的关系:
    • 第一类: 通过父类与子类的关系进行实现
    • 第二类: 通过两个类之间的关系进行实现
    • 第三类: 通过类的状态进行实现
    • 第四类: 通过中间类进行实现
      -

策略模式(Strategy)

  • 策略模式:
    • 定义了一系列算法,并将每个算法封装起来,可以相互替换,算法的变化不会影响到使用算法的用户
    • 设计一个接口,为一系列实现类提供统一的方法,多个实现类实现该接口
    • 设计一个抽象类(选用,作为辅助类),提供辅助函数
      在这里插入图片描述
  • ICalculator接口提供统一的方法
  • AbstractCalculator是抽象辅助类,提供辅助方法
- ICalculator
public interface ICalculator{
	public int calculate(String exp);
}




- AbstractCalculator
public abstract class AbstractCalcuator{
	public int[] split(String exp,String opt){
		String array[]=exp.split(opt);
		int arrayInt[]=new int[2];
		arrayInt[0]=Integer.parseInt(array[0]);
		arrayInt[1]=Integer.parseInt(array[1]);

		return arrayInt;
	}
} 




- Plus
public class Plus extends AbstractCalculator implements ICalculator{
	@Override
	public int calculate(String exp){
		int arrayInt[]=split(exp,"\\+");
		return arrayInt[0]+arrayInt[1];
	}
}



- Minus
public class Minus extends AbstractCalculator implements ICalculator{
	@Override
	public int calculate(String exp){
		int arrayInt[]=split(exp,"-");
		return arrayInt[0]-arrayInt[1];
	}
	
}




- Multiply
public class Multiply extends AbstractCalculator implements ICalculator{
	@Override
	public int calculate(String exp){
		int arrayInt[]=split(exp,"\\*");
		return arrayInt[0]*arrayInt[1];
	}
}




- StrategyTest
public class StrategyTest{
	public static void mian(String[] args){
		String exp="2+8";
		ICalculator cal=new Plus();
		int result=cal.calculate(exp);
		System.out.println(result);
	}
}
  • 策略模式的决定权在于用户:
    • 系统本身提供不同算法的实现
    • 新增或删除算法
    • 对各种算法做封装
  • 策略模式多用在算法决策系统中,外部用户只需要决定使用哪一个算法即可

模板方法模式(Template Method)

  • 模板方法模式:
    • 一个抽象类中,定义一个主方法
    • 再定义无数个方法,可以是抽象的,也可以是实际的方法
    • 定义一个子类,继承抽象类,重写抽象类中的方法
    • 通过调用抽象类,实现对子类的调用
      -
    • AbstractCalculator类中定义一个主方法calculate()
    • calculate() 调用split()
    • PlusMinus分别继承AbstractCalculator
    • 通过对AbstractCalculator的调用实现对子类的调用
public abstract class AbstractCalculator{
	/* 主方法,实现对本类的其它方法的调用 */
	public final int calculate(String exp,String opt){
		int array[]=split(exp,opt)
		return calculate(array[0],array[1]);
	}

	/* 抽象方法,需要子类进行重写 */
	abstract public int calculate(int num1,int num2);
	
	public int[] split(String exp,String opt){
			String array[]=exp.split(opt);
			int arrayInt[]=new int[2];
			arrayInt[0]=Integer.parseInt(array[0]);
			arrayInt[1]=Integer.parseInt(array[1]);
			return arrayInt;
	}
}




- Plus
public class Plus extends AbstractCalculator{
	@Override
	public int calculate(int num1,int num2){
		return num1+num2;
	}
}




- StrategyTest
public class StrategyTest{
	public static void main(String[] args){
		String exp="8+8";
		AbstractCalculator cal=new Plus();
		int result=cal.calculate(exp,"\\+");
		System.out.println(result);
	}
}

Test的执行过程:

  • 首先将exp和"\ \ +"做参数,调用AbstractCalculator类里的**calculate(String,String)**方法
  • calculate(String,String) 里调用同类的split()
  • 然后再调用calculate(int,int) 方法
  • 从这个方法进入到子类中
  • 执行完return num1+num2之后,将值返回到AbstractCalculator类,赋值给result, 打印出来

观察者模式(Observer)

  • 观察者模式是类与类之间的关系,不涉及继承
  • 观察者模式类似邮件订阅和RSS订阅:
    • 当你订阅了该内容,如果后续有更新,会及时接收到通知
  • 观察者模式: 当一个对象变化时,依赖该对象的对象都会接收到通知,并且随着变化.对象之间是一对多的关系
    在这里插入图片描述
  • MySubject类是主对象
  • Observer1Observer2是依赖于MySubject的对象
  • MySubject变化时 ,Observer1Observer2必然变化
  • AbstractSubject类中定义者需要监控的对象列表,可以对这些对象进行修改:增加或者删除被监控对象
  • MySubject变化时 ,AbstractSubject类负责通知在列表内存在的对象
- Observer
public interface Observer{
	public void update();
}




- Observer1
public class Observer1 implements Observer{
	@Override
	public void update(){
		System.out.println("Observer1 has received!");
	}
}




- Observer2
public class Observer2 implements Observer{
	@Override
	public void update(){
		System.out.println("Observer2 has received!");
	}
}




- Subject
public interface Subject{
	/* 增加观察者 */
	public void add(Observer observer);

	/* 删除观察者 */
	public void del(Observer observer);

	/* 通知所有观察者 */
	public void notifyObservers();

	/* 自身的操作 */
	public void operation();
}




- AbstractSubject
public abstract class AbstractSubject implements Subject{
	private Vector<Observer> vector=new Vector<Observer>();
	
	@Override
	public void add(Observer observer){
		vector.add(observer);
	} 
	
	@Override
	public void del(Observer observer){
		vector.remove(observer);
	}

	@Override
	public void notifyObservers(){
		Enumeration<Observer> enumo=vector.elements();
		while(enumo.hasMoreElements()){
			enumo.nextElement().update();
		}
	}
}




- MySubject
public class MySubject extends AbstractSubject{
	@Override
	public void operation(){
		System.out.println("update self!");
		notifyObservers();
	}
}




- ObserverTest
public class ObserverTest{
	public static void main(String[] args){
		Subject sub=new MySubject();
		sub.add(new Observer1());
		sub.add(new Observer2());

		sub.operation();
	}
}
  • 观察者模式根据关系图,新建项目,使用代码按照总体思路走一遍,这样容易理解观察者模式的思想

迭代子模式(Iterator)

  • 迭代子模式是类与类之间的关系,不涉及继承
  • 迭代子模式: 顺序访问聚集中的对象. 包含两层意思:
    • 聚集对象: 需要遍历的对象
    • 迭代器对象: 用于对聚集对象进行遍历访问
      在这里插入图片描述
  • MyCollection中定义了集合的一些操作
  • MyIterator中定义了一系列迭代操作,并且持有Collection实例
- Collection
public interface Collection{
	public Iterator iterator();

	/* 取得集合元素 */
	public Object get(int i);

	/* 取得集合大小 */
	public int size();
}




- Iterator
public interface Iterator{
	// 前移
	puublic Object previous();

	// 后移
	public Object next();
	public boolean hasNext();

	// 取得第一个元素
	public Object first(); 
}




- MyCollection
public class MyCollection implements Collection{
	public String string[]={"A","B","C","D","E"};

	@Override
	public Iterator iterator(){
		return new MyIterator(this);
	}

	@Override
	public Object get(int i){
		return string[i];
	}

	@Override
	public int size(){
		return string.length;
	}
}




- MyIterator
public class MyIterator implements Iterator{
	private Collection collection;
	private int pos=-1;

	public MyIterator(Collection collection){
		this.collection=collection;
	}

	@Override
	pbulic Object previous(){
		if(pos>0){
			pos--;
		}
		return collection.get(pos);
	}

	@Override
	public Object next(){
		if(pos<collection.size()-1){
			pos++;
		}
		return collection.get(pos);
	}

	@Override
	public Object hasNext(){
		if(pos<collection.size()-1){
			return true;
		}else{
			return false;
		}
	}

	@Override
	public Object first(){
		pos=0;
		return collection.get(pos);
	}


}




- Test
public class Test{
	Collection collection=new MyCollection();
	Iterator it=collection.iterator();

	whhile(it.hasNext()){
		System.out.println(it.next());
	}
}
  • JDK中各个类都是这些基本的集合,加上一些设计模式,再加一些优化组合到一起的,只要掌握这些,可以写出自定义的集合类,甚至框架

责任链模式(Chain of Responsibility)

  • 类与类之间的关系,不涉及继承
  • 责任链模式:
    • 有多个对象,每个对象持有对下一个对象的引用,这样形成一条链,直到某个对象决定处理该请求
    • 请求发出者并不清楚到底最终哪个对象会处理该请求
  • 责任链模式可以实现 :在隐瞒客户端的情况下,对系统进行动态调整
    在这里插入图片描述
  • Abstracthandler类提供了getset方法,方便MyHandler类设置和修改引用对象
  • MyHandler类是核心,实例化后生成一系列相互持有的对象,构成一条链
- Handler
public interface Handler{
	public void operator();
}




- AbstractHandler
public abstract class AbstractHandler{

	private Handler handler;

	private Handler getHandler(){
		return handler;
	}

	private void setHandler(Handler handler){
		this.handler=handler;
	}
}




- MyHandler
public class MyHandler extends AbstractHandler implements Handler{
	private String name;
	
	public MyHandler(String name){
		this.name=name;
	}

	@Override
	public void operator(){
		System.out.println(name+"deal!");
		if(getHandler()!=null){
			getHandler().operator();
		}
	}
} 




- Test
public class Test{
	public static void main(String[] args){
		MyHandler h1=new MyHandler("h1");
		MyHanlder h2=new MyHandler("h2");
		MyHandler h3=new MyHandler("h3");
		
		h1.setHandler(h2);
		h2.setHandler(h3);

		h1.operator();
	}
}
  • 链接上的请求可以是一条链,可以是一个树,还可以是一个环
  • 模式本身不受这个约束,需要自定义实现
  • 在同一个时刻,命令只允许由一个对象传给另一个对象,不允许传给多个对象

命令模式(Command)

  • 类与类之间的关系,不涉及继承
  • 命令模式理解示例:
    • 司令员的作用是: 发出口令
    • 口令经过传递,传到士兵耳中,士兵去执行
    • 这个过程好在:司令,口令,士兵三者相互解藕
    • 任何一方都不用去依赖其它方,只需要做好自身的事即可
    • 司令员要的是结果,不会去关注士兵到底怎么实现的
      -
  • Invoker是调用者(司令员)
  • Receiver是被调用者(士兵)
  • MyCommand是命令,实现了Command接口,持有接收对象
- Command
public interface Command{
	public void exe();
}




- MyCommand
public class MyCommand implements Command{
	private Receiver receiver;

	public MyCommand(Receiver receiver){
		this.receiver=receiver;
	}

	@Override
	public void exe(){
		receiver.action();
	}
} 




- Receiver
public class Receiver{
	public void action(){
		System.out.println("Command Received!");
	}
}




- Invoker
public class Invoker{
	private Command command;

	public Invoker(Command command){
		this.command=command;
	}

	public void action(){
		command.exe();
	}
}




- Test
public class Test{
	public static void main(String[] args){
		Receiver receiver=new Receiver();
		Command cmd=new MyCommand(receiver);
		Invoker Invoker=new Invoker(cmd);
		invoker.action();
	}
}
  • 命令模式的目的: 达到命令的发出者和执行者之间的解耦,实现请求和执行分开
  • Struts其实就是一种将请求和呈现分离的技术,使用了命令模式的设计思想

备忘录模式(Memento)

  • 备忘录模式 :主要目的是保存一个对象的某个状态,以便在适当的时候恢复对象
  • 备忘录模式理解:
    • 假设有原始类A,A中有各种属性,A可以决定需要备份的属性
    • 备忘录类B用来存储A的一些内部状态
    • 类C用来存储备忘录,并且只能存储,不能进行修改等操作
      在这里插入图片描述
  • Original类是原始类,里面有需要保存的属性value及创建一个备忘录类,用来保存value
  • Memento类是备忘录类
  • Storage类是存储备忘录的类,持有Memento类的实例
- Original
public class Original{
	private String value;

	private String getValue(){
		return value;
	}

	private void setValue(String value){
		this.value=value;
	}

	public Original(String value){
		this.value=value;
	}

	public Memento createMemento(){
		return new Memento(value);
	}

	public void restoreMemento(Memento memento){
		this.value=memento.getValue();
	}
}




- Memento
public class Memento{
	private String value;

	public Memento(String value){
		this.value=value;
	}

	public String getValue(){
		return value;
	}

	public void setValue(String value){
		this.value=value;
	}
}




- Storage
public class Storage{
	private Memento memento;

	public Storage(Memento memento){
		this.memento=memento;
	}

	public Memento getMemento(){
		return memento;
	}

	public void setMemento(Memento memento){
		this.memento=memento;
	}
}




- Test
public class Test{
	public static void main(String[] args){
		// 创建原始类
		Original original=new Original("egg");

		// 创建备忘录
		Storage storage=new Storage(original.createMemento());

		// 修改原始类的状态
		System.out.println("初始状态为:"+original.getValue());
		original.setValue("bulk");
		System.out.println("修改后的状态:"+original.getValue());

		// 恢复原始类的状态
		original.restoreMemento(storage.getMemento());
		System.out.println("恢复后的状态为:"+original.getValue());
	}
}
  • 新建原始类时 ,value被初始化为egg, 后经过修改,将value值修改为bulk, 最后进行恢复状态,结果成功恢复

状态模式(State)

  • 状态模式 :当对象的状态改变时,同时改变对象的行为
  • 状态模式理解示例:
    • QQ有几种不同的状态:在线,隐身,忙碌等
    • 每个状态对应不同的操作,而且好友也能看到相关的状态
  • 状态模式包括两层含义:
    • 可以通过改变状态来获得不同的行为
    • 对象状态的变化可以被发现
      -
  • State类是个状态类
  • Context类可以实现切换
- State
public class State{
	private String value;

	private String getValue(){
		return value;
	}

	private void setValue(String value){
		this.value=value;
	}

	public void method1(){
		System.out.println("Execute the first opt!");
	}

	public void method2(){
		System.out.println("Execute the second opt!");
	}
}




- Context
public class Context{
	private State state;

	private Context(State state){
		this.state=state;
	}

	public State getState(){
		return state;
	}

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

	public void method(){
		if(state.getValue().equals("state1")){
			state.method1();
		}else if(state.getValue().equals("state2")){
			state.method2();
		}
	}
}




- Test
public class Test{
	public static void main(String[] args){
		State state=new State();
		Context context=new Context(state);

		// 设置第一种状态
		state.setValue("state1");
		context.method();

		// 设置第二种状态
		state.setValue("state2");
		context.method();
	}
}
  • 状态模式的应用场景十分广泛:在做网站的时候,希望根据对象的属性,区别一些功能等,比如说权限控制等等

访问者模式(Visitor)

  • 访问者模式将数据结构和作用于结构上的操作解耦,使得操作集合可以相对自由地进行演化
  • 访问者模式适用于数据结构相对稳定,算法容易变化的系统:
    • 访问者模式使得算法操作增加变得更加容易
  • 若系统数据结构对象易于变化,经常有新的对象增加进来,则不适合使用访问者模式
  • 访问者模式的特点:
    • 优点: 增加操作容易
      • 增加操作意味着增加新的访问者
      • 访问者模式将有关行为集中到一个访问者对象中, 这些行为的改变不影响系统数据结构
    • 缺点: 增加新的数据结构很困难
  • 访问者模式 :是一种分离对象数据结构和行为的方法,通过这种分离,可以达到为一个被访问者动态添加新的操作而无需做任何修改的效果
    -
  • 一个Visitor类,存放要访问的对象
  • Subject类中有accept方法,接收将要访问的对象 ,getSubject() 获取将要被访问的属性
- Visitor
public interface Visitor{
	public void visit(Subject sub);
}




- MyVisitor
public class MyVisitor implements Visitor{
	@Override
	public void visit(Subject sub){
		System.out.println("visit the subject:"+sub.getSubject());
	}
}




- Subject
public interface Subject{
	public void accept(Visitor visitor);
	public String getSubject();
}




- MySubject
public class MySubject implements Subject{
	@Override
	public void accept(Visitor visitor){
		visitor.visit(this);
	}
	@Override
	public String getSubject(){
		return "love";
	}
}




- Test
public class Test{
	public static void main(String[] args){
		Visitor visitor=new MyVisitor();
		Subject sub=new MySubject();
		sub.accept(visitor);
	}
}
  • 访客模式适用场景:
    • 如果要为一个现有的类增加新功能:
      • 新功能是否会与现有功能出现兼容性问题
      • 以后会不会还有新功能需要添加
      • 如果类不允许修改代码怎么处理
    • 这些问题最好的解决办法就是访客模式
    • 访问者模式适用于数据结构相对稳定的系统,将数据结构和算法解耦

中介者模式(Mediator)

  • 中介者模式是用来降低类与类之间的耦合的:
    • 类与类之间有依赖关系的话,不利于功能的拓展和维护
    • 因为只要修改一个对象,其它关联的对象都要进行修改
  • 中介者模式: 只需要关心Mediator类的关系,具体类与类之间的关系及调度交给Mediator, 与Spring容器的作用类似
    -
  • User类统一接口
  • User1User2分别是不同的对象:
    • 二者之间有关联
    • 如果不采用中介者模式.则需要二者相互持有引用,这样二者的耦合度很高
    • 为了解耦,引入了Mediator类,提供统一接口
  • MyMediator为实现类:
    • 持有User1User2的实例,用来实现对User1User2的控制
    • 这样User1User2两个对象就可以相互独立,只需保持与Mediator之间的关系就可以
  • Mediator类用来维护
- Mediator
public interface Mediator{
	public void createMediator();

	public void workAll();
}




- MyMediator
public class MyMediator implements Mediator{
	private User user1;
	private User user2;

	public User getUser1(){
		return user1;
	}

	public User getUser2(){
		return user2;
	}

	@Override
	public void createMediator(){
		user1=new User1(this);
		user2=new User2(this);
	}

	@Override
	public void workAll(){
		user1.work();
		user2.work();
	}
}




- User
public abstract class User{
	private Mediator mediator;
	
	public Mediator getMediator(){
		return mediator;
	}

	public User(Mediator mediator){
		this.mediator=mediator;
	}

	public abstract void work();
}




- User1
public class User1 extends User{
	public User1(Mediator mediator){
		super(mediator);
	}

	@Override
	public void work(){
		System.out.println("user1 exe!");
	}
}




- User2
public User2 extends User{
	public User2(Mediator mediator){
		super(mediator);
	}

	@Override
	public void work(){
		System.out.println("user2 exe!");
	}
}




- Test
public class Test{
	public static void main(String[] args){
		Mediator mediator=new MyMediator();

		mediator.createMediator();
		mediator.workAll();
	}
}

解释器模式(Interpreter)

  • 解释器模式一般主要应用在OOP开发中的编译器开发中,适用面比较窄
    在这里插入图片描述
  • Context类是一个上下文环境类
  • PlusMinus分别是计算的实现
- Expression
public interface Expression{
	public int interpret(Context context);
}




- Plus
public class Plus implements Expression{
	@Override
	public int interpret(Context context){
		return context.getNum1()+context.getNum2();
	}
}




- Minus
public class Minus implements Expression{
	@Override
	public void interpret(Context context){
		return context.getNum1()-context.getNum2();
	}
}




- Context
public class Context{
	private int num1;
	private int num2;

	public Context(int num1,int num2){
		this.num1=num1;
		this.num2=num2;
	}

	public int getNum1(){
		return num1;
	}

	public void setNum1(int num1){
		this.num1=num1;
	}

	public int getNum2(){
		return num2;
	}

	public void setNum2(int num2){
		this.num2=num2;
	}
}




- Test
public class Test{
	public static void main(String[] args){
		// 计算 9+2-8
		int result=new Minus().interpret((new Context(new Plus().interpret(new Context(9,2)),8)));
		System.out.println(result);
	}
}
  • 解释器模式是来用作各种各样的解释器
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。