命令模式在日常生活中非常普遍。只要涉及到控制行为,就会有命令模式的出现。
今天的分析,以日常生活中开关灯为例,从耦合较高的代码一步步进行改进。
Part1:
问题1:编写代码,实现生活中开关灯的操作
分析:和现实生活一样,要实现开关灯,那么必须要有两个物体(对象),灯和开关。
灯有两个状态,开(on),关(off)。
开关有两个动作,开灯(turnonLight),关灯(turnoffLight)。简单实现代码如下:
灯类:
class Light{
//灯打开状态
public void on(){
System.out.println("light is on");
}
//灯关闭状态
public void off(){
System.out.println("light is off");
}
}
开关类:
class Switch{
private Light l1 = null;
public Switch(Light l){
l1=l;
}
//使用开关开灯
public void turnonLight(){
System.out.print("press the switch and ");
l1.on();
}
//使用开关关灯
public void turnoffLight(){
System.out.print("press the switch and ");
l1.off();
}
}
场景类:
public class LightControl {
public static void main(String[] args) {
// TODO Auto-generated method stub
Light light = new Light(); //买一个灯回家
Switch lightSwitch = new Switch(light);//安装灯对应的开关
lightSwitch.turnonLight();//使用开关开灯
lightSwitch.turnoffLight();//使用开关关灯
}
}
输出为:
press the switch and light is on
press the switch and light is off
Part2:
上面代码实现了简单的开关控制,但是我们发现switch类封装的操作太多,不利于扩展,比如遇到了下面的问题。
问题2:购买了一种灯,这种灯的开关可以设置灯的颜色,晚上回家,我先将灯的颜色设置为黄色,然后打开,睡觉时关闭。
分析:开关增加了一个新的命令,需要修改开关类,再增加setColor()方法。
如果这种需求很多,每次都要修改原有类,不仅不利于代码维护,还增加了大量的测试时间。
Note:好的代码结构应该是【对扩展开放,对修改关闭】。【】内为引用内容。
那么,就需要将switch类进行分解,使得每一个命令单独成类。由于每一个命令实现方式类似,为了后续扩展,抽象出命令抽象类。
为什么不用接口呢?因为每个命令类中都要维护一个Light变量,而接口中所有的变量都是static final的常量。
abstract class Command{
protected Light l; //灯对象
public Command(Light l1){
l = l1;
}
public abstract void action(); //命令执行
}
每个命令封装成类:
开灯类:
class TurnOn extends Command{
public TurnOn(Light l1){
super(l1);
}
public void action(){
l.on(); //开灯
}
}
关灯及设置颜色与开灯类结构相同,不再赘述。
因为新灯增加了功能,构建的新类如下:
class Light{
private String color;
//灯打开状态
public void on(){
System.out.println("light is on");
}
//灯关闭状态
public void off(){
System.out.println( "light is off");
}
//******(新添加)设置颜色为黄色
public void color(){
System.out.print("Yellow ");
}
}
场景类:
public class LightControl {
public static void main(String[] args) {
// TODO Auto-generated method stub
Light light = new Light(); //买一个灯回家
SetColor sc = new SetColor(light); //设置灯的颜色
sc.action();
TurnOn turnon = new TurnOn(light); //开灯
turnon.action();
TurnOff turnoff = new TurnOff(light); //关灯
turnoff.action();
}
}
输出:
Yellow light is on
light is off
通过将switch分解,现在每次需要增加新的命令,只需要创建对应的新类,然后调用即可,无需修改原来存在的类。
Part3:
上面的代码耦合性已经不高,但是还是有点问题。
问题3:如果某些命令需要组合使用,比如在开灯前,要check一下周围的光线,或者需要执行其它的命令,那我每次都需要new一个新的对象,然后执行命令对应的run方法。
有没有更简单的方式呢?
解决方案:将所有命令封装到一个类中,通过组合不同的命令,实现控制。(Invoker类很像观察者模式中被观察对象类)
分析:创建Control类,Control类要有运行(execute)方法及绑定命令(setCommand)的方法。
Control类代码:
class Control{ List<Command> listCmd = new LinkedList<Command>(); //绑定Command public void setCommand(List<Command> c){ listCmd = c; } //调用Command运行 public void execute(){ Iterator<Command> it = listCmd.iterator(); while(it.hasNext()){ it.next().action(); } } }
场景类代码:
public class LightControl { public static void main(String[] args) { // TODO Auto-generated method stub Light light = new Light(); //买一个灯回家 SetColor sc = new SetColor(light); //灯的颜色设置开关 TurnOn turnon = new TurnOn(light); //灯的打开开关 TurnOff turnoff = new TurnOff(light);//灯的关闭开关 List<Command> l1 = new LinkedList<Command>(); l1.add(sc); l1.add(turnon); l1.add(turnoff); Control ctr = new Control(); ctr.setCommand(l1); ctr.execute(); //命令单个或者组合使用 } }
输出:Yellow light is onlight is off