设计模式

[TOC]

一、创建型设计模式

1. 单例(Singleton)

目的:

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

类图:

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

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

实现:

1. 延迟实例化:线程不安全

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

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


public class Singleton {



private static Singleton uniqueInstance;



private Singleton() {

}



public static Singleton getUniqueInstance() {

if (uniqueInstance == null) {

uniqueInstance = new Singleton();

}

return uniqueInstance;

}

}

2. 直接实例化:线程安全

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

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


private static Singleton uniqueInstance = new Singleton();

3. 延迟实例化加锁:线程安全、不推荐

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

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


public static synchronized Singleton getUniqueInstance() {

if (uniqueInstance == null) {

uniqueInstance = new Singleton();

}

return uniqueInstance;

}

4. 双重校验锁:线程安全(感觉还不错)

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

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


public class Singleton {



private volatile static Singleton uniqueInstance;



private Singleton() {

}



public static Singleton getUniqueInstance() {

if (uniqueInstance == null) { // 第一次 if

synchronized (Singleton.class) {

if (uniqueInstance == null) { // 第二次 if

uniqueInstance = new Singleton();

}

}

}

return uniqueInstance;

}

}

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


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 的指令重排,保证在多线程环境下也能正常运行。

5. 静态内部类

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

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

之前线程不安全是多个线程认为实例未创建,而进行多次创建实例,现在实例放在静态内部类里面,由于静态内部类也是第一次被调用时候才加载,再实例化里面的对象,第一个获取实例的线程会创建静态内部类中的实例,它不会被多次创建。


public class Singleton {



private Singleton() {

}



private static class SingletonHolder {

private static final Singleton INSTANCE = new Singleton();

}



// 返回静态内部类中的 静态常量对象

public static Singleton getUniqueInstance() {

return SingletonHolder.INSTANCE;

}

}

6. 枚举类型


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

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

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

例子

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

JDK

2. 简单工厂(Simple Factory)

目标:

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

类图:

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

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

实现:


public interface Product {

}



public class ConcreteProduct implements Product {

}



public class ConcreteProduct1 implements Product {

}



public class ConcreteProduct2 implements Product {

}

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


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 是简单工厂实现,它被所有需要进行实例化的客户类调用。


public class SimpleFactory {



public Product createProduct(int type) {

if (type == 1) {

return new ConcreteProduct1();

} else if (type == 2) {

return new ConcreteProduct2();

}

return new ConcreteProduct();

}

}



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)

目标:

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

类图:

实现:


public abstract class Factory {

abstract public Product factoryMethod();

public void doSomething() {

Product product = factoryMethod();

// do something with the product

}

}



// 不同的子工厂

public class ConcreteFactory extends Factory {

public Product factoryMethod() {

return new ConcreteProduct();

}

}



public class ConcreteFactory1 extends Factory {

public Product factoryMethod() {

return new ConcreteProduct1();

}

}



public class ConcreteFactory2 extends Factory {

public Product factoryMethod() {

return new ConcreteProduct2();

}

}

JDK

4. 抽象工厂(Abstract Factory)

目标:

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

着重于“对象家族”

类图:

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

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

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

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

实现:


public class AbstractProductA {

}



public class AbstractProductB {

}



public class ProductA1 extends AbstractProductA {

}



public class ProductA2 extends AbstractProductA {

}



public class ProductB1 extends AbstractProductB {

}



public class ProductB2 extends AbstractProductB {

}



// 抽象工厂具有生产多个对象的能力

// 多个对象可以相互组合

public abstract class AbstractFactory {

abstract AbstractProductA createProductA();

abstract AbstractProductB createProductB();

}



public class ConcreteFactory1 extends AbstractFactory {

AbstractProductA createProductA() {

return new ProductA1();

}



AbstractProductB createProductB() {

return new ProductB1();

}

}



public class ConcreteFactory2 extends AbstractFactory {

AbstractProductA createProductA() {

return new ProductA2();

}



AbstractProductB createProductB() {

return new ProductB2();

}

}



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

5. 生成器(Builder)

目标:

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

