设计模式(Design Patterns)是常见的软件设计问题的可重用解决方案,可以提高代码的可重用性和可维护性。最早提出设计模式的人(Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides)合称“四人帮”(Gang of Four,GOF),他们所提出的设计模式主要基于以下两个面向对象设计原则:对接口编程而不是对实现编程、优先使用对象组合而不是继承。
相关阅读:Mermaid绘图库基本教程
设计模式的六大原则……
- 单一职责原则(SRP):一个类只应该有一个引起它变化的原因。
- 开放封闭原则(OCP):软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。
- 里氏替换原则(LSP):子类型必须能够替换掉它们的父类型。
- 依赖倒置原则(DIP):高层模块不应该依赖于低层模块,两者都应该依赖于抽象接口;抽象接口不应该依赖于具体实现,具体实现应该依赖于抽象接口。
- 接口隔离原则(ISP):不应该强迫一个类实现它不需要的接口,应该将接口拆分成更小和更具体的部分,以便客户端只需要知道它们感兴趣的部分。
- 迪米特法则(LOD):一个对象应该对其他对象有尽可能少的了解,通常称为“最少知识原则”。
Hyplus目录
- 1 创建型模式(Creational Patterns)
- 2 结构型模式(Structural Patterns)
- 3 行为型模式(Behavioral Patterns)
- 3.1 责任链模式(Chain of Responsibility Pattern)
- 3.2 命令模式(Command Pattern)
- 3.3 迭代器模式(Iterator Pattern)
- 3.4 中介者模式(Mediator Pattern)
- 3.5 备忘录模式(Memento Pattern)
- 3.6 解释器模式(Interpreter Pattern)
- 3.7 状态模式(State Pattern)
- 3.8 策略模式(Strategy Pattern)
- 3.9 模板方法模式(Template Method Pattern)
- 3.10 访问者模式(Visitor Pattern)
- 3.11 观察者模式(Observer Pattern)
- 4 其他
1 创建型模式(Creational Patterns)
创建型模式关注于对象的创建,提供了更灵活的对象创建方式。
1.1 单例模式(Singleton Pattern)
核心思想:确保一个类只有一个实例,并提供一个全局访问点。
实现方式:通过私有化构造函数和提供一个静态方法来获取实例。
应用场景:数据库连接池、配置管理类、日志记录器。
优点:
- 控制实例数量:保证只有一个实例。
- 提供全局访问点:方便在全局范围内访问该实例。
单例模式实现方式对比及应用场景:
实现方式 | 延迟加载 | 线程安全 | 实现复杂度 | 性能影响 | 反射/反序列化防护 |
---|---|---|---|---|---|
饿汉式(静态常量/代码块) | 否 | 是 | 低 | 较好(无同步开销) | 需额外判断防护 |
懒汉式(同步方法) | 是 | 是 | 低 | 每次调用均有同步开销 | 需额外判断防护 |
双重检查锁定 | 是 | 是 | 中 | 初次同步,后续无同步 | 需额外判断防护 |
静态内部类 | 是 | 是 | 中 | JVM机制,无额外同步 | 需额外判断防护 |
枚举 | 是 | 是 | 最低 | 最优(JVM保证) | 天然防护 |
1.1.1 饿汉式(Eager Initialization)
优点:
- 实现简单。
- 线程安全,无需额外的同步控制。
缺点:
- 没有延迟加载效果(即使应用中可能永远不会使用这个实例,也会在类加载时创建)。
存在如下两种实现方式:
- 使用静态常量,在类加载时就完成创建单例实例。这样保证了线程安全,因为Java类的加载是线程安全的
public class Singleton {
// 类加载时创建实例,保证线程安全
private static final Singleton INSTANCE = new Singleton();
// 私有构造方法,防止外部实例化
private Singleton() {
// 防止通过反射调用私有构造方法创建多个实例
if (INSTANCE != null) {
throw new IllegalStateException("实例已经存在");
}
}
// 全局访问点
public static Singleton getInstance() {
return INSTANCE;
}
}
- 利用静态代码块在类加载时创建实例,与静态常量方式类似
public class Singleton {
private static final Singleton INSTANCE;
// 静态代码块,在类加载时执行
static {
INSTANCE = new Singleton();
}
private Singleton() {
if (INSTANCE != null) {
throw new IllegalStateException("实例已经存在");
}
}
public static Singleton getInstance() {
return INSTANCE;
}
}
1.1.2 懒汉式(Lazy Initialization)
存在如下几种实现方式——
1.1.2.1 线程不安全
在第一次调用getInstance()
时创建实例,实现延迟加载。但在多线程环境下存在竞争条件,可能创建多个实例。
示例:
public class Singleton {
// 延迟加载实例
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
// 多线程环境下可能出现问题
instance = new Singleton();
}
return instance;
}
}
问题与风险:
- 在多线程场景下,两个线程可能同时判断
instance == null
,从而各自创建一个实例,违背了单例原则。
1.1.2.2 加锁同步
通过在getInstance()
方法上加synchronized
关键字,保证同一时刻只有一个线程进入该方法,从而确保只创建一个实例。
示例:
public class Singleton {
private static Singleton instance;
private Singleton() {}
// 方法同步,保证线程安全
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
优点:
- 实现简单。
- 能够保证线程安全。
缺点:
- 整个方法加锁,每次调用都需要同步,降低了性能(尽管实例只创建一次,但每次访问都涉及同步开销)。
1.1.2.3 双重检查锁定(Double-Check Locking)
在进入同步块前先进行一次非同步检查,只有当实例为null
时才进入同步块;在同步块内部再进行一次检查,确保只创建一次实例。【推荐】
必须将实例声明为volatile
,防止由于指令重排序造成线程安全问题。
示例:
public class Singleton {
// volatile关键字确保多线程环境下变量的可见性和禁止指令重排序
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) { // 第一次检查(无锁)
synchronized (Singleton.class) {
if (instance == null) { // 第二次检查(有锁)
instance = new Singleton();
}
}
}
return instance;
}
}
双重检查(Double-Check)的作用:第一次检查是为了避免每次都进入同步块,提高效率;第二次检查是为了防止多个线程在同步块内同时创建实例。
volatile
的作用:在Java中,实例化对象(instance = new Singleton();
)实际上可分为以下几个步骤:① 分配内存空间 → ② 初始化对象 → ③ 将内存地址赋值给instance
变量。由于指令重排序,步骤②和③可能会调换,若没有volatile
修饰,另一个线程可能会看到一个未完全初始化的对象。
优点:
- 线程安全、实现延迟加载。
- 同步代码块只在第一次初始化时执行,提高了效率。
缺点:
- 实现相对复杂,需要正确使用
volatile
和双重检查机制。
1.1.3 静态内部类
利用JVM类加载机制实现延迟加载和线程安全。静态内部类只有在外部类调用getInstance()
时才会被加载,从而实现延迟加载效果,同时JVM保证类加载时的线程安全性。【推荐】
示例:
public class Singleton {
private Singleton() {}
// 静态内部类,负责持有Singleton实例
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
// 当调用getInstance()时,SingletonHolder会被加载并初始化INSTANCE
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
优点:
- 实现延迟加载。
- JVM在加载类时保证线程安全,无需显式同步。
- 代码简洁易懂。
缺点:
- 静态内部类的机制对初学者可能不够直观,需要理解类加载的原理。
1.1.4 枚举
使用枚举类型实现单例,利用Java枚举的特性保证单例的唯一性和线程安全性。【推荐】
枚举实现不仅天然防止了反射攻击,还能防止反序列化重新创建实例,因为Java保证了每个枚举常量在JVM中都是唯一的。
反射攻击:
- 基本思想:即使构造器为私有,通过反射仍然可以调用构造方法来创建对象,从而破坏单例。
- 应对策略:在构造方法中判断是否已有实例存在,如果存在则抛出异常(如上面饿汉式示例中的处理)。
反序列化:
- 基本思想:如果单例类实现了
Serializable
接口,反序列化时会创建一个新的实例,破坏单例。- 应对策略:可通过实现
readResolve()
方法来返回同一个实例,或使用枚举方式天然避免该问题。
示例:
public enum Singleton {
INSTANCE; // 枚举中的唯一实例
// 可以添加其他方法
public void someMethod() {
// 实现具体逻辑
}
}
使用方式:
public class TestEnumSingleton {
public static void main(String[] args) {
// 获取枚举单例实例
Singleton singleton = Singleton.INSTANCE;
singleton.someMethod();
}
}
优点:
- 实现简单、代码精炼。
- 天然线程安全。
- 防止反射和反序列化破坏单例(反射很难创建枚举实例)。
缺点:
- 如果需要继承其他类或实现某种接口,枚举的局限性可能会带来一些限制。
- 在某些场景下,枚举的语法风格可能不符合团队的编码规范。
- 不支持延迟加载,因为枚举常量在类加载时就已被实例化,需要延迟加载请使用双重检查锁定或静态内部类。
1.2 原型模式(Prototype Pattern)
核心思想:通过复制现有的实例来创建新实例,而不是通过构造函数。
实现方式:实现Cloneable
接口并重写clone()
方法来复制对象。
应用场景:游戏角色复制、避免重复初始化开销。
优点:
- 减少创建新对象的开销:通过复制现有对象创建新对象。
- 动态配置对象:可以在运行时配置对象状态。
示例:
abstract class Shape implements Cloneable {
private String id;
protected String type;
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getType() { return type; }
public void setType(String type) { this.type = type; }
public abstract void draw();
@Override
public Object clone() {
Object clone = null;
try {
clone = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
}
class Circle extends Shape {
public Circle() {
type = "Circle";
}
@Override
public void draw() {
System.out.println("Inside Circle::draw() method.");
}
}
class Square extends Shape {
public Square() {
type = "Square";
}
@Override
public void draw() {
System.out.println("Inside Square::draw() method.");
}
}
class ShapeCache {
private static Map<String, Shape> shapeMap = new HashMap<>();
public static Shape getShape(String shapeId) {
Shape cachedShape = shapeMap.get(shapeId);
return (Shape) cachedShape.clone();
}
// For each shape run database query and create shape
// shapeMap.put(shapeKey, shape);
// for example, we are adding three shapes
public static void loadCache() {
Circle circle = new Circle();
circle.setId("1");
shapeMap.put(circle.getId(), circle);
Square square = new Square();
square.setId("2");
shapeMap.put(square.getId(), square);
}
}
public class PrototypePatternDemo {
public static void main(String[] args) {
ShapeCache.loadCache();
Shape clonedShape = ShapeCache.getShape("1");
System.out.println("Shape: " + clonedShape.getType());
Shape clonedShape2 = ShapeCache.getShape("2");
System.out.println("Shape: " + clonedShape2.getType());
}
}
在本例中,Shape
是一个抽象类,实现了Cloneable
接口并重写了clone()
方法。Circle
和Square
是Shape
的具体子类,实现了draw()
方法。ShapeCache
类是一个缓存,存储了Shape
对象的副本。PrototypePatternDemo
类是一个演示类,使用ShapeCache
来获取Shape
对象的副本。
在loadCache()
方法中,创建两个Shape
对象的副本,并将它们存储在shapeMap
中。在main()
方法中,使用getShape()
方法来获取Shape
对象的副本,并输出它们的类型。由于使用了原型模式,所以可以通过复制现有对象来创建新对象,而无需实例化类。
1.3 工厂方法模式(Factory Method Pattern)
核心思想:定义一个创建对象的接口,但由子类决定实例化哪个类。
实现方式:将对象的创建逻辑放在子类中,而不是在客户端代码中。
应用场景:日志记录器(文件/数据库日志)、跨平台 UI 组件。
优点:
- 灵活性:可以在运行时决定创建对象的类型。
- 符合开闭原则:对扩展开放,对修改关闭。
示例:
abstract class Animal {
public abstract void sound();
}
class Cat extends Animal {
@Override
public void sound() {
System.out.println("Meow");
}
}
class Dog extends Animal {
@Override
public void sound() {
System.out.println("Bark");
}
}
// 工厂类
class AnimalFactory {
// 根据传入的参数创建具体的产品类对象的静态方法
public static Animal createAnimal(String type) {
if (type.equalsIgnoreCase("dog")) {
return new Dog();
} else if (type.equalsIgnoreCase("cat")) {
return new Cat();
} else {
throw new IllegalArgumentException("Invalid animal type: " + type);
}
}
}
public class FactoryMethodPatternDemo {
public static void main(String[] args) {
Animal dog = AnimalFactory.createAnimal("dog");
dog.sound();
Animal cat = AnimalFactory.createAnimal("cat");
cat.sound();
}
}
1.4 抽象工厂模式(Abstract Factory Pattern)
核心思想:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
实现方式:通过定义多个工厂接口,每个接口负责创建一组相关的对象。
应用场景:跨平台UI组件库(如Windows/MacOS风格的按钮和文本框)。
与工厂方法的区别:工厂方法关注单一产品,抽象工厂处理产品族。
优点:
- 一致性:确保创建的一系列对象具有一致性。
- 扩展性:易于扩展产品系列,而不影响现有代码。
// 抽象产品类
abstract class Animal {
public abstract void sound();
}
class Cat extends Animal {
@Override
public void sound() {
System.out.println("Meow");
}
}
class Dog extends Animal {
@Override
public void sound() {
System.out.println("Bark");
}
}
// 抽象工厂类
abstract class AnimalFactory {
public abstract Animal createAnimal();
}
class CatFactory extends AnimalFactory {
@Override
public Animal createAnimal() {
return new Cat();
}
}
class DogFactory extends AnimalFactory {
@Override
public Animal createAnimal() {
return new Dog();
}
}
public class AbstractFactoryPatternDemo {
public static void main(String[] args) {
AnimalFactory dogFactory = new DogFactory();
Animal dog = dogFactory.createAnimal();
dog.sound();
AnimalFactory catFactory = new CatFactory();
Animal cat = catFactory.createAnimal();
cat.sound();
}
}
1.5 建造者模式(Builder Pattern)
核心思想:使用多个简单的对象一步一步构建一个复杂的对象。
实现方式:定义一个建造者接口和具体建造者类,通过建造者类来创建复杂对象。
应用场景:StringBuilder
、AlertDialog.Builder
。
优点:
- 解耦:将复杂对象的构建与表示解耦。
- 灵活性:可以根据需要创建不同表示的复杂对象。
示例:
class Car {
private String make;
private String model;
private int year;
private String engine;
private int seats;
public Car(String make, String model, int year, String engine, int seats) {
this.make = make;
this.model = model;
this.year = year;
this.engine = engine;
this.seats = seats;
}
/* ... getter setter ... */
}
class CarBuilder {
private String make;
private String model;
private int year;
private String engine;
private int seats;
public CarBuilder setMake(String make) {
this.make = make;
return this;
}
public CarBuilder setModel(String model) {
this.model = model;
return this;
}
public CarBuilder setYear(int year) {
this.year = year;
return this;
}
public CarBuilder setEngine(String engine) {
this.engine = engine;
return this;
}
public CarBuilder setSeats(int seats) {
this.seats = seats;
return this;
}
public Car build() {
return new Car(make, model, year, engine, seats);
}
}
public class BuilderPatternDemo {
public static void main(String[] args) {
CarBuilder carBuilder = new CarBuilder();
Car car = carBuilder.setMake("Hyplus")
.setModel("S37")
.setYear(2096)
.setEngine("ReAge")
.setSeats(8)
.build();
}
}
2 结构型模式(Structural Patterns)
结构型模式关注如何将类或对象组合成更大的结构,以便更好地实现功能。
2.1 适配器模式(Adapter Pattern)
核心思想:将一个类的接口转换成客户希望的另一个接口。适配器模式使得原本接口不兼容的类可以合作。
实现方式:通过引入一个适配器类,将目标接口转换为适配者接口。
应用场景:旧系统改造、第三方库集成。
优点:
- 接口兼容:使得接口不兼容的类可以协作。
- 复用性:可以复用现有的类。
示例:
class Adaptee {
public void specificRequest() {
System.out.println("Adaptee's specific request");
}
}
interface Target {
public void request();
}
class Adapter implements Target {
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
public void request() {
adaptee.specificRequest();
}
}
public class AdapterPatternDemo {
public static void main(String[] args) {
Adaptee adaptee = new Adaptee();
Target target = new Adapter(adaptee);
target.request();
}
}
上例中定义了一个目标接口Target
、一个源接口Adaptee
和一个适配器类Adapter
。适配器类实现了目标接口,并且将请求转发到源接口的特定请求方法。用于测试的Client
类中创建了一个适配器对象,并使用它来调用目标接口的方法。
2.2 桥接模式(Bridge Pattern)
核心思想:将抽象部分与实现部分分离,使它们可以独立地变化。
实现方式:通过定义抽象类和实现类,将它们的变化解耦。
应用场景:不同图形API(OpenGL/Vulkan)与形状(圆/方)的组合。
优点:
- 解耦:抽象和实现的解耦,使得它们可以独立变化。
- 灵活性:可以独立地扩展抽象和实现。
示例:
public interface Color {
void applyColor();
}
public abstract class Shape {
protected Color color;
public Shape(Color color) {
this.color = color;
}
public abstract void applyColor();
}
public class Red implements Color {
@Override
public void applyColor() {
System.out.println("Applying red color");
}
}
public class Blue implements Color {
@Override
public void applyColor() {
System.out.println("Applying blue color");
}
}
public class Circle extends Shape {
public Circle(Color color) {
super(color);
}
@Override
public void applyColor() {
System.out.print("Circle applying color: ");
color.applyColor();
}
}
public class Square extends Shape {
public Square(Color color) {
super(color);
}
@Override
public void applyColor() {
System.out.print("Square applying color: ");
color.applyColor();
}
}
public class BridgePatternDemo {
public static void main(String[] args) {
Color blue = new Blue();
Shape square = new Square(new Red());
Shape circle = new Circle(blue);
square.applyColor();
circle.applyColor();
}
}
2.3 组合模式(Composite Pattern)
核心思想:将对象组合成树形结构以表示部分-整体层次结构,使得客户端对单个对象和组合对象的使用具有一致性。
实现方式:通过定义一个组件接口,将叶子节点和容器节点统一处理。
应用场景:文件系统(文件/文件夹统一处理)、UI容器组件。
优点:
- 一致性:对单个对象和组合对象的一致性操作。
- 简化客户端代码:客户端代码可以统一处理叶子节点和容器节点。
示例:
interface IComponent {
void display();
}
abstract class Component implements IComponent {
protected String name;
public Component(String name) {
this.name = name;
}
public abstract void add(IComponent component);
public abstract void remove(IComponent component);
}
class Composite extends Component {
private List<IComponent> children = new ArrayList<>();
public Composite(String name) {
super(name);
}
@Override
public void add(IComponent component) {
children.add(component);
}
@Override
public void remove(IComponent component) {
children.remove(component);
}
@Override
public void display() {
System.out.println("Composite: " + name);
for (IComponent component : children) {
component.display();
}
}
}
class Leaf implements IComponent {
private String name;
public Leaf(String name) {
this.name = name;
}
@Override
public void display() {
System.out.println("Leaf: " + name);
}
}
public class CompositePatternDemo {
public static void main(String[] args) {
Component root = new Composite("root");
Component branch1 = new Composite("branch1");
Component branch2 = new Composite("branch2");
Component leaf1 = new Leaf("leaf1");
Component leaf2 = new Leaf("leaf2");
Component leaf3 = new Leaf("leaf3");
root.add(branch1);
root.add(branch2);
branch1.add(leaf1);
branch2.add(leaf2);
branch2.add(leaf3);
root.display();
}
}
在上例中,使用抽象类Component
表示树形结构中的节点,包括叶节点和组合节点。组合节点包含子节点,可以通过add()
和remove()
方法来添加和删除子节点。叶节点没有子节点,但可以实现共同的操作方法。具体的组合节点Composite
继承自Component
类,实现自己的操作方法。
2.4 装饰器模式(Decorator Pattern)
核心思想:动态地给一个对象添加一些额外的职责。装饰器模式提供了比继承更灵活的扩展功能的方式。
实现方式:通过定义装饰器类来扩展被装饰对象的功能。
应用场景:Java I/O流(BufferedInputStream
包装FileInputStream)。
优点:
- 灵活性:可以动态地扩展对象的功能。
- 避免子类爆炸:通过装饰器而不是继承来扩展功能。
示例:
// 组件接口
interface Component {
void operation();
}
// 具体组件
class ConcreteComponent implements Component {
@Override
public void operation() {
System.out.println("ConcreteComponent operation");
}
}
// 装饰器抽象类
abstract class Decorator implements Component {
protected Component component;
public Decorator(Component component) {
this.component = component;
}
@Override
public void operation() {
component.operation();
}
}
// 具体装饰器
class ConcreteDecorator extends Decorator {
public ConcreteDecorator(Component component) {
super(component);
}
@Override
public void operation() {
super.operation();
addedBehavior();
}
private void addedBehavior() {
System.out.println("ConcreteDecorator addedBehavior");
}
}
public class DecoratorPatternDemo {
public static void main(String[] args) {
Component component = new ConcreteComponent();
Component decorator = new ConcreteDecorator(component);
decorator.operation(); // 执行装饰后的操作
}
}
2.5 外观模式(Facade Pattern)
核心思想:为子系统中的一组接口提供一个一致的界面,使得子系统更容易使用。
实现方式:通过定义一个外观类来封装子系统的复杂性,提供简化的接口。
应用场景:Spring的JdbcUtils
封装数据库操作细节。
优点:
- 简化使用:提供简单的接口来访问复杂的子系统。
- 解耦:将客户端与子系统解耦。
示例:
class CPU {
public void processData() {
System.out.println("正在处理数据...");
}
}
class Memory {
public void load() {
System.out.println("正在加载内存...");
}
}
class HardDrive {
public void readData() {
System.out.println("正在读取硬盘数据...");
}
}
// 外观类
class ComputerFacade {
private CPU cpu;
private Memory memory;
private HardDrive hardDrive;
public ComputerFacade() {
cpu = new CPU();
memory = new Memory();
hardDrive = new HardDrive();
}
public void start() {
System.out.println("启动计算机...");
cpu.processData();
memory.load();
hardDrive.readData();
System.out.println("计算机启动完毕!");
}
}
public class FacadePatternDemo {
public static void main(String[] args) {
ComputerFacade computer = new ComputerFacade();
computer.start();
}
}
2.6 享元模式(Flyweight Pattern)
核心思想:运用共享技术有效地支持大量细粒度的对象。
实现方式:通过将对象的共享部分与独享部分分开,将共享部分提取出来。
应用场景:棋类游戏的棋子对象复用、字符串常量池。
优点:
- 节省内存:通过共享来减少内存使用。
- 提高性能:减少对象创建和管理的开销。
示例:
interface Shape {
void draw();
}
// 具体享元类
class Circle implements Shape {
private String color;
private int x;
private int y;
private int radius;
public Circle(String color) {
this.color = color;
}
public void setX(int x) { this.x = x; }
public void setY(int y) { this.y = y; }
public void setRadius(int radius) { this.radius = radius; }
@Override
public void draw() {
System.out.println("画了一个" + color + "的圆,半径为" + radius + ",位置为(" + x + "," + y + ")");
}
}
// 享元工厂类
class ShapeFactory {
private static final Map<String, Shape> circleMap = new HashMap<>();
public static Shape getCircle(String color) {
Circle circle = (Circle) circleMap.get(color);
if (circle == null) {
circle = new Circle(color);
circleMap.put(color, circle);
System.out.println("创建了一个" + color + "的圆");
}
return circle;
}
}
public class FlyweightPatternDemo {
private static final String[] colors = { "红色", "绿色", "蓝色", "黄色", "黑色" };
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
Circle circle = (Circle) ShapeFactory.getCircle(getRandomColor());
circle.setX(getRandomX());
circle.setY(getRandomY());
circle.setRadius(100);
circle.draw();
}
}
private static String getRandomColor() {
return colors[(int) (Math.random() * colors.length)];
}
private static int getRandomX() {
return (int) (Math.random() * 100);
}
private static int getRandomY() {
return (int) (Math.random() * 100);
}
}
上例中的Shape
接口定义了一个draw()
方法,用于绘制图形。具体享元类Circle
实现了Shape
接口,并存储了圆的颜色、位置和半径等信息。享元工厂类ShapeFactory
维护了一个用于存储已经创建的圆对象的Map,在客户端需要绘制圆时,可通过ShapeFactory
获取已存在的圆对象,若不存在,则创建一个新的圆对象,并将其存储在Map中。在客户端代码中随机生成20个圆,每个圆的颜色、位置和半径都是随机的。通过ShapeFactory
获取圆对象,并调用其draw()
方法来绘制图形。由于相同颜色的圆对象被共享,因此减少了内存使用和对象创建的开销。
2.7 代理模式(Proxy Pattern)
核心思想:为其他对象提供一种代理以控制对这个对象的访问。
实现方式:通过定义代理类来控制对真实对象的访问。
应用场景:AOP、RPC。
优点:
- 控制访问:可以在代理中实现对真实对象的控制。
- 增强功能:可以在代理中增加额外的功能,如延迟加载。
代理模式又分为静态代理、jdk动态代理、cglib动态代理三种实现方式。
2.7.1 静态代理
被代理对象与代理对象需实现相同的接口或继承相同父类,因此需定义一个接口或抽象类。
示例:
// 代理接口
interface IHello {
String hi(String key);
}
// 代理接口实现类
class HelloImpl implements IHello {
@Override
public String hi(String key) {
String str = "hello:" + key;
System.out.println("HelloImpl! " + str);
return str;
}
}
// 静态代理类
class HelloStaticProxy implements IHello {
private IHello hello;
public HelloStaticProxy(IHello hello) {
this.hello = hello;
}
@Override
public String hi(String key) {
System.out.println(">>> static proxy start");
String result = hello.hi(key);
System.out.println(">>> static proxy end");
return result;
}
}
public class StaticProxyDemo {
public static void main(String[] args) {
IHello helloProxy = new HelloStaticProxy(new HelloImpl());
helloProxy.hi("world");
}
}
2.7.2 JDK动态代理
基于接口的一种代理方式,目标对象必须实现接口。
原理:利用反射机制,动态生成匿名类继承Proxy类并实现了要代理的接口。
由于Java不支持多继承,故本方法不能代理类。
示例:
// 代理接口
interface IHello {
String hi(String key);
}
// 代理接口实现类
class HelloImpl implements IHello {
@Override
public String hi(String key) {
String str = "hello:" + key;
System.out.println("HelloImpl! " + str);
return str;
}
}
// JDK动态代理类
class JdkProxy implements InvocationHandler {
private Object target;
public JdkProxy(Object target) {
this.target = target;
}
// 获取被代理接口实例对象
public <T> T getProxy() {
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(">>> JdkProxy start");
Object result = method.invoke(target, args);
System.out.println(">>> JdkProxy end");
return result;
}
}
public class JdkProxyDemo {
public static void main(String[] args) {
JdkProxy proxy = new JdkProxy(new HelloImpl());
IHello helloProxy = proxy.getProxy();
helloProxy.hi("jdk proxy!");
}
}
2.7.3 CGLIB动态代理
需事先导入CGLIB(Code Generation Library)的jar包:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>${cglib.version}</version>
</dependency>
目标对象可以不用实现接口,不能针对final
类进行代理。
原理:动态生成class
继承目标对象。
示例:
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
// 目标类
class ServiceImpl {
public void doSomething() {
System.out.println("do something");
}
}
// CGLIB代理类
public class CglibProxy {
public static <T> T getProxy(Class<?> clazz) {
// 创建Enhancer对象
Enhancer enhancer = new Enhancer();
// 设置要代理的类
enhancer.setSuperclass(clazz);
// 设置代理逻辑
enhancer.setCallback((MethodInterceptor) (o, method, args, methodProxy) -> {
System.out.println("before do something");
// 调用原始对象的方法
Object result = methodProxy.invokeSuper(o, args);
System.out.println("after do something");
return result;
});
// 创建代理对象
return (T) enhancer.create();
}
}
public class CglibProxyDemo {
public static void main(String[] args) throws Exception {
ServiceImpl service = new ServiceImpl();
ServiceImpl proxy = CglibProxy.getProxy(service.getClass());
// 通过代理对象调用方法
// proxy.getClass().getMethod("doSomething").invoke(proxy);
proxy.doSomething();
}
}
3 行为型模式(Behavioral Patterns)
行为型模式关注对象之间的沟通和职责分配。
3.1 责任链模式(Chain of Responsibility Pattern)
核心思想:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。
实现方式:通过定义处理请求的链,并逐步将请求传递给链中的各个对象,直到找到合适的处理者。
应用场景:Servlet Filter、审批流程等。
优点:
- 解耦:发送者和接收者解耦。
- 灵活性:可以动态地添加或修改处理者。
示例:
interface Handler {
Handler setNextHandler(Handler nextHandler);
void handleRequest(Request request);
}
// 抽象处理器
abstract class AbstractHandler implements Handler {
private Handler nextHandler;
public Handler setNextHandler(Handler nextHandler) {
this.nextHandler = nextHandler;
return this.nextHandler;
}
public Handler getNextHandler() {
return nextHandler;
}
}
// 具体处理器
class ConcreteHandler1 extends AbstractHandler {
public void handleRequest(Request request) {
if (request.getType().equals("Type1")) {
System.out.println("ConcreteHandler1 handles request " + request);
} else {
getNextHandler().handleRequest(request);
}
}
}
class ConcreteHandler2 extends AbstractHandler {
public void handleRequest(Request request) {
if (request.getType().equals("Type2")) {
System.out.println("ConcreteHandler2 handles request " + request);
} else {
getNextHandler().handleRequest(request);
}
}
}
class ConcreteHandler3 extends AbstractHandler {
public void handleRequest(Request request) {
if (request.getType().equals("Type3")) {
System.out.println("ConcreteHandler3 handles request " + request);
} else {
getNextHandler().handleRequest(request);
}
}
}
// 请求类
class Request {
private String type;
public Request(String type) {
this.type = type;
}
public String getType() {
return type;
}
public String toString() {
return "Request [type=" + type + "]";
}
}
public class ChainOfResponsibilityPatternDemo {
public static void main(String[] args) {
Handler handler1 = new ConcreteHandler1();
Handler handler2 = new ConcreteHandler2();
Handler handler3 = new ConcreteHandler3();
handler1.setNextHandler(handler2)
.setNextHandler(handler3);
handler1.handleRequest(new Request("Type1"));
handler1.handleRequest(new Request("Type2"));
handler1.handleRequest(new Request("Type3"));
}
}
ConcreteHandler1 handles request Request [type=Type1]
ConcreteHandler2 handles request Request [type=Type2]
ConcreteHandler3 handles request Request [type=Type3]
上例演示了如何创建一个处理器链,并将请求沿着链传递,直到有一个处理程序处理它为止,其中具体处理器类ConcreteHandler1
、ConcreteHandler2
和ConcreteHandler3
继承自AbstractHandler
类,并实现了handleRequest()
方法。客户端代码创建了一个处理器链,并将请求发送到链的第一个处理器。当请求到达处理器时,它会检查请求类型是否与处理器可以处理的类型匹配。如果是,处理器将处理请求。否则,它将请求传递给链中的下一个处理器,直到有一个处理程序处理它为止。
3.2 命令模式(Command Pattern)
核心思想:将请求封装成一个对象,从而使你能够用不同的请求对客户进行参数化、队列化请求、以及支持可撤销操作。
实现方式:通过定义命令接口和具体命令类,将请求封装为对象,并将其传递给调用者。
应用场景:GUI菜单操作、事务管理。
优点:
- 解耦:发送者和接收者解耦。
- 灵活性:可以动态地创建、撤销请求。
示例:
interface Command {
void execute(String[] args);
}
// 具体命令
class CreateFileCommand implements Command {
public void execute(String[] args) {
// 根据给定的名称和内容创建文件的代码
System.out.println("创建文件: " + String.join(", ", args));
}
}
class DeleteFileCommand implements Command {
public void execute(String[] args) {
// 根据给定的名称删除文件的代码
System.out.println("删除文件: "+String.join(", ",args) );
}
}
// 命令执行者
class CommandExecutor {
private Map<String, Command> commands = new HashMap<>();
public CommandExecutor() {
// 关联命令名称与具体命令
commands.put("create", new CreateFileCommand());
commands.put("delete", new DeleteFileCommand());
}
public void executeCommand(String commandName, String[] args) {
// 查找对应的命令并执行
Command command = commands.get(commandName);
if (command != null) {
command.execute(args);
} else {
System.out.println("Unknown command: " + commandName);
}
}
}
public class CommandPatternDemo {
public static void main(String[] args) {
CommandExecutor executor = new CommandExecutor();
// 使用命令执行者执行命令
executor.executeCommand("create", new String[]{"file.txt", "Hello World!"});
executor.executeCommand("delete", new String[]{"file.txt"});
executor.executeCommand("unknown", new String[]{});
}
}
3.3 迭代器模式(Iterator Pattern)
核心思想:提供一种方法访问一个容器对象中各个元素,而又不暴露该对象的内部表示。
实现方式:通过定义迭代器接口和具体迭代器类来遍历集合对象中的元素。
应用场景:集合类、数据库查询结果等。
优点:
- 简化访问:提供统一的访问方式。
- 解耦:容器和迭代器解耦。
示例:
interface Iterator<T> {
boolean hasNext();
T next();
}
class ArrayIterator<T> implements Iterator<T> {
private T[] array;
private int currentIndex;
public ArrayIterator(T[] array) {
this.array = array;
this.currentIndex = 0;
}
public boolean hasNext() {
return currentIndex < array.length;
}
public T next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
T element = array[currentIndex];
currentIndex++;
return element;
}
}
public class IteratorPatternDemo {
public static void main(String[] args) {
Integer[] array = {1, 2, 3, 4, 5};
Iterator<Integer> iterator = new ArrayIterator<>(array);
// 使用迭代器遍历数组
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
3.4 中介者模式(Mediator Pattern)
核心思想:定义一个对象来封装一组对象之间的交互,使得对象之间的耦合松散,从而使得它们可以独立地改变。
实现方式:通过定义中介者接口和具体中介者类来协调对象之间的交互。
应用场景:聊天室消息转发、MVC控制器。
优点:
- 降低耦合:将对象间的交互集中在中介者中。
- 易于维护:中介者可以集中处理复杂的交互逻辑。
示例:
// 中介者接口
interface Mediator {
void send(String message, Colleague colleague); // 用于处理对象之间的交互
}
// 组件对象抽象类
abstract class Colleague {
protected Mediator mediator; // 用于处理对象之间的通信
public Colleague(Mediator mediator) {
this.mediator = mediator;
}
public abstract void receive(String message);
public abstract void send(String message);
}
// 组件对象实现类
class ConcreteColleague1 extends Colleague {
public ConcreteColleague1(Mediator mediator) {
super(mediator);
}
@Override
public void receive(String message) {
System.out.println("Colleague1 received message: " + message);
}
@Override
public void send(String message) {
System.out.println("Colleague1 sends message: " + message);
mediator.send(message, this);
}
}
class ConcreteColleague2 extends Colleague {
public ConcreteColleague2(Mediator mediator) {
super(mediator);
}
@Override
public void receive(String message) {
System.out.println("Colleague2 received message: " + message);
}
@Override
public void send(String message) {
System.out.println("Colleague2 sends message: " + message);
mediator.send(message, this);
}
}
// 中介者实现类,协调组件对象之间的通信
class ConcreteMediator implements Mediator {
private ConcreteColleague1 colleague1;
private ConcreteColleague2 colleague2;
public void setColleague1(ConcreteColleague1 colleague1) {
this.colleague1 = colleague1;
}
public void setColleague2(ConcreteColleague2 colleague2) {
this.colleague2 = colleague2;
}
@Override
public void send(String message, Colleague colleague) {
if (colleague == colleague1) {
colleague2.receive(message);
} else {
colleague1.receive(message);
}
}
}
public class MediatorPatternDemo {
public static void main(String[] args) {
ConcreteMediator mediator = new ConcreteMediator();
ConcreteColleague1 colleague1 = new ConcreteColleague1(mediator);
ConcreteColleague2 colleague2 = new ConcreteColleague2(mediator);
mediator.setColleague1(colleague1);
mediator.setColleague2(colleague2);
colleague1.send("Hello, Colleague2.");
colleague2.send("Hello, Colleague1.");
}
}
3.5 备忘录模式(Memento Pattern)
核心思想:在不暴露对象内部状态的情况下,捕获一个对象的内部状态,并在该对象外部保存这个状态。可以在以后将对象恢复到保存的状态。
实现方式:通过定义备忘录类来保存对象的状态,并通过发起人类和恢复者类来实现状态的恢复。
应用场景:文本编辑器撤销功能、游戏存档。
优点:
- 状态恢复:可以在需要的时候恢复对象的状态。
- 封装性:不暴露对象的内部状态。
示例:
// 原始对象
class Originator {
private String state; // 需要保存的状态
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
//创建备忘录对象,并将当前状态保存到备忘录对象中
public Memento createMemento() {
return new Memento(state);
}
// 从备忘录对象中恢复先前的状态
public void restore(Memento memento) {
state = memento.getState();
}
}
// 备忘录对象
class Memento {
private String state;
public Memento(String state) {
this.state = state;
}
public String getState() {
return state;
}
}
// Caretaker类负责管理备忘录对象
class Caretaker {
private Memento memento;
public void setMemento(Memento memento) {
this.memento = memento;
}
public Memento getMemento() {
return memento;
}
}
public class MementoPatternDemo {
public static void main(String[] args) {
Originator originator = new Originator();
Caretaker caretaker = new Caretaker();
// 保存原始对象的状态到备忘录对象中
originator.setState("State 1");
caretaker.setMemento(originator.createMemento());
// 修改原始对象的状态
originator.setState("State 2");
// 从备忘录对象中恢复先前的状态
originator.restore(caretaker.getMemento());
System.out.println("Current state: " + originator.getState());
}
}
3.6 解释器模式(Interpreter Pattern)
核心思想:给定一个语言,定义其文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。
实现方式:通过定义解释器类和表达式类,将文法规则和解释逻辑分开。
应用场景:编译器、SQL解析器、游戏规则引擎。
优点:
- 易于扩展:可以通过增加新的终结符和非终结符来扩展语法。
- 灵活性:可以定义复杂的语言规则。
示例:
interface Expression {
int interpret(Context context);
}
// 终结符表达式
class NumberExpression implements Expression {
private int value;
public NumberExpression(int value) {
this.value = value;
}
public int interpret(Context context) {
return value;
}
}
// 非终结符表达式
class AddExpression implements Expression {
private Expression left;
private Expression right;
public AddExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
public int interpret(Context context) {
return left.interpret(context) + right.interpret(context);
}
}
// 上下文
class Context {
private Map<String, Integer> variables = new HashMap<>();
public void setVariable(String name, int value) {
variables.put(name, value);
}
public int getVariable(String name) {
return variables.get(name);
}
}
// 解释器
class Interpreter {
private Expression expression;
public Interpreter(Expression expression) {
this.expression = expression;
}
public int interpret(Context context) {
return expression.interpret(context);
}
}
// 使用解释器执行表达式
public class InterpreterPatternDemo {
public static void main(String[] args) {
// 创建上下文
Context context = new Context();
context.setVariable("a", 10);
context.setVariable("b", 20);
// 创建表达式
Expression expression = new AddExpression(
new NumberExpression(context.getVariable("a")),
new NumberExpression(context.getVariable("b"))
);
// 创建解释器并执行表达式
Interpreter interpreter = new Interpreter(expression);
int result = interpreter.interpret(context);
System.out.println("Result: " + result);
}
}
在测试类中定义了两个终结符表达式(NumberExpression
)和一个非终结符表达式(AddExpression
),用于表示加法操作。还定义了一个上下文(Context
),用于存储变量和函数,以及一个解释器(Interpreter
),用于执行表达式。最后使用解释器执行一个简单的加法表达式,并打印结果。
3.7 状态模式(State Pattern)
核心思想:允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
实现方式:通过定义状态接口和具体状态类,将对象的状态和行为分开,使得状态改变时可以改变行为。
应用场景:游戏状态机、自动售货机、线程状态等。
优点:
- 状态独立:每个状态都有自己的行为。
- 易于扩展:可以增加新的状态而不改变现有代码。
示例:
// 状态接口
interface State {
void handle(Context context);
}
// 具体状态
class ConcreteStateA implements State {
@Override
public void handle(Context context) {
System.out.println("Handling state A");
context.setState(new ConcreteStateB()); // 切换到状态B
}
}
class ConcreteStateB implements State {
@Override
public void handle(Context context) {
System.out.println("Handling state B");
context.setState(new ConcreteStateA()); // 切换到状态A
}
}
// 上下文类
class Context {
private State state;
public Context(State state) {
this.state = state;
}
public void setState(State state) {
this.state = state;
}
public void request() {
state.handle(this);
}
}
public class StatePatternDemo {
public static void main(String[] args) {
Context context = new Context(new ConcreteStateA());
context.request(); // 处理状态A
context.request(); // 处理状态B
}
}
3.8 策略模式(Strategy Pattern)
核心思想:定义一系列算法,将每一个算法封装起来,并使它们可以相互替换。策略模式让算法独立于使用它的客户而独立变化。
实现方式:通过定义策略接口和具体策略类,将算法封装为对象,并在运行时选择使用。
应用场景:算法选择、游戏策略、数据排序等。
优点:
- 灵活性:可以动态选择算法。
- 易于扩展:可以新增策略而不影响现有代码。
示例:
interface PaymentStrategy {
void pay(double amount);
}
class CreditCardPayment implements PaymentStrategy {
private String cardNumber;
private String cvv;
private String expiryDate;
public CreditCardPayment(String cardNumber, String cvv, String expiryDate) {
this.cardNumber = cardNumber;
this.cvv = cvv;
this.expiryDate = expiryDate;
}
public void pay(double amount) {
System.out.println("Paying " + amount + " using credit card.");
}
}
class PayPalPayment implements PaymentStrategy {
private String email;
private String password;
public PayPalPayment(String email, String password) {
this.email = email;
this.password = password;
}
public void pay(double amount) {
System.out.println("Paying " + amount + " using PayPal.");
}
}
class CashPayment implements PaymentStrategy {
public void pay(double amount) {
System.out.println("Paying " + amount + " using cash.");
}
}
class PaymentProcessor {
private PaymentStrategy strategy;
public PaymentProcessor(PaymentStrategy strategy) {
this.strategy = strategy;
}
public void setStrategy(PaymentStrategy strategy) {
this.strategy = strategy;
}
public void processPayment(double amount) {
strategy.pay(amount);
}
}
public class StrategyPatternDemo {
public static void main(String[] args) {
PaymentProcessor processor = new PaymentProcessor(new CreditCardPayment("1234 5678 9012 3456", "123", "12/23"));
processor.processPayment(100.0);
processor.setStrategy(new PayPalPayment("hyexample@hypay.top", "hypassword"));
processor.processPayment(50.0);
processor.setStrategy(new CashPayment());
processor.processPayment(25.0);
}
}
3.9 模板方法模式(Template Method Pattern)
核心思想:定义一个操作中的算法的骨架,将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下重新定义算法中的某些步骤。
实现方式:通过定义模板方法在父类中,并将一些步骤的实现延迟到子类中。
应用场景:Servlet的service()
方法调度doGet()
/doPost()
等。
优点:
- 复用性:将公共算法逻辑放在父类中。
- 灵活性:子类可以改变某些步骤的实现而不改变算法结构。
示例:
abstract class Game {
abstract void initialize();
abstract void startPlay();
abstract void endPlay();
// 模板方法
public final void play() {
// 初始化游戏
initialize();
// 开始游戏
startPlay();
// 结束游戏
endPlay();
}
}
class Cricket extends Game {
@Override
void endPlay() {
System.out.println("Cricket Game Finished!");
}
@Override
void initialize() {
System.out.println("Cricket Game Initialized! Start playing.");
}
@Override
void startPlay() {
System.out.println("Cricket Game Started. Enjoy the game!");
}
}
class Football extends Game {
@Override
void endPlay() {
System.out.println("Football Game Finished!");
}
@Override
void initialize() {
System.out.println("Football Game Initialized! Start playing.");
}
@Override
void startPlay() {
System.out.println("Football Game Started. Enjoy the game!");
}
}
public class TemplatePatternDemo {
public static void main(String[] args) {
Game game = new Cricket();
game.play();
game = new Football();
game.play();
}
}
在本例中,Game
类是一个抽象类,定义了一个play()
方法作为模板方法。Cricket
和Football
类是具体的实现类,其实现了抽象类中定义的抽象方法。在测视力中先后创建一个Cricket
对象和一个Football
对象,并调用其play()
方法。由此即可在不改变算法结构的情况下重新定义算法中的某些步骤。
3.10 访问者模式(Visitor Pattern)
核心思想:表示一个作用于某对象结构中的各元素的操作,其可以在不改变元素类的前提下定义作用于这些元素的新操作。
实现方式:通过定义访问者接口和具体访问者类,将操作和对象结构分离,使得可以在不改变对象结构的情况下增加新的操作。
应用场景:编译器语法树分析、复杂对象结构遍历等。
优点:
- 扩展性:可以在不改变对象结构的情况下增加新的操作。
- 操作集中:操作被集中在访问者中,使得相关操作更易于维护。
示例:
interface Expression {
void accept(Visitor visitor);
}
class NumberExpression implements Expression {
private int value;
public NumberExpression(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
class AdditionExpression implements Expression {
private Expression left;
private Expression right;
public AdditionExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
public Expression getLeft() {
return left;
}
public Expression getRight() {
return right;
}
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
interface Visitor {
void visit(NumberExpression expression);
void visit(AdditionExpression expression);
}
class PrinterVisitor implements Visitor {
public void visit(NumberExpression expression) {
System.out.print(expression.getValue());
}
public void visit(AdditionExpression expression) {
System.out.print("(");
expression.getLeft().accept(this);
System.out.print("+");
expression.getRight().accept(this);
System.out.print(")");
}
}
class CalculatorVisitor implements Visitor {
private int result;
public void visit(NumberExpression expression) {
result = expression.getValue();
}
public void visit(AdditionExpression expression) {
expression.getLeft().accept(this);
int leftValue = result;
expression.getRight().accept(this);
int rightValue = result;
result = leftValue + rightValue;
}
public int getResult() {
return result;
}
}
public class VisitorPatternDemo {
public static void main(String[] args) {
// 构建表达式树:1 + (2 + 3)
Expression expression = new AdditionExpression(
new NumberExpression(1),
new AdditionExpression(
new NumberExpression(2),
new NumberExpression(3)
)
);
// 计算表达式的值
CalculatorVisitor calculator = new CalculatorVisitor();
expression.accept(calculator);
System.out.println("Result: " + calculator.getResult());
// 打印表达式的字符串表示
PrinterVisitor printer = new PrinterVisitor();
expression.accept(printer);
}
}
3.11 观察者模式(Observer Pattern)
核心思想:定义对象之间的一对多依赖,使得当一个对象改变状态时,所有依赖于它的对象都得到通知并被自动更新。
实现方式:通过定义观察者接口和被观察者类来实现一对多的通知机制。
应用场景:事件驱动、消息订阅。
优点:
- 解耦:观察者和被观察者之间的解耦。
- 动态更新:自动更新所有观察者。
示例:
// 抽象主题接口
interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
// 具体主题实现类
class WeatherStation implements Subject {
private List<Observer> observers;
private float temperature;
private float humidity;
private float pressure;
public WeatherStation() {
this.observers = new ArrayList<>();
}
public void registerObserver(Observer observer) {
observers.add(observer);
}
public void removeObserver(Observer observer) {
observers.remove(observer);
}
public void notifyObservers() {
System.out.println(">> 通知所有观察者 <<");
for (Observer observer : observers) {
System.out.println("------观察者: " + observer.name() + "-----------");
observer.update(temperature, humidity, pressure);
}
}
public void setMeasurements(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
notifyObservers();
}
}
// 抽象观察者接口
interface Observer {
void update(float temperature, float humidity, float pressure);
String name();
}
// 具体观察者实现类
class Display implements Observer {
private float temperature;
private float humidity;
private float pressure;
private String name;
public Display(String name){
this.name = name;
}
public String name() {
return this.name;
}
public void update(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
display();
}
public void display() {
System.out.println("Temperature: " + temperature);
System.out.println("Humidity: " + humidity);
System.out.println("Pressure: " + pressure);
}
}
public class ObserverPatternDemo {
public static void main(String[] args) {
// 使用观察者模式实现气象站
WeatherStation weatherStation = new WeatherStation();
Display display1 = new Display("01");
Display display2 = new Display("02");
weatherStation.registerObserver(display1);
weatherStation.registerObserver(display2);
weatherStation.setMeasurements(25.0f, 60.0f, 1013.0f);
weatherStation.removeObserver(display2);
weatherStation.setMeasurements(26.0f, 65.0f, 1012.0f);
}
}
Java中可直接使用Observable
抽象类和Observer
接口来实现观察者模式
Observable
抽象类表示一个可观察的对象,具有添加、删除和通知观察者的方法。当Observable
对象的状态发生改变时,会调用其notifyObservers()
方法,通知所有的观察者对象,并更新它们的状态。Observable
类还提供了setChanged()
和clearChanged()
方法,用于标记Observable
对象的状态是否发生了改变。Observer
接口表示观察者对象,具有用于更新状态的update()
方法。当Observable
对象的状态发生改变时,会调用观察者对象的update()
方法,传递更新的数据。
示例:
import java.util.Observable;
import java.util.Observer;
// 具体主题类,继承Observable抽象类
class WeatherStation extends Observable {
private float temperature;
private float humidity;
private float pressure;
public void setMeasurements(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
setChanged();
notifyObservers();
}
public float getTemperature() {
return temperature;
}
public float getHumidity() {
return humidity;
}
public float getPressure() {
return pressure;
}
}
// 具体观察者类
class Display implements Observer {
private float temperature;
private float humidity;
private float pressure;
public void update(Observable o, Object arg) {
if (o instanceof WeatherStation) {
WeatherStation weatherStation = (WeatherStation) o;
this.temperature = weatherStation.getTemperature();
this.humidity = weatherStation.getHumidity();
this.pressure = weatherStation.getPressure();
display();
}
}
public void display() {
System.out.println("Temperature: " + temperature);
System.out.println("Humidity: " + humidity);
System.out.println("Pressure: " + pressure);
}
}
public class JdkObserverPatternDemo {
public static void main(String[] args) {
// 使用JDK自带观察者模式实现气象站
WeatherStation weatherStation = new WeatherStation();
Display display1 = new Display();
Display display2 = new Display();
weatherStation.addObserver(display1);
weatherStation.addObserver(display2);
weatherStation.setMeasurements(25.0f, 60.0f, 1013.0f);
weatherStation.deleteObserver(display2);
weatherStation.setMeasurements(26.0f, 65.0f, 1012.0f);
}
}
4 其他
Java中其他常见的设计模式(不属于GOF的设计模式)。
4.1 过滤器模式(Filter Pattern)
Java过滤器模式用于在请求到达目标对象之前或之后,对请求进行处理或过滤。该模式可以用于实现不同的功能,如验证、授权、日志记录、压缩等,将不同的操作当作过滤链中的一个过滤器。
示例:
public interface Filter {
public void doFilter(String request);
}
// 授权过滤器
public class AuthenticationFilter implements Filter {
public void doFilter(String request) {
System.out.println("Authenticating request: " + request);
}
}
// 日志过滤器
public class LoggingFilter implements Filter {
public void doFilter(String request) {
System.out.println("Logging request: " + request);
}
}
// 过滤器链
public class FilterChain {
private List<Filter> filters = new ArrayList<Filter>();
private int index = 0;
public void addFilter(Filter filter) {
filters.add(filter);
}
public void doFilter(String request) {
if (index == filters.size()) {
return;
}
Filter filter = filters.get(index);
index++;
filter.doFilter(request);
doFilter(request);
}
}
public class FilterPatternDemo {
public static void main(String[] args) {
FilterChain chain = new FilterChain();
chain.addFilter(new AuthenticationFilter());
chain.addFilter(new LoggingFilter());
chain.doFilter("request");
}
}
在上例中,首先定义一个接口Filter
,该接口包含一个方法doFilter()
,用于处理请求;然后实现两个过滤器类,一个用于验证请求,另一个用于记录日志;最后定义一个FilterChain
类,用于链接多个过滤器。
4.2 空对象模式(Null Object Pattern)
Java空对象模式是一种行为型设计模式,允许在不返回null
的情况下提供默认行为。这种模式通常用于需要处理null
对象的情况下,以避免NullPointerException
异常。
该模式包括两个主要组件:抽象类和具体类。
示例:
interface User {
String getName();
boolean hasAccess();
}
// 具体类
class RealUser implements User {
private String name;
private boolean hasAccess;
public RealUser(String name, boolean hasAccess) {
this.name = name;
this.hasAccess = hasAccess;
}
public String getName() {
return name;
}
public boolean hasAccess() {
return hasAccess;
}
}
// 空对象
class NullUser implements User {
public String getName() {
return "Guest";
}
public boolean hasAccess() {
return false;
}
}
// 用于创建用户的工厂类
class UserFactory {
// 根据名称和权限创建一个用户
public static User getUser(String name, boolean hasAccess) {
if (name == null) {
return new NullUser();
}
return new RealUser(name, hasAccess);
}
}
public class NullObjectPatternDemo {
public static void main(String[] args) {
User user1 = UserFactory.getUser("Alice", true);
User user2 = UserFactory.getUser(null, false);
System.out.println("User 1: " + user1.getName() + ", has access: " + user1.hasAccess());
System.out.println("User 2: " + user2.getName() + ", has access: " + user2.hasAccess());
}
}