设计模式实战命令模式:原理篇
Posted mo_weifeng
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式实战命令模式:原理篇相关的知识,希望对你有一定的参考价值。
前言
小明在公司负责开发音乐应用,用户听音乐的时候可以点击播放、暂停、上一首、下一首,可以循环播放。
小明是这样写的:
/**
* 循环播放的音乐播放器实例
*/
public class PlayerManager
private List<String> mList = new ArrayList();
private int pos = 0;
public PlayerManager()
public int getPos()
return pos;
public List<String> getList()
return mList;
public void setList(List<String> list)
mList = list;
public void start()
System.out.println("开始播放:" + mList.get(pos));
public void stop()
System.out.println("暂停播放:" + mList.get(pos));
public void next()
System.out.println("播放下一首==============================>");
if (mList.size() == (pos + 1))
pos = 0;
else
pos++;
start();
public void pre()
System.out.println("播放上一首==============================>");
if (pos == 0)
pos = mList.size() - 1;
else
pos--;
start();
public void test()
PlayerManager player = new PlayerManager();
List<String> mList = new ArrayList();
mList.add("1.mp3");
mList.add("2.mp3");
mList.add("3.mp3");
player.setList(mList);
player.start();
player.stop();
player.next();
player.next();
player.next();
player.next();
player.pre();
player.pre();
player.pre();
player.pre();
输出结果
开始播放:1.mp3
暂停播放:1.mp3
播放下一首==============================>
开始播放:2.mp3
播放下一首==============================>
开始播放:3.mp3
播放下一首==============================>
开始播放:1.mp3
播放下一首==============================>
开始播放:2.mp3
播放上一首==============================>
开始播放:1.mp3
播放上一首==============================>
开始播放:3.mp3
播放上一首==============================>
开始播放:2.mp3
播放上一首==============================>
开始播放:1.mp3
小明的领导一看,说这样写不行,如果业务逻辑更复杂了,比如增加顺序播放、随机播放、单首循环播放,或者在播放前要判断是否会员,是否购买该歌曲,岂不是要修改一直Player类?Player类越来越臃肿。Player应该只负责播放器的功能,不应该掺杂业务逻辑,不符合单一职责。Player的扩展性也很低,如果要增加新的播放器命令,比如音效、倍速播放、定时关闭等等功能,只能在原来的基础上去增加。
领导建议使用命令模式,播放器负责播放逻辑,用户负责请求,命令负责执行逻辑。
使用命令模式改造
改造时,增加一个业务逻辑:当播放某首没有权限的歌时,自动切换到下一首歌。
播放器只负责播放器的功能,比如播放和暂停。
/**
* 音乐播放器实例
*/
public class Player
public Player()
public void start(String url)
System.out.println("开始播放:" + url);
public void stop(String url )
System.out.println("暂停播放:" + url);
抽象出命令类
public interface Command
/**
* 命令执行方法
*/
void execute();
具体命令
public class StartCommand implements Command
private Player mPlayer;
public StartCommand(Player player)
mPlayer = player;
@Override
public void execute(String url)
mPlayer.start(url);
public class StopCommand implements Command
private Player mPlayer;
public StopCommand(Player player)
mPlayer = player;
@Override
public void execute(String url)
mPlayer.stop(url);
Buttons通过命令对播放器进行控制,写一些业务逻辑
public class Buttons
private List<String> mList = new ArrayList();
private int pos = 0;
private StartCommand mStartCommand;
private StopCommand mStopCommand;
public void setStartCommand(StartCommand startCommand)
mStartCommand = startCommand;
public void setStopCommand(StopCommand stopCommand)
mStopCommand = stopCommand;
public void start()
String url = mList.get(pos);
//假设3.map没有权限播放
if (url.equals("3.mp3"))
System.out.println("没有“3.mp3”的播放权限,将播放下一首歌");
//发送播放下一首歌的指令
next();
return;
mStartCommand.execute(mList.get(pos));
public void stop()
mStopCommand.execute(mList.get(pos));
public void next()
pos = getNextUrlPos();
start();
public void pre()
pos = getPreUrlPos();
start();
public List<String> getList()
return mList;
public void setList(List<String> list)
mList = list;
public int getNextUrlPos()
int next;
if (mList.size() == (pos + 1))
next = 0;
else
next = pos + 1;
return next;
public int getPreUrlPos()
int next;
if (pos == 0)
next = mList.size() - 1;
else
next = pos - 1;
return next;
开始测试
public void test()
Player player = new Player();
StartCommand startCommand = new StartCommand(player);
StopCommand stopCommand = new StopCommand(player);
Buttons buttons = new Buttons();
buttons.setStartCommand(startCommand);
buttons.setStopCommand(stopCommand);
List<String> mList = new ArrayList();
mList.add("1.mp3");
mList.add("2.mp3");
mList.add("3.mp3");
buttons.setList(mList);
buttons.start();
buttons.stop();
buttons.next();
buttons.next();
buttons.next();
buttons.pre();
buttons.pre();
buttons.pre();
输出结果
开始播放:1.mp3
暂停播放:1.mp3
开始播放:2.mp3
没有“3.mp3”的播放权限,将播放下一首歌
开始播放:1.mp3
开始播放:2.mp3
开始播放:1.mp3
没有“3.mp3”的播放权限,将播放下一首歌
开始播放:1.mp3
没有“3.mp3”的播放权限,将播放下一首歌
开始播放:1.mp3
可以看到,播放器类只专注于自己的播放和暂停功能即可。Buttons不直接调用播放器的方法,而是通过具体命令去调用,这样无论播放器怎么改动,只会影响命令,不会影响Buttons。Buttons负责具体的业务逻辑,如果有一天,需要另外做一套有不同逻辑的播放界面,那么只需要再另外创建一个Buttons,再写一套业务逻辑即可。如果不使用命令模式,如果需要改动业务逻辑,岂不是要在Player上修改?Player将逻辑越来越复杂,让人难以理解。
命令模式讲解
看一下命令模式的UML图
Receiver,接收者:执行具体逻辑的底层代码,任何一个类都可以成为一个接收者,封装了具体操作逻辑的方法称为行动方法。
Command,抽象命令:定义所有具体命令类的抽象接口。
ConcreteCommand,具体命令:实现了Command接口,负责执行接收者的方法。
Invoker,请求者:负责调用具体命令
意图:
将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。
主要解决:
在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适。
命令模式进阶
假设我们想一次性执行多个命令呢?
public class Controller
private List<Command> Commands = new ArrayList<>();
public void addCommand(Command Command)
Commands.add(Command);
public void execute(Command Command)
Command.execute();
public void executes()
for (Command Command:
Commands)
Command.execute();
Commands.clear();
// 控制条可以执行单挑命令也可以批量执行多条命令
VideoPlayer player = new VideoPlayer();
Controller controller = new Controller();
controller.execute(new PlayCommand(player));
controller.addCommand(new PauseCommand(player));
controller.addCommand(new PlayCommand(player));
controller.addCommand(new StopCommand(player));
controller.addCommand(new SpeedCommand(player));
controller.executes();
假设我们想对命令做一个撤销功能呢?比如键盘打字后,按“Ctrl+Z”可以返回上一个操作
ICommand命令类
public interface ICommand
void write();
void undo();
WriteCommand具体命令类
public class WriteCommand implements ICommand
public String word;
public Tablet tablet;
public WriteCommand( Tablet tablet,String word)
this.word = word;
this.tablet = tablet;
@Override
public void write()
tablet.write(word);
@Override
public void undo()
tablet.undo();
接收者Tablet ,负责具体的写字和撤销逻辑
/**
* 写字板实例
*/
public class Tablet
private List<String> mList = new ArrayList<>();
public void write(String word)
mList.add(word);
System.out.println("显示:" + mList.toString());
public void undo()
if (mList.size() > 0)
mList.remove(mList.size() - 1);
System.out.println("显示:" + mList.toString());
调用者Invoker类,将执行过的命令的都保存起来,方便撤销
public class Invoker
private List<WriteCommand> mCommands = new ArrayList<>();
public void execute(WriteCommand command)
mCommands.add(command);
command.write();
public void executes(List<WriteCommand> commands)
mCommands.addAll(commands);
for (ICommand command :
commands)
command.write();
public void undo()
if (mCommands.size() > 0)
WriteCommand writeCommand = mCommands.get(mCommands.size() - 1);
writeCommand.undo();
测试
public void test5()
Tablet tablet = new Tablet();
Invoker invoker = new Invoker();
invoker.execute(new WriteCommand设计模式实战命令模式:原理篇