封装调用:将方法调用给封装起来。
这次讲的是命令模式,他的作用就是方法给封装起来,有需要的时候就调用,调用者并不需要关心它是如何实现的。
我们来看一张流程图
注:
1、订单封装了准备餐点的请求,
2、女服务员的工作是接受订单,然后调用订单的orderUp方法,女服务员并不关心订单内容是什么,她只需调用orderUp方法
3、厨师就是一个对象,他是真正知道餐点的具体内容的人,一旦女服务员调用orderUp方法,厨师就接手,实现餐点的具体方法,这里厨师和女服务员是解耦的,订单封装了餐点的细节,她只要调用每个订单的方法即可,而厨师看了订单就知道该做些什么餐点。
好的,我重新绘制了一张图反映命令模式如下图,流程与上图相同。
OK,我们基本了解了命令模式的流程怎样
下面写一个模拟遥控器打开电灯和门这么个动作的Demo例子
1、写电灯和门的类
电灯
package Entity;
public class Light {
public Light() {
}
public void on() {
System.out.println("灯亮了");
}
public void off() {
System.out.println("灯灭了");
}
}
门
package Entity;
public class Door {
public Door() {
}
public void OpenDoor() {
System.out.println("门开了");
}
public void OffDoor() {
System.out.println("门关了");
}
public void StopDoor(){
System.out.println("门停止了");
}
public void DoorLightOn(){
System.out.println("门里的灯亮了");
}
}
2、创建命令接口
package Interface;
/**
* 命令接口
*
* @author Joy
*
*/
public interface Command {
// 命令执行方法
public void execute();
}
3、实现一个打开电灯的命令
package Implements;
import Entity.Light;
import Interface.Command;
public class LightOnCommand implements Command {
Light light;
// 构造器中传入某个电灯类型
// 以便让这个命令控制,然后记录在light实例变量中,
// 当调用execute时,light会根据类型不同执行不同灯亮方法
public LightOnCommand(Light light) {
this.light = light;
}
// 执行灯亮方法
@Override
public void execute() {
light.on();
}
}
门的实现方法
package Implements;
import Entity.Door;
import Interface.Command;
public class DoorOpenCommand implements Command {
Door door;
public DoorOpenCommand(Door door) {
this.door = door;
}
@Override
public void execute() {
door.OpenDoor();
}
}
4、调用命令对象(遥控器)
package Control;
import Interface.Command;
/**
* 简单遥控器
*相当于调用者
* @author Joy
*
*/
public class SimpleRemoteControl {
// 命令对象类型,相当于插槽控制着一个装置
Command slot;
public SimpleRemoteControl() {
}
// 这个方法用来设置插槽控制的命令
// 如果客户需要改变遥控器按钮的行为,可以多次调用此方法
public void setCommand(Command command) {
slot = command;
}
// 执行方法
public void buttonWasPressed() {
slot.execute();
}
}
5、测试类(使用遥控器)
package TestMain;
import Control.SimpleRemoteControl;
import Entity.Door;
import Entity.Light;
import Implements.DoorOpenCommand;
import Implements.LightOnCommand;
public class LightTestMain {
public static void main(String[] args) {
// 遥控器相当于命令的调用者,会传入一个命令对象,可以用来发送请求
SimpleRemoteControl remote = new SimpleRemoteControl();
// 创建一个电灯对象,此对象也就是请求中的接收者
Light light = new Light();
Door door = new Door();
// 创建打开电灯动作的类,并传入接收者(light)
LightOnCommand lightOn = new LightOnCommand(light);
DoorOpenCommand doorOpen = new DoorOpenCommand(door);
// 命令传给调用者(遥控器)
remote.setCommand(lightOn);
// 模拟按下按钮
remote.buttonWasPressed();
remote.setCommand(doorOpen);
remote.buttonWasPressed();
}
}
效果图
这就是个基本命令模式的使用算是牛刀小试了一下,
命令模式定义:将“请求”封装成对象,以便使用不同的请求、队列或者日志
来参数化其他对象,命令模式也支持可撤销的操作。
下面是这模式的类图
接下来继续模拟一个遥控器,只不过这次需求变得复杂了起来。
示例图如下,
每个遥控器的插槽都对应一个命令吗,这样遥控器就变为了“调用者”,当按下按钮,对应的命令对象的execute方法就会被调用,结果就是接收者(例如:电灯,天花板电扇,音响)的动作被调用。
代码开始
1、实体类
电灯
package Entity;
public class Light {
//所处位置
String location;
public Light(String location) {
this.location = location;
}
public void on(){
System.out.println(location+"灯亮了");
}
public void off(){
System.out.println(location+"灯关了");
}
}
音响
package Entity;
/**
* 音响类
*
* @author Joy
*
*/
public class Stereo {
//location 一个地点变量
String location;
public Stereo(String location) {
this.location = location;
}
public void on(){
System.out.println(location+"音响启动");
}
public void off(){
System.out.println(location+"音响关闭");
}
public void setDVD(){
System.out.println(location+"音响放一张DVD并播放");
}
public void setCD(){
System.out.println(location+"音响放一张CD并播放");
}
public void setRadio(){
System.out.println(location+"音响以收音机无线电形式播放");
}
public void setVolume(int volume) {
System.out.println(location + " 音响音量设置为 " + volume);
}
}
电视
package Entity;
public class TV {
String location;
int channel;//电视频道
public TV(String location) {
this.location = location;
}
public void TVOn(){
System.out.println(location+"电视自动打开了");
}
public void TVOff(){
System.out.println(location+"电视自动关闭了");
}
//电视调频道
public void setTVChannel(int channel){
this.channel=channel;
System.out.println(location+"电视自动调到"+channel+"频道");
}
}
2、创建命令接口对象
package Interface;
public interface Command {
//执行
public void execute();
//撤销
public void undo();
}
3、实现遥控器类(调用者)
package Control;
import Implements.NoCommand;
import Interface.Command;
/**
* 实现遥控器
*
* @author Joy
*/
public class RemoteControl {
// 此时遥控器要处理7个开关控制,使用数组
Command[] onCommands = new Command[7];
Command[] offCommands = new Command[7];
// 撤销当然要先知道之前的命令
// 撤销变量,用来追踪最后被调用的命令
Command undoCommand;
// 初始化遥控器类,一开始都是无操作noCommand是一个无操作对象
// 在测试输出时,没有被明确指明命令的插槽,其命令默认为noCommand对象
public RemoteControl() {
Command noCommand = new NoCommand();
for (int i = 0; i < onCommands.length; i++) {
onCommands[i] = noCommand;
offCommands[i] = noCommand;
}
undoCommand = noCommand;
}
/**
*
* @param slot
* :插槽的位置(类似索引值)
* @param onCommand
* :开的命令
* @param offCommand
* :关的命令 这些命令被记录在开关数组对应的插槽位置上,以便使用
*/
public void setCommand(int slot, Command onCommand, Command offCommand) {
onCommands[slot] = onCommand;
offCommands[slot] = offCommand;
}
/**
* 开关按钮是对应的插槽位置负责调用对应的方法
* 遥控器上面开关按钮的不同位置就可以控制不同类型的灯
* undoCommand:当按下遥控器按钮时,我们取得这个命令,并记录在undoCommand里
* @param slot
*/
public void onButtonWasPushed(int slot) {
onCommands[slot].execute();
undoCommand=onCommands[slot];
}
public void offButtonWasPushed(int slot) {
offCommands[slot].execute();
undoCommand=offCommands[slot];
}
//添加一个撤销按钮
public void undoButtonWasPushed(){
//撤销
undoCommand.undo();
}
// 打印每个插槽和它对应的命令
@Override
public String toString() {
StringBuffer sbf = new StringBuffer();
sbf.append("\n======================遥控器======================\n");
for (int i = 0; i < onCommands.length; i++) {
sbf.append("[插槽" + i + "]" + onCommands[i].getClass().getName()
+ "\t" + offCommands[i].getClass().getName() + "\n");
}
return sbf.toString();
}
}
4、实现各个命令(7个)
package Implements;
import Entity.Light;
import Interface.Command;
public class LightOffCommand implements Command {
//具体对象变量
Light light;
public LightOffCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.off();
}
@Override
public void undo() {
light.on();
}
}
package Implements;
import Entity.Light;
import Interface.Command;
public class LightOnCommand implements Command {
// 具体对象变量
Light light;
public LightOnCommand(Light light) {
this.light = light;
}
// 执行打开电灯方法
@Override
public void execute() {
light.on();
}
// 撤销操作,关闭电灯
@Override
public void undo() {
light.off();
}
}
package Implements;
import Interface.Command;
public class NoCommand implements Command {
// 这是个无操作类,插槽内的类没有实例化是就走这个对象
@Override
public void execute() {
}
@Override
public void undo() {
// TODO 自动生成的方法存根
}
}
package Implements;
import Entity.Stereo;
import Interface.Command;
public class StereoOffCommand implements Command {
// 具体对象变量
Stereo stereo;
public StereoOffCommand(Stereo stereo) {
this.stereo = stereo;
}
public void execute() {
stereo.off();
}
@Override
public void undo() {
stereo.on();
}
}
package Implements;
import Entity.Stereo;
import Interface.Command;
/**
* 音响的
*
* @author Joy
*
*/
public class StereoOnWithCDCommand implements Command {
// 具体对象变量
Stereo stereo;
public StereoOnWithCDCommand(Stereo stereo) {
this.stereo = stereo;
}
// 具体实现方法(方法再去调用实现方法)
public void execute() {
stereo.on();
stereo.setCD();
stereo.setVolume(11);
}
@Override
public void undo() {
stereo.off();
}
}
package Implements;
import Entity.TV;
import Interface.Command;
public class TVOffCommand implements Command {
TV tv;
public TVOffCommand(TV tv) {
this.tv = tv;
}
@Override
public void execute() {
tv.TVOff();
}
@Override
public void undo() {
tv.TVOn();
}
}
package Implements;
import Entity.TV;
import Interface.Command;
public class TVOnCommand implements Command {
TV tv;
public TVOnCommand(TV tv) {
this.tv = tv;
}
@Override
public void execute() {
tv.TVOn();
tv.setTVChannel(15);
}
@Override
public void undo() {
tv.TVOff();
}
}
5、测试类
package TestMain;
import Control.RemoteControl;
import Entity.Light;
import Entity.Stereo;
import Entity.TV;
import Implements.LightOffCommand;
import Implements.LightOnCommand;
import Implements.StereoOffCommand;
import Implements.StereoOnWithCDCommand;
import Implements.TVOffCommand;
import Implements.TVOnCommand;
public class TestMain {
public static void main(String[] args) {
// 实例化遥控器
RemoteControl remoteControl = new RemoteControl();
// 实例化需要控制对象,并传入房子位置
Stereo stereo = new Stereo("客厅");
Light light = new Light("客厅");
TV tv = new TV("卧室");
// 调用设备开关方法
StereoOnWithCDCommand stereoOnWichCD = new StereoOnWithCDCommand(stereo);
StereoOffCommand stereoOffWithCD = new StereoOffCommand(stereo);
LightOnCommand lightOn = new LightOnCommand(light);
LightOffCommand lightOff = new LightOffCommand(light);
TVOnCommand tvOn = new TVOnCommand(tv);
TVOffCommand tvOff = new TVOffCommand(tv);
// 设置插槽位置(遥控器的哪个按钮对应哪个设备开关)
remoteControl.setCommand(0, lightOn, lightOff);
remoteControl.setCommand(3, stereoOnWichCD, stereoOffWithCD);
remoteControl.setCommand(5, tvOn, tvOff);
// 输出插槽位置
System.out.println(remoteControl);
// 按下开关
remoteControl.onButtonWasPushed(0);
remoteControl.offButtonWasPushed(0);
remoteControl.onButtonWasPushed(3);
remoteControl.offButtonWasPushed(3);
remoteControl.onButtonWasPushed(5);
remoteControl.offButtonWasPushed(5);
}
}
效果图
+1~~~~
在这个实例当中我特意预留了撤销的功能,让我们看看加上撤销功能(undo)的遥控器是怎么运行的把。
新建一个undoCommandTest类
package TestMain;
import Control.RemoteControl;
import Entity.Light;
import Implements.LightOffCommand;
import Implements.LightOnCommand;
public class undoCommandTest {
public static void main(String[] args) {
// 实例化遥控器
RemoteControl remoteControl = new RemoteControl();
// 实例化需要控制对象,并传入房子位置
Light light = new Light("客厅");
// 调用设备开关方法
LightOnCommand lightOn = new LightOnCommand(light);
LightOffCommand lightOff = new LightOffCommand(light);
// 设置插槽位置(遥控器的哪个按钮对应哪个设备开关)
remoteControl.setCommand(0, lightOn, lightOff);
// 按下开关
remoteControl.onButtonWasPushed(0);
remoteControl.offButtonWasPushed(0);
// 输出插槽位置
System.out.println(remoteControl);
// 撤销
System.out.println("按下撤销按钮");
remoteControl.undoButtonWasPushed();
System.out.println("");
remoteControl.offButtonWasPushed(0);
remoteControl.onButtonWasPushed(0);
System.out.println(remoteControl);
System.out.println("按下撤销按钮");
remoteControl.undoButtonWasPushed();
}
}
效果图
看来撤销的功能也OK,我脑中甚至浮现出在JavaWeb里这个撤销的效果了,23333。
感谢你看到这里,命令模式的上部分到这里就结束了,本人文笔随便,若有不足或错误之处望给予指点,90度弯腰~~~很快我会发布命令模式下的内容,生命不息,编程不止!
参考书籍:《Head First 设计模式》
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。