Header First设计模式学习笔记——命令模式

问题引入

        近来,智能家居闹得比较凶,这里我们想要实现一个简单的自动家居,由一个遥控器来完成电灯、音响、风扇的开关。

模式定义

        将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤消的操作

认识模式

        对于这个模式我最欣赏的就是他将“做什么”与"怎么做"的问题或者说是“动作的请求者”与"动作的实现者”进行了完美的解耦。并且,它支持undo的操作。

问题解决

      命令模式实现步骤:   

         * 1、定义一个Command接口,这个接口中只含有execute()方法;

         * 2、定义各个命令对象(需要实现上述的Command接口),它包含具体实现功能的对象引用,并在execute()方法中定义相应的操作;

         * 3、将这个命令对象交给”动作请求者“。

直接上代码:

一、不带undo的遥控器

1>这是Command接口

package my.oschina.net.design.commands;

public interface Com {
	public void execute();
	//这个undo()撤销方法我们一会儿再说
	public void undo();
}

2>我们来定义一个开(关)灯的命令

先看看这个灯的类吧

package my.oschina.net.design.commands;

/**
 * 他包括灯的一切操作(关灯、开灯)
 * @author Eswin
 *
 */
public class Light
{
	public Light(){}
	
	public void lightOn()
	{
		System.out.println("The light is on !");
	}
			
	public void lightOff()
	{
		System.out.println("The light is off !");
	}
}

这是开关灯的命令对象

package my.oschina.net.design.commands;

public class LightOnCommand implements Com{

	private Light light;
	
	public LightOnCommand(Light light)
	{
		this.light = light;
	}
	
	public void execute()
	{
		light.lightOn();
	}

	@Override
	//显然开灯的撤销就是要关灯
	public void undo() {
		// TODO Auto-generated method stub
		light.lightOff();
	}		
}

package my.oschina.net.design.commands;

public class LightOffCommand implements Com{

	private Light light;
	
	public LightOffCommand(Light light)
	{
		this.light = light;
	}
	
	public void execute()
	{
		light.lightOff();
	}

	@Override
	//显然开灯的撤销就是要开灯
	public void undo() {
		// TODO Auto-generated method stub
		light.lightOn();
	}
}

同理对于fans也是一样的

3>是时候告诉”接收者“遥控器他可以实现的命令了

这是一个简单遥控器类(只有一个按钮,我们只是来测试这个命令,下面有个更全面的例子)

package my.oschina.net.design.commands;

public class ControlBar {
	//命令对象的引用
	Com com;
	
	public ControlBar(){}
	
	/*
	 * 通过传入Com参数决定遥控器可以实现的命令
	 */
	public void setCommand(Com com)
	{
		this.com = com;
	}
	
	//一旦我们按下按钮(下命令),就有真正的接受者完成相应的任务,我们的遥控器并不需要知道到底是如何实现的
	public void pressDown()
	{
		com.execute();
	}
}

Test一下吧

package my.oschina.net.design.commands;

public class TestCom {
	
	public static void main(String[] args)
	{
		Light light = new Light();	
		Com com1 = new LightOnCommand(light);
		Com com2 = new LightOffCommand(light);
		
		Fans fans = new Fans();
		Com com3 = new FansOnCommand(fans);
		Com com4 = new FansOffCommand(fans);
		
		ControlBar clb = new ControlBar();
		clb.setCommand(com1);
		clb.pressDown();
		
		clb.setCommand(com2);
		clb.pressDown();
		
		clb.setCommand(com3);
		clb.pressDown();
		
		clb.setCommand(com4);
		clb.pressDown();	
	}
}

可以看到我们设置好命令,然后只需要PressDown按钮,就可以完成相应的命令

结果如下:

221627_JIEv_1446623.png

二、带有undo的遥控器

这是一个很炫酷的功能。你肯定遇到过这样尴尬的情况,当你做下一个决定的那一刻你就发现自己后悔了(很奇妙),怎么办???没事,我们提供了undo功能!

undo说到底很简单,就是撤销上步操作(没错,就是ctrl + z),换句话说,就是执行与你上一步的动作相反的动作(你开了我就关)

回头看看LightOnCommand和LightOffCommand命令中的undo操作!确实很简单!

好了,我们可以在遥控器上加上这个硬件按钮了。

接下来我们创建一个真实的遥控器,遥控器上有多个控制按钮,分别实现各个功能。

这是遥控器类:

package my.oschina.net.design.commands;

import java.util.Stack;

public class ControlBar2 {

        //我们将命令放在数组中更加清晰一点
	Com[] oncoms;
	Com[] offcoms;
	
	//这是个撤销命令对象的引用
	Com undoCom;
	
	//用栈来保存每次需要撤销的操作,从而可以一直undo(当然也是有一定次数限制的)
	public Stack<Com> in_undo_satck = new Stack<Com>();
	
