设计模式
创建型
单例模式(Singleton)
目的
确保一个类只有一个实例,并提供该实例的全局访问点
UML
使用一个私有构造函数,一个私有静态变量以及一个共有静态函数来实现
私有构造函数保证了不能通过构造函数来创建对象实例,只能通过公有静态函数返回唯一的私有静态变量
实现
懒汉式-线程不安全
私有静态变量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();
这句代码是分三步执行的
- 为 uniqueInstance 分配内存空间
- 初始化 uniqueInstance
- 将 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
实现
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()
方法创建,该方法是抽象的,需要子类去实现
实现
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,而工厂模式使用了继承
实现
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
实现
下面是一个简易的 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
实现
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)
实现
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:可以设置命令与命令的接收者
实现
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:上下文,包含解释器之外的一些全局信息
实现
下面是一个规则校验器实现,具有 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
实现
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:同事,相关对象
实现
Alarm(闹钟)、CoffeePot(咖啡壶)、Calendar(日历)、Sprinkler(喷头)是一组相关的对象,在某个对象的事件产生时需要去操作其它对象,形成了下面这种依赖结构:
使用中介者模式可以将复杂的依赖结构变成星形结构:
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 访问本备忘录的内部状态
实现
以下实现了一个简单计算器程序,可以输入两个值,然后计算这两个值的和。备忘录模式允许将这两个值存储起来,然后在某个时刻用存储的状态进行恢复
/**
* @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();
}