【韩老师设计模式8】模板方法和命令模式,ConfigurableApplicationContext,JdbcTemplate

模式划分

行为型 模式(10种)

  • 责任链

    • 模拟618电商 大促销 期间,项目上线 流程 多级负责人 审批 场景
  • 命令

    • 模拟高档餐厅,八大菜系,小二点单 厨师烹饪 场景
  • 迭代器

    • 模拟公司 组织 架构树 结构关系,深度迭代 遍历 人员 信息 输出场景
  • 中介者

    • 按照Mybatis 原理,手写 ORM框架,给JDBC方式操作数据库,增加多个中介者 场景
  • 备忘录

    • 模拟互联网 系统上线过程中,配置文件回滚 场景
  • 观察者

    • 模拟类似 小客车 指标 摇号过程,监听消息 通知用户中签 场景
  • 状态

    • 模拟系统营销活动,状态流程 审核发布上线 场景
  • 策略

    • 模拟多种 营销类型 优惠券,折扣金额 计算策略 场景
  • 模板方法

    • 模拟爬虫 各类电商商品,生成营销推广 海报场景
    • 抽奖流程 标准化,模板模式
  • 访问者

    • 模拟家长 与 校长,对学生 和 老是的 不同视角信息,访问场景。
  • 还有 解释器 模式

  • 责 命 迭 中 解 备 观 状 策 模 访

  • 责 命 中解迭备 观 状 策 模 访

  • 模仿 中解迭备 策责 观状命

    • 我先迭一个,你们随意,大概是 我先喝一杯吧。

模板方法

编写制作豆浆的程序,说明如下:

  1. 制作豆浆的流程 选材—>添加配料—>浸泡—>放到豆浆机打碎
  2. 通过添加不同的配料,可以制作出不同口味的豆浆
  3. 选材、浸泡和放到豆浆机打碎这几个步骤对于制作每种口味的豆浆都是一样的
  4. 请使用 模板方法模式 完成 (说明: 因为模板方法模式,比较简单, 很容易就
    想到这个方案,因此就直接使用, 不再使用传统的方案来引出模板方法模式 )
  • 统计 代码的运行时间。也可以用。

基本介绍

  1. 模板方法模式(Template Method Pattern),又叫模板模式(Template Pattern),
    在一个抽象类公开定义了执行它的方法的模板。它的子类可以按需要重写方法
    实现,但调用将以抽象类中定义的方式进行。
  2. 简单说, 模板方法模式 定义一个操作中的算法的骨架,而将一些步骤延迟到子
    类中,使得子类可以不改变一个算法的结构,就可以重定义该算法的某些特定
    步骤
  3. 这种类型的设计模式属于行为型模式。

对原理类图的说明-即(模板方法模式的角色及职责)

  1. AbstractClass 抽象类, 类中实现了模板方法(template),定义了算法的骨
    架,具体子类需要去实现 其它的抽象方法operationr2,3,4
  2. ConcreteClass 实现抽象方法operationr2,3,4, 以完成算法中特点子类的步骤
beat
v.
(反复地)敲,击,打;狠打,猛抽;搅拌,混合;拍动,扇动(翅膀);(心脏)跳动,搏动;击败,战胜;抢……之先,赶在……之前;避开,避免
n.
敲,击;(音乐或诗歌的)强音拍;(经常去或经过的)路线,地点;以之字形航线抢风航行
adj.
<非正式>筋疲力尽,疲惫不堪;垮掉的一代的

bean
n.
豆,菜豆,豆科植物;籽实,豆形种子;用于强调丝毫,一点;<旧>脑袋;(Bean)描述Java的软件组件模型(Enterprise Java Bean)
v.
(用某物)击中(某人)头部

soya
n.
大豆,[作物]黄豆

peanut
英
/ˈpiːnʌt/
n.
花生,花生米;<非正式>很少的钱(peanuts);小片聚苯乙烯泡沫塑料(peanuts)

condiment
调味品;佐料

抽象类 指定了流程

//抽象类,表示豆浆
public abstract class SoyaMilk {

	//模板方法, make , 模板方法可以做成final , 不让子类去覆盖.
	final void make() {
		
		select(); 
		addCondiments();
		soak();
		beat();
		
	}
	
	//选材料
	void select() {
		System.out.println("第一步:选择好的新鲜黄豆  ");
	}
	
	//添加不同的配料, 抽象方法, 子类具体实现
	abstract void addCondiments();
	
	//浸泡
	void soak() {
		System.out.println("第三步, 黄豆和配料开始浸泡, 需要3小时 ");
	}
	 
	void beat() {
		System.out.println("第四步:黄豆和配料放到豆浆机去打碎  ");
	}
}

