面试爱考 | 设计模式

news/2024/9/20 2:24:18/ 标签: 面试, 设计模式, java, c++
  • 一、概述
  • 二、创建型
    • 1. 单例(Singleton)
      • Intent
      • Class Diagram
      • Implementation
      • Examples
      • JDK
    • 2. 简单工厂(Simple Factory)
      • Intent
      • Class Diagram
      • Implementation
    • 3. 工厂方法(Factory Method)
      • Intent
      • Class Diagram
      • Implementation
      • JDK
    • 4. 抽象工厂(Abstract Factory)
      • Intent
      • Class Diagram
      • Implementation
      • JDK
    • 5. 生成器(Builder)
      • Intent
      • Class Diagram
      • Implementation
      • JDK
    • 6. 原型模式(Prototype)
      • Intent
      • Class Diagram
      • Implementation
      • JDK
  • 三、行为型
    • 1. 责任链(Chain Of Responsibility)
      • Intent
      • Class Diagram
      • Implementation
      • JDK
    • 2. 命令(Command)
      • Intent
      • Class Diagram
      • Implementation
      • JDK
    • 3. 解释器(Interpreter)
      • Intent
      • Class Diagram
      • Implementation
      • JDK
    • 4. 迭代器(Iterator)
      • Intent
      • Class Diagram
      • Implementation
      • JDK
    • 5. 中介者(Mediator)
      • Intent
      • Class Diagram
      • Implementation
      • JDK
    • 6. 备忘录(Memento)
      • Intent
      • Class Diagram
      • Implementation
      • JDK
    • 7. 观察者(Observer)
      • Intent
      • Class Diagram
      • Implementation
      • JDK
    • 8. 状态(State)
      • Intent
      • Class Diagram
      • Implementation
    • 9. 策略(Strategy)
      • Intent
      • Class Diagram
      • 与状态模式的比较
      • Implementation
      • JDK
    • 10. 模板方法(Template Method)
      • Intent
      • Class Diagram
      • Implementation
      • JDK
    • 11. 访问者(Visitor)
      • Intent
      • Class Diagram
      • Implementation
      • JDK
    • 12. 空对象(Null)
      • Intent
      • Class Diagram
      • Implementation
  • 四、结构型
    • 1. 适配器(Adapter)
      • Intent
      • Class Diagram
      • Implementation
      • JDK
    • 2. 桥接(Bridge)
      • Intent
      • Class Diagram
      • Implementation
      • JDK
    • 3. 组合(Composite)
      • Intent
      • Class Diagram
      • Implementation
      • JDK
    • 4. 装饰(Decorator)
      • Intent
      • Class Diagram
      • Implementation
      • 设计原则
      • JDK
    • 5. 外观(Facade)
      • Intent
      • Class Diagram
      • Implementation
      • 设计原则
    • 6. 享元(Flyweight)
      • Intent
      • Class Diagram
      • Implementation
      • JDK
    • 7. 代理(Proxy)
      • Intent
      • Class Diagram
      • Implementation
      • JDK
  • 参考资料

一、概述

设计模式是解决问题的方案,学习现有的设计模式可以做到经验复用。

拥有设计模式词汇,在沟通时就能用更少的词汇来讨论,并且不需要了解底层细节。

二、创建型

1. 单例(Singleton)

Intent

确保一个类只有一个实例,并提供该实例的全局访问点。

Class Diagram

使用一个私有构造函数、一个私有静态变量以及一个公有静态函数来实现。

私有构造函数保证了不能通过构造函数来创建对象实例,只能通过公有静态函数返回唯一的私有静态变量。


Implementation

Ⅰ 懒汉式-线程不安全

以下实现中,私有静态变量 uniqueInstance 被延迟实例化,这样做的好处是,如果没有用到该类,那么就不会实例化 uniqueInstance,从而节约资源。

这个实现在多线程环境下是不安全的,如果多个线程能够同时进入 if (uniqueInstance == null) ,并且此时 uniqueInstance 为 null,那么会有多个线程执行 uniqueInstance = new Singleton(); 语句,这将导致实例化多次 uniqueInstance。

java">public class Singleton {private static Singleton uniqueInstance;private Singleton() {}public static Singleton getUniqueInstance() {if (uniqueInstance == null) {uniqueInstance = new Singleton();}return uniqueInstance;}
}
Ⅱ 饿汉式-线程安全

线程不安全问题主要是由于 uniqueInstance 被实例化多次,采取直接实例化 uniqueInstance 的方式就不会产生线程不安全问题。

但是直接实例化的方式也丢失了延迟实例化带来的节约资源的好处。

java">private static Singleton uniqueInstance = new Singleton();
Ⅲ 懒汉式-线程安全

只需要对 getUniqueInstance() 方法加锁,那么在一个时间点只能有一个线程能够进入该方法,从而避免了实例化多次 uniqueInstance。

但是当一个线程进入该方法之后,其它试图进入该方法的线程都必须等待,即使 uniqueInstance 已经被实例化了。这会让线程阻塞时间过长,因此该方法有性能问题,不推荐使用。

java">public static synchronized Singleton getUniqueInstance() {if (uniqueInstance == null) {uniqueInstance = new Singleton();}return uniqueInstance;
}
Ⅳ 双重校验锁-线程安全

uniqueInstance 只需要被实例化一次,之后就可以直接使用了。加锁操作只需要对实例化那部分的代码进行,只有当 uniqueInstance 没有被实例化时,才需要进行加锁。

双重校验锁先判断 uniqueInstance 是否已经被实例化,如果没有被实例化,那么才对实例化语句进行加锁。

java">public class Singleton {private volatile static Singleton uniqueInstance;private Singleton() {}public static Singleton getUniqueInstance() {if (uniqueInstance == null) {synchronized (Singleton.class) {if (uniqueInstance == null) {uniqueInstance = new Singleton();}}}return uniqueInstance;}
}

考虑下面的实现,也就是只使用了一个 if 语句。在 uniqueInstance == null 的情况下,如果两个线程都执行了 if 语句,那么两个线程都会进入 if 语句块内。虽然在 if 语句块内有加锁操作,但是两个线程都会执行 uniqueInstance = new Singleton(); 这条语句,只是先后的问题,那么就会进行两次实例化。因此必须使用双重校验锁,也就是需要使用两个 if 语句:第一个 if 语句用来避免 uniqueInstance 已经被实例化之后的加锁操作,而第二个 if 语句进行了加锁,所以只能有一个线程进入,就不会出现 uniqueInstance == null 时两个线程同时进行实例化操作。

java">if (uniqueInstance == null) {synchronized (Singleton.class) {uniqueInstance = new Singleton();}
}

uniqueInstance 采用 volatile 关键字修饰也是很有必要的, uniqueInstance = new Singleton(); 这段代码其实是分为三步执行:

  1. 为 uniqueInstance 分配内存空间
  2. 初始化 uniqueInstance
  3. 将 uniqueInstance 指向分配的内存地址

但是由于 JVM 具有指令重排的特性,执行顺序有可能变成 1>3>2。指令重排在单线程环境下不会出现问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例。例如,线程 T1 执行了 1 和 3,此时 T2 调用 getUniqueInstance() 后发现 uniqueInstance 不为空,因此返回 uniqueInstance,但此时 uniqueInstance 还未被初始化。

使用 volatile 可以禁止 JVM 的指令重排,保证在多线程环境下也能正常运行。

Ⅴ 静态内部类实现

当 Singleton 类被加载时,静态内部类 SingletonHolder 没有被加载进内存。只有当调用 getUniqueInstance() 方法从而触发 SingletonHolder.INSTANCE 时 SingletonHolder 才会被加载,此时初始化 INSTANCE 实例,并且 JVM 能确保 INSTANCE 只被实例化一次。

这种方式不仅具有延迟初始化的好处,而且由 JVM 提供了对线程安全的支持。

java">public class Singleton {private Singleton() {}private static class SingletonHolder {private static final Singleton INSTANCE = new Singleton();}public static Singleton getUniqueInstance() {return SingletonHolder.INSTANCE;}
}
Ⅵ 枚举实现
java">public enum Singleton {INSTANCE;private String objName;public String getObjName() {return objName;}public void setObjName(String objName) {this.objName = objName;}public static void main(String[] args) {// 单例测试Singleton firstSingleton = Singleton.INSTANCE;firstSingleton.setObjName("firstName");System.out.println(firstSingleton.getObjName());Singleton secondSingleton = Singleton.INSTANCE;secondSingleton.setObjName("secondName");System.out.println(firstSingleton.getObjName());System.out.println(secondSingleton.getObjName());// 反射获取实例测试try {Singleton[] enumConstants = Singleton.class.getEnumConstants();for (Singleton enumConstant : enumConstants) {System.out.println(enumConstant.getObjName());}} catch (Exception e) {e.printStackTrace();}}
}
firstName
secondName
secondName
secondName