类图:

实现:


// Builder Pattern

public class NutritionFacts {

private final int servingSize;

private final int servings;

private final int calories;

private final int fat;

private final int sodium;

private final int carbohydrate;



// 静态 Builder 类

public static class Builder {

// 必选参数

private final int servingSize;

private final int servings;



// 可选参数 - 初始化为默认值

private int calories = 0;

private int fat = 0;

private int sodium = 0;

private int carbohydrate = 0;



public Builder(int servingSize, int servings) {

this.servingSize = servingSize;

this.servings = servings;

}



// 类似 setter 的方法,会返回 Builder 本身

public Builder calories(int val)

{ calories = val; return this; }

public Builder fat(int val)

{ fat = val; return this; }

public Builder sodium(int val)

{ sodium = val; return this; }

public Builder carbohydrate(int val)

{ carbohydrate = val; return this; }



// 最终的 build 方法,返回的是外部的 NutrititionFacts 对象

public NutritionFacts build() {

return new NutritionFacts(this);

}

}



// 传入 builder,变量赋值为 Builder 里面的值

private NutritionFacts(Builder builder) {

servingSize = builder.servingSize;

servings = builder.servings;

calories = builder.calories;

fat = builder.fat;

sodium = builder.sodium;

carbohydrate = builder.carbohydrate;

}



public static void main(String[] args) {

NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8)

.calories(100).sodium(35).carbohydrate(27).build();

}

}

public abstract 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 code

if (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) // overflow

throw new OutOfMemoryError();

newCapacity = Integer.MAX_VALUE;

}

value = Arrays.copyOf(value, newCapacity);

}



abstract String void build();

}



public class StringBuilder extends AbstractStringBuilder {

public StringBuilder() {

super(16);

}



@Override

public String build() {

// Create a copy, don't share the array

return new String(value, 0, count);

}



@Override

public String toString() {

// Create a copy, don't share the array

return new String(value, 0, count);

}

}



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

}

String s = new String(sb.build());

System.out.println(sb);

}

}

// abcdefghijklmnopqrstuvwxyz

JDK

6. 原型模式(Prototype)

目标:

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

主要是这个 clone() 方法。

类图:

实现:


public abstract class Prototype {

abstract Prototype myClone();

}



public class ConcretePrototype extends Prototype {



private String filed;



public ConcretePrototype(String filed) {

this.filed = filed;

}



@Override

Prototype myClone() {

return new ConcretePrototype(filed);

}



@Override

public String toString() {

return filed;

}

}

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

二、行为型

1. 责任链(Chain Of Responsibility)

目标:

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

类图:

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

实现:


public abstract class Handler {



protected Handler successor; // 后继的 handler





public Handler(Handler successor) {

this.successor = successor;

}





protected abstract void handleRequest(Request request);

}



public class ConcreteHandler1 extends Handler {



public ConcreteHandler1(Handler successor) {

super(successor);

}





@Override

protected void handleRequest(Request request) {

if (request.getType() == RequestType.TYPE1) {

// 当前 Handler 处理逻辑

System.out.println(request.getName() + " is handle by ConcreteHandler1");

return;

}

// 调用 successor 的处理逻辑

if (successor != null) {

successor.handleRequest(request);

}

}

}



public class ConcreteHandler2 extends Handler {



public ConcreteHandler2(Handler successor) {

super(successor);

}





@Override

protected 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);

}

}

}

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;

}

}





public enum RequestType {

TYPE1, TYPE2

}

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

2. 命令(Command)

目的:

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

  • 使用命令来参数化其它对象

  • 将命令放入队列中进行排队

  • 将命令的操作记录到日志中

  • 支持可撤销的操作

类图:

  • Command:命令

  • Receiver:命令接收者,也就是命令真正的执行者

  • Invoker:通过它来调用命令

  • Client:可以设置命令与命令的接收者

实现:

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


// 命令接口,具有执行方法

public interface Command {

void execute();

}



// 开灯命令

public class LightOnCommand implements Command {

Light light; // Reciever:命令的执行者



public LightOnCommand(Light light) {

this.light = light;

}



@Override

public void execute() {

light.on();

}

}