不同的实现类

public class PeanutSoyaMilk extends SoyaMilk {

	@Override
	void addCondiments() {
		System.out.println(" 加入上好的花生 ");
	}
}


		SoyaMilk peanutSoyaMilk = new PeanutSoyaMilk();
		peanutSoyaMilk.make();
----制作花生豆浆----
第一步:选择好的新鲜黄豆  
 加入上好的花生 
第三步, 黄豆和配料开始浸泡, 需要3小时 
第四步:黄豆和配料放到豆浆机去打碎  

钩子方法

应用实例要求
编写制作豆浆的程序,说明如下:
• 制作豆浆的流程 选材—>添加配料—>浸泡—>放到豆浆机打碎
• 通过添加不同的配料,可以制作出不同口味的豆浆
• 选材、浸泡和放到豆浆机打碎这几个步骤对于制作每种口味的豆浆都是一样的(红
豆、花生豆浆。。。 )

//抽象类,表示豆浆
public abstract class SoyaMilk {

	//模板方法, make , 模板方法可以做成final , 不让子类去覆盖.
	final void make() {
		
		select(); 
		if(customerWantCondiments()) {
			addCondiments();
		}
		soak();
		beat();
		
	}
	
	//添加不同的配料, 抽象方法, 子类具体实现
	abstract void addCondiments();
	
	//钩子方法,决定是否需要添加配料
	boolean customerWantCondiments() {
		return true;
	}
}

重写钩子方法

public class PureSoyaMilk extends SoyaMilk{

	@Override
	void addCondiments() {
		//空实现
	}
	
	@Override
	boolean customerWantCondiments() {
        //纯豆浆,返回false,不会调用 加配料的方法
		return false;
	}
 
}

		System.out.println("----制作纯豆浆----");
		SoyaMilk pureSoyaMilk = new PureSoyaMilk();
		pureSoyaMilk.make();
----制作纯豆浆----
第一步:选择好的新鲜黄豆  
第三步, 黄豆和配料开始浸泡, 需要3小时 
第四步:黄豆和配料放到豆浆机去打碎  

IOC

接口

ConfigurableApplicationContext

public interface ConfigurableApplicationContext extends ApplicationContext, Lifecycle, Closeable {

    void refresh() throws BeansException, IllegalStateException;
}

抽象类

  • 子类
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
    //模板 方法
    	@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory. 里面是抽象方法
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			try {
                //钩子方法
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

               	//钩子方法
				// Initialize other special beans in specific context subclasses.
				onRefresh();

			}

			catch (BeansException ex) {
				// Propagate exception to caller.
				throw ex;
			}

			finally {
				resetCommonCaches();
			}
		}
	}

}
	//钩子 方法
	protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
	}
	protected void onRefresh() throws BeansException {
	}

	protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        //两个抽象方法
		refreshBeanFactory();
		return getBeanFactory();
	}

	//都是 抽象的方法
	protected abstract void refreshBeanFactory() throws BeansException, 			IllegalStateException;

	@Override
	public abstract ConfigurableListableBeanFactory getBeanFactory() throws IllegalStateException;

抽象类的子类

public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
		
    //对 抽象方法,进行了实现
    @Override
	protected final void refreshBeanFactory() throws IllegalStateException {
		this.beanFactory.setSerializationId(getId());
	}

    @Override
	public final ConfigurableListableBeanFactory getBeanFactory() {
		return this.beanFactory;
	}
}
  • AbstractRefreshableConfigApplicationContext

    • 抽象类类,也是子类,同样实现了。这个两个方法。其子类:
    • ClassPathXmlApplicationContext
    • FileSystemXmlApplicationContext 提供给别人使用

事项和细节

模板方法模式的注意事项和细节

  1. 基本思想是: 算法只存在于一个地方,也就是在父类中,容易修改。 需要修改算
    法时,只要修改父类的模板方法或者已经实现的某些步骤,子类就会继承这些修改
  2. 实现了最大化代码复用。 父类的模板方法和已实现的某些步骤会被子类继承而直接
    使用。
  3. 既统一了算法,也提供了很大的灵活性。 父类的模板方法确保了算法的结构保持不
    变,同时由子类提供部分步骤的实现。
  4. 该模式的不足之处:每一个不同的实现都需要一个子类实现,导致类的个数增加,
    使得系统更加庞大
  5. 一般模板方法都加上final关键字, 防止子类重写模板方法.
  6. 模板方法模式使用场景: 当要完成在某个过程, 该过程要执行一系列步骤 , 这一
    系列的步骤基本相同,但其个别步骤在实现时 可能不同,通常考虑用模板方法模
    式来处理