该实现可以防止反射攻击。在其它实现中,通过 setAccessible() 方法可以将私有构造函数的访问级别设置为 public,然后调用构造函数从而实例化对象,如果要防止这种攻击,需要在构造函数中添加防止多次实例化的代码。该实现是由 JVM 保证只会实例化一次,因此不会出现上述的反射攻击。

该实现在多次序列化和序列化之后,不会得到多个实例。而其它实现需要使用 transient 修饰所有字段,并且实现序列化和反序列化的方法。

Examples

  • Logger Classes
  • Configuration Classes
  • Accesing resources in shared mode
  • Factories implemented as Singletons

JDK

  • java.lang.Runtime#getRuntime()
  • java.awt.Desktop#getDesktop()
  • java.lang.System#getSecurityManager()

2. 简单工厂(Simple Factory)

Intent

在创建一个对象时不向客户暴露内部细节,并提供一个创建对象的通用接口。

Class Diagram

简单工厂把实例化的操作单独放到一个类中,这个类就成为简单工厂类,让简单工厂类来决定应该用哪个具体子类来实例化。

这样做能把客户类和具体子类的实现解耦,客户类不再需要知道有哪些子类以及应当实例化哪个子类。客户类往往有多个,如果不使用简单工厂,那么所有的客户类都要知道所有子类的细节。而且一旦子类发生改变,例如增加子类,那么所有的客户类都要进行修改。


Implementation

java">public interface Product {
}
java">public class ConcreteProduct implements Product {
}
java">public class ConcreteProduct1 implements Product {
}
java">public class ConcreteProduct2 implements Product {
}

以下的 Client 类包含了实例化的代码,这是一种错误的实现。如果在客户类中存在这种实例化代码,就需要考虑将代码放到简单工厂中。

java">public class Client {public static void main(String[] args) {int type = 1;Product product;if (type == 1) {product = new ConcreteProduct1();} else if (type == 2) {product = new ConcreteProduct2();} else {product = new ConcreteProduct();}// do something with the product}
}

以下的 SimpleFactory 是简单工厂实现,它被所有需要进行实例化的客户类调用。

java">public class SimpleFactory {public Product createProduct(int type) {if (type == 1) {return new ConcreteProduct1();} else if (type == 2) {return new ConcreteProduct2();}return new ConcreteProduct();}
}
java">public class Client {public static void main(String[] args) {SimpleFactory simpleFactory = new SimpleFactory();Product product = simpleFactory.createProduct(1);// do something with the product}
}

3. 工厂方法(Factory Method)

Intent

定义了一个创建对象的接口,但由子类决定要实例化哪个类。工厂方法把实例化操作推迟到子类。

Class Diagram

在简单工厂中,创建对象的是另一个类,而在工厂方法中,是由子类来创建对象。

下图中,Factory 有一个 doSomething() 方法,这个方法需要用到一个产品对象,这个产品对象由 factoryMethod() 方法创建。该方法是抽象的,需要由子类去实现。


Implementation

java">public abstract class Factory {abstract public Product factoryMethod();public void doSomething() {Product product = factoryMethod();// do something with the product}
}
java">public class ConcreteFactory extends Factory {public Product factoryMethod() {return new ConcreteProduct();}
}
java">public class ConcreteFactory1 extends Factory {public Product factoryMethod() {return new ConcreteProduct1();}
}
java">public class ConcreteFactory2 extends Factory {public Product factoryMethod() {return new ConcreteProduct2();}
}

JDK

  • java.util.Calendar
  • java.util.ResourceBundle
  • java.text.NumberFormat
  • java.nio.charset.Charset
  • java.net.URLStreamHandlerFactory
  • java.util.EnumSet
  • javax.xml.bind.JAXBContext

4. 抽象工厂(Abstract Factory)

Intent

提供一个接口,用于创建 相关的对象家族

Class Diagram

抽象工厂模式创建的是对象家族,也就是很多对象而不是一个对象,并且这些对象是相关的,也就是说必须一起创建出来。而工厂方法模式只是用于创建一个对象,这和抽象工厂模式有很大不同。

抽象工厂模式用到了工厂方法模式来创建单一对象,AbstractFactory 中的 createProductA() 和 createProductB() 方法都是让子类来实现,这两个方法单独来看就是在创建一个对象,这符合工厂方法模式的定义。

至于创建对象的家族这一概念是在 Client 体现,Client 要通过 AbstractFactory 同时调用两个方法来创建出两个对象,在这里这两个对象就有很大的相关性,Client 需要同时创建出这两个对象。

从高层次来看,抽象工厂使用了组合,即 Cilent 组合了 AbstractFactory,而工厂方法模式使用了继承。


Implementation

java">public class AbstractProductA {
}
java">public class AbstractProductB {
}
java">public class ProductA1 extends AbstractProductA {
}
java">public class ProductA2 extends AbstractProductA {
}
java">public class ProductB1 extends AbstractProductB {
}
java">public class ProductB2 extends AbstractProductB {
}
java">public abstract class AbstractFactory {abstract AbstractProductA createProductA();abstract AbstractProductB createProductB();
}
java">public class ConcreteFactory1 extends AbstractFactory {AbstractProductA createProductA() {return new ProductA1();}AbstractProductB createProductB() {return new ProductB1();}
}
java">public class ConcreteFactory2 extends AbstractFactory {AbstractProductA createProductA() {return new ProductA2();}AbstractProductB createProductB() {return new ProductB2();}
}
java">public class Client {public static void main(String[] args) {AbstractFactory abstractFactory = new ConcreteFactory1();AbstractProductA productA = abstractFactory.createProductA();AbstractProductB productB = abstractFactory.createProductB();// do something with productA and productB}
}

JDK

  • javax.xml.parsers.DocumentBuilderFactory
  • javax.xml.transform.TransformerFactory
  • javax.xml.xpath.XPathFactory

5. 生成器(Builder)

Intent

封装一个对象的构造过程,并允许按步骤构造。

Class Diagram


Implementation

以下是一个简易的 StringBuilder 实现,参考了 JDK 1.8 源码。

java">public class AbstractStringBuilder {protected char[] value;protected int count;public AbstractStringBuilder(int capacity) {count = 0;value = new char[capacity];}public AbstractStringBuilder append(char c) {ensureCapacityInternal(count + 1);value[count++] = c;return this;}private void ensureCapacityInternal(int minimumCapacity) {// overflow-conscious codeif (minimumCapacity - value.length > 0)expandCapacity(minimumCapacity);}void expandCapacity(int minimumCapacity) {int newCapacity = value.length * 2 + 2;if (newCapacity - minimumCapacity < 0)newCapacity = minimumCapacity;if (newCapacity < 0) {if (minimumCapacity < 0) // overflowthrow new OutOfMemoryError();newCapacity = Integer.MAX_VALUE;}value = Arrays.copyOf(value, newCapacity);}
}
java">public class StringBuilder extends AbstractStringBuilder {public StringBuilder() {super(16);}@Overridepublic String toString() {// Create a copy, don't share the arrayreturn new String(value, 0, count);}
}
java">public class Client {public static void main(String[] args) {StringBuilder sb = new StringBuilder();final int count = 26;for (int i = 0; i < count; i++) {sb.append((char) ('a' + i));}System.out.println(sb.toString());}
}
abcdefghijklmnopqrstuvwxyz

JDK

  • java.lang.StringBuilder
  • java.nio.ByteBuffer
  • java.lang.StringBuffer
  • java.lang.Appendable
  • Apache Camel builders

6. 原型模式(Prototype)

Intent

使用原型实例指定要创建对象的类型,通过复制这个原型来创建新对象。

Class Diagram


Implementation

java">public abstract class Prototype {abstract Prototype myClone();
}
java">public class ConcretePrototype extends Prototype {private String filed;public ConcretePrototype(String filed) {this.filed = filed;}@OverridePrototype myClone() {return new ConcretePrototype(filed);}@Overridepublic String toString() {return filed;}
}
java">public class Client {public static void main(String[] args) {Prototype prototype = new ConcretePrototype("abc");Prototype clone = prototype.myClone();System.out.println(clone.toString());}
}
abc

JDK

  • java.lang.Object#clone()

三、行为型

1. 责任链(Chain Of Responsibility)

Intent

使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链发送该请求,直到有一个对象处理它为止。

Class Diagram

  • Handler:定义处理请求的接口,并且实现后继链(successor)

Implementation