// 关灯命令

public class LightOffCommand implements Command {

Light light;



public LightOffCommand(Light light) {

this.light = light;

}



@Override

public void execute() {

light.off();

}

}

// Reciever (真正执行命令)

public class Light {



public void on() {

System.out.println("Light is on!");

}



public void off() {

System.out.println("Light is off!");

}

}

/**

* 遥控器 Invoker (实际调用者)

*/

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();

}

}

public class Client {

public static void main(String[] args) {

Invoker invoker = new Invoker(); // Invoker

Light light = new Light(); // Reciever

Command lightOnCommand = new LightOnCommand(light); // Command 设置 Reciever

Command lightOffCommand = new LightOffCommand(light);

invoker.setOnCommand(lightOnCommand, 0); // Invoker 设置 Command

invoker.setOffCommand(lightOffCommand, 0);

invoker.onButtonWasPushed(0); // Invoker 执行 Command,真正由 Reciever 执行

invoker.offButtonWasPushed(0);

}

}

JDK

3. 解释器 (Interpreter)

目的:

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

类图:

  • TerminalExpression:终结符表达式,每个终结符都需要一个 TerminalExpression

  • Context:上下文,包含解释器之外的一些全局信息。

实现:

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

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

这里的 Context 指的是 String


public abstract class Expression {

public abstract boolean interpret(String str);

}



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;

}

}





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

}

}



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

}

}



public class Client {



/**

* 构建解析树

*/

public static Expression buildInterpreterTree() {

// Literal

Expression terminal1 = new TerminalExpression("A");

Expression terminal2 = new TerminalExpression("B");

Expression terminal3 = new TerminalExpression("C");

Expression terminal4 = new TerminalExpression("D");

// B C

Expression 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

4. 迭代器(Iterator)

目标:

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

类图:

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

实现:


public interface Aggregate {

Iterator createIterator();

}



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;

}

}



@Override

public Iterator createIterator() {

return new ConcreteIterator<Integer>(items);

}

}



public interface Iterator<Item> {



Item next();



boolean hasNext();

}





public class ConcreteIterator<Item> implements Iterator {



private Item[] items;

private int position = 0;



public ConcreteIterator(Item[] items) {

this.items = items;

}



@Override

public Object next() {

return items[position++];

}



@Override

public boolean hasNext() {

return position < items.length;

}

}



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)

目标:

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

类图:

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

  • Colleague:同事,相关对象

实现:

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

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


public abstract class Colleague { // 相关者,同事

public abstract void onEvent(Mediator mediator);

}



public class Alarm extends Colleague {



@Override

public void onEvent(Mediator mediator) {

mediator.doEvent("alarm");

}



public void doAlarm() {

System.out.println("doAlarm()");

}

}





public class CoffeePot extends Colleague {

@Override

public void onEvent(Mediator mediator) {

mediator.doEvent("coffeePot");

}



public void doCoffeePot() {

System.out.println("doCoffeePot()");

}

}





public class Calender extends Colleague {

@Override

public void onEvent(Mediator mediator) {

mediator.doEvent("calender");

}



public void doCalender() {

System.out.println("doCalender()");

}

}



public class Sprinkler extends Colleague {

@Override

public void onEvent(Mediator mediator) {

mediator.doEvent("sprinkler");

}



public void doSprinkler() {

System.out.println("doSprinkler()");

}

}



// 中介

public abstract class Mediator {

public abstract void doEvent(String eventType);

}



// 中介集中了各类对象

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;

}



// 中介根据事件信息,去做对应的事

@Override

public 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() {

// ...

}

}





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

}

}





// doAlarm()

// doCoffeePot()

// doCalender()

// doSprinkler()

JDK

6. 备忘录(Memento)

目标:

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

类图:

  • Originator:原始对象

  • Caretaker:负责保存好备忘录

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

实现:

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

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


/**

* Originator Interface:原始对象接口

*/