命令模式

  1. 我们买了一套智能家电,有照明灯、风扇、冰箱、洗衣机,我们只要在手机上安装app就
    可以控制对这些家电工作。
  2. 这些智能家电来自不同的厂家,我们不想针对每一种家电都安装一个App,分别控制, 我
    们希望只要一个app就可以控制全部智能家电。
  3. 要实现一个app控制所有智能家电的需要,则每个智能家电厂家都要提供一个统一的接口
    给app调用, 这时 就可以考虑使用命令模式。
  4. 命令模式可将“动作的请求者”从“动作的执行者”对象中解耦出来.
  5. 在我们的例子中,动作的请求者是手机app,动作的执行者是每个厂商的一个家电产品

基本介绍

  1. 命令模式(Command Pattern): 在软件设计中,我们经常需要
    向某些对象发送请求,但是并不知道请求的接收者是谁,也不知
    道被请求的操作是哪个,
    我们只需在程序运行时指定具体的请求接收者即可,此时,可以
    使用命令模式来进行设计

  2. 命名模式使得请求发送者与请求接收者消除彼此之间的耦合,让
    对象之间的调用关系更加灵活,实现解耦。

  3. 在命名模式中,会将一个请求封装为一个对象, 以便使用不同参
    数来表示不同的请求(即命名),同时命令模式也支持可撤销的操作。

  4. 通俗易懂的理解:将军发布命令,士兵去执行。其中有几个角色:
    将军(命令发布者)、士兵(命令的具体执行者)、命令(连接将
    军和士兵)。
    Invoker是调用者(将军), Receiver是被调用者(士兵),
    MyCommand是命令,实现了Command接口,持有接收对象

  5. Invoker 是调用者角色

  6. Command: 是命令角色,需要执行的所有命令都在这里,可以是接口或抽象类

  7. Receiver: 接受者角色,知道如何实施和执行一个请求相关的操作

  8. ConcreteCommand: 将一个接受者对象与一个动作绑定,调用接受者相应的操作,实现execute

  • Invoker(就是下面的遥控器)
    • 依赖命令接口
      • 命令和 接收者的连接类 ConcreteCommand
        • 调用:接收者 Receiver (士兵)

顶层接口

//创建命令接口
public interface Command {

	//执行动作(操作)
	public void execute();
	//撤销动作(操作)
	public void undo();
}

被调用者(真实的执行者)

public class LightReceiver {

	public void on() {
		System.out.println(" 电灯打开了.. ");
	}
	
	public void off() {
		System.out.println(" 电灯关闭了.. ");
	}
}

命令类(组合 连接)

  • 这里是 开命令,还会有关命令
public class LightOnCommand implements Command {
	//聚合LightReceiver
	LightReceiver light;
	
	//构造器
	public LightOnCommand(LightReceiver light) {
		super();
		this.light = light;
	}
	
	@Override
	public void execute() {
		//调用接收者的方法
		light.on();
	}

	@Override
	public void undo() {
		//调用接收者的方法
		light.off();
	}
}
  • 关 命令类 和 开命令类 相仿。
    • 执行:改为关。
    • 撤销:改为开。

空命令

/**
 * 没有任何命令,即空执行: 用于初始化每个按钮, 当调用空命令时,对象什么都不做
 * 其实,这样是一种设计模式, 可以省掉对空判断
 */
public class NoCommand implements Command {
    
	@Override
	public void execute() {
	}
    
	@Override
	public void undo() {
	}
}

遥控器

public class RemoteController {

	// 开 按钮的命令数组
	Command[] onCommands;
	Command[] offCommands;

	// 执行撤销的命令
	Command undoCommand;

	// 构造器,完成对按钮初始化

	public RemoteController() {

		onCommands = new Command[5];
		offCommands = new Command[5];

		for (int i = 0; i < 5; i++) {
			onCommands[i] = new NoCommand();
			offCommands[i] = new NoCommand();
		}
	}

	// 给我们的按钮设置你需要的命令
	public void setCommand(int no, Command onCommand, Command offCommand) {
		onCommands[no] = onCommand;
		offCommands[no] = offCommand;
	}

	// 按下开按钮
	public void onButtonWasPushed(int no) { // no 0
		// 找到你按下的开的按钮, 并调用对应方法
		onCommands[no].execute();
		// 记录这次的操作,用于撤销
		undoCommand = onCommands[no];

	}

	// 按下开按钮
	public void offButtonWasPushed(int no) { // no 0
		// 找到你按下的关的按钮, 并调用对应方法
		offCommands[no].execute();
		// 记录这次的操作,用于撤销
		undoCommand = offCommands[no];

	}
	