java">public abstract class Handler {protected Handler successor;public Handler(Handler successor) {this.successor = successor;}protected abstract void handleRequest(Request request);
}
java">public class ConcreteHandler1 extends Handler {public ConcreteHandler1(Handler successor) {super(successor);}@Overrideprotected void handleRequest(Request request) {if (request.getType() == RequestType.TYPE1) {System.out.println(request.getName() + " is handle by ConcreteHandler1");return;}if (successor != null) {successor.handleRequest(request);}}
}
java">public class ConcreteHandler2 extends Handler {public ConcreteHandler2(Handler successor) {super(successor);}@Overrideprotected void handleRequest(Request request) {if (request.getType() == RequestType.TYPE2) {System.out.println(request.getName() + " is handle by ConcreteHandler2");return;}if (successor != null) {successor.handleRequest(request);}}
}
java">public class Request {private RequestType type;private String name;public Request(RequestType type, String name) {this.type = type;this.name = name;}public RequestType getType() {return type;}public String getName() {return name;}
}
java">public enum RequestType {TYPE1, TYPE2
}
java">public class Client {public static void main(String[] args) {Handler handler1 = new ConcreteHandler1(null);Handler handler2 = new ConcreteHandler2(handler1);Request request1 = new Request(RequestType.TYPE1, "request1");handler2.handleRequest(request1);Request request2 = new Request(RequestType.TYPE2, "request2");handler2.handleRequest(request2);}
}
request1 is handle by ConcreteHandler1
request2 is handle by ConcreteHandler2

JDK

  • java.util.logging.Logger#log()
  • Apache Commons Chain
  • javax.servlet.Filter#doFilter()

2. 命令(Command)

Intent

将命令封装成对象中,具有以下作用:

  • 使用命令来参数化其它对象
  • 将命令放入队列中进行排队
  • 将命令的操作记录到日志中
  • 支持可撤销的操作

Class Diagram

  • Command:命令
  • Receiver:命令接收者,也就是命令真正的执行者
  • Invoker:通过它来调用命令
  • Client:可以设置命令与命令的接收者

Implementation

设计一个遥控器,可以控制电灯开关。


java">public interface Command {void execute();
}
java">public class LightOnCommand implements Command {Light light;public LightOnCommand(Light light) {this.light = light;}@Overridepublic void execute() {light.on();}
}
java">public class LightOffCommand implements Command {Light light;public LightOffCommand(Light light) {this.light = light;}@Overridepublic void execute() {light.off();}
}
java">public class Light {public void on() {System.out.println("Light is on!");}public void off() {System.out.println("Light is off!");}
}
java">/*** 遥控器*/
public class Invoker {private Command[] onCommands;private Command[] offCommands;private final int slotNum = 7;public Invoker() {this.onCommands = new Command[slotNum];this.offCommands = new Command[slotNum];}public void setOnCommand(Command command, int slot) {onCommands[slot] = command;}public void setOffCommand(Command command, int slot) {offCommands[slot] = command;}public void onButtonWasPushed(int slot) {onCommands[slot].execute();}public void offButtonWasPushed(int slot) {offCommands[slot].execute();}
}
java">public class Client {public static void main(String[] args) {Invoker invoker = new Invoker();Light light = new Light();Command lightOnCommand = new LightOnCommand(light);Command lightOffCommand = new LightOffCommand(light);invoker.setOnCommand(lightOnCommand, 0);invoker.setOffCommand(lightOffCommand, 0);invoker.onButtonWasPushed(0);invoker.offButtonWasPushed(0);}
}

JDK

  • java.lang.Runnable
  • Netflix Hystrix
  • javax.swing.Action

3. 解释器(Interpreter)

Intent

为语言创建解释器,通常由语言的语法和语法分析来定义。

Class Diagram

  • TerminalExpression:终结符表达式,每个终结符都需要一个 TerminalExpression。
  • Context:上下文,包含解释器之外的一些全局信息。

Implementation

以下是一个规则检验器实现,具有 and 和 or 规则,通过规则可以构建一颗解析树,用来检验一个文本是否满足解析树定义的规则。

例如一颗解析树为 D And (A Or (B C)),文本 “D A” 满足该解析树定义的规则。

这里的 Context 指的是 String。

java">public abstract class Expression {public abstract boolean interpret(String str);
}
java">public class TerminalExpression extends Expression {private String literal = null;public TerminalExpression(String str) {literal = str;}public boolean interpret(String str) {StringTokenizer st = new StringTokenizer(str);while (st.hasMoreTokens()) {String test = st.nextToken();if (test.equals(literal)) {return true;}}return false;}
}
java">public class AndExpression extends Expression {private Expression expression1 = null;private Expression expression2 = null;public AndExpression(Expression expression1, Expression expression2) {this.expression1 = expression1;this.expression2 = expression2;}public boolean interpret(String str) {return expression1.interpret(str) && expression2.interpret(str);}
}
java">public class OrExpression extends Expression {private Expression expression1 = null;private Expression expression2 = null;public OrExpression(Expression expression1, Expression expression2) {this.expression1 = expression1;this.expression2 = expression2;}public boolean interpret(String str) {return expression1.interpret(str) || expression2.interpret(str);}
}
java">public class Client {/*** 构建解析树*/public static Expression buildInterpreterTree() {// LiteralExpression terminal1 = new TerminalExpression("A");Expression terminal2 = new TerminalExpression("B");Expression terminal3 = new TerminalExpression("C");Expression terminal4 = new TerminalExpression("D");// B CExpression alternation1 = new OrExpression(terminal2, terminal3);// A Or (B C)Expression alternation2 = new OrExpression(terminal1, alternation1);// D And (A Or (B C))return new AndExpression(terminal4, alternation2);}public static void main(String[] args) {Expression define = buildInterpreterTree();String context1 = "D A";String context2 = "A B";System.out.println(define.interpret(context1));System.out.println(define.interpret(context2));}
}
true
false

JDK

  • java.util.Pattern
  • java.text.Normalizer
  • All subclasses of java.text.Format
  • javax.el.ELResolver

4. 迭代器(Iterator)

Intent

提供一种顺序访问聚合对象元素的方法,并且不暴露聚合对象的内部表示。

Class Diagram

  • Aggregate 是聚合类,其中 createIterator() 方法可以产生一个 Iterator;
  • Iterator 主要定义了 hasNext() 和 next() 方法。
  • Client 组合了 Aggregate,为了迭代遍历 Aggregate,也需要组合 Iterator。

Implementation

java">public interface Aggregate {Iterator createIterator();
}
java">public class ConcreteAggregate implements Aggregate {private Integer[] items;public ConcreteAggregate() {items = new Integer[10];for (int i = 0; i < items.length; i++) {items[i] = i;}}@Overridepublic Iterator createIterator() {return new ConcreteIterator<Integer>(items);}
}
java">public interface Iterator<Item> {Item next();boolean hasNext();
}
java">public class ConcreteIterator<Item> implements Iterator {private Item[] items;private int position = 0;public ConcreteIterator(Item[] items) {this.items = items;}@Overridepublic Object next() {return items[position++];}@Overridepublic boolean hasNext() {return position < items.length;}
}
java">public class Client {public static void main(String[] args) {Aggregate aggregate = new ConcreteAggregate();Iterator<Integer> iterator = aggregate.createIterator();while (iterator.hasNext()) {System.out.println(iterator.next());}}
}

JDK

5. 中介者(Mediator)

Intent

集中相关对象之间复杂的沟通和控制方式。

Class Diagram

  • Mediator:中介者,定义一个接口用于与各同事(Colleague)对象通信。
  • Colleague:同事,相关对象

Implementation

Alarm(闹钟)、CoffeePot(咖啡壶)、Calendar(日历)、Sprinkler(喷头)是一组相关的对象,在某个对象的事件产生时需要去操作其它对象,形成了下面这种依赖结构:


使用中介者模式可以将复杂的依赖结构变成星形结构:


java">public abstract class Colleague {public abstract void onEvent(Mediator mediator);
}
java">public class Alarm extends Colleague {@Overridepublic void onEvent(Mediator mediator) {mediator.doEvent("alarm");}public void doAlarm() {System.out.println("doAlarm()");}
}
java">public class CoffeePot extends Colleague {@Overridepublic void onEvent(Mediator mediator) {mediator.doEvent("coffeePot");}public void doCoffeePot() {System.out.println("doCoffeePot()");}
}
java">public class Calender extends Colleague {@Overridepublic void onEvent(Mediator mediator) {mediator.doEvent("calender");}public void doCalender() {System.out.println("doCalender()");}
}
java">public class Sprinkler extends Colleague {@Overridepublic void onEvent(Mediator mediator) {mediator.doEvent("sprinkler");}public void doSprinkler() {System.out.println("doSprinkler()");}
}
java">public abstract class Mediator {public abstract void doEvent(String eventType);
}
java">public class ConcreteMediator extends Mediator {private Alarm alarm;private CoffeePot coffeePot;private Calender calender;private Sprinkler sprinkler;public ConcreteMediator(Alarm alarm, CoffeePot coffeePot, Calender calender, Sprinkler sprinkler) {this.alarm = alarm;this.coffeePot = coffeePot;this.calender = calender;this.sprinkler = sprinkler;}@Overridepublic void doEvent(String eventType) {switch (eventType) {case "alarm":doAlarmEvent();break;case "coffeePot":doCoffeePotEvent();break;case "calender":doCalenderEvent();break;default:doSprinklerEvent();}}public void doAlarmEvent() {alarm.doAlarm();coffeePot.doCoffeePot();calender.doCalender();sprinkler.doSprinkler();}public void doCoffeePotEvent() {// ...}public void doCalenderEvent() {// ...}public void doSprinklerEvent() {// ...}
}
java">public class Client {public static void main(String[] args) {Alarm alarm = new Alarm();CoffeePot coffeePot = new CoffeePot();Calender calender = new Calender();Sprinkler sprinkler = new Sprinkler();Mediator mediator = new ConcreteMediator(alarm, coffeePot, calender, sprinkler);// 闹钟事件到达,调用中介者就可以操作相关对象alarm.onEvent(mediator);}
}
java">doAlarm()
doCoffeePot()
doCalender()
doSprinkler()

JDK

  • All scheduleXXX() methods of java.util.Timer
  • java.util.concurrent.Executor#execute()
  • submit() and invokeXXX() methods of java.util.concurrent.ExecutorService
  • scheduleXXX() methods of java.util.concurrent.ScheduledExecutorService
  • java.lang.reflect.Method#invoke()

6. 备忘录(Memento)

Intent

在不违反封装的情况下获得对象的内部状态,从而在需要时可以将对象恢复到最初状态。

Class Diagram

  • Originator:原始对象
  • Caretaker:负责保存好备忘录
  • Menento:备忘录,存储原始对象的的状态。备忘录实际上有两个接口,一个是提供给 Caretaker 的窄接口:它只能将备忘录传递给其它对象;一个是提供给 Originator 的宽接口,允许它访问到先前状态所需的所有数据。理想情况是只允许 Originator 访问本备忘录的内部状态。

Implementation

以下实现了一个简单计算器程序,可以输入两个值,然后计算这两个值的和。备忘录模式允许将这两个值存储起来,然后在某个时刻用存储的状态进行恢复。

实现参考:Memento Pattern - Calculator Example - Java Sourcecode

java">/*** Originator Interface*/
public interface Calculator {// Create MementoPreviousCalculationToCareTaker backupLastCalculation();// setMementovoid restorePreviousCalculation(PreviousCalculationToCareTaker memento);int getCalculationResult();void setFirstNumber(int firstNumber);void setSecondNumber(int secondNumber);
}
java">/*** Originator Implementation*/
public class CalculatorImp implements Calculator {private int firstNumber;private int secondNumber;@Overridepublic PreviousCalculationToCareTaker backupLastCalculation() {// create a memento object used for restoring two numbersreturn new PreviousCalculationImp(firstNumber, secondNumber);}@Overridepublic void restorePreviousCalculation(PreviousCalculationToCareTaker memento) {this.firstNumber = ((PreviousCalculationToOriginator) memento).getFirstNumber();this.secondNumber = ((PreviousCalculationToOriginator) memento).getSecondNumber();}@Overridepublic int getCalculationResult() {// result is adding two numbersreturn firstNumber + secondNumber;}@Overridepublic void setFirstNumber(int firstNumber) {this.firstNumber = firstNumber;}@Overridepublic void setSecondNumber(int secondNumber) {this.secondNumber = secondNumber;}
}
java">/*** Memento Interface to Originator** This interface allows the originator to restore its state*/
public interface PreviousCalculationToOriginator {int getFirstNumber();int getSecondNumber();
}
java">/***  Memento interface to CalculatorOperator (Caretaker)*/
public interface PreviousCalculationToCareTaker {// no operations permitted for the caretaker
}
java">/*** Memento Object Implementation* <p>* Note that this object implements both interfaces to Originator and CareTaker*/
public class PreviousCalculationImp implements PreviousCalculationToCareTaker,PreviousCalculationToOriginator {private int firstNumber;private int secondNumber;public PreviousCalculationImp(int firstNumber, int secondNumber) {this.firstNumber = firstNumber;this.secondNumber = secondNumber;}@Overridepublic int getFirstNumber() {return firstNumber;}@Overridepublic int getSecondNumber() {return secondNumber;}
}
java">/*** CareTaker object*/
public class Client {public static void main(String[] args) {// program startsCalculator calculator = new CalculatorImp();// assume user enters two numberscalculator.setFirstNumber(10);calculator.setSecondNumber(100);// find resultSystem.out.println(calculator.getCalculationResult());// Store result of this calculation in case of errorPreviousCalculationToCareTaker memento = calculator.backupLastCalculation();// user enters a numbercalculator.setFirstNumber(17);// user enters a wrong second number and calculates resultcalculator.setSecondNumber(-290);// calculate resultSystem.out.println(calculator.getCalculationResult());// user hits CTRL + Z to undo last operation and see last resultcalculator.restorePreviousCalculation(memento);// result restoredSystem.out.println(calculator.getCalculationResult());}
}
110
-273
110

JDK

  • java.io.Serializable

7. 观察者(Observer)

Intent

定义对象之间的一对多依赖,当一个对象状态改变时,它的所有依赖都会收到通知并且自动更新状态。

主题(Subject)是被观察的对象,而其所有依赖者(Observer)称为观察者。


Class Diagram

主题(Subject)具有注册和移除观察者、并通知所有观察者的功能,主题是通过维护一张观察者列表来实现这些操作的。

观察者(Observer)的注册功能需要调用主题的 registerObserver() 方法。


Implementation

天气数据布告板会在天气信息发生改变时更新其内容,布告板有多个,并且在将来会继续增加。


java">public interface Subject {void registerObserver(Observer o);void removeObserver(Observer o);void notifyObserver();
}
java">public class WeatherData implements Subject {private List<Observer> observers;private float temperature;private float humidity;private float pressure;public WeatherData() {observers = new ArrayList<>();}public void setMeasurements(float temperature, float humidity, float pressure) {this.temperature = temperature;this.humidity = humidity;this.pressure = pressure;notifyObserver();}@Overridepublic void registerObserver(Observer o) {observers.add(o);}@Overridepublic void removeObserver(Observer o) {int i = observers.indexOf(o);if (i >= 0) {observers.remove(i);}}@Overridepublic void notifyObserver() {for (Observer o : observers) {o.update(temperature, humidity, pressure);}}
}
java">public interface Observer {void update(float temp, float humidity, float pressure);
}
java">public class StatisticsDisplay implements Observer {public StatisticsDisplay(Subject weatherData) {weatherData.reisterObserver(this);}@Overridepublic void update(float temp, float humidity, float pressure) {System.out.println("StatisticsDisplay.update: " + temp + " " + humidity + " " + pressure);}
}
java">public class CurrentConditionsDisplay implements Observer {public CurrentConditionsDisplay(Subject weatherData) {weatherData.registerObserver(this);}@Overridepublic void update(float temp, float humidity, float pressure) {System.out.println("CurrentConditionsDisplay.update: " + temp + " " + humidity + " " + pressure);}
}
java">public class WeatherStation {public static void main(String[] args) {WeatherData weatherData = new WeatherData();CurrentConditionsDisplay currentConditionsDisplay = new CurrentConditionsDisplay(weatherData);StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);weatherData.setMeasurements(0, 0, 0);weatherData.setMeasurements(1, 1, 1);}
}
CurrentConditionsDisplay.update: 0.0 0.0 0.0
StatisticsDisplay.update: 0.0 0.0 0.0
CurrentConditionsDisplay.update: 1.0 1.0 1.0
StatisticsDisplay.update: 1.0 1.0 1.0

JDK

  • java.util.Observer
  • java.util.EventListener
  • javax.servlet.http.HttpSessionBindingListener
  • RxJava

8. 状态(State)

Intent

允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它所属的类。

Class Diagram


Implementation

糖果销售机有多种状态,每种状态下销售机有不同的行为,状态可以发生转移,使得销售机的行为也发生改变。


