设计模式 ~ 行为型模式 ~ 命令模式 ~ Command Pattern。
what。
日常生活中,我们出去吃饭都会遇到下面的场景。
顾客把订单交给女招待。
↓
女招待拿了订单,然后放在柜台,然后喊一声:“订单来了。”
↓
快餐初始根据订单准备餐点。
将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行存储、传递、调用、增加与管理。
结构。
命令模式包含以下主要角色。
-
抽象命令类(Command)。
定义命令的接口,声明执行的方法。 -
具体命令(Concrete Command)。
具体的命令,实现命令接口;通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。 -
实现者/接收者(Receiver)。
接收者,真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。 -
调用者/请求者(Invoker)。
要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。
eg.。
将上面的案例用代码实现,那我们就需要分析命令模式的角色在该案例中由谁来充当。
服务员:就是调用者角色,由她来发起命令。
资深大厨:就是接收者角色,真正命令执行的对象。
订单:命令中包含订单。
优点。
-
降低系统的耦合度。命令模式能将调用操作的对象与实现该操作的对象解耦。
-
增加或删除命令非常方便。采用命令模式增加与删除命令不会影响其他类,它满足“开闭原则”,对扩展比较灵活。
-
可以实现宏命令。命令模式可以与组合模式结合,将多个命令装配成一个组合命令,即宏命令。
-
方便实现 Undo 和 Redo 操作。命令模式可以与后面介绍的备忘录模式结合,实现命令的撤销与恢复。
缺点。
使用命令模式可能会导致某些系统有过多的具体命令类。
系统结构更加复杂。
使用场景。
-
系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。
-
系统需要在不同的时间指定请求、将请求排队和执行请求。
-
系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作。
JDK 源码解析。
Runable 是一个典型命令模式,Runnable 担当命令的角色,Thread 充当的是调用者,start(); 方法就是其执行方法。
- 抽象命令角色。
/*
* Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package java.lang;
/**
* The <code>Runnable</code> interface should be implemented by any
* class whose instances are intended to be executed by a thread. The
* class must define a method of no arguments called <code>run</code>.
* <p>
* This interface is designed to provide a common protocol for objects that
* wish to execute code while they are active. For example,
* <code>Runnable</code> is implemented by class <code>Thread</code>.
* Being active simply means that a thread has been started and has not
* yet been stopped.
* <p>
* In addition, <code>Runnable</code> provides the means for a class to be
* active while not subclassing <code>Thread</code>. A class that implements
* <code>Runnable</code> can run without subclassing <code>Thread</code>
* by instantiating a <code>Thread</code> instance and passing itself in
* as the target. In most cases, the <code>Runnable</code> interface should
* be used if you are only planning to override the <code>run()</code>
* method and no other <code>Thread</code> methods.
* This is important because classes should not be subclassed
* unless the programmer intends on modifying or enhancing the fundamental
* behavior of the class.
*
* @author Arthur van Hoff
* @see java.lang.Thread
* @see java.util.concurrent.Callable
* @since JDK1.0
*/
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
- 调用者。
public
class Thread implements Runnable {
/* What will be run. */
private Runnable target;
/**
* Causes this thread to begin execution; the Java Virtual Machine
* calls the <code>run</code> method of this thread.
* <p>
* The result is that two threads are running concurrently: the
* current thread (which returns from the call to the
* <code>start</code> method) and the other thread (which executes its
* <code>run</code> method).
* <p>
* It is never legal to start a thread more than once.
* In particular, a thread may not be restarted once it has completed
* execution.
*
* @exception IllegalThreadStateException if the thread was already
* started.
* @see #run()
* @see #stop()
*/
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
private native void start0();
}
会调用一个 native 方法 start0();,调用系统方法,开启一个线程。而接收者是对程序员开放的,可以自己定义接收者。