设计
设计思路
Java三大特性:封装、继承、多态。当没有系统的了解过设计模式的时候,只知道书本上的定义,尽管背的挺熟,但是对实际应用还是一知半解。经常会提出一些疑问,例如,抽象类和接口不是一样吗?它们之间是可以互相替换的吗?抽象类和普通类有什么区别?如果我定义一个普通类当作抽象类一样作为超类使用,不也是没问题的吗?其实这一切都是在没有设计的范畴之内讨论的,如果有了设计模式,就会对这些问题有一个比较直观的感觉,能做到该用什么类型的时候能正确选择。
封装:减少高层组件对底层组件集合的依赖,对程序提供者(server端)来说,可以避免暴露过多细节;对程序使用者(client)来说,使用更简便,逻辑更清晰,更容易维护。应该有一个程序提供者和程序使用者的概念,(以一个例子阐述,程序提供者和程序使用者。小时候玩的街机有一款赌币的,投币、选择一种水果、拍开始按钮,然后跑马灯开始猛转,最后落在选中的水果上就可以赢得对应的倍数。当发现苹果的命中率过高,需要调整策略时,服务端只需要修改Fruit类型下的Apple的策略就好,赌币机的调用完全不用动,甚至赌币机都不知道已经修改了)写程序应该是从使用者的实际使用方式出发,对需求进行分解,将每一部分封装成独立组件,最后组成一个高层组件供使用者调用,这样如果服务端需要修改部分代码,客户端也不需要知道。但是往往程序员既是提供者也是使用者,写代码的过程中就把两个角色混淆了,导致代码耦合度高,概念不清的现象发生。
继承:明确需要向上转型时才使用,否则尽量使用组合的方式来实现(继承是一种强依赖,组合弹性更好)。
多态:封装和继承都是为了达到多态的目的,多态是一种对象之间关系形态,可以大大降低代码之间的依赖性。几乎所有的设计模式都要用到接口和抽象类,利用java多态的特性,使客户端(程序使用者)和服务端(程序提供者)之间尽量解耦(decouple),使得程序更加灵活更易扩展。
解耦合:解耦就是使两者之间的感知度尽量降低。例如,Person(人)调用eating(吃东西)的情况下,如果写成:
public class Person () {
Pancake pc = null;
public Person (Pancack pc) {
this.pc = pc;
}
public void eating() {
System.out.println("I can eat "+pc.name);
}
}
class Pancack {
public String name;
}
就会造成Person和Pancake强感知,两个类的关系就是:非你不可。如果另一个人(又new了一个Person)想吃个包子(Steamed),就没办法了。因此可以设计成:
public class Person () {
Food fd = null;
public Person (Food fd) {
this.fd = fd;
}
public void eating() {
System.out.println("I can eat "+fd.name);
}
}
class Pancack implements Food {
public String name;
}
class Steamed implements Food {
public String name;
}
public interface Food {
}
利用java的多态,使用Food接口使Person和实际食物进行解耦,此时Person可以看作程序使用者,Food看作程序提供者,Person根本不知道具体要吃的是什么,只是会吃(有eating方法),知道是一种食物(Food接口的实现)即可。
更进一步,如果吃煎饼(Pancake)的时候需要搭配一碗豆浆(Soy),因为有纸袋包装,吃之前就不用洗手了,而吃包子的时候想再来一碗粥(Porridge) ,假设包子没有包装袋,吃之前要先洗手。。。然而这两种吃法又没有明显的模板可套,因此可以考虑把吃实现交由具体的食物实现(ConcreteFood),修改之后的类图:
在这里,把Food接口修改为抽象类 ,并持有一个饮料(Beverage)对象,提供一个抽象的吃法(eatStep)方法交由子类去实现,Person在执行eating时,只需要调用子类重写过的eatStep方法即可,这一次,将Person和食物使用方式的逻辑解耦。
设计原则
- 把易变的部分封装并剥离出来,让它们成为独立的一部分
- 把公共的部分抽取出来,让它们更易用
- 程序是会成长的,不需要它出生时就是完美的
- 如果想解耦,那么就不要努力实现它,尽量从更高的抽象层次思考问题,《thinking in java》中写过:“抽象能力的强弱,影响最终能处理问题复杂程度的高低”
- ==============================================具体操作↓
- 面向超类编程
- 多使用组合,少使用继承,努力让两个交互对象之间低耦合(继承后就是强关联了,耦合度更高)
- 对扩展开放,对修改关闭
- 只能高层组件调用底层组件(Hollywood Law 类之间关系的统一管理)
个人愚见:
如果刚开始学习编程,在连接数据库时一般会把connectString硬编码到程序中。后来发现部署的时候数据库需要是生产环境的而不是开发环境的,这时想到把connectString提取出来当成一个公共变量。部署的时候发现是jar/war包,不能直接修改源代码,又决定再从公共变量提取到一个文本文件中当作配置项关联到项目中。当一套代码需要匹配多个环境时(例如一个数据库连接的class,需要跑在测试环境,准生产环境,生产环境,每个环境都有一套独立的数据库)就需要把配置文件外提了,jar包是jar包配置文件是配置文件,通过启动参数关联上(例如 springboot的启动 ava -jar xxx.jar --spring.config.location=./applaction.properties 或者 docker 命令中的-v 挂载数据或者配置项 再或者 kubernetes中configMap 都是为了把易变的部分抽离出去)。这个演进过程就是设计原则第一条的单方面体现。编码上更多的是用封装和多态来完成。
当代码量越来越大,不可避免的重复代码就越来越多。系统也随之变大,一个完整的系统之下往往有难以计数的子系统提供支持,每个子系统只负责把一件事做好做精,然后依赖某种协议(http、rpc等)再把子系统组合起来。但是无论使用什么样的协议,都会遇到一些共性的问题,例如:服务是否可用的监控、调用超时处理、调用失败异常捕获、如果调用链特别长还需要有熔断机制、如果子系统压力过大又需要启动多个进程通过负载均衡来分担压力,随之带来的副本集的管理工作、如果是面向公网的还要考虑网络安全问题等。以上这个情景就促使SpringCloud生态的诞生和发展。近几年面试基本都会问到的微服务、dubbo、zookeeper、消息中间件(kafka、ActiveMQ、RocketMQ、RabbitMQ)。相对的webservice这种服务就慢慢淡出人们的视线了,但是实际上它只是换了个名字而已,dubbo把RPC规范封装的更好,更易用了。这个演进过程就是第二条原则的一方面体现。还有一些就是前辈们摸索出来的经验和教训了。
这些原则是编程的根本。无论如何发展,编程总是围绕着这些原则在演进。至于为什么这些原则是根本,我认为这些原则本身就是一种抽象层次很高的抽象,而解决复杂问题的能力就取决于抽象层次的高低。抽象层次越高,能理解的方向就越宽泛,越没有固定的方式。就像中国的哲学,个人认为理解的最高境界就是把这些思想运用到生活上,从小就被教育要学以致用,把学到的东西和生活结合到一起,才能发挥其真正的威力。正如在某一领域非常杰出的人才往往都会在自己的专业领域领悟到生活的方式和精神层面的东西。比如截拳道的创始人李小龙,我最佩服的武术家,他之所以要创立截拳道,正是在集百家之长后对自我的一个表达,如果有了解截拳道的人就会知道,这其实不是一门死板功夫,它更灵活,更重视基础和精神层面的东西,最重要的是它是开源的,允许任何人创建自己的招式,和现在的程序员很像。类似的还有永恒的曼巴精神。这都是他们在自己的专业领域不断深入,达到一定境界之后,归纳、总结的抽象层次很高的哲学。
模式
策略模式(Strategy Pattern)
定义一个策略接口,客户端依赖此接口,但是策略是由具体类实现,达到客户端里的策略是灵活可替换的目的,同时也把对象和具体行为进行解耦。
定义一个Duck的抽象类,它和呱呱叫和飞行两个策略(接口)是组合(has-a)的关系。呱呱叫有正常叫和不会叫两种策略,飞行有正常飞行,滑行,不会飞三种策略。
鸳鸯类(MandarinDuck)继承了抽象类Duck,初始化时需要针对飞行和呱呱叫创建两个具体策略(ConcreteStrategy),作为构造参数传入,鸳鸯应该是正常飞和正常叫;同理橡皮鸭(RubberDuck)只需要创建不会飞和不会叫的策略即可。
public interface FlyBehavior {
public void fly();
}
public interface QuackBehavior {
public void quack();
}
public class Flight implememts FlyBehavior {
public void fly() {
System.out.println("像个正常的鸭子在飞");
}
}
public class Slide implememts FlyBehavior {
public void fly() {
System.out.println("天鹅般的滑行");
}
}
public class CanNot implememts FlyBehavior {
public void fly() {
System.out.println("飞不起来");
}
}
public class CommonQuack implements QuackBehavior {
public void quack () {
System.out.println("像个正常的鸭子在嘎嘎叫");
}
}
public class MuteQuack implements QuackBehavior {
public void quack () {
System.out.println("无法出声");
}
}
public abstract class Duck {
protected FlyBehavior fb;
protected QuackBehavior qb;
protected void quack () {
qb.quack();
}
protected void fly() {
fb.fly();
}
}
public class MandarinDuck extends Duck {
public MandarinDuck (FlyBehavior subfb, QuackBehavior subqb) {
fb = subfb;
qb = subqb;
}
}
public class Test {
public static void main(String [] args) {
Duck mandarinDuck = new MandarinDuck(new Flight(), new MuteQuack());
mandarinDuck.fly();
mandarinDuck.quack();
Duck rubberDuck = new RubberDuck(new CanNot(), new CommonQuack());
rubberDuck .fly();
rubberDuck .quack();
}
}
总结一下,就是把易变的部分拆出来(行为接口),然后通过面向接口编程的方式,将易变的父类通过组合的方式融合进来。
观察者模式(Observer Pattern)
使用广泛,例如swing中按钮的事件监听,消息中间件的数据收发,发布订阅,广播等。
必要的几个对象:
被观察者(Observable):例如微信公众号,淘宝店铺的关注。
观察者(Observer):针对微信公众号的是微信用户,针对淘宝店铺的就是店铺粉丝。
核心实现思想是,观察者通过将自身的引用发送给被观察者(订阅),被观察者在有新事件时通知(notify)观察者。
定义被观察者接口,提供了添加观察者、删除观察者、通知三个抽象方法。观察者接口,提供接收的抽象方法。具体被观察者实现被观察者接口,并维护一个观察者的集合(这个集合可能是数组、List、Hash表、Concurrency等),具体的观察者实现观察者接口,并持有一个被观察者对象的引用(为了方便自己取消订阅) 。当被观察者有消息要发送时,只需要循环观察者列表并调用receive方法即可。
值得注意,引用的都是接口,不是具体实现类,这样代码维护起来更容易。
public interface Observable {
public void addObserver();
public void delObserver();
public void notify();
}
public class ConcreteObservable implements Observable {
List<Observer> observerList = new ArrayList<Observer>(1);
public void addObserver(Observer ob) {
this.observerList.add(ob);
}
public void delObserver(Observer ob) {
this.observerList.del(ob);
}
public void notify() {
for (Observer item : observerList ) {
item.receive("要发送的消息", this);
}
}
}
public interface Observer {
public void receive(Observable ob, String msg);
}
public class ConcreteObserver implements Observer {
private Observable ob;
public void receive(Observable ob, String msg){
if (ob == null) {
this.ob = ob;
}
System.out.println("收到了消息:"+msg);
}
public void cancelSubscription() {
ob.delObserver(this);
}
}
public class Test {
public static void main(String [] args) {
Observable ob = new ConcreteObservable();
Observer obs = new ConcreteObserver();
ob.addObserver(obs );
ob.notify();
obs.cancelSubscription();
ob.notify();
}
}
装饰器模式(Decorator Pattern)
用于职能补充。例如日志管理类的输出是普通文本,可以使用一个装饰器(decorator)将日志输出类进行装饰,将输出转化为json格式。java的流装饰:InputStream -- FileInputStream -- BufferdInputStream -- LineNumberInputStream。
有一个组件接口(Component),具体组件由子类ConcreteComponent实现。与其平行的有一个装饰器抽象类(Decorator),它们都是继承自Component,而具体的装饰器由继承自装饰器抽象类,因此具体装饰器也是和组件是相同类型。具体装饰类(ConcreteDecorator)的初始化方法中会传入一个具体组件,这个组件就是我们要装饰的对象,这也就是为什么Decorator类中要有一个Component的引用。
public interface Component {
public String operation1();
public String operation2();
}
public class ConcreteComponent implements Component {
public String operation1() {
return "具体组件的第一个操作";
}
public String operation2() {
return "具体组件的第二个操作";
}
}
public abstract class Decorator implements Component {
public Component c;
// 抽象类可以不实现接口方法,或实现部分方法,这里就都不实现了,由子类实现
}
public class ConcreteDecoratorA extends Decorator {
public ConcreteDecoratorA( Component component) {
super(component);
}
public String operation1() {
String d1o1 = "装饰器A的第一个操作";
return super.operation1() + d1o1;
}
public void operation2() {
String d1o2 = "装饰器A的第二个操作";
return super.operation2() + d1o2;
}
}
public class ConcreteDecoratorB extends Decorator {
public ConcreteDecoratorB( Component component) {
super(component);
}
public String operation1() {
String d2o1 = "装饰器B的第一个操作";
return super.operation1() + d2o1;
}
public void operation2() {
String d2o2 = "装饰器B的第二个操作";
return super.operation2() + d2o2;
}
}
public class Test {
public static void main(String [] args) {
Component component = new ConcreteComponent();
component = new ConcreteDecoratorA(component);
component = new ConcreteDecoratorB(component);
component.operation1();
component.operation2();
}
}
装饰器模式意味着将会有一大堆的装饰器要被创建,使用这个设计模式要明确被装饰者是谁,需要装饰什么样的职能。设计初衷是补充核心功能,像插件一样。
工厂模式(Factory Pattern)
工厂模式致力于降低客户端在获取对象时,和对象的实现耦合度过高的问题。一般使用的是抽象工厂,核心思想是工厂类是抽象的,创建具体对象的方法也是抽象的,具体实现委托给子工厂实现,客户端只持有一个抽象工厂的引用即可。
值得注意的是工厂方法是以解决创建对象为首要目的。
客户端持有一个抽象工厂的引用(Factory),Factory是一个抽象类,有一个抽象方法create,由子类实现。实现了客户端与具体工厂的解耦。
public abstract Factory {
public void giveProduct() {
System.out.println("我要开始创建了,虽然不知道会做出个什么。。。");
Product p = create();
System.out.println(p.getDescription());
System.out.println("做好了,不过因为我是一个空返回的,我就先不给你了。。。");
}
public abstract Product create();
}
public class ConcreteFactoryA extends Factory {
public Product create() {
System.out.println("我创建出来一个A产品");
Product p = new ProductA();
return p;
}
}
public class ConcreteFactoryB extends Factory {
public Product create() {
System.out.println("我创建出来一个B产品");
Product p = new ProductB();
return p;
}
}
public class Client {
public Factory f;
public Client(Factory f) {
this.f = f;
}
public Product getProduct() {
f.giveProduct();
}
}
public class Test {
public static void main(String [] args) {
Client cA = new Client(new ConcreteFactoryA());
cA.getProduct();
Client cB = new Client(new ConcreteFactoryB());
cB.getProduct();
}
}
命令模式(Command Pattern)
广泛应用于,物联网对象之间的交互,日志处理,消息队列等领域。核心思想是把面向业务处理对象的请求,转为面向命令对象的请求,从而降低调用者和业务处理者之间的耦合度。
Invoker是实际调用和调度的对象,维护了一个命令集合,集合可以使用队列、栈、数组、列表、链表、并发集合等,Receiver是实际逻辑处理类的接口,调用者和处理者之间通过Command接口进行解耦合,ConcreteCommand持有Receiver的引用,通过执行execute方法进行调用。
public interface Receiver {
public void logic();
}
public class ConcreteReceiverA implements Receiver {
public void logic() {
System.out.println("A逻辑");
}
}
public class ConcreteReceiverB implements Receiver {
public void logic() {
System.out.println("B逻辑");
}
}
public interface Command {
public void execute();
}
public class ConcreteCommandA implements Command {
Reciever receiver;
public ConcreteCommandA(Reciever receiver) {
this.receiver = receiver;
}
public void execute() {
System.out.println("命令实现类开始干活了。。。");
receiver.logic();
System.out.println("命令实现类完工了。。。");
}
}
public class Invoker {
private List<Command> list;
public void setCommand(Command c) {
list.add(c);
}
public void delCommand(Command c) {
list.remove(c);
}
public void execute() {
// 在这处理command list并调用
}
}
在Invoker中的execute方法是一个扩展性很强的点,可以进行调度、定时、数据处理等很多操作。如果是需要命令有顺序或者线程安全,修改List<Command> list 的类型即可。
适配器模式(Adaptor Pattern)
客户端和服务端进行通信需要传递一个参数,客户端生成的是一个Turkey(火鸡),服务端要求是一个Duck(鸭子),可是他们的基本功能都一样,例如,quack()
这时需要添加一个适配器(Adaptor),来把火鸡伪装成是一个鸭子,给服务端。
public interface Duck {
public void quack();
}
public class Adaptor implements Duck {
public Turkey turkey;
public Adaptor(Turkey t) {
this.turkey = t;
}
public void quack() {
this.turkey.quack();
}
}
public class Server {
public Duck duck;
public Server(Duck duck) {
this.duck = duck;
}
public void quack() {
this.duck.quack();
}
}
public class Test {
public void main(String [] args) {
Duck adaptor = new Adaptor(new Turkey());
Server server = new Server(adaptor);
server.quack();
}
}
适配器模式还有一种多继承的模型:
这种模型的思想就是让适配器既是目标类(target)的子类,又是被适配类型(adaptee)的子类。
外观模式(Facade Pattern)
外观模式是为了把客户端从复杂子系统中解放出来,如果Client需要依赖3个子系统,那么可以把这3个子系统的逻辑调用封装成一个独立的调用,这样可以避免客户端过多的了解系统内部结构,也是解耦合的一种表现。
模板模式(Template Pattern)
对相似对象算法的封装,面向的是算法(也可以理解为逻辑处理流程),需要区分抽象工厂模式,它面向的是对象的创建。
定义一个抽象模板(AbstractTemplate),有一个template方法,里面定义了函数调用以及调用时序,operations为具体操作,部分是抽象的,hook方法是为了控制template方法中部分流程用的。ConcreteTemplate对抽象方法进行实现。
public abstract class AbstractTemplate {
public Object template() {
operation1();
if(hook()) {
operation2();
}
operation3();
}
public void operation1() {
System.out.println("我是第一个操作");
}
public abstract void operation2();
public abstract void operation3();
public abstract boolean hook();
}
public class ConcreteTemplate extends AbstractTemplate {
private boolean hook = false;
public void setHook(boolean hk) {
this.hook = hk;
}
public void operation2() {
System.out.println("我是第二个操作");
}
public void operation3() {
System.out.println("我是第三个操作");
}
public boolean hook() {
return this.hook;
}
}
public class Test {
public static void main(String [] args) {
AbstractTemplate template = new ConcreteTemplate();
template.setHook(true);
template.template();
System.out.println("==============");
template.setHook(false);
template.template();
}
}
模板模式引入了一个很重要的原则:好莱坞原则(Don't call us, we'll call you),核心思想就是组件之间的调用方式,只能是高层组件调用低层组件。以Spring框架为例,对象加载组件(higher)和对象组件(lower)之间的关系,在没有用到对象的时候,加载组件不会调用到对象组件,对象组件也不要主动去调用加载器。
迭代器模式
collections的统一处理方式,面向的对象是集合,单个系统对应不同类型的集合数据,可以做到同一套逻辑处理。
底层是数组时,需要根据index遍历数据,底层是ArrayList时需要调用get方法遍历数据(还可以有Map,table等数据类型),此时封装一个迭代器,使用一个统一的接口Iterator作为父类,接口中只定义了最基本的两个方法,hasNext用来判断是否已经结束,next用来获取下一个元素。具体实现由对应数据类型的子类实现,子类中持有一个当前数据类型的引用。
public Interface Iterator {
public boolean hasNext();
public Object next();
}
public class ArrayIterator implememts Iterator {
private String [] array;
private int currentIndex;
public ArrayIterator(String [] arr) {
this.array = arr;
}
public boolean hasNext() {
if(array == null || array.length == 0 || array.length <= currentIndex) {
return false;
} else {
return true;
}
}
public Object next() {
Object obj = array[currentIndex];
currentIndex++;
return obj;
}
}
public class ListIterator implememts Iterator {
private List<Object> list;
private int currentIndex;
public ArrayIterator( List<Object> list) {
this.list = list;
}
public boolean hasNext() {
if(list == null || list.size() == 0 || list.size() <= currentIndex) {
return false;
} else {
return true;
}
}
public Object next() {
Object obj = list.get(currentIndex);
currentIndex++;
return obj;
}
}
public class Client {
public List<Iterator> its = null;
public Client(String[] arr, List<Object> list) {
its.add(new ArrayIterator(arr));
its.add(new ListIteratorJ(list));
}
public void print() {
for(Iteraotr item : its) {
if(item.hasNext()) {
System.out.println(item.next().toString());
}
}
}
}
public class Test {
public static void main(String [] args) {
Client client = new Client({"Tom", "jerry", "derry"}, new ArrayList(){xxxxxxx});
client.print();
}
}
迭代器模式把集合的判断是否为空和取值的逻辑抽取出来了,这也符合设计原则的第一条 (separate the instability)。另外迭代器模式,特别适合做递归,树、图的遍历。
状态模式
代理模式
复合模式
桥接模式
两个抽象类之间的组合,具体的实现由子类去做,两个分支都是独立的但又能联系到一起。
设计模式一句话总结(摘自知乎)
作者:EnjoyMoving
链接:https://www.zhihu.com/question/22390561/answer/208894758
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
创建型模式
这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用新的运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。
- 工厂模式与抽象工厂模式 (Factory Pattern)(Abstract Factory Pattern):不同条件下创建不同实例
- 单例模式 (Singleton Pattern):保证一个类仅有一个实例
- 建造者模式 (Builder Pattern):将一个复杂的构建过程与其具表示细节相分离,使得同样的构建过程可以创建不同的表示
- 原型模式 (Prototype Pattern):通过拷贝原型创建新的对象
结构型模式
这些设计模式关注类和对象的组合。
- 适配器模式 (Adapter Pattern):使得原本由于接口不兼容而不能一起工作的那些类可以一起工作
- 装饰器模式 (Decorator Pattern):保持接口,增强性能:修饰类继承被修饰对象的抽象父类,依赖被修饰对象的实例(被修饰对象依赖注入),以实现接口扩展
- 桥接模式 (Bridge Pattern):两个维度独立变化,依赖方式实现抽象与实现分离:需要一个作为桥接的接口/抽象类,多个角度的实现类依赖注入到抽象类,使它们在抽象层建立一个关联关系
- 外观模式 (Facade Pattern):在客户端和复杂系统之间再加一层,这一次将调用顺序、依赖关系等处理好。即封装底层实现,隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的高层接口
- 代理模式 (Proxy Pattern):为其他对象提供一种代理以控制对这个对象的访问:增加中间层(代理层),代理类与底层实现类实现共同接口,并创建底层实现类对象(底层实现类对象依赖注入代理类),以便向外界提供功能接口
- 过滤器模式 (Filter、Criteria Pattern):使用不同的标准来过滤一组对象,通过逻辑运算以解耦的方式把它们连接起来
- 组合模式 (Composite Pattern):用户对单个对象和组合对象的使用具有一致性的统一接口
- 享元模式 (Flyweight Pattern):享元工厂类控制;HashMap实现缓冲池重用现有的同类对象,如果未找到匹配的对象,则创建新对象
行为型模式
这些设计模式特别关注对象之间的通信。
- 责任链模式(Chain of Responsibility Pattern):拦截的类都实现统一接口,每个接收者都包含对下一个接收者的引用。将这些对象连接成一条链,并且沿着这条链传递请求,直到有对象处理它为止。
- 观察者模式(Observer Pattern):一对多的依赖关系,在观察目标类里有一个 ArrayList 存放观察者们。当观察目标对象的状态发生改变,所有依赖于它的观察者都将得到通知,使这些观察者能够自动更新(即使用推送方式)
- 模板模式(Template Pattern):将这些通用算法抽象出来,在一个抽象类中公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行
- 命令模式(Command Pattern):将"行为请求者"与"行为实现者"解耦:调用者依赖命令,命令依赖接收者,调用者Invoker→命令Command→接收者Receiver
- 解释器模式(Interpreter Pattern):给定一个语言,定义它的文法表示,并定义一个解释器,这个解释器使用该标识来解释语言中的句子
- 迭代器模式(Iterator Pattern):集合中含有迭代器:分离了集合对象的遍历行为,抽象出一个迭代器类来负责,无须暴露该对象的内部表示
- 中介者模式(Mediator Pattern):对象与对象之间存在大量的关联关系,将对象之间的通信关联关系封装到一个中介类中单独处理,从而使其耦合松散,可以独立地改变它们之间的交互
- 策略模式(Strategy Pattern):策略对象依赖注入到context对象,context对象根据它的策略改变而改变它的相关行为(可通过调用内部的策略对象实现相应的具体策略行为)
- 状态模式(State Pattern):状态对象依赖注入到context对象,context对象根据它的状态改变而改变它的相关行为(可通过调用内部的状态对象实现相应的具体行为)
- 备忘录模式(Memento Pattern):通过一个备忘录类专门存储对象状态。客户通过备忘录管理类管理备忘录类。
- 空对象模式(Null Object Pattern):创建一个未对该类做任何实现的空对象类,该空对象类将无缝地使用在需要检查空值的地方。不要为了屏蔽null而使用空对象,应保持用null,远比用非null的值来替代“无值”要好。(慎用)