java">public interface State {/*** 投入 25 分钱*/void insertQuarter();/*** 退回 25 分钱*/void ejectQuarter();/*** 转动曲柄*/void turnCrank();/*** 发放糖果*/void dispense();
}
java">public class HasQuarterState implements State {private GumballMachine gumballMachine;public HasQuarterState(GumballMachine gumballMachine) {this.gumballMachine = gumballMachine;}@Overridepublic void insertQuarter() {System.out.println("You can't insert another quarter");}@Overridepublic void ejectQuarter() {System.out.println("Quarter returned");gumballMachine.setState(gumballMachine.getNoQuarterState());}@Overridepublic void turnCrank() {System.out.println("You turned...");gumballMachine.setState(gumballMachine.getSoldState());}@Overridepublic void dispense() {System.out.println("No gumball dispensed");}
}
java">public class NoQuarterState implements State {GumballMachine gumballMachine;public NoQuarterState(GumballMachine gumballMachine) {this.gumballMachine = gumballMachine;}@Overridepublic void insertQuarter() {System.out.println("You insert a quarter");gumballMachine.setState(gumballMachine.getHasQuarterState());}@Overridepublic void ejectQuarter() {System.out.println("You haven't insert a quarter");}@Overridepublic void turnCrank() {System.out.println("You turned, but there's no quarter");}@Overridepublic void dispense() {System.out.println("You need to pay first");}
}
java">public class SoldOutState implements State {GumballMachine gumballMachine;public SoldOutState(GumballMachine gumballMachine) {this.gumballMachine = gumballMachine;}@Overridepublic void insertQuarter() {System.out.println("You can't insert a quarter, the machine is sold out");}@Overridepublic void ejectQuarter() {System.out.println("You can't eject, you haven't inserted a quarter yet");}@Overridepublic void turnCrank() {System.out.println("You turned, but there are no gumballs");}@Overridepublic void dispense() {System.out.println("No gumball dispensed");}
}
java">public class SoldState implements State {GumballMachine gumballMachine;public SoldState(GumballMachine gumballMachine) {this.gumballMachine = gumballMachine;}@Overridepublic void insertQuarter() {System.out.println("Please wait, we're already giving you a gumball");}@Overridepublic void ejectQuarter() {System.out.println("Sorry, you already turned the crank");}@Overridepublic void turnCrank() {System.out.println("Turning twice doesn't get you another gumball!");}@Overridepublic void dispense() {gumballMachine.releaseBall();if (gumballMachine.getCount() > 0) {gumballMachine.setState(gumballMachine.getNoQuarterState());} else {System.out.println("Oops, out of gumballs");gumballMachine.setState(gumballMachine.getSoldOutState());}}
}
java">public class GumballMachine {private State soldOutState;private State noQuarterState;private State hasQuarterState;private State soldState;private State state;private int count = 0;public GumballMachine(int numberGumballs) {count = numberGumballs;soldOutState = new SoldOutState(this);noQuarterState = new NoQuarterState(this);hasQuarterState = new HasQuarterState(this);soldState = new SoldState(this);if (numberGumballs > 0) {state = noQuarterState;} else {state = soldOutState;}}public void insertQuarter() {state.insertQuarter();}public void ejectQuarter() {state.ejectQuarter();}public void turnCrank() {state.turnCrank();state.dispense();}public void setState(State state) {this.state = state;}public void releaseBall() {System.out.println("A gumball comes rolling out the slot...");if (count != 0) {count -= 1;}}public State getSoldOutState() {return soldOutState;}public State getNoQuarterState() {return noQuarterState;}public State getHasQuarterState() {return hasQuarterState;}public State getSoldState() {return soldState;}public int getCount() {return count;}
}
java">public class Client {public static void main(String[] args) {GumballMachine gumballMachine = new GumballMachine(5);gumballMachine.insertQuarter();gumballMachine.turnCrank();gumballMachine.insertQuarter();gumballMachine.ejectQuarter();gumballMachine.turnCrank();gumballMachine.insertQuarter();gumballMachine.turnCrank();gumballMachine.insertQuarter();gumballMachine.turnCrank();gumballMachine.ejectQuarter();gumballMachine.insertQuarter();gumballMachine.insertQuarter();gumballMachine.turnCrank();gumballMachine.insertQuarter();gumballMachine.turnCrank();gumballMachine.insertQuarter();gumballMachine.turnCrank();}
}
You insert a quarter
You turned...
A gumball comes rolling out the slot...
You insert a quarter
Quarter returned
You turned, but there's no quarter
You need to pay first
You insert a quarter
You turned...
A gumball comes rolling out the slot...
You insert a quarter
You turned...
A gumball comes rolling out the slot...
You haven't insert a quarter
You insert a quarter
You can't insert another quarter
You turned...
A gumball comes rolling out the slot...
You insert a quarter
You turned...
A gumball comes rolling out the slot...
Oops, out of gumballs
You can't insert a quarter, the machine is sold out
You turned, but there are no gumballs
No gumball dispensed

9. 策略(Strategy)

Intent

定义一系列算法,封装每个算法,并使它们可以互换。

策略模式可以让算法独立于使用它的客户端。

Class Diagram

  • Strategy 接口定义了一个算法族,它们都实现了 behavior() 方法。
  • Context 是使用到该算法族的类,其中的 doSomething() 方法会调用 behavior(),setStrategy(Strategy) 方法可以动态地改变 strategy 对象,也就是说能动态地改变 Context 所使用的算法。

与状态模式的比较

状态模式的类图和策略模式类似,并且都是能够动态改变对象的行为。但是状态模式是通过状态转移来改变 Context 所组合的 State 对象,而策略模式是通过 Context 本身的决策来改变组合的 Strategy 对象。所谓的状态转移,是指 Context 在运行过程中由于一些条件发生改变而使得 State 对象发生改变,注意必须要是在运行过程中。

状态模式主要是用来解决状态转移的问题,当状态发生转移了,那么 Context 对象就会改变它的行为;而策略模式主要是用来封装一组可以互相替代的算法族,并且可以根据需要动态地去替换 Context 使用的算法。

Implementation

设计一个鸭子,它可以动态地改变叫声。这里的算法族是鸭子的叫声行为。

java">public interface QuackBehavior {void quack();
}
java">public class Quack implements QuackBehavior {@Overridepublic void quack() {System.out.println("quack!");}
}
java">public class Squeak implements QuackBehavior{@Overridepublic void quack() {System.out.println("squeak!");}
}
java">public class Duck {private QuackBehavior quackBehavior;public void performQuack() {if (quackBehavior != null) {quackBehavior.quack();}}public void setQuackBehavior(QuackBehavior quackBehavior) {this.quackBehavior = quackBehavior;}
}
java">public class Client {public static void main(String[] args) {Duck duck = new Duck();duck.setQuackBehavior(new Squeak());duck.performQuack();duck.setQuackBehavior(new Quack());duck.performQuack();}
}
squeak!
quack!

JDK

  • java.util.Comparator#compare()
  • javax.servlet.http.HttpServlet
  • javax.servlet.Filter#doFilter()

10. 模板方法(Template Method)

Intent

定义算法框架,并将一些步骤的实现延迟到子类。

通过模板方法,子类可以重新定义算法的某些步骤,而不用改变算法的结构。

Class Diagram


Implementation

冲咖啡和冲茶都有类似的流程,但是某些步骤会有点不一样,要求复用那些相同步骤的代码。


java">public abstract class CaffeineBeverage {final void prepareRecipe() {boilWater();brew();pourInCup();addCondiments();}abstract void brew();abstract void addCondiments();void boilWater() {System.out.println("boilWater");}void pourInCup() {System.out.println("pourInCup");}
}
java">public class Coffee extends CaffeineBeverage {@Overridevoid brew() {System.out.println("Coffee.brew");}@Overridevoid addCondiments() {System.out.println("Coffee.addCondiments");}
}
java">public class Tea extends CaffeineBeverage {@Overridevoid brew() {System.out.println("Tea.brew");}@Overridevoid addCondiments() {System.out.println("Tea.addCondiments");}
}
java">public class Client {public static void main(String[] args) {CaffeineBeverage caffeineBeverage = new Coffee();caffeineBeverage.prepareRecipe();System.out.println("-----------");caffeineBeverage = new Tea();caffeineBeverage.prepareRecipe();}
}
boilWater
Coffee.brew
pourInCup
Coffee.addCondiments
-----------
boilWater
Tea.brew
pourInCup
Tea.addCondiments

JDK

  • java.util.Collections#sort()
  • java.io.InputStream#skip()
  • java.io.InputStream#read()
  • java.util.AbstractList#indexOf()

11. 访问者(Visitor)

Intent