public interface Calculator {



// Create Memento:创建备忘录

PreviousCalculationToCareTaker backupLastCalculation();



// setMemento:设置备忘录

void restorePreviousCalculation(PreviousCalculationToCareTaker memento);



int getCalculationResult();



void setFirstNumber(int firstNumber);



void setSecondNumber(int secondNumber);

}

/**

* Originator Implementation:原始对象实现

*/

public class CalculatorImp implements Calculator {



private int firstNumber;

private int secondNumber;



@Override

public PreviousCalculationToCareTaker backupLastCalculation() {

// 创造一个备忘录用来存储两个数字

return new PreviousCalculationImp(firstNumber, secondNumber);

}



@Override

public void restorePreviousCalculation(PreviousCalculationToCareTaker memento) {

// 从备忘录中恢复两个数字

this.firstNumber = ((PreviousCalculationToOriginator) memento).getFirstNumber();

this.secondNumber = ((PreviousCalculationToOriginator) memento).getSecondNumber();

}



@Override

public int getCalculationResult() {

// result is adding two numbers

return firstNumber + secondNumber;

}



@Override

public void setFirstNumber(int firstNumber) {

this.firstNumber = firstNumber;

}



@Override

public void setSecondNumber(int secondNumber) {

this.secondNumber = secondNumber;

}

}

/**

* Memento Interface to Originator

* 备忘录允许原始对象获取其初始存储状态的接口

*/

public interface PreviousCalculationToOriginator {

int getFirstNumber();

int getSecondNumber();

}



/**

* Memento interface to CalculatorOperator (Caretaker)

* 备忘录提供给 Caretaker 的接口

*/

public interface PreviousCalculationToCareTaker {

// no operations permitted for the caretaker

}



/**

* 备忘录对象实现

* 这个备忘录 Momento 对象是实现了分别提供给 Originator 和 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;

}



@Override

public int getFirstNumber() {

return firstNumber;

}



@Override

public int getSecondNumber() {

return secondNumber;

}

}

/**

* CareTaker object:这里就是客户端

*/

public class Client {



public static void main(String[] args) {

// program starts

Calculator calculator = new CalculatorImp();



// assume user enters two numbers

calculator.setFirstNumber(10);

calculator.setSecondNumber(100);



// find result

System.out.println(calculator.getCalculationResult());



// Store result of this calculation in case of error

PreviousCalculationToCareTaker memento = calculator.backupLastCalculation();



// user enters a number

calculator.setFirstNumber(17);



// user enters a wrong second number and calculates result

calculator.setSecondNumber(-290);



// calculate result

System.out.println(calculator.getCalculationResult());



// user hits CTRL + Z to undo last operation and see last result

calculator.restorePreviousCalculation(memento);



// result restored

System.out.println(calculator.getCalculationResult());

}

}

110

-273

110

JDK

  • java.io.Serializable

7. 观察者(Observer)

目标:

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

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

img

类图:

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

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

实现:

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


public interface Subject {

// 注册观察者

void resisterObserver(Observer o);

// 删除观察者

void removeObserver(Observer o);

// 通知观察者

void notifyObserver();

}

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(); //设置了温度、湿度、气压就进行更新

}



@Override

public void resisterObserver(Observer o) {

observers.add(o);

}



@Override

public void removeObserver(Observer o) {

int i = observers.indexOf(o);

if (i >= 0) {

observers.remove(i);

}

}



@Override

public void notifyObserver() {

for (Observer o : observers) {

// 观察者进行更新

o.update(temperature, humidity, pressure);

}

}

}

public interface Observer {

void update(float temp, float humidity, float pressure);

}

public class StatisticsDisplay implements Observer {



public StatisticsDisplay(Subject weatherData) {

// 观察者向主题请求注册

weatherData.resisterObserver(this);

}



@Override

public void update(float temp, float humidity, float pressure) {

System.out.println("StatisticsDisplay.update: " + temp + " " + humidity + " " + pressure);

}

}

