跳到主要内容

设计模式

创建型

单例模式(Singleton)

目的

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

UML

使用一个私有构造函数,一个私有静态变量以及一个共有静态函数来实现

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

20240729231716

实现

懒汉式-线程不安全

私有静态变量uniqueInstance被延迟实例化,这样做的好处就是,如果没有用到该类,那么就不会实例化uniqueInstance,从而节约资源 但是在多线程环境下是不安全的,如果多个线程能同时进入if判断,并且这时候uniqueInstance为 null,那么会有多个线程执行实例化语句,这会导致实例化多次uniqueInstance

public class Lazy {

private static Lazy uniqueInstance;

private Lazy() {
}

public static Lazy getUniqueInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Lazy();
}
return uniqueInstance;
}
}
饿汉式-线程安全

线程不安全的主要问题是由于uniqueInstance被实例化多次,采取直接实例化uniqueInstance的方式就不会产生线程不安全问题,但是直接实例化的方式也丢失了延迟实例化带来的节约资源的好处

private static Lazy uniqueInstance = new Lazy();
懒汉式-线程安全

只要对getUniqueInstance()方法加锁,在同一时间只能有一个线程进入该方法,从而避免了实例多次

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

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

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

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

public class Singleton {
private volatile static Singleton uniqueInstance;

private Singleton() {
}

public static synchronized Singleton getUniqueInstance() {
if (uniqueInstance == null) {
synchronized (Singleton.class) {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
}
}
return uniqueInstance;
}
}

为什么要判断两次?

在 uniqueInstance 为 null 的情况下,如果两个线程都进入了 if 块中。虽然 if 块中有加锁操作,但是两个线程都会执行uniqueInstance = new Singleton();,只是先后的问题,那么就会执行两次实例化

因此必须判断两次:第一个 if 语句用来避免 uniqueInstance 已经被实例化后的加锁操作,而第二个 if 语句进行了加锁,所以只能有一个线程进入,就不会出现uniqueInstance == null时两个线程同时进行实例化操作

为什么要使用 volatile 关键字?

uniqueInstance = new Singleton();这句代码是分三步执行的

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

但是由于 JVM 有指令重排的特性,执行顺序有可能会变成 1 -> 3 -> 2。指令重排在单线程下不会出现问题,但是多线程下会导致线程获取一个没有初始化的实例

例如,线程 T1 执行了 1 和 3,此时 T2 调用 getUniqueInstance() 后发现 uniqueInstance 不为空,因此返回 uniqueInstance,但此时 uniqueInstance 还未被初始化

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

静态内部类实现

当 Signleton 类被加载时,静态内部类 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;
}
}
枚举实现
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();
}
}
}

这种实现可以防止反射攻击,在其他实现中,可以通过setAccessible()方法将私有构造函数的访问级别设置为 public,然后调用构造函数从而实例化对象。如果要防止这种攻击,需要在构造函数中添加防止多次实例化的代码。该实现是由 JVM 保证只会实例化一次,因此不会出现反射攻击 该实现在多次序列化和序列化之后,不会得到多个实例。而其它实现需要使用 transient 修饰所有字段,并且实现序列化和反序列化的方法

简单工厂(Simple Factory)

目的

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

UML

20240729231825

实现

public interface Product {
}
public class ConcreteProduct implements Product{
}
public class ConcreteProduct1 implements Product{
}
public class ConcreteProduct2 implements 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
}
}

工厂方法(Factory Method)

目的

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

UML

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

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

20240729231858

实现

public abstract class Factory {
abstract public Product factoryMethod();

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

抽象工厂(Abstract Factory)

目的

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

UML

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

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

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

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

20240729231925

实现

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
}
}

生成器(Builder)

目的

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

UML

20240729232000

实现

下面是一个简易的 StringBuilder

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) {
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) {
throw new OutOfMemoryError();
}
newCapacity = Integer.MAX_VALUE;
}
value = Arrays.copyOf(value, newCapacity);
}
}
public class StringBuilder extends AbstractStringBuilder{
public StringBuilder() {
super(16);
}

@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));
}
System.out.println(sb.toString());
}
}

原型模式(Prototype)

目的

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

UML

20240729232023

实现

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

行为型

责任链(Chain of Responsibility)

目的

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

UML

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

20240729232046

实现

public enum RequestType {
TYPE1, TYPE2
}
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 abstract class Handler {

protected Handler successor;

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) {
System.out.println(request.getName() + " is handle by ConcreteHandler1");
}
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 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);
}
}

命令(Command)

目的

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

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

UML

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

20240729232112

20240729232119

实现

public class Light {
public void on() {
System.out.println("Light is on!");
}

public void off() {
System.out.println("Light is off!");
}
}
public interface Command {
void execute();
}
public class LightOnCommand implements Command{
Light light;

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

解释器(Interpreter)

目的

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

UML

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

20240729232144

实现

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

例如一棵解析树为 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) {
this.literal = str;
}

@Override
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;
}

@Override
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));
}
}

迭代器(Iterator)