	ControlBar2()
	{
		//记得生成这个数组,要不然出现空指针的现象
		oncoms = new Com[3];
		offcoms = new Com[3];
	}
	/**
	 * 
	 * @param i 遥控器的命令插槽,用于标识控制的是什么(电灯?电扇?)
	 * @param oncom 控制对象的开命令
	 * @param offcom 控制对象的关命令
	 */
	public void setCommand(int i, Com oncom, Com offcom)
	{	
		oncoms[i] = oncom;
		offcoms[i] = offcom;		
	}
	
	//开按钮
	public void pressOn(int i)
	{
		oncoms[i].execute();
		记录下上一次执行的动作,
		undoCom = oncoms[i];
		
		//将上一次将执行的动作记录到栈中,一会弹出一次调用undo()方法
		in_undo_satck.push(undoCom);
		
	}
	
	//关按钮
	public void pressOff(int i)
	{
		offcoms[i].execute();
		undoCom = offcoms[i];
		
		//将上一次将执行的动作记录到栈中,一会弹出一次调用undo()方法
		in_undo_satck.push(undoCom);
		
	}	
	
	/*带参数的undo撤销按钮,根据传入的i(标识控制哪个对象),对相应的开操作做撤销,这个可以不要
	public void Pressundo(int i)
	{
		oncoms[i].undo();	
		in_undo_satck.push(undoCom);
	}
	*/
	
	//undo撤销按钮(终于见到你了)
	public void Pressundo()
	{
	        //将上一次将执行的动作记录到栈中,一会弹出一次调用undo()方法,撤销的命令也可以压入栈中,进行撤销操作
		if(!in_undo_satck.isEmpty())		
			in_undo_satck.pop().undo();
	}
}

好了,Test一下

package my.oschina.net.design.commands;

public class TestControlBar2 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//创建一个Light对象
		Light light = new Light();
		//用Light对象初始化LightOnComman与LightOffCommand命令对象
		Com com1 = new LightOnCommand(light);
		Com com2 = new LightOffCommand(light);
		
		//创建一个Fans对象
		Fans fans = new Fans();
		//用Light对象初始化FansOnComman与FansOffCommand命令对象
		Com com3 = new FansOnCommand(fans);
		Com com4 = new FansOffCommand(fans);
		
		//这是一个遥控器
		ControlBar2 clb = new ControlBar2();	
		//在各个插槽上绑定命令	
		clb.setCommand(0, com1, com2);
		clb.setCommand(1, com3, com4);
		
		//记住了我们绑定的命令0上绑定的是对Light的操作
		clb.pressOn(0);	
		clb.pressOff(0);
		
		clb.pressOn(1);	
		clb.pressOff(1);
		clb.pressOn(1);	
		clb.pressOn(0);	
		
		clb.pressOn(1);	
		clb.pressOff(1);

		System.out.println("UNDO ------------> ");
		while( clb.in_undo_satck.iterator().hasNext())
			clb.Pressundo();
			
	}
		
}

预测结果:

    从分割线(UNDO --------------->)处,两边是成镜像对称的。

实际结果(丝毫不差):

233446_WkUS_1446623.png

好了,到这里为止,命令模式差不多了。

不过这里还有一个问题,就是风扇的撤销是不是太过简陋了?的确,风扇除了开就是关?显然不是嘛!我们可是可以调风速的呀!

这个问题其实也很好解决,我们在只要在设计风扇的命令的时候添加一个Speed的字段,记录下设置风速之前的Speed,撤销的时候再调回去就OK啦!

模式延伸

        对于大多数请求-响应模式的功能,比较适合使用命令模式。正如命令模式定义说的那样,命令模式对实现记录日志、撤销操作等功能比较方便。


转载于:https://my.oschina.net/v5871314/blog/283250

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第1关的任务是运用HTML5的语义化元素设计一个包含头部标题和文章内容的文章区。在这个任务中,我们需要使用header元素来表示文章区的头部。 在HTML5中,header元素用于表示页面或一个区块的头部部分。它通常包含页面的标题或区块的标题。在这个任务中,我们可以使用header元素来包含文章的标题。 下面是一个示例代码,展示了如何使用header元素来表示文章区的头部,并设置了一个茗茶推荐的标题:"茗茶推荐——祁门红茶": ``` <!DOCTYPE html> <html> <head> <title>页面结构</title> <style type="text/css"> header{ border-bottom:4px double #eee; text-align:center; font-size :20px } </style> </head> <body> <!-- ********* Begin ******* --> <article> <header><h3>茗茶推荐——祁门红茶</h3></header> <p>祁门红茶,茶叶原料选用当地的中叶,中生种茶树制作,是中国历史名茶,著名红茶精品。</p> </article> <!-- ********* End ********* --> </body> </html> ``` 通过设置header元素的样式,我们可以为文章区的头部添加一条底边,并设置居中对齐和字体大小。同时,我们在header元素内部使用h3标签来显示文章标题。 希望这个回答对你有帮助,祝你成功!<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [EduCoder-Web程序设计基础-html5—结构元素-第2关:header元素和article元素的应用](https://blog.csdn.net/weixin_51402180/article/details/121688659)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值