为一个对象结构(比如组合结构)增加新能力。

Class Diagram

  • Visitor:访问者,为每一个 ConcreteElement 声明一个 visit 操作
  • ConcreteVisitor:具体访问者,存储遍历过程中的累计结果
  • ObjectStructure:对象结构,可以是组合结构,或者是一个集合。

Implementation

java">public interface Element {void accept(Visitor visitor);
}
java">class CustomerGroup {private List<Customer> customers = new ArrayList<>();void accept(Visitor visitor) {for (Customer customer : customers) {customer.accept(visitor);}}void addCustomer(Customer customer) {customers.add(customer);}
}
java">public class Customer implements Element {private String name;private List<Order> orders = new ArrayList<>();Customer(String name) {this.name = name;}String getName() {return name;}void addOrder(Order order) {orders.add(order);}public void accept(Visitor visitor) {visitor.visit(this);for (Order order : orders) {order.accept(visitor);}}
}
java">public class Order implements Element {private String name;private List<Item> items = new ArrayList();Order(String name) {this.name = name;}Order(String name, String itemName) {this.name = name;this.addItem(new Item(itemName));}String getName() {return name;}void addItem(Item item) {items.add(item);}public void accept(Visitor visitor) {visitor.visit(this);for (Item item : items) {item.accept(visitor);}}
}
java">public class Item implements Element {private String name;Item(String name) {this.name = name;}String getName() {return name;}public void accept(Visitor visitor) {visitor.visit(this);}
}
java">public interface Visitor {void visit(Customer customer);void visit(Order order);void visit(Item item);
}
java">public class GeneralReport implements Visitor {private int customersNo;private int ordersNo;private int itemsNo;public void visit(Customer customer) {System.out.println(customer.getName());customersNo++;}public void visit(Order order) {System.out.println(order.getName());ordersNo++;}public void visit(Item item) {System.out.println(item.getName());itemsNo++;}public void displayResults() {System.out.println("Number of customers: " + customersNo);System.out.println("Number of orders:    " + ordersNo);System.out.println("Number of items:     " + itemsNo);}
}
java">public class Client {public static void main(String[] args) {Customer customer1 = new Customer("customer1");customer1.addOrder(new Order("order1", "item1"));customer1.addOrder(new Order("order2", "item1"));customer1.addOrder(new Order("order3", "item1"));Order order = new Order("order_a");order.addItem(new Item("item_a1"));order.addItem(new Item("item_a2"));order.addItem(new Item("item_a3"));Customer customer2 = new Customer("customer2");customer2.addOrder(order);CustomerGroup customers = new CustomerGroup();customers.addCustomer(customer1);customers.addCustomer(customer2);GeneralReport visitor = new GeneralReport();customers.accept(visitor);visitor.displayResults();}
}
customer1
order1
item1
order2
item1
order3
item1
customer2
order_a
item_a1
item_a2
item_a3
Number of customers: 2
Number of orders:    4
Number of items:     6

JDK

  • javax.lang.model.element.Element and javax.lang.model.element.ElementVisitor
  • javax.lang.model.type.TypeMirror and javax.lang.model.type.TypeVisitor

12. 空对象(Null)

Intent

使用什么都不做的空对象来代替 NULL。

一个方法返回 NULL,意味着方法的调用端需要去检查返回值是否是 NULL,这么做会导致非常多的冗余的检查代码。并且如果某一个调用端忘记了做这个检查返回值,而直接使用返回的对象,那么就有可能抛出空指针异常。

Class Diagram


Implementation

java">public abstract class AbstractOperation {abstract void request();
}
java">public class RealOperation extends AbstractOperation {@Overridevoid request() {System.out.println("do something");}
}
java">public class NullOperation extends AbstractOperation{@Overridevoid request() {// do nothing}
}
java">public class Client {public static void main(String[] args) {AbstractOperation abstractOperation = func(-1);abstractOperation.request();}public static AbstractOperation func(int para) {if (para < 0) {return new NullOperation();}return new RealOperation();}
}

四、结构型

1. 适配器(Adapter)

Intent

把一个类接口转换成另一个用户需要的接口。


Class Diagram


Implementation

鸭子(Duck)和火鸡(Turkey)拥有不同的叫声,Duck 的叫声调用 quack() 方法,而 Turkey 调用 gobble() 方法。

要求将 Turkey 的 gobble() 方法适配成 Duck 的 quack() 方法,从而让火鸡冒充鸭子!

java">public interface Duck {void quack();
}
java">public interface Turkey {void gobble();
}
java">public class WildTurkey implements Turkey {@Overridepublic void gobble() {System.out.println("gobble!");}
}
java">public class TurkeyAdapter implements Duck {Turkey turkey;public TurkeyAdapter(Turkey turkey) {this.turkey = turkey;}@Overridepublic void quack() {turkey.gobble();}
}
java">public class Client {public static void main(String[] args) {Turkey turkey = new WildTurkey();Duck duck = new TurkeyAdapter(turkey);duck.quack();}
}

JDK

  • java.util.Arrays#asList()
  • java.util.Collections#list()
  • java.util.Collections#enumeration()
  • javax.xml.bind.annotation.adapters.XMLAdapter

2. 桥接(Bridge)

Intent

将抽象与实现分离开来,使它们可以独立变化。

Class Diagram

  • Abstraction:定义抽象类的接口
  • Implementor:定义实现类接口

Implementation

RemoteControl 表示遥控器,指代 Abstraction。

TV 表示电视,指代 Implementor。

桥接模式将遥控器和电视分离开来,从而可以独立改变遥控器或者电视的实现。

java">public abstract class TV {public abstract void on();public abstract void off();public abstract void tuneChannel();
}
java">public class Sony extends TV {@Overridepublic void on() {System.out.println("Sony.on()");}@Overridepublic void off() {System.out.println("Sony.off()");}@Overridepublic void tuneChannel() {System.out.println("Sony.tuneChannel()");}
}
java">public class RCA extends TV {@Overridepublic void on() {System.out.println("RCA.on()");}@Overridepublic void off() {System.out.println("RCA.off()");}@Overridepublic void tuneChannel() {System.out.println("RCA.tuneChannel()");}
}
java">public abstract class RemoteControl {protected TV tv;public RemoteControl(TV tv) {this.tv = tv;}public abstract void on();public abstract void off();public abstract void tuneChannel();
}
java">public class ConcreteRemoteControl1 extends RemoteControl {public ConcreteRemoteControl1(TV tv) {super(tv);}@Overridepublic void on() {System.out.println("ConcreteRemoteControl1.on()");tv.on();}@Overridepublic void off() {System.out.println("ConcreteRemoteControl1.off()");tv.off();}@Overridepublic void tuneChannel() {System.out.println("ConcreteRemoteControl1.tuneChannel()");tv.tuneChannel();}
}
java">public class ConcreteRemoteControl2 extends RemoteControl {public ConcreteRemoteControl2(TV tv) {super(tv);}@Overridepublic void on() {System.out.println("ConcreteRemoteControl2.on()");tv.on();}@Overridepublic void off() {System.out.println("ConcreteRemoteControl2.off()");tv.off();}@Overridepublic void tuneChannel() {System.out.println("ConcreteRemoteControl2.tuneChannel()");tv.tuneChannel();}
}
java">public class Client {public static void main(String[] args) {RemoteControl remoteControl1 = new ConcreteRemoteControl1(new RCA());remoteControl1.on();remoteControl1.off();remoteControl1.tuneChannel();RemoteControl remoteControl2 = new ConcreteRemoteControl2(new Sony());remoteControl2.on();remoteControl2.off();remoteControl2.tuneChannel();}
}

JDK

  • AWT (It provides an abstraction layer which maps onto the native OS the windowing support.)
  • JDBC

3. 组合(Composite)

Intent

将对象组合成树形结构来表示“整体/部分”层次关系,允许用户以相同的方式处理单独对象和组合对象。

Class Diagram

组件(Component)类是组合类(Composite)和叶子类(Leaf)的父类,可以把组合类看成是树的中间节点。

组合对象拥有一个或者多个组件对象,因此组合对象的操作可以委托给组件对象去处理,而组件对象可以是另一个组合对象或者叶子对象。


Implementation