	// 按下撤销按钮
	public void undoButtonWasPushed() {
		undoCommand.undo();
	}

}

使用

//使用命令设计模式,完成通过遥控器,对电灯的操作
		
		//创建电灯的对象(接受者)
		LightReceiver lightReceiver = new LightReceiver();
		
		//创建电灯相关的开关命令
		LightOnCommand lightOnCommand = new LightOnCommand(lightReceiver);
		LightOffCommand lightOffCommand = new LightOffCommand(lightReceiver);
		
		//需要一个遥控器
		RemoteController remoteController = new RemoteController();
		
		//给我们的遥控器设置命令, 比如 no = 0 是电灯的开和关的操作
		remoteController.setCommand(0, lightOnCommand, lightOffCommand);
		
		System.out.println("--------按下灯的开按钮-----------");
		remoteController.onButtonWasPushed(0);
		System.out.println("--------按下灯的关按钮-----------");
		remoteController.offButtonWasPushed(0);
		System.out.println("--------按下撤销按钮-----------");
		remoteController.undoButtonWasPushed();
  • 符合开闭原则,增加电视的开关,RemoteController 无需变化。

JdbcTemplate

  • JdbcTemplate 即是:命令调用者 。Invoker是(将军)
  • 以 内部类 方式,实现命令接口
public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {

	@Override
	public void query(String sql, RowCallbackHandler rch) throws DataAccessException {
		query(sql, new RowCallbackHandlerResultSetExtractor(rch));
	}
}
  • 调查query方法
	@Override
	public <T> T query(final String sql, final ResultSetExtractor<T> rse) throws DataAccessException {
		Assert.notNull(sql, "SQL must not be null");


        //子类,同时又 充当了 士兵的角色
		class QueryStatementCallback implements StatementCallback<T>, SqlProvider {
			@Override
			public T doInStatement(Statement stmt) throws SQLException {
				ResultSet rs = null;

			}
			@Override
			public String getSql() {
				return sql;
			}
		}
		//调用 execute ,此方法 是在 jdbcTemplate 实现的
		return execute(new QueryStatementCallback());
	}

@Override
	public <T> T execute(StatementCallback<T> action) throws DataAccessException {

		try {
			Connection conToUse = con;
			//doInStatement 就是上面 内部类 实现的一个方法
			T result = action.doInStatement(stmtToUse);
			handleWarnings(stmt);
			return result;
		}

		finally {
			JdbcUtils.closeStatement(stmt);
			DataSourceUtils.releaseConnection(con, getDataSource());
		}
	}
//命令接口
public interface StatementCallback<T> {

	T doInStatement(Statement stmt) throws SQLException, DataAccessException;
}

- StatementCallback 接口 ,类似命令接口(Command)
- class QueryStatementCallback implements StatementCallback, SqlProvider , 匿名内
部类, 实现了命令接口, 同时也充当命令接收者
- 命令调用者 是 JdbcTemplate , 其中execute(StatementCallback action) 方法中,调
用action.doInStatement 方法. 不同的 实现 StatementCallback 接口的对象,对应不同
的doInStatemnt 实现逻辑
- 另外实现 StatementCallback 命令接口的子类还有 QueryStatementCallback、

不同之处在于,正规的 命令模式:命令接收者(士兵),不会实现命令的。

  • 此处的命令调用者(指挥者):还充当了 整合接收者
    • ConcreteCommand: 将一个接受者对象与一个动作绑定,调用接受者相应的操作,实现execute

细节

命令模式的注意事项和细节

  1. 将发起请求的对象与执行请求的对象解耦。发起请求的对象是调用者,调用者只要
    调用命令对象的execute()方法就可以让接收者工作,而不必知道具体的接收者对
    象是谁、是如何实现的,命令对象会负责让接收者执行请求的动作, 也就是说: ”
    请求发起者”和“请求执行者”之间的解耦是通过命令对象实现的,命令对象起到
    了纽带桥梁的作用。
  2. 容易设计一个命令队列。只要把命令对象放到列队,就可以多线程的执行命令
  3. 容易实现对请求的撤销和重做
  4. 命令模式不足:可能导致某些系统有过多的具体命令类, 增加了系统的复杂度, 这
    点在在使用的时候要注意
  5. 空命令也是一种设计模式,它为我们省去了判空的操作。在上面的实例中,如果没
    有用空命令,我们每按下一个按键都要判空,这给我们编码带来一定的麻烦。
  6. 命令模式经典的应用场景:界面的一个按钮都是一条命令、 模拟CMD(DOS命令)
    订单的撤销/恢复、触发-反馈机制

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值