设计模式实例——基于命令模式的事件处理框架

在面向对象程序设计中,命令模式是一种很常用的模式。

定义:通常情况下,行为的发送者与接收者之间是一种紧耦合,命令模式就是将一组行为封装成对象,实现二者之间的松耦合。

角色定义:

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");
    }
}


 下来为这两个行为定义两个Command,及他们的父类,用来做一些相同的工作 
  

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!");
        }
    }
}

上面主要的代码都做了注释,大家都能看懂它的工作原理。就是做一个队列,然后有一个线程不停的去检测这个队列,如果有命令就执行,反之,等待。当添加一个命令时,则notify检测线程继续工作。

在多线程中,一定要注意线程的同步,否则会出现脏数据,还有就是锁的运用,不然会出现死锁等一些问题,而且这些问题都不容易查出来,所以在多个线程操作同一个数据时一定要加锁。

下来就是客户端对象,客户端是创建命令的地方。

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种设计模式没有谁好谁坏,适合的才是最好的。



你可能感兴趣的:(java设计模式,命令模式,消息队列,面向对象设计,消费者-生产者模式)