在面向对象程序设计中,命令模式是一种很常用的模式。
定义:通常情况下,行为的发送者与接收者之间是一种紧耦合,命令模式就是将一组行为封装成对象,实现二者之间的松耦合。
角色定义:
Command:命令封装对象,可以在其中存储一些有关行为信息和数据。
Invoker:实际的命令发送者,它相当于一个调度中心。
Receiver:事件的接收者,也就是实际做事的那个对象。
Client:客户端,它创建出命令。
今天我们基于命令模式来做一个简单的事件处理模型。总体思路就是用一个队列去保存这些命令,然后有一个线程去检测这个队列,当这个队列中有命令时,则取出来实现它。
首先,我们新建一个Command的接口
public interface ICommand {
void execute();
}
然后再定义一个Receiver,实际做工作的那个对象。这个类有两个行为,一个是上传文件,一个是获取网络数据。
public class HttpWorker extends Worker{
public void uploadFile() {
System.out.println("HttpWorker->uploadFile");
}
public void getNetworkData() {
System.out.println("HttpWorker->getNetworkData");
}
}
public abstract class HttpCommand implements ICommand{
protected HttpWorker httpWorker;
public HttpCommand() {
super();
httpWorker = new HttpWorker();
}
}
public class UploadFileCommand extends HttpCommand {
@Override
public void execute() {
httpWorker.uploadFile();
}
}
public class GetNetworkDataCommand extends HttpCommand {
@Override
public void execute() {
httpWorker.getNetworkData();
}
}
public class EventWorker {
private Queue commandQueue;
private WorkerThread workerThread;
private Object lock;
public void start() {
lock = new Object();
commandQueue = new LinkedBlockingQueue<>();
workerThread = new WorkerThread();
workerThread.start();
}
public void add(ICommand command) {
synchronized (commandQueue) {
System.out.println("add a command");
commandQueue.add(command);
synchronized (lock) {
lock.notify();
}
}
}
public void remove(ICommand command) {
synchronized (commandQueue) {
commandQueue.remove(command);
}
}
public void stop() {
workerThread.setStart(false);
//在停止的时候,有可能工作线程在等待,所以要notify一下
synchronized (lock) {
lock.notify();
}
commandQueue.clear();
commandQueue = null;
workerThread = null;
lock = null;
}
private class WorkerThread extends Thread {
private boolean isStart = false;
public void setStart(boolean b) {
isStart = b;
}
@Override
public synchronized void start() {
isStart = true;
super.start();
}
//这种写法会出现死锁,注意!!!
// public void run() {
// System.out.println("EventWorkerThread work!");
// while(true) {
// synchronized (commandQueue) {
// if(!isStart) break;
// if(commandQueue.isEmpty()) {
// System.out.println("queue is empty->wait\n\n");
// synchronized (lock) {
// try {
// lock.wait();
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
// } else {
//
// ICommand c = commandQueue.poll();
// if(c != null) {
// c.execute();
// }
// }
// }
// }
// System.out.println("EventWorkerThread stop!");
// }
public void run() {
System.out.println("EventWorkerThread work!");
while(true) {
//如果停止循环则退出
if(!isStart) break;
//线程同步,先获取commandQueue的锁
synchronized (commandQueue) {
//如果命令队列不为空,则取出顶部的消息
if(!commandQueue.isEmpty()) {
ICommand c = commandQueue.poll();
if(c != null) {
c.execute();
}
//继续循环
continue;
}
}
//如果队列为空,是进行等待
synchronized (lock) {
try {
System.out.println("queue is empty->wait\n\n");
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println("EventWorkerThread stop!");
}
}
}
在多线程中,一定要注意线程的同步,否则会出现脏数据,还有就是锁的运用,不然会出现死锁等一些问题,而且这些问题都不容易查出来,所以在多个线程操作同一个数据时一定要加锁。
下来就是客户端对象,客户端是创建命令的地方。
public class Client {
public static void main(String[] args) throws InterruptedException {
EventWorker eventWorker = new EventWorker();
eventWorker.start();
ICommand uploadFileCommand = new UploadFileCommand();
ICommand getNetworkDataCommand = new GetNetworkDataCommand();
Thread.sleep(3000);
for(int i = 0; i < 10; i++) {
if(i % 2 == 0) {
eventWorker.add(uploadFileCommand);
} else {
eventWorker.add(getNetworkDataCommand);
}
Thread.sleep(2000);
}
eventWorker.stop();
}
}
好了,这个框架就算大概完成了,这只是一个很简单的例子,在实际工作中,可能要比这复杂的多。运用模式可以使我们的代码更加高效和可维护,但是也不要滥用模式,23种设计模式没有谁好谁坏,适合的才是最好的。