HeadFrist设计模式学习之做一个万能遥控器(命令模式)

来源:互联网 发布:软件销售好做吗 编辑:程序博客网 时间:2024/05/16 09:00
让我们先来看一下命令模式的定义:
将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。
看一下遥控器的需求:
可以控制所有的家电,进行开关灯/电视。。。。等操作
先来一个看起来没什么毛病的遥控器来控制:
class Remotecontrol {


Light light;
TV tv;
public Remotecontrol(Light light,TV tv) {
// TODO Auto-generated constructor stub
this.light=light;
this.tv=tv;
}
public void lightOn()
{
light.on();
}
public void lightoff()
{
light.off();
}
public void tvOn()
{
tv.on();
}
public void tvoff()
{
tv.off();
}
public static void main(String[] args) {

Remotecontrol remotecontrol = new Remotecontrol(new Light(), new TV());
remotecontrol.lightOn();
remotecontrol.lightoff();
remotecontrol.tvOn();
remotecontrol.tvoff();
}


}
运行结果------------
light open
      light closed
TV open
TV closed
--------------------
既然是个看起来没毛病的遥控器,那其实就是有毛病的,这个问题也是显而易见:
每增加一个新的电器,遥控器的代码就需要修改。
造成这个问题的原因是遥控器的代码违反了两个重要的设计原则:
1.面对超类/接口编程
2.封装变化
很重要的一点是:遥控器其实并不需要知道关于灯、电视开关的细节。为了屏蔽这些细节(封装变化的过程),我们可以设计一个接口,像这样:
interface Command {
public abstract void on();
public abstract void off();



}
让灯和电视都实现这个接口,这样,遥控器不需要知道是灯开,只需要知道一个Command的方法执行就行了。
我们也迎来的遥控器2.0版:
class Remotecontrol {


Command[] commands;
public Remotecontrol() {
// TODO Auto-generated constructor stub
commands=new Command[2];
}
public void addCommand(int index,Command c)
{
commands[index]=c;
}
public void pressedOn(int index)
{
commands[index].on();
}
public void pressedOff(int index)
{
commands[index].off();
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Remotecontrol remotecontrol = new Remotecontrol();
remotecontrol.addCommand(0, new Light());
remotecontrol.addCommand(1, new TV());
remotecontrol.pressedOn(0);
remotecontrol.pressedOff(0);
remotecontrol.pressedOn(1);
remotecontrol.pressedOff(1);
}


}
运行结果---------------
light open
light closed
TV open
TV closed
-----------------------
相比于最初版,2.0版在添加电器时,再也不需要修改遥控器本身的代码,只要让电器实现Command接口就能将其挂载在遥控器上,遥控器也不必关心具体的实现细节(但是仍知道一些细节)。
但是。。。问题真的解决了吗?
并没有。。。2.0版只能控制电器的开关,如果电器有更加多的指令。。。比如电风扇,一共有开、关、低速、中速、高速五个指令,2.0并不能胜任。
按照上面的解决思路,可以将Command接口扩展成五个抽象方法,但是这样做会造成接口的浪费和很多无意义的空方法,既然扩展不成,那么我们可以收缩,让Command接口
只有一个抽象方法,一个类对应一种命令,即将“请求”和执行“请求”的对象封装在一起,也就是本文所说的“命令模式”。
收缩后的Command接口:
interface Command {
public abstract void execute();




}
比原来更简单的接口,更具有意义的是下面的将“请求”和执行“请求”的对象封装在一起的具体类:
class LightOn implements Command {
Light light;
  public LightOn(Light light) {
// TODO Auto-generated constructor stub
this.light=light;
}
@Override
public void execute() {
// TODO Auto-generated method stub
light.on();
}
}
下面是运用了命令模式的遥控器3.0:
 class Remotecontrol {


Command[] commands;
public Remotecontrol() {
// TODO Auto-generated constructor stub
commands=new Command[9];
}
public void addCommand(int index,Command c)
{
commands[index]=c;
}
public void pressed(int index)
{
commands[index].execute();
}

public static void main(String[] args) {
// TODO Auto-generated method stub
//初始化家电
Light light=new Light();
TV tv=new TV();
Fan fan=new Fan();     
Remotecontrol remotecontrol = new Remotecontrol();
//依次加载命令
remotecontrol.addCommand(0, new LightOn(light));
remotecontrol.addCommand(1, new LightOff(light));
remotecontrol.addCommand(2, new TVOn(tv));
remotecontrol.addCommand(3, new TVOff(tv));
remotecontrol.addCommand(4, new fanOn(fan));
remotecontrol.addCommand(5, new fanOff(fan));
remotecontrol.addCommand(6, new fanLow(fan));
remotecontrol.addCommand(7, new fanMid(fan));
remotecontrol.addCommand(8, new fanHigh(fan));
for(int n=0;n<=8;n++)
{
remotecontrol.pressed(n);
}

}


}
运行结果----------------
light open
light closed
TV open
TV closed
fan open
fan closed
fan low-speed
fan mid-speed
fan high-speed
------------------------
在这个过程中,我们虽然增加了很多类,但是这是值得的,我们真正做到将遥控器与家电的细节独立开,遥控器连Command的execute方法的细节都不清楚(细节有具体的对象决定),
这正符合所说的:类型要在运行时决定,而不是在编译时决定。 
1 0