public class CurrentConditionsDisplay implements Observer {



public CurrentConditionsDisplay(Subject weatherData) {

weatherData.resisterObserver(this);

}



@Override

public void update(float temp, float humidity, float pressure) {

System.out.println("CurrentConditionsDisplay.update: " + temp + " " + humidity + " " + pressure);

}

}

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

8. 状态(State)

目标:

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

类图:

  • Context:一个拥有状态的上下文对象

  • State: 状态

一个拥有各种状态的对象(但当前状态只有一个),对象根据当前状态采取行动,但不同状态会有不同的操作,这些操作又会改变对象内部的状态。

实现:

img


// 状态接口

public interface State {

/**

* 投入 25 分钱

*/

void insertQuarter();



/**

* 退回 25 分钱

*/

void ejectQuarter();



/**

* 转动曲柄

*/

void turnCrank();



/**

* 发放糖果

*/

void dispense();

}

// 有 25 分钱的状态

public class HasQuarterState implements State {



// 每个状态都对应外部拥有状态的实例

private GumballMachine gumballMachine;



public HasQuarterState(GumballMachine gumballMachine) {

this.gumballMachine = gumballMachine;

}



@Override

public void insertQuarter() {

System.out.println("You can't insert another quarter");

}



@Override

public void ejectQuarter() {

System.out.println("Quarter returned");

// 设置新状态

gumballMachine.setState(gumballMachine.getNoQuarterState());

}



@Override

public void turnCrank() {

System.out.println("You turned...");

gumballMachine.setState(gumballMachine.getSoldState());

}



@Override

public void dispense() {

System.out.println("No gumball dispensed");

}

}

public class NoQuarterState implements State {



GumballMachine gumballMachine;



public NoQuarterState(GumballMachine gumballMachine) {

this.gumballMachine = gumballMachine;

}



@Override

public void insertQuarter() {

System.out.println("You insert a quarter");

gumballMachine.setState(gumballMachine.getHasQuarterState());

}



@Override

public void ejectQuarter() {

System.out.println("You haven't insert a quarter");

}



@Override

public void turnCrank() {

System.out.println("You turned, but there's no quarter");

}



@Override

public void dispense() {

System.out.println("You need to pay first");

}

}

public class SoldOutState implements State {



GumballMachine gumballMachine;



public SoldOutState(GumballMachine gumballMachine) {

this.gumballMachine = gumballMachine;

}



@Override

public void insertQuarter() {

System.out.println("You can't insert a quarter, the machine is sold out");

}



@Override

public void ejectQuarter() {

System.out.println("You can't eject, you haven't inserted a quarter yet");

}



@Override

public void turnCrank() {

System.out.println("You turned, but there are no gumballs");

}



@Override

public void dispense() {

System.out.println("No gumball dispensed");

}

}

public class SoldState implements State {



GumballMachine gumballMachine;



public SoldState(GumballMachine gumballMachine) {

this.gumballMachine = gumballMachine;

}



@Override

public void insertQuarter() {

System.out.println("Please wait, we're already giving you a gumball");

}



@Override

public void ejectQuarter() {

System.out.println("Sorry, you already turned the crank");

}



@Override

public void turnCrank() {

System.out.println("Turning twice doesn't get you another gumball!");

}



@Override

public void dispense() {

gumballMachine.releaseBall();

if (gumballMachine.getCount() > 0) {

gumballMachine.setState(gumballMachine.getNoQuarterState());

} else {

System.out.println("Oops, out of gumballs");

gumballMachine.setState(gumballMachine.getSoldOutState());

}

}

}

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;

}

}

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)

目标:

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

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

类图:

  • Strategy 接口定义了一个算法族,它们都实现了 behavior() 方法。

  • Context 是使用到该算法族的类,其中的 doSomething() 方法会调用 behavior()setStrategy(Strategy) 方法可以动态地改变 strategy 对象,也就是说能动态地改变 Context 所使用的算法。

与状态模式的比较

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

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

实现:

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


public interface QuackBehavior {

void quack();

}



public class Quack implements QuackBehavior {

@Override

public void quack() {

System.out.println("quack!");

}

}



public class Squeak implements QuackBehavior{

@Override

public void quack() {

System.out.println("squeak!");

}

}



