模板方法模式和策略模式的应用场景浅析


最近闲下来整理一下模板方法模式和策略模式的区别和应用场景

一、定义

模板方法模式:定义一个算法的骨架,将骨架中的特定步骤延迟到子类中。模板方法模式使得子类可以不改变算法的结构即可重新定义该算法的某些特定步骤

再看策略模式:

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

对于熟悉这两个定义的人来说,一目了然(然而当初我学习设计模式的时候,真觉得自己没学过语文)。换一种表达就是,模板模式注重的是一个骨架,定义一套步骤,1、2、3、4、5…,每个步骤做什么操作,可以被子类覆盖,但步骤的执行顺序不能改变;而策略模式注重的是操作本身,定义一个操作会在特定时刻调用,但具体的操作可以用子类实现。
应用场景可以看下面的例子,上“栗子”。

二、举个例子

前段时间跟同事讨论了一下鱼的做法,引出了代码实现,这次就举这个例子,权当需求。

1.杀鱼

杀一条鱼的顺序,杀鱼->去鱼鳞->清内脏,每一步具体操作都可能不一样,比如杀鱼可以用刀背、擀面杖等,鱼鳞可以用到刮、可以用手拔(我第一次杀鱼就是用手拔的鱼鳞<-_->),但是整体流程确实几乎不变的。

2.做鱼

一条鱼根据烹饪方式不同可以做出很多道菜,可以清蒸、红烧、炭烤等,每种烹饪方式的过程都不大相同。

三、伪代码实现

1.杀鱼

1.1分析

杀鱼的方式有很多,但却可以认为有一个固有流程,杀鱼->去鱼鳞->清内脏,每一个步骤都可以因方式的不同而被替换,但总体流程不变;通过上面的定义可以选择模板模式实现

1.2实现

  • 定义模板类PrepareFishTemplate
/**
 * 准备食材鱼
 */
public abstract class PrepareFishTemplate {

	/**
	 * 清理鱼鳞
	 */
	public abstract void clearScales();

	/**
	 * 清理内脏
	 */
	public abstract void clearViscera();

	/**
	 * 杀鱼
	 */
	public abstract void kill();

	public final void perpare() {
		this.kill();
		this.clearScales();
		this.clearViscera();
	}

}
  • 每种杀鱼方式都需要定义单独类去继承模板类,并实现相应方法
    • 常规方式CommonPrepareFish
    /**
     * 常规处理鱼
     */
    public class CommonPrepareFish extends PrepareFishTemplate {
    
    	@Override
    	public void clearScales() {
    		System.out.println("使用刀刮去鱼鳞...");
    	}
    
    	@Override
    	public void clearViscera() {
    		System.out.println("用手将内脏抠出...");
    	}
    
    	@Override
    	public void kill() {
    		System.out.println("---------开始常规处理鱼--------------");
    		System.out.println("把鱼用刀背拍死...");
    	}
    
    }
    
    • 新手处理NovicePrepareFish
    /**
     * 新手处理鱼
     */
    public class NovicePrepareFish extends PrepareFishTemplate {
    
    	@Override
    	public void clearScales() {
    		System.out.println("用手将鱼鳞全部拔出...");
    	}
    
    	@Override
    	public void clearViscera() {
    		System.out.println("用手将内脏抠出...");
    	}
    
    	@Override
    	public void kill() {
    		System.out.println("---------开始新手处理鱼--------------");
    		System.out.println("抓着鱼摔打到不再反抗...");
    	}
    
    }
    
    • 佛系处理
    /**
     * 佛系处理鱼
     */
    public class GodPrepareFish extends PrepareFishTemplate {
    
    	@Override
    	public void clearScales() {
    		System.out.println("试图说服鱼鳞摆脱鱼的束缚...");
    	}
    
    	@Override
    	public void clearViscera() {
    		System.out.println("试图说服内脏自己跑出来...");
    	}
    
    	@Override
    	public void kill() {
    		System.out.println("---------开始佛系处理鱼--------------");
    		System.out.println("鱼呀,别挣扎了,命运已经注定了(巴拉巴拉到鱼不再反抗)...");
    	}
    
    }
    
  • 运行程序