目的

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

UML

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

20240729232212

实现

public interface Iterator<Item> {

Item next();

boolean hasNext();
}
public interface Aggregate {
Iterator createIterator();
}
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 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 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());
}
}
}

中介者(Mediator)

目的

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

UML

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

20240729232235

实现

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

20240729232245

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

20240729232253

public abstract class Mediator {
public abstract void doEvent(String eventType);
}
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 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);
}
}

备忘录(Memento)

目的

在不违反封装的情况下获取对象的内部状态,从而在需要的时候可以将对象的状态重置

UML

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

20240729232325

实现

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

/**
* @author kbws
* @date 2024/7/28
* @description: Memento interface to CalculatorOperator (Caretaker)
*/
public interface PreviousCalculationToCareTaker {
// no operations permitted for the caretaker
}
/**
* @author kbws
* @date 2024/7/28
* @description: Memento Interface to Originator
* This interface allows the originator to restore its state
*/
public interface PreviousCalculationToOriginator {
int getFirstNumber();
int getSecondNumber();
}
public class PreviousCalculationImp implements PreviousCalculationToOriginator, PreviousCalculationToCareTaker {
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;
}
}
/**
* @author kbws
* @date 2024/7/28
* @description: Originator Interface
*/
public interface Calculator {
// Create Memento
PreviousCalculationToCareTaker backupLastCalculation();

// setMemento
void restorePreviousCalculation(PreviousCalculationToCareTaker memento);

int getCalculationResult();

void setFirstNumber(int firstNumber);

void setSecondNumber(int secondNumber);
}
public class CalculatorImp implements Calculator{

private int firstNumber;
private int secondNumber;

@Override
public PreviousCalculationToCareTaker backupLastCalculation() {
// create a memento object used for restoring two numbers
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;
}
}
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());
}
}

观察者(Observer)

目的

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

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

UML

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

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

20240729232401

实现

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

public interface Observer {
void update(float temp, float humidity, float pressure);
}
public interface Subject {
void registerObserver(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 registerObserver(Observer o) {
observers.add(o);
}

@Override
public void removeObserver(Observer o) {
int i = observers.indexOf(o);
if (i >= 0) {
observers.remove(o);
}
}

@Override
public void notifyObserver() {
for (Observer o : observers) {
o.update(temperature, humidity, pressure);
}
}
}
public class StatisticsDisplay implements Observer{

public StatisticsDisplay(Subject weatherData) {
weatherData.registerObserver(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.registerObserver(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);
}
}

状态(State)

目的

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

UML

20240729232428

实现

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

20240729232436

public interface State {
/**
* 投入 25 分钱
*/
void insertQuarter();

/**
* 退回 25 分钱
*/
void ejectQuarter();

/**
* 转动曲柄
*/
void turnCrank();

/**
* 发放糖果
*/
void dispense();
}
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();
}
}

策略(Strategy)

目的

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

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

UML

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

20240729232507

与状态模式比较

状态模式的类图和策略模式类似,并且都是能够动态改变对象的行为。但是状态模式是通过状态转移来改变 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();
}
}

模板方法(Template Method)

目的

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

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

UML

20240729232533

实现

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

20240729232541

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

访问者(Visitor)

目的

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

UML

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

20240729232603

实现

public interface Element {
void accept(Visitor visitor);
}
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);
}
}
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);
}
}
}
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);
}
}
public interface Visitor {
void visit(Customer customer);

void visit(Order order);

void visit(Item item);
}
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();
}
}

空对象(Null)

目的

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

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

UML

20240729232635

实现

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

结构型

适配器(Adapter)

目的

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

UML

20240729232656

实现

鸭子(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!");
}
}
public class TurkeyAdapter implements Duck{
Turkey turkey;

public TurkeyAdapter(Turkey turkey) {
this.turkey = turkey;
}

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

桥接(Bridge)

目的

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

UML

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

20240729232718

实现

RemoteControl 表示遥控器,代指 Abstraction

TV 代表电视,代指 Implementor

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

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();
RemoteControl remoteControl2 = new ConcreteRemoteControl2(new Sony());
remoteControl2.on();
remoteControl2.off();
remoteControl2.tuneChannel();
}
}

组合(Composite)

目的

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

UML

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

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

20240729232749

实现

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("left:" + 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();
}
}

装饰(Decorator)

目的

给对象动态添加功能

UML

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

20240729232843

实现

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

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

20240729232851

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

外观(Facade)

目的

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

UML

20240729232918

实现

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()");
}
}
public class Facade {
private SubSystem subSystem = new SubSystem();

public void watchMovie() {
subSystem.turnOnTV();
subSystem.setCD("a movie");
subSystem.startWatching();
}
}
public class Client {
public static void main(String[] args) {
Facade facade = new Facade();
facade.watchMovie();
}
}

享元(Flyweight)

目的

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

UML

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

20240729232937

实现

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

代理(Proxy)

目的

控制对其它对象的访问

UML

代理有以下四类:

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

20240729232959

实现

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

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