java">public abstract class Component {protected String name;public Component(String name) {this.name = name;}public void print() {print(0);}abstract void print(int level);abstract public void add(Component component);abstract public void remove(Component component);
}
java">public class Composite extends Component {private List<Component> child;public Composite(String name) {super(name);child = new ArrayList<>();}@Overridevoid print(int level) {for (int i = 0; i < level; i++) {System.out.print("--");}System.out.println("Composite:" + name);for (Component component : child) {component.print(level + 1);}}@Overridepublic void add(Component component) {child.add(component);}@Overridepublic void remove(Component component) {child.remove(component);}
}
java">public class Leaf extends Component {public Leaf(String name) {super(name);}@Overridevoid print(int level) {for (int i = 0; i < level; i++) {System.out.print("--");}System.out.println("left:" + name);}@Overridepublic void add(Component component) {throw new UnsupportedOperationException(); // 牺牲透明性换取单一职责原则,这样就不用考虑是叶子节点还是组合节点}@Overridepublic void remove(Component component) {throw new UnsupportedOperationException();}
}
java">public class Client {public static void main(String[] args) {Composite root = new Composite("root");Component node1 = new Leaf("1");Component node2 = new Composite("2");Component node3 = new Leaf("3");root.add(node1);root.add(node2);root.add(node3);Component node21 = new Leaf("21");Component node22 = new Composite("22");node2.add(node21);node2.add(node22);Component node221 = new Leaf("221");node22.add(node221);root.print();}
}
Composite:root
--left:1
--Composite:2
----left:21
----Composite:22
------left:221
--left:3

JDK

  • javax.swing.JComponent#add(Component)
  • java.awt.Container#add(Component)
  • java.util.Map#putAll(Map)
  • java.util.List#addAll(Collection)
  • java.util.Set#addAll(Collection)

4. 装饰(Decorator)

Intent

为对象动态添加功能。

Class Diagram

装饰者(Decorator)和具体组件(ConcreteComponent)都继承自组件(Component),具体组件的方法实现不需要依赖于其它对象,而装饰者组合了一个组件,这样它可以装饰其它装饰者或者具体组件。所谓装饰,就是把这个装饰者套在被装饰者之上,从而动态扩展被装饰者的功能。装饰者的方法有一部分是自己的,这属于它的功能,然后调用被装饰者的方法实现,从而也保留了被装饰者的功能。可以看到,具体组件应当是装饰层次的最低层,因为只有具体组件的方法实现不需要依赖于其它对象。


Implementation

设计不同种类的饮料,饮料可以添加配料,比如可以添加牛奶,并且支持动态添加新配料。每增加一种配料,该饮料的价格就会增加,要求计算一种饮料的价格。

下图表示在 DarkRoast 饮料上新增新添加 Mocha 配料,之后又添加了 Whip 配料。DarkRoast 被 Mocha 包裹,Mocha 又被 Whip 包裹。它们都继承自相同父类,都有 cost() 方法,外层类的 cost() 方法调用了内层类的 cost() 方法。


java">public interface Beverage {double cost();
}
java">public class DarkRoast implements Beverage {@Overridepublic double cost() {return 1;}
}
java">public class HouseBlend implements Beverage {@Overridepublic double cost() {return 1;}
}
java">public abstract class CondimentDecorator implements Beverage {protected Beverage beverage;
}
java">public class Milk extends CondimentDecorator {public Milk(Beverage beverage) {this.beverage = beverage;}@Overridepublic double cost() {return 1 + beverage.cost();}
}
java">public class Mocha extends CondimentDecorator {public Mocha(Beverage beverage) {this.beverage = beverage;}@Overridepublic double cost() {return 1 + beverage.cost();}
}
java">public class Client {public static void main(String[] args) {Beverage beverage = new HouseBlend();beverage = new Mocha(beverage);beverage = new Milk(beverage);System.out.println(beverage.cost());}
}
3.0

设计原则

类应该对扩展开放,对修改关闭:也就是添加新功能时不需要修改代码。饮料可以动态添加新的配料,而不需要去修改饮料的代码。

不可能把所有的类设计成都满足这一原则,应当把该原则应用于最有可能发生改变的地方。

JDK

  • java.io.BufferedInputStream(InputStream)
  • java.io.DataInputStream(InputStream)
  • java.io.BufferedOutputStream(OutputStream)
  • java.util.zip.ZipOutputStream(OutputStream)
  • java.util.Collections#checkedList|Map|Set|SortedSet|SortedMap

5. 外观(Facade)

Intent

提供了一个统一的接口,用来访问子系统中的一群接口,从而让子系统更容易使用。

Class Diagram


Implementation

观看电影需要操作很多电器,使用外观模式实现一键看电影功能。

java">public class SubSystem {public void turnOnTV() {System.out.println("turnOnTV()");}public void setCD(String cd) {System.out.println("setCD( " + cd + " )");}public void startWatching(){System.out.println("startWatching()");}
}
java">public class Facade {private SubSystem subSystem = new SubSystem();public void watchMovie() {subSystem.turnOnTV();subSystem.setCD("a movie");subSystem.startWatching();}
}
java">public class Client {public static void main(String[] args) {Facade facade = new Facade();facade.watchMovie();}
}

设计原则

最少知识原则:只和你的密友谈话。也就是说客户对象所需要交互的对象应当尽可能少。

6. 享元(Flyweight)

Intent

利用共享的方式来支持大量细粒度的对象,这些对象一部分内部状态是相同的。

Class Diagram

  • Flyweight:享元对象
  • IntrinsicState:内部状态,享元对象共享内部状态
  • ExtrinsicState:外部状态,每个享元对象的外部状态不同

Implementation

java">public interface Flyweight {void doOperation(String extrinsicState);
}
java">public class ConcreteFlyweight implements Flyweight {private String intrinsicState;public ConcreteFlyweight(String intrinsicState) {this.intrinsicState = intrinsicState;}@Overridepublic void doOperation(String extrinsicState) {System.out.println("Object address: " + System.identityHashCode(this));System.out.println("IntrinsicState: " + intrinsicState);System.out.println("ExtrinsicState: " + extrinsicState);}
}
java">public class FlyweightFactory {private HashMap<String, Flyweight> flyweights = new HashMap<>();Flyweight getFlyweight(String intrinsicState) {if (!flyweights.containsKey(intrinsicState)) {Flyweight flyweight = new ConcreteFlyweight(intrinsicState);flyweights.put(intrinsicState, flyweight);}return flyweights.get(intrinsicState);}
}
java">public class Client {public static void main(String[] args) {FlyweightFactory factory = new FlyweightFactory();Flyweight flyweight1 = factory.getFlyweight("aa");Flyweight flyweight2 = factory.getFlyweight("aa");flyweight1.doOperation("x");flyweight2.doOperation("y");}
}
Object address: 1163157884
IntrinsicState: aa
ExtrinsicState: x
Object address: 1163157884
IntrinsicState: aa
ExtrinsicState: y

JDK

Java 利用缓存来加速大量小对象的访问时间。

  • java.lang.Integer#valueOf(int)
  • java.lang.Boolean#valueOf(boolean)
  • java.lang.Byte#valueOf(byte)
  • java.lang.Character#valueOf(char)

7. 代理(Proxy)

Intent

控制对其它对象的访问。

Class Diagram

代理有以下四类:

  • 远程代理(Remote Proxy):控制对远程对象(不同地址空间)的访问,它负责将请求及其参数进行编码,并向不同地址空间中的对象发送已经编码的请求。
  • 虚拟代理(Virtual Proxy):根据需要创建开销很大的对象,它可以缓存实体的附加信息,以便延迟对它的访问,例如在网站加载一个很大图片时,不能马上完成,可以用虚拟代理缓存图片的大小信息,然后生成一张临时图片代替原始图片。
  • 保护代理(Protection Proxy):按权限控制对象的访问,它负责检查调用者是否具有实现一个请求所必须的访问权限。
  • 智能代理(Smart Reference):取代了简单的指针,它在访问对象时执行一些附加操作:记录对象的引用次数;当第一次引用一个对象时,将它装入内存;在访问一个实际对象前,检查是否已经锁定了它,以确保其它对象不能改变它。

Implementation

以下是一个虚拟代理的实现,模拟了图片延迟加载的情况下使用与图片大小相等的临时内容去替换原始图片,直到图片加载完成才将图片显示出来。

java">public interface Image {void showImage();
}
java">public class HighResolutionImage implements Image {private URL imageURL;private long startTime;private int height;private int width;public int getHeight() {return height;}public int getWidth() {return width;}public HighResolutionImage(URL imageURL) {this.imageURL = imageURL;this.startTime = System.currentTimeMillis();this.width = 600;this.height = 600;}public boolean isLoad() {// 模拟图片加载,延迟 3s 加载完成long endTime = System.currentTimeMillis();return endTime - startTime > 3000;}@Overridepublic void showImage() {System.out.println("Real Image: " + imageURL);}
}
java">public class ImageProxy implements Image {private HighResolutionImage highResolutionImage;public ImageProxy(HighResolutionImage highResolutionImage) {this.highResolutionImage = highResolutionImage;}@Overridepublic void showImage() {while (!highResolutionImage.isLoad()) {try {System.out.println("Temp Image: " + highResolutionImage.getWidth() + " " + highResolutionImage.getHeight());Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}highResolutionImage.showImage();}
}
java">public class ImageViewer {public static void main(String[] args) throws Exception {String image = "http://image.jpg";URL url = new URL(image);HighResolutionImage highResolutionImage = new HighResolutionImage(url);ImageProxy imageProxy = new ImageProxy(highResolutionImage);imageProxy.showImage();}
}