public class Duck {



private QuackBehavior quackBehavior;



public void performQuack() {

if (quackBehavior != null) {

quackBehavior.quack();

}

}



public void setQuackBehavior(QuackBehavior quackBehavior) {

this.quackBehavior = quackBehavior;

}

}



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)

目标:

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

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

类图:

实现:

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


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

}

}



public class Coffee extends CaffeineBeverage {

@Override

void brew() {

System.out.println("Coffee.brew");

}



@Override

void addCondiments() {

System.out.println("Coffee.addCondiments");

}

}

public class Tea extends CaffeineBeverage {

@Override

void brew() {

System.out.println("Tea.brew");

}



@Override

void addCondiments() {

System.out.println("Tea.addCondiments");

}

}

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)

在访问者模式(Visitor Pattern)中,我们使用了一个访问者类,它改变了元素类的执行算法通过这种方式,元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行为型模式。根据模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作。

意图:主要将数据结构与数据操作分离。

主要解决:稳定的数据结构和易变的操作耦合问题。

何时使用:需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作”污染”这些对象的类,使用访问者模式将这些封装到类中。

如何解决:在被访问的类里面加一个对外提供接待访问者的接口。

关键代码:在数据基础类里面有一个方法接受访问者,将自身引用传入访问者。

应用实例:您在朋友家做客,您是访问者,朋友接受您的访问,您通过朋友的描述,然后对朋友的描述做出一个判断,这就是访问者模式。

优点: 1、符合单一职责原则。 2、优秀的扩展性。 3、灵活性。

缺点: 1、具体元素对访问者公布细节,违反了迪米特原则。 2、具体元素变更比较困难。 3、违反了依赖倒置原则,依赖了具体类,没有依赖抽象。

使用场景: 1、对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。 2、需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作”污染”这些对象的类,也不希望在增加新操作时修改这些类。

注意事项:访问者可以对功能进行统一,可以做报表、UI、拦截器与过滤器。

类图:

  • Visitor:访问者,为每一个 ConcreteElement 声明一个 visit() 操作
  • ConcreteVisitor:具体访问者,可以记录遍历的信息,比如遍历的累计结果
  • ObjectStructure:对象结构,可以是组合结构,或者是一个集合。

实现:

顾客订购商品:

遍历访问顾客Customer(是一个 Element),遍历订购 Order (也是一个 Element),遍历商品 Item(也是一个 Element)。


// 元素:接受一个 Visitor

public interface Element {

void accept(Visitor visitor);

}



// ObjectStruct:一系列 Elemtent 的聚合,这里 Element 是 Customer

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

}

}



// Element:顾客(有一系列订购)

public class Customer implements Element {



private String name;

// 有一系列 order

private List<Order> orders = new ArrayList<>();



Customer(String name) {

this.name = name;

}



String getName() {

return name;

}



void addOrder(Order order) {

orders.add(order);

}



// Element 的 accept(visitor) 让 visitor 来访问它

public void accept(Visitor visitor) {

visitor.visit(this);

// 继续遍历 order Element

for (Order order : orders) {

order.accept(visitor);

}

}

}



// Order 也是 Element,里面还有一系列 Items

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

}

}

}



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

}

}



// Visitor 接口,可以 visit 3种不同的 Element

public interface Visitor {

void visit(Customer customer);



void visit(Order order);



void visit(Item item);

}



// 具体的 Visitor,里面保存了访问计数

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

}

}



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)

目标:

使用什么都不做的空对象来代替 NULL。这样的 Null 对象也可以在数据不可用的时候提供默认的行为。

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

类图:

实现:


public abstract class AbstractOperation {

abstract void request();

}

public class RealOperation extends AbstractOperation {

@Override

void request() {

System.out.println("do something");

}

}

public class NullOperation extends AbstractOperation{

@Override

void request() {

// do nothing

}

}

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)

目标:

适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。把一个类接口转换成另一个用户需要的接口。

类图:

实现:

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

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


public interface Duck {

void quack();

}



public interface Turkey {

void gobble();

}



