COMMAND 模式
一个简单的 command 模式实现
public interface IReceiver {
public void action();
}
public class ConcreteReceiver implements IReceiver {
public void action() {
System.out.println("ConcreteReceiver 执行 action");
}
}
public interface ICommand {
public void execute();
}
public class ConcreteCommand implements ICommand {
private IReceiver receiver;
public ConcreteCommand(IReceiver receiver) {
this.receiver = receiver;
}
public void execute() {
receiver.action();
}
}
public class Invoker {
public ICommand command;
public Invoker(ICommand command) {
this.command = command;
}
public void action() {
command.execute();
}
}
public class Client {
public static void main(String[] args) {
IReceiver receiver = new ConcreteReceiver();
ICommand command = new ConcreteCommand(receiver);
Invoker invoker = new Invoker(command);
invoker.action();
}
}
代码解释
- IReceiver: 一个
接收者
接口,action 是一个抽象的执行方法。 - ConcreteReceiver: 一个具体的接收者实现类,实现了 action 方法。
- ICommand: 一个
命令
接口,execute 是一个执行命令的抽象方法。 - ConcreteCommand:一个具体的命令实现类,关联了一个 IReceiver 对象,execute 方法的具体类容是执行 IReceiver 的 action 方法。
- Invoker: 一个
调用者
类,关联了一个 ICommand 对象,action 方法实际上执行的是 ICommand 的 execute 方法。 - Client: 一个
客户端
类。演示了调用者
传递一个命令对象
,命令对象
调用接受者对象
的 action 方法。在这个过程中,命令对象
充当了一个中间人的角色,调用者
和接收者
命令模式的简要说明
在 Client 中演示了调用者
传递一个命令对象
,命令对象
调用接受者对象
的 action 方法。在这个过程中,命令对象
充当了一个中间人的角色,将接收者
的执行方法进行了封装
,调用者
和接收者
是完全解耦的。
命令模式是对命令的封装。命令模式把发出命令的责任和执行命令的责任分割开,委派给不同的对象。
每一个命令都是一个操作:请求的一方发出请求要求执行一个操作;接收的一方收到请求,并执行操作。命令模式允许请求的一方和接收的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否被执行、何时被执行,以及是怎么被执行的。
优点
- 命令模式使新的命令很容易地被加入到系统里。
- 允许接收请求的一方决定是否要否决请求。
- 能较容易地设计一个命令队列。
- 可以容易地实现对请求的撤销和恢复。
- 在需要的情况下,可以较容易地将命令记入日志。
应用场景
- 数据库事务控制
- 设备控制
- 多线程核心
- GUI 的 do/undo 管理
参考
- https://design-patterns.readthedocs.io/zh_CN/latest/behavioral_patterns/command.html
- https://www.cnblogs.com/java-my-life/archive/2012/06/01/2526972.html
- http://www.runoob.com/design-pattern/command-pattern.html
- https://blog.csdn.net/hguisu/article/details/7549895
- https://blog.csdn.net/zhengzhb/article/details/7550895
TEMPLATE METHOD 模式和 STRATEGY 模式
在了解 template method 模式和 strategy 模式之前先看这两个模式的简单实现。
一个 template method 模式的简单实现
public abstract class AbstractDaily {
public abstract void eat();
public abstract void sleep();
public abstract void doSomething();
public void myDaily() {
eat();
doSomething();
sleep();
}
}
public class StudentDaily extends AbstractDaily {
public void eat() {
System.out.println("吃点营养餐补补脑");
}
public void sleep() {
System.out.println("带着梦想睡觉");
}
public void doSomething() {
System.out.println("认真读书");
}
}
public class ProgramerDaily extends AbstractDaily {
public void eat() {
System.out.println("吃着不是那么友好的外卖");
}
public void sleep() {
System.out.println("睡觉的时候想着什么时候身边会有一个她");
}
public void doSomething() {
System.out.println("想要一个她不是很容易么,自己 new 一个");
}
}
public class DailyClient {
public static void main(Stringp[] args) {
Daily studentDaily = new StudentDaily();
studentDaily.doSomething();
Daily programerDaily = new ProgramerDaily();
programerDaily.doSomething();
}
}
一个 strategy 模式的简单实现
public interface IDaily {
public abstract void eat();
public abstract void sleep();
public abstract void doSomething();
}
public class StudentDaily implements IDaily {
public void eat() {
System.out.println("吃点营养餐补补脑");
}
public void sleep() {
System.out.println("带着梦想睡觉");
}
public void doSomething() {
System.out.println("认真读书");
}
}
public class ProgramerDaily implements IDaily {
public void eat() {
System.out.println("吃着不是那么友好的外卖");
}
public void sleep() {
System.out.println("睡觉的时候想着什么时候身边会有一个她");
}
public void doSomething() {
System.out.println("想要一个她不是很容易么,自己 new 一个");
}
}
public class MyDaily {
private IDaily daily;
public MyDaily(IDaily daily) {
this.daily = daily;
}
public void myDaily() {
daily.eat();
daily.doSomething();
daily.sleep();
}
}
public class DailyClient {
public static void main(Stringp[] args) {
MyDaily myDaily = new MyDaily();
myDaily.myDaily();
}
}
结合上面的代码解释两种模式
在 AbstractDaily 和 MyDaily 中都有一个 myDaily
方法,这个 myDaily 方法描述了一个人的日常:eat
(吃)、doSomething
(活动)和sleep
(睡觉)。这个 myDaily 封装了一个人的日常,也就相当于一个通用算法
。template method 模式和 strategy 都能很好的实现 myDaily 这个算法,但实现 myDaily 需要调用的抽象方法[eat()、 sleep()和 doSomething()]
的方式不一样。
- template method 是通过
继承
,让子类去实现 eat()、 sleep()和 doSomething()。 - strategy 将 eat()、 sleep()和 doSomething() 的实现
委托
给了 IDaily 和它的实现类。
小结
template method 模式和 strategy 模式都实现了对高层算法和底层具体实现的分离,都允许高层的算法独立于它的具体实现细节重用。
尽管 template method 模式允许一个通用算法操纵多个可能的具体实现,但是由于 strategy 模式完全遵循 DIP 原则,从而允许每个具体实现都可以被多个不同的通用算法操纵。
strategy 模式虽然允许具体实现细节独立于高层的算法重用,不过要一些额外的复杂性、内存以及运行时间开销作为代价。
参考
- TEMPLATE METHOD 模式
- STRATEGY 模式
FACADE(外观) 模式和 MEDIATOR(中介者) 模式
facade 模式
-
简介
facade 模式定义了一个
高层接口
,这个高层接口封装了子系统的一组接口调用
。客户端无需知道子系统中的复杂接口调用关系,只需要调用高层接口就好了。 -
使用场景
- 为复杂的模块或子系统提供外界访问的模块。
- 子系统相对独立。
- 预防低水平人员带来的风险。
-
优点
- 减少系统相互依赖。
- 提高灵活性。
- 提高了安全性。
-
缺点
- 不符合开闭原则,如果要改东西很麻烦,继承重写都不合适。
-
注意事项
- 在层次化结构中,可以使用外观模式定义系统中每一层的入口。
mediator 模式
-
简介
中介者模式(Mediator Pattern)是用来降低多个对象和类之间的通信复杂性。这种模式提供了一个中介类,该类通常处理不同类之间的通信,并支持松耦合,使代码易于维护。中介者模式属于行为型模式。
-
使用场景
- 系统中对象之间存在比较复杂的引用关系,导致它们之间的依赖关系结构混乱而且难以复用该对象。
- 想通过一个中间类来封装多个类中的行为,而又不想生成太多的子类。
-
优点
- 降低了类的复杂度,将一对多转化成了一对一。
- 各个类之间的解耦。
- 符合迪米特原则。
-
缺点
- 中介者会庞大,变得复杂难以维护。
-
注意事项
- 不应当在职责混乱的时候使用。
参考
-
FACADE 模式
-
MEDIATOR 模式(中介者模式)
SINGLETON 模式和 MONOSTATE 模式
在一些程序中,我们需要一些基础类只能有一个对象,例如:用于创建系统其它对象的工厂类。singleton 模式和 monostate 模式就是能够实现强制对象单一性的两种方式。
- singleton 是通过将构造函数私有化,提供一个 static 方法获取实例(
对象只会被创建一次
)的方式来实现对象的单一性。 - monostate 没有构造函数私有化,你可以创建多个 monostate 对象。但是 monostate 中的
所有属性是 static
的。那么,不论你创建多少个 monostate 对象,它们的属性都是相同的,因此实现了对象的单一性。
一个简单的 singleton 实现
public class SingleTon {
private static SingleTon instance = null;
private SingleTon() {}
public static SingleTon getInstance() {
if (instance == null) {
return new SingleTon();
}
return instance;
}
}
一个简单的 monostate 实现
public class Monostate {
private static String name;
public Monostate() {}
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
singleton 模式的优缺点
-
优点
- 跨平台:使用合适的中间件(例如:RMI),可以把 singleton 模式扩展为多个 JVM 和多个计算机工作。
- 适用于任何类:只需把一个类的构造函数变成私有的,并且在其中增加相应的静态函数和变量,就可以把这个类变为 singleton。
- 可以通过派生创建:给定一个类,可以创建它的一个 singleton 子类。
- 延迟求值:如果 singleton 从未使用过,那么就不会创建它。
-
缺点
- 摧毁方式未定义:没有好的方法去摧毁一个 singleton,或者解除其职责。即使添加一个 decommission 方法把 instance 置为 null,系统中其它模块仍然持有该对象实例的引用。
- 不能继承:从 singleton 类派生出来的类并不是 singleton。如果要使其成为 singleton,必须要增加所需的静态函数和变量。
- 效率问题:每次调用 getInstance 方法都会执行 if 语句,就大多数调用而言,if 语句是多余的。
- 不透明性:singleton 的使用者知道他们在使用一个 singleton,因为他们必须要调用 getInstance 方法。
monostate 模式的优缺点
-
优点
- 透明性:使用 monostate 对象使用常规对象没有什么区别。使用者不需要知道对象是 monostate。
- 可派生性:monostate 的派生类都是 monostate。事实上,monostate 的所有派生类都是同一个 monostate 的一部分。他们共享相同的静态变量。
- 多态性:由于 monostate 的方法不是静态的,所以可以在派生类中复写他们。因此,不同的派生类可以基于同样的静态变量表现出不同的行为。
-
缺点
- 不可转换性:不能透过派生把常规类转换成 monostate 类。
- 效率问题:因为 monostate 是真正的对象,所以会导致许多的创建和摧毁开销。
- 内存占用:即使从未使用 monostate,它的变量也要占内存空间。
- 平台局限性:monostate 不能跨多个 JVM 或者多个平台工作。
如何选择 singleton 与 monostate
如果希望通过派生去约束一个现存类,并且不介意它的所有调用者都必须调用 getInstance 方法来获取访问,那么 singleton 是最合适的。
如果希望类的单一性本质对使用者透明,或者希望使用单一对象的多态派生对象,那么 monostate 是最合适的。
参考
- https://www.cnblogs.com/hibernate6/archive/2012/01/11/2521954.html
- https://blog.csdn.net/nirvana_li/article/details/1449126
NULL OBJECT 模式
为什么需要 null object 模式
Employee e = DB.getEmployeeByName("Peter");
if (e != null) {
System.out.println(e.getName());
}
我们通常从其它方法获取一个对象后,会编写与上面类似的代码,我们需要对获取的对象进行 null 检查,如果不进行检查直接使用的话很可能会出现空指针异常。因为我们没办法保证我们从别处获得对象一定是非空的。
如果代码中到处充斥着 null 检查,代码会非常丑陋,所以就有了 null object 模式。
null object 模式的简单实现
-
先定义一个接口 IEmployee
public interface IEmployee { boolean isNull(); String getName(); }
-
接着定义一个实现 IEmployee 接口的具体类 AlibabaEmloyee
public class AlibabaEmloyee implements IEmployee { @Override public boolean isNull() { return false; } @Override public String getName() { return "name"; } }
-
然后定义一个实现 IEmployee 接口的空类 NullEmloyee
public class NullEmloyee implements IEmployee { @Override public boolean isNull() { return true; } @Override public String getName() { return ""; } }
-
紧接着编写一个 DB 类
public class DB { public static IEmployee getEmployeeByName(String name) { if ("Peter".equals(name)) { return new NullEmployee(); } return new AlibabaEmployee(); } }
-
最后编写一个测试代码
public class Test { public static void main(String[] args) { IEmployee e = DB.getEmployeeByName("Peter"); System.out.println(e.getName()); } }
在测试代码中,我们不再需要对 IEmployee 对象进行 null 检查,NullEmployee 替代了 null。
null object 模式的不足
虽然 null object 模式比直接进行 null 检查要优雅,但需要额外编写大量的辅助类,我门需要为一个类创建一个接口和一个空类,这使得代码非常臃肿。还有其它可以优雅处理 null 的方法,请参考这篇文章。