public class Main {

	public static void main(String[] args) {
		PrepareFishTemplate common = new CommonPrepareFish();
		PrepareFishTemplate novice = new NovicePrepareFish();
		PrepareFishTemplate god = new GodPrepareFish();
		common.perpare();
		novice.perpare();
		god.perpare();
	}
}

运行效果如下:

---------开始常规处理鱼--------------
把鱼用刀背拍死…
使用刀刮去鱼鳞…
用手将内脏抠出…
---------开始新手处理鱼--------------
抓着鱼摔打到不再反抗…
用手将鱼鳞全部拔出…
用手将内脏抠出…
---------开始佛系处理鱼--------------
鱼呀,别挣扎了,命运已经注定了(巴拉巴拉到鱼不再反抗)…
试图说服鱼鳞摆脱鱼的束缚…
试图说服内脏自己跑出来…

2.做鱼

2.1分析

把鱼做成菜就跟杀鱼的逻辑不一样了,每道菜都需要特定的流程操作,步骤不一而定,但整体入口出口一致,即入口需要确定一条鱼,出口输出一道菜。每道菜都用拥有自己的烹饪逻辑,且根据目标菜品来选择烹饪过程,故在此选择策略模式。

2.2实现

  • 定义烹饪接口
/**
 * 鱼类烹饪接口
 */
public interface CookFishInterface {

	/**
	 * 烹饪
	 */
	public void cook();
}
  • 定义实现类
/**
 * 碳烤鱼
 */
public class GrilledFish implements CookFishInterface {

	public void cook() {
		System.out.println("----开始做一道碳烤鱼-----");
		System.out.println("碳烤鱼的各种步骤...");
		System.out.println("----碳烤鱼出炉-----");
	}

}
/**
 * 生鱼片
 */
public class Sashimi implements CookFishInterface {

	public void cook() {
		System.out.println("----开始做生鱼片-----");
		System.out.println("生鱼片的各种步骤...");
		System.out.println("----生鱼片做成-----");
	}

}
/**
 * 清蒸鱼
 */
public class SteamedFish implements CookFishInterface {

	public void cook() {
		System.out.println("----开始做一道清蒸鱼-----");
		System.out.println("清蒸鱼的各种步骤...");
		System.out.println("----清蒸鱼出炉-----");
	}

}
  • 定义上下文
/**
 * 鱼类烹饪上下文
 */
public class FishContext {
	private CookFishInterface cookFishInterface;

	public FishContext(CookFishInterface fishInterface) {
		this.cookFishInterface = fishInterface;
	}

	public void cook() {
		System.out.println("------------开始烹饪----------");
		cookFishInterface.cook();
	}
}
  • 运行代码
public class Main {
	public static void main(String[] args) {
		FishContext context1 = new FishContext(new GrilledFish());
		context1.cook();
		FishContext context2 = new FishContext(new SteamedFish());
		context2.cook();
		FishContext context3 = new FishContext(new Sashimi());
		context3.cook();
	}
}

输出结果如下:

------------开始烹饪----------
----开始做一道碳烤鱼-----
碳烤鱼的各种步骤…
----碳烤鱼出炉-----
------------开始烹饪----------
----开始做一道清蒸鱼-----
清蒸鱼的各种步骤…
----清蒸鱼出炉-----
------------开始烹饪----------
----开始做生鱼片-----
生鱼片的各种步骤…
----生鱼片做成-----

四、总结

可以看出,模板模式是将一整套流程提取出来做了封装,对于流程内的实现可以被替换,而策略模式替换的是整个操作;换句话说,当需要确定一系列不可被更改顺序的操作,但其中步骤可以被替换时,选择策略模式;当需要多种可以被替换的算法实现时,选择策略模式。

PS:实际开发过程中,一般不会单一模式使用,比如模板模式中每一个步骤都可以是一个策略模式实现;而策略模式的上下文可以使用工厂模式,从而实现外部不知道有哪些策略,但可以根据传入参数有工厂选择具体的策略。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章