JDK

  • java.lang.reflect.Proxy
  • RMI

参考资料

  • 弗里曼. Head First 设计模式 [M]. 中国电力出版社, 2007.
  • Gamma E. 设计模式: 可复用面向对象软件的基础 [M]. 机械工业出版社, 2007.
  • Bloch J. Effective java[M]. Addison-Wesley Professional, 2017.
  • Design Patterns
  • Design patterns implemented in Java
  • The breakdown of design patterns in JDK

http://www.ppmy.cn/news/1527844.html

相关文章

Halo 开发者指南——项目运行、构建

准备工作 环境要求 OpenJDK 17 LTSNode.js 20 LTSpnpm 9IntelliJ IDEAGitDocker&#xff08;可选&#xff09; 名词解释 工作目录 指 Halo 所依赖的工作目录&#xff0c;在 Halo 运行的时候会在系统当前用户目录下产生一个 halo-next 的文件夹&#xff0c;绝对路径为 ~/ha…

钻机、塔吊等大型工程设备,如何远程维护、实时采集运行数据?

在建筑和工程领域&#xff0c;重型设备的应用不可或缺&#xff0c;无论是在道路与桥梁建设、高层建筑施工&#xff0c;还是在风电、石油等能源项目的开发中&#xff0c;都会用到塔吊、钻机等大型机械工程设备。 随着数字化升级、工业4.0成为行业发展趋势&#xff0c;为了进一步…

MacOS安装homebrew,jEnv,多版本JDK

1 安装homebrew homebrew官网 根据官网提示&#xff0c;运行安装命令 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"安装后&#xff0c;bash会提示执行两条命令 (echo; echo eval "$(/opt/homebrew/b…

海外问卷调查是做什么的,适合什么人做

疫情结束后的市场大环境萧条&#xff0c;特别是实体经济&#xff0c;许多中小微企业要么倒闭&#xff0c;要么垂死挣扎&#xff0c;等待重生。于是有一些老板&#xff0c;就转型做起了互联网生意。 抖音这几年的直播带货、短视频带货等等飞速崛起&#xff0c;成就了多少百万富…

如何用站群服务器做抢购秒杀平台

随着各种电商购物节的开幕&#xff0c;全球外贸、直播电商抢购活动愈发火热&#xff0c;外贸行业容纳了海量的公司、组织和个人。为了营销&#xff0c;人们使用海外站群服务器抢货的做法已经不再稀奇&#xff0c;因为使用海外站群服务器操作抢购秒杀商品&#xff0c;可以拥有多…

爬坑--docker构建容器ssh连接容器环境变量会发生变化

问题 通过 Dockerfile 语句在镜像中安装了 openssh, 但是在创建容器之后, 通过 docker exec -it 容器 bash 和通过远程 ssh, 会发现两边终端的 PATH 不一致, ssh 连接明显缺少一些东西. 解决方案 在 Dockerfile 最后添加: RUN echo "export PATH${PATH}" >>…

适合博客的组件库

在选择适合博客的组件库时&#xff0c;需要考虑博客的主题、内容类型以及预期的用户体验。以下是一些推荐的组件库&#xff0c;它们各自具有独特的特点和优势&#xff0c;能够帮助你提升博客的视觉效果和用户体验&#xff1a; React Markdown&#xff1a;非常适合技术博客和教…

图新地球-将地图上大量的地标点批量输出坐标到csv文件【kml转excel】

0.序 有很多用户需要在卫星影像、或者无人机航测影像、倾斜模型上去标记一些地物的位置&#xff08;如电线杆塔、重点单位、下水盖等&#xff09; 标记的位置最终又需要提交坐标文本文件给上级单位或者其他部门使用&#xff0c;甚至需要转为平面直角坐标。 本文的重点是通过of…

福利:kafka--生产者消费者

kafka是一个分布式流媒体平台&#xff0c;类似于消息队列或企业消息传递系统 案例一&#xff1a;生产者--消费者 1.导入依赖 <dependency><groupId>org.apache.kafka</groupId><artifactId>kafka-clients</artifactId></dependency> 2…

小琳AI课堂:强化学习初阶

大家好&#xff0c;这里是小琳AI课堂。今天我们来聊聊强化学习&#xff0c;一种让机器通过“实践”学习的方法。&#x1f916; 强化学习&#xff0c;听起来就像是给机器装上了成长的心智。想象一下&#xff0c;有个小机器人在迷宫里探险&#xff0c;它要找到出口。每次尝试走一…

6年前倒闭的机器人独角兽,再次杀入AGV市场

导语 大家好&#xff0c;我是社长&#xff0c;老K。专注分享智能制造和智能仓储物流等内容。 新书《智能物流系统构成与技术实践》 在科技创新的浪潮中&#xff0c;一个曾经辉煌又迅速陨落的企业正悄然重生&#xff0c;引发业界广泛关注。 曾经的协作机器人鼻祖Rethink Robotic…

专业学习|GERT网络概览(学习资源、原理介绍、变体介绍)

一、GERT 网络概览 GERT(Graphical Evaluation Review Technique&#xff0c;图示评审技术)是一种结合流线图理论(Flow Graphical Theory)、矩母函数(Moment Generating Function)、计划评审技术(Program Evaluation Review Technique)解决随机网络问题的方法&#xff0c;描述各…

leetcode41. 缺失的第一个正数,原地哈希表

leetcode41. 缺失的第一个正数 给你一个未排序的整数数组 nums &#xff0c;请你找出其中没有出现的最小的正整数。 请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,0] 输出&#xff1a;3 解释&#xf…

【高等数学学习记录】数列的极限

【高等数学&学习记录】数列的极限 从事测绘工作多年&#xff0c;深刻感受到基础知识的重要及自身在这方面的短板。 为此&#xff0c;打算重温测绘工作所需基础知识。练好基本功&#xff0c;为测绘工作赋能。 1 知识点 1.1 数列极限的定义 设 { x n } \lbrace x_n \rbrace…

将有序数组——>二叉搜索树

给你一个整数数组 nums &#xff0c;其中元素已经按 升序 排列&#xff0c;请你将其转换为一棵平衡二叉搜索树。 示例 1&#xff1a; 输入&#xff1a;nums [-10,-3,0,5,9] 输出&#xff1a;[0,-3,9,-10,null,5] 解释&#xff1a;[0,-10,5,null,-3,null,9] 也将被视为正确答案…

光芯片版图绘制软件测评

光芯片版图绘制软件测评 正文KLayout优点缺点IPKISS优点缺点PIC Studio优点缺点GDSFactory优点缺点正文 KLayout KLayout 是光芯片版图绘制软件的最底层软件。市面上的大多数版图绘制软件都是围绕这一软件展开的。版图绘制软件最终生成的文件均以 .gds 结尾。 优点 免费可以…

【西电电装实习】6. 手装无人机的蓝牙断连debug

文章目录 前言零、闪灯状态零零、翻滚角&#xff0c;俯仰角&#xff0c;偏航角一、问题描述二、现象解释三、解决方案参考文献 前言 在 西电无人机电装实习 时遇到的问题使用蓝牙芯片 CH582F。沁恒的蓝牙芯片CH582F是一款集成了BLE&#xff08;Bluetooth Low Energy&#xff0…

基于微信小程序的健身房管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 基于微信小程序JavaSpringBootVueMySQL的健…

Linux上使用touch修改文件时间属性的限制

缘由 在Linux上我想多个进程对于同一个文件进行访问和修改&#xff0c;并且根据文件的最后修改时间来判断时间是否需要更新缓存中的文件&#xff0c;这样能够达到减少每次加载文件时间的损耗。 尝试的做法 每当我修改文件后&#xff0c;为了确保文件的最后修改时间的属性生效…

QUIC的丢包处理

QUIC的重试数据包&#xff08;Retry Packet&#xff09;为什么会触发重启另一个连接 安全性考量 防止重放攻击 重试数据包&#xff08;Retry Packet&#xff09;是在初始握手过程中由服务端发送给客户端&#xff0c;用于验证客户端的IP地址&#xff0c;以防止重放攻击。 在一…