public class WildTurkey implements Turkey {

@Override

public void gobble() {

System.out.println("gobble!");

}



// 适配器:转 Duck 方法为 Turkey方法

public class TurkeyAdapter implements Duck {

Turkey turkey;



public TurkeyAdapter(Turkey turkey) {

this.turkey = turkey;

}



// 鸭子的 quack() 调用 turkey 的 quack()

@Override

public void quack() {

turkey.gobble();

}

}



public class Client {

public static void main(String[] args) {

Turkey turkey = new WildTurkey();

Duck duck = new TurkeyAdapter(turkey);

duck.quack();

}

}

JDK

2. 桥接(Bridge)

目标:

将抽象与实现分离开来,使它们可以独立变化。这种模式涉及到一个作为桥接的接口,使得实体类的功能独立于接口实现类。这两种类型的类可被结构化改变而互不影响。

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

实现:

RemoteControl 表示遥控器,指代 Abstraction

TV 表示电视,指代 Implementor

TV 是遥控器的一部分,可以有不同的遥控器和不同的 TV。


public abstract class TV {

public abstract void on();



public abstract void off();



public abstract void tuneChannel();

}



public class Sony extends TV {

@Override

public void on() {

System.out.println("Sony.on()");

}



@Override

public void off() {

System.out.println("Sony.off()");

}



@Override

public void tuneChannel() {

System.out.println("Sony.tuneChannel()");

}

}



public class RCA extends TV {

@Override

public void on() {

System.out.println("RCA.on()");

}



@Override

public void off() {

System.out.println("RCA.off()");

}



@Override

public void tuneChannel() {

System.out.println("RCA.tuneChannel()");

}

}



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();

}



public class ConcreteRemoteControl1 extends RemoteControl {

public ConcreteRemoteControl1(TV tv) {

super(tv);

}



@Override

public void on() {

System.out.println("ConcreteRemoteControl1.on()");

tv.on();

}



@Override

public void off() {

System.out.println("ConcreteRemoteControl1.off()");

tv.off();

}



@Override

public void tuneChannel() {

System.out.println("ConcreteRemoteControl1.tuneChannel()");

tv.tuneChannel();

}

}



public class ConcreteRemoteControl2 extends RemoteControl {

public ConcreteRemoteControl2(TV tv) {

super(tv);

}



@Override

public void on() {

System.out.println("ConcreteRemoteControl2.on()");

tv.on();

}



@Override

public void off() {

System.out.println("ConcreteRemoteControl2.off()");

tv.off();

}



@Override

public void tuneChannel() {

System.out.println("ConcreteRemoteControl2.tuneChannel()");

tv.tuneChannel();

}

}



public class Client {

public static void main(String[] args) {

RemoteControl remoteControl1 = new ConcreteRemoteControl1(new RCA());

remoteControl1.on();

remoteControl1.off();

remoteControl1.tuneChannel();

}

}

ConcreteRemoteControl1.on()

RCA.on()

ConcreteRemoteControl1.off()

RCA.off()

ConcreteRemoteControl1.tuneChannel()

RCA.tuneChannel()

JDK

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

  • JDBC

3. 组合(Composite)

目标:

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

类图:

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

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

实现:


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

}



public class Composite extends Component {



private List<Component> child;



public Composite(String name) {

super(name);

child = new ArrayList<>();

}



@Override

void 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);

}

}



@Override

public void add(Component component) {

child.add(component);

}



@Override

public void remove(Component component) {

child.remove(component);

}

}





public class Leaf extends Component {

public Leaf(String name) {

super(name);

}



@Override

void print(int level) {

for (int i = 0; i < level; i++) {

System.out.print("--");

}

System.out.println("leaf:" + name);

}



@Override

public void add(Component component) {

throw new UnsupportedOperationException(); // 牺牲透明性换取单一职责原则,这样就不用考虑是叶子节点还是组合节点

}



@Override

public void remove(Component component) {

throw new UnsupportedOperationException();

}

}





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();

}

}

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)

目标:

为对象动态添加功能。

类图:

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

实现:

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

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


public interface Beverage {

double cost();

}



public class DarkRoast implements Beverage {

@Override

public double cost() {

return 1;

}

}



public class HouseBlend implements Beverage {

@Override

public double cost() {

return 1;

}

}



// 装饰器也继承自 Beverage

public abstract class CondimentDecorator implements Beverage {

protected Beverage beverage;

}



public class Milk extends CondimentDecorator {



public Milk(Beverage beverage) {

this.beverage = beverage;

}



// 不仅调用被装饰者的方法,还添加了自己的功能

@Override

public double cost() {

return 1 + beverage.cost();

}

}



public class Mocha extends CondimentDecorator {



public Mocha(Beverage beverage) {

this.beverage = beverage;

}



@Override

public double cost() {

return 1 + beverage.cost();

}

}



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)

Facade :[fə'sɑ:d] 也称门面模式。

目标:

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

类图:

实现:

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

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


public class SubSystem {

public void turnOnTV() {

System.out.println("turnOnTV()");

}



public void setCD(String cd) {

System.out.println("setCD( " + cd + " )");

}



public void starWatching(){

System.out.println("starWatching()");

}

}



public class Facade {

private SubSystem subSystem = new SubSystem();



// 一键完成复杂操作

public void watchMovie() {

subSystem.turnOnTV();

subSystem.setCD("a movie");

subSystem.starWatching();

}

}

public class Client {

public static void main(String[] args) {

Facade facade = new Facade();

facade.watchMovie();

}

}

6. 享元(Flyweight)

这种中文名不知道是谁翻译的:flayweight 意为蝇量级。该模式通常利用缓存来加速大量小对象的访问时间。

目标:

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

类图:

  • Flyweight:享元对象

  • IntrinsicState:内部状态,享元对象共享内部状态

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

实现:


public interface Flyweight {

void doOperation(String extrinsicState);

}



public class ConcreteFlyweight implements Flyweight {



private String intrinsicState;



public ConcreteFlyweight(String intrinsicState) {

this.intrinsicState = intrinsicState;

}



@Override

public void doOperation(String extrinsicState) {

System.out.println("Object address: " + System.identityHashCode(this));

System.out.println("IntrinsicState: " + intrinsicState);

System.out.println("ExtrinsicState: " + extrinsicState);

}

}



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

}

}



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)

目标:

控制对其它对象的访问。

类图:

代理有以下四类:

  • 远程代理(Remote Proxy):控制对远程对象(不同地址空间)的访问,它负责将请求及其参数进行编码,并向不同地址空间中的对象发送已经编码的请求。

  • 虚拟代理(Virtual Proxy):根据需要创建开销很大的对象,它可以缓存实体的附加信息,以便延迟对它的访问,例如在网站加载一个很大图片时,不能马上完成,可以用虚拟代理缓存图片的大小信息,然后生成一张临时图片代替原始图片。

  • 保护代理(Protection Proxy):按权限控制对象的访问,它负责检查调用者是否具有实现一个请求所必须的访问权限。

  • 智能代理(Smart Reference):取代了简单的指针,它在访问对象时执行一些附加操作:记录对象的引用次数;当第一次引用一个对象时,将它装入内存;在访问一个实际对象前,检查是否已经锁定了它,以确保其它对象不能改变它。

实现:

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


public interface Image {

void showImage();

}



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;

}



@Override

public void showImage() {

System.out.println("Real Image: " + imageURL);

}

}



public class ImageProxy implements Image {



private HighResolutionImage highResolutionImage;



public ImageProxy(HighResolutionImage highResolutionImage) {

this.highResolutionImage = highResolutionImage;

}



@Override

public 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();

}

}



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();

}

}

Temp Image: 600 600

Temp Image: 600 600

Temp Image: 600 600

Temp Image: 600 600

Temp Image: 600 600

Temp Image: 600 600

Temp Image: 600 600

Temp Image: 600 600

Temp Image: 600 600

Temp Image: 600 600

Real Image: http://image.jpg

JDK

  • java.lang.reflect.Proxy

  • RMI