浅谈设计模式

本文最后更新于:1 年前

设计模式分类

设计模式是软件设计中常见问题的通用解决方案。以下是一些常用的设计模式,它们通常被分为三大类:创建型模式、结构型模式和行为型模式。

创建型模式(Creational Patterns)

  • 单例模式(Singleton):确保一个类只有一个实例,并提供一个全局访问点。
  • 工厂方法模式(Factory Method):定义一个用于创建对象的接口,让子类决定实例化哪一个类。
  • 抽象工厂模式(Abstract Factory):创建相关或依赖对象的家族,而不需明确指定具体类。
  • 建造者模式(Builder):构建一个复杂的对象,并允许按步骤构造。
  • 原型模式(Prototype):通过拷贝现有的实例创建新的实例,而不是通过新建。

结构型模式(Structural Patterns)

  • 适配器模式(Adapter):允许对象间的接口不兼容问题通过一个“适配器”解决。
  • 装饰器模式(Decorator):动态地给一个对象添加额外的职责。
  • 代理模式(Proxy):为其他对象提供一个代替或占位符,以控制对它的访问。
  • 外观模式(Facade):为子系统中的一组接口提供一个统一的高层接口。
  • 桥接模式(Bridge):将抽象部分与其实现部分分离,使它们可以独立地变化。
  • 组合模式(Composite):将对象组合成树形结构,以表示“部分-整体”的层次结构。
  • 享元模式(Flyweight):通过共享来高效地支持大量细粒度的对象。

行为型模式(Behavioral Patterns)

  • 策略模式(Strategy):定义一系列算法,把它们一个个封装起来,并使它们可互换。
  • 模板方法模式(Template Method):在方法中定义算法的框架,延迟到子类中实现。
  • 观察者模式(Observer):对象间的一对多依赖关系,当一个对象改变时,所有依赖于它的对象都会被通知。
  • 迭代器模式(Iterator):顺序访问一个聚合对象中的各个元素,不暴露其内部的表示。
  • 责任链模式(Chain of Responsibility):使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。
  • 命令模式(Command):将请求封装为一个对象,从而使用户可用不同的请求对客户进行参数化。
  • 备忘录模式(Memento):在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。
  • 状态模式(State):允许一个对象在其内部状态发生改变时改变其行为。
  • 访问者模式(Visitor):为一个对象结构(如组合结构)增加新能力。

这些设计模式是软件工程中的基础知识,它们帮助开发者避免重复发明轮子,提供经过验证的解决方案来解决常见问题。每种设计模式都有其特定的使用场景和优缺点,合理运用设计模式可以提高代码的灵活性和可维护性。

设计模式示例

创建型模式(Creational Patterns)

单例模式(Singleton)

单例模式是一种常用的设计模式,确保某个类只有一个实例,并提供一个全局访问点。以下是使用Java实现单例模式的一个简单示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class Singleton {

// 私有静态变量,保存类的唯一实例
private static Singleton instance;

// 私有构造函数,防止外部通过new创建实例
private Singleton() {
}

// 公有静态方法,提供获取类唯一实例的途径
public static Singleton getInstance() {
if (instance == null) {
// 同步代码块,确保线程安全
synchronized (Singleton.class) {
// 再次检查instance是否为null,防止多线程环境下创建多个实例
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}

// 其他业务方法
public void doSomething() {
System.out.println("Doing something...");
}
}

// 测试单例类
public class SingletonTest {
public static void main(String[] args) {
// 获取单例对象
Singleton singleton = Singleton.getInstance();
singleton.doSomething();

// 获取另一个单例对象,实际上是同一个对象
Singleton anotherSingleton = Singleton.getInstance();
singleton.doSomething();

// 打印两个引用是否相同
System.out.println(singleton == anotherSingleton); // 输出 true
}
}

这个示例中,Singleton类通过以下方式确保单例:

  • 私有静态变量instance用于保存类的唯一实例。
  • 私有构造函数防止外部通过new关键字创建新实例。
  • 公有静态方法getInstance()提供获取类唯一实例的途径。如果instancenull,则创建一个新的实例。这里使用了双重检查锁定(double-checked locking)模式来确保线程安全,同时避免每次调用getInstance()时都进行同步,提高性能。

请注意,双重检查锁定模式在多线程环境中是必要的,以确保只有一个实例被创建。在Java 5及以后的版本中,推荐使用更简单的实现方式,即通过使用volatile关键字声明实例变量,避免双重检查锁定:

1
2
3
4
5
6
7
8
9
10
11
12
private static volatile Singleton instance;

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

使用volatile关键字可以确保多线程环境下instance变量的可见性,即一个线程对instance的写入对其他线程立即可见。

工厂方法模式(Factory Method)

工厂方法模式是一种创建型设计模式,它提供了一个创建对象的接口,但让实现这个接口的类来决定实例化哪一个类。工厂方法让类的实例化推迟到子类中进行。

以下是一个简单的工厂方法模式示例,假设我们有一个形状的接口Shape和几个实现了该接口的具体形状类,然后我们创建一个形状工厂ShapeFactory来生成具体形状的实例。

首先定义形状接口Shape

1
2
3
public interface Shape {
void draw();
}

然后实现几个具体的形状类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Inside Circle::draw() method.");
}
}

public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}

public class Square implements Shape {
@Override
public void draw() {
System.out.println("Inside Square::draw() method.");
}
}

接下来创建工厂类ShapeFactory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ShapeFactory {

// 工厂方法,返回Shape类型的实例
public Shape getShape(String shapeType) {
if (shapeType == null) {
return null;
}
if (shapeType.equalsIgnoreCase("CIRCLE")) {
return new Circle();
} else if (shapeType.equalsIgnoreCase("RECTANGLE")) {
return new Rectangle();
} else if (shapeType.equalsIgnoreCase("SQUARE")) {
return new Square();
}
return null;
}
}

最后,我们可以通过工厂类来获取具体形状的实例并使用它们:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class FactoryPatternDemo {
public static void main(String[] args) {
ShapeFactory shapeFactory = new ShapeFactory();

// 获取形状类型为"CIRCLE"的实例并绘制
Shape shape1 = shapeFactory.getShape("CIRCLE");
shape1.draw();

// 获取形状类型为"RECTANGLE"的实例并绘制
Shape shape2 = shapeFactory.getShape("RECTANGLE");
shape2.draw();

// 获取形状类型为"SQUARE"的实例并绘制
Shape shape3 = shapeFactory.getShape("SQUARE");
shape3.draw();
}
}

在这个示例中,ShapeFactory类是一个具体的工厂,它实现了工厂方法getShape(),该方法根据传入的字符串参数来决定实例化哪个形状类。客户端代码通过调用getShape()方法并传入相应的参数来获取Shape对象的实例,而无需知道具体的类是如何实现的。这种方式提高了代码的灵活性和可扩展性。

抽象工厂模式(Abstract Factory)

抽象工厂模式是一种创建型设计模式,用于创建相关或依赖对象的家族,而不需明确指定具体类。这种模式通常用于一个系统需要与多个产品族中的某一个产品族进行交互,但又希望客户端不必指定具体要使用哪一个产品族。

以下是一个简单的抽象工厂模式示例,假设我们有一个颜色(Color)接口和形状(Shape)接口,以及它们的具体实现。我们创建一个抽象工厂接口AbstractFactory,以及两个具体的工厂类来生成颜色和形状的对象。

首先定义颜色和形状的接口:

1
2
3
4
5
6
7
public interface Color {
void applyColor();
}

public interface Shape {
void draw();
}

然后实现几个具体的颜色和形状类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class RedColor implements Color {
@Override
public void applyColor() {
System.out.println("Applying Red color.");
}
}

public class BlueColor implements Color {
@Override
public void applyColor() {
System.out.println("Applying Blue color.");
}
}

public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Drawing a Circle.");
}
}

public class Square implements Shape {
@Override
public void draw() {
System.out.println("Drawing a Square.");
}
}

接下来定义抽象工厂接口:

1
2
3
4
public interface AbstractFactory {
Color getColor();
Shape getShape();
}

创建两个具体的工厂类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class RedCircleFactory implements AbstractFactory {
@Override
public Color getColor() {
return new RedColor();
}

@Override
public Shape getShape() {
return new Circle();
}
}

public class BlueSquareFactory implements AbstractFactory {
@Override
public Color getColor() {
return new BlueColor();
}

@Override
public Shape getShape() {
return new Square();
}
}

最后,我们可以通过具体的工厂类来获取颜色和形状的实例并使用它们:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class AbstractFactoryDemo {
public static void main(String[] args) {
AbstractFactory redCircleFactory = new RedCircleFactory();
Color red = redCircleFactory.getColor();
red.applyColor();
Shape circle = redCircleFactory.getShape();
circle.draw();

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

AbstractFactory blueSquareFactory = new BlueSquareFactory();
Color blue = blueSquareFactory.getColor();
blue.applyColor();
Shape square = blueSquareFactory.getShape();
square.draw();
}
}

在这个示例中,AbstractFactory是一个抽象工厂接口,定义了创建颜色和形状的方法。RedCircleFactoryBlueSquareFactory是具体的工厂类,它们实现了AbstractFactory接口,并具体化了创建哪种颜色和形状对象。客户端代码通过调用具体工厂的getColor()getShape()方法来获取颜色和形状对象的实例,而无需知道具体的类实现。这种方式使得系统更加灵活,易于扩展,并且能够保持产品的一致性。

建造者模式(Builder)

建造者模式(Builder Pattern)是一种创建型设计模式,用于构建一个复杂的对象。它允许按步骤构造一个复杂的对象,并允许修改内部表示而不影响其他对象。这种模式通常用于创建一个由多个部分组成的对象,同时保持对象的构造代码与表示代码分离。

以下是一个使用Java实现建造者模式的简单示例:

假设我们有一个复杂的对象Car,它由多个部件组成,如引擎(Engine)、轮胎(Wheels)和车身(Body)。

首先定义Car类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class Car {
private Engine engine;
private Wheels wheels;
private Body body;

// 构造函数私有化,通过建造者模式来构建对象
private Car(Builder builder) {
this.engine = builder.engine;
this.wheels = builder.wheels;
this.body = builder.body;
}

// 产品方法,返回构建后的Car对象
public static class Builder {
private Engine engine;
private Wheels wheels;
private Body body;

public Builder setEngine(Engine engine) {
this.engine = engine;
return this;
}

public Builder setWheels(Wheels wheels) {
this.wheels = wheels;
return this;
}

public Builder setBody(Body body) {
this.body = body;
return this;
}

public Car build() {
return new Car(this);
}
}

@Override
public String toString() {
return "Car{" +
"engine=" + engine +
", wheels=" + wheels +
", body=" + body +
'}';
}
}

然后定义Car的各个组成部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class Engine {
private String type;

public Engine(String type) {
this.type = type;
}

@Override
public String toString() {
return "Engine{" +
"type='" + type + '\'' +
'}';
}
}

public class Wheels {
private int count;

public Wheels(int count) {
this.count = count;
}

@Override
public String toString() {
return "Wheels{" +
"count=" + count +
'}';
}
}

public class Body {
private String model;

public Body(String model) {
this.model = model;
}

@Override
public String toString() {
return "Body{" +
"model='" + model + '\'' +
'}';
}
}

最后,我们可以通过建造者模式来构建一个Car对象:

1
2
3
4
5
6
7
8
9
10
11
public class BuilderPatternDemo {
public static void main(String[] args) {
Car car = new Car.Builder()
.setEngine(new Engine("V8"))
.setWheels(new Wheels(4))
.setBody(new Body("Sedan"))
.build();

System.out.println(car);
}
}

在这个示例中,Car类有一个内部的静态类Builder,它负责构建Car对象。Builder类提供了设置Car各个组成部分的方法,并返回自身以支持链式调用。build()方法用于创建并返回一个Car对象。客户端代码通过调用CarBuilder类的方法来设置所需的属性,并最终构建一个Car对象。建造者模式使得Car对象的创建过程非常灵活和清晰。

原型模式(Prototype)

原型模式(Prototype Pattern)是一种创建型设计模式,用于通过拷贝现有的实例来创建新的实例。这种模式主要适用于对象的创建成本高,或者对象的创建过程复杂的情况。原型模式允许保留一个或多个现有对象的副本,当需要新对象时,可以通过复制这些副本来获得,而不是从头开始创建。

在Java中,实现原型模式通常借助于Cloneable接口和Object类的clone()方法。以下是一个简单的原型模式示例:

首先定义一个可克隆的类,实现Cloneable接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Prototype implements Cloneable {
private String data;

public Prototype(String data) {
this.data = data;
}

public Prototype clone() {
try {
return (Prototype) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}

@Override
public String toString() {
return "Prototype{" +
"data='" + data + '\'' +
'}';
}
}

然后创建一个管理原型对象的工厂类:

1
2
3
4
5
6
7
8
9
10
11
public class PrototypeFactory {
private Prototype prototype;

public PrototypeFactory(Prototype prototype) {
this.prototype = prototype;
}

public Prototype createPrototype() {
return prototype.clone();
}
}

最后,客户端代码通过工厂类来获取原型对象的副本:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class PrototypePatternDemo {
public static void main(String[] args) {
Prototype prototype = new Prototype("Original");
PrototypeFactory factory = new PrototypeFactory(prototype);

// 创建原型对象的副本
Prototype clonedPrototype = factory.createPrototype();
System.out.println(clonedPrototype);

// 验证原型和副本是否相等
System.out.println(prototype == clonedPrototype); // 输出 false,表明是两个不同的对象
}
}

在这个示例中,Prototype类实现了Cloneable接口,并重写了clone()方法来实现对象的深拷贝或浅拷贝。PrototypeFactory类负责创建原型对象的副本。客户端代码通过调用工厂的createPrototype()方法来获取原型对象的副本。

原型模式的优点包括:

  • 简化了创建过程,特别是当创建新对象的成本较高时。
  • 可以在运行时通过拷贝来实现对象的快速复制。
  • 避免了对象创建过程中的复杂性。

然而,原型模式也有其局限性,例如:

  • 如果对象的克隆涉及到更多的资源分配和复杂构造,则可能需要更复杂的处理。
  • 需要确保对象是可克隆的,这可能要求对象的内部状态都可以被复制。

在实际应用中,原型模式可以用于实现对象的快速复制,特别是在需要大量相似对象的场景中。

结构型模式(Structural Patterns)

适配器模式(Adapter)

适配器模式(Adapter Pattern)是一种结构型设计模式,用于使不兼容的接口能够一起工作。这种模式涉及到一个适配器类,它将一个类的接口转换成客户端期望的另一个接口。适配器模式让原本由于接口不兼容而不能一起工作的类可以一起工作,这样做的好处是不会修改现有代码的基础上增加新的功能。

以下是使用Java实现适配器模式的一个简单示例:

假设我们有一个Mammal接口,它有一个makeSound()方法。我们还有一个具体的类Dog,实现了Mammal接口。现在,我们有一个Bird类,它有一个不同的接口,有一个tweet()方法。我们希望使用Bird对象的地方能够像使用Mammal接口一样调用makeSound()方法。

首先定义Mammal接口和Dog类:

1
2
3
4
5
6
7
8
9
10
public interface Mammal {
void makeSound();
}

public class Dog implements Mammal {
@Override
public void makeSound() {
System.out.println("Woof woof!");
}
}

然后定义Bird类:

1
2
3
4
5
public class Bird {
void tweet() {
System.out.println("Tweet tweet!");
}
}

接下来,创建一个适配器类BirdAdapter,它实现了Mammal接口,并在内部使用Bird对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class BirdAdapter implements Mammal {
private Bird bird;

public BirdAdapter(Bird bird) {
this.bird = bird;
}

@Override
public void makeSound() {
// 适配:将Bird的tweet()方法适配为Mammal的makeSound()
bird.tweet();
}
}

最后,客户端代码可以像使用Mammal接口一样使用BirdAdapter

1
2
3
4
5
6
7
8
public class AdapterPatternDemo {
public static void main(String[] args) {
Bird bird = new Bird();
Mammal mammal = new BirdAdapter(bird);

mammal.makeSound(); // 通过适配器调用,实际执行的是Bird的tweet()方法
}
}

在这个示例中,BirdAdapter是一个适配器类,它实现了Mammal接口,内部持有一个Bird对象的引用。makeSound()方法调用了Bird对象的tweet()方法,从而实现了接口的适配。

适配器模式的关键优点包括:

  • 允许不兼容的接口协同工作。
  • 增加了类的兼容性,使得原本独立的类可以一起使用。
  • 遵循开闭原则,对扩展开放,对修改封闭。

适配器模式的缺点可能包括:

  • 过多地使用适配器可能会使系统变得复杂,难以管理。
  • 如果适配器很多,它们可能需要维护,这会增加系统的负担。

装饰器模式(Decorator)

装饰器模式(Decorator Pattern)是一种结构型设计模式,允许用户在不改变对象自身的基础上,向一个对象添加新的功能。这种模式通过创建一个包装对象,也就是装饰者,来包裹实际对象。装饰者持有实际对象的引用,并提供额外的功能。

以下是使用Java实现装饰器模式的一个简单示例:

首先定义一个组件接口,这里以咖啡为例:

1
2
3
public interface Coffee {
double cost();
}

然后实现几个简单的具体咖啡组件:

1
2
3
4
5
6
public class SimpleCoffee implements Coffee {
@Override
public double cost() {
return 10.0;
}
}

接下来定义装饰器抽象类,它也实现咖啡接口,并持有一个咖啡对象的引用:

1
2
3
4
5
6
7
8
9
10
11
12
public abstract class CoffeeDecorator implements Coffee {
protected Coffee coffee;

public CoffeeDecorator(Coffee coffee) {
this.coffee = coffee;
}

@Override
public double cost() {
return coffee.cost();
}
}

实现具体的装饰者类,为咖啡添加不同的调料:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Milk extends CoffeeDecorator {
public Milk(Coffee coffee) {
super(coffee);
}

@Override
public double cost() {
return super.cost() + 2.0;
}
}

public class Whip extends CoffeeDecorator {
public Whip(Coffee coffee) {
super(coffee);
}

@Override
public double cost() {
return super.cost() + 1.5;
}
}

最后,客户端代码可以动态地为咖啡添加各种调料:

1
2
3
4
5
6
7
8
9
10
11
12
public class DecoratorPatternDemo {
public static void main(String[] args) {
Coffee simpleCoffee = new SimpleCoffee();
System.out.println("Cost of Simple Coffee: " + simpleCoffee.cost());

Coffee milkCoffee = new Milk(simpleCoffee);
System.out.println("Cost of Milk Coffee: " + milkCoffee.cost());

Coffee whipCoffee = new Whip(milkCoffee);
System.out.println("Cost of Whip Coffee: " + whipCoffee.cost());
}
}

在这个示例中,Coffee是一个接口,定义了咖啡的成本计算方法。SimpleCoffee是实现了Coffee接口的具体咖啡组件。CoffeeDecorator是一个抽象类,它也实现了Coffee接口,并持有一个Coffee类型的对象引用,它还实现了一个构造函数,用于设置被装饰的咖啡对象。

MilkWhip是具体的装饰者类,它们继承自CoffeeDecorator并实现了自己的cost()方法,在调用被装饰对象的cost()方法后,分别添加了额外的成本。

客户端代码通过创建具体的咖啡对象和装饰者对象,动态地构建了一个具有多种调料的咖啡,并计算了总成本。装饰器模式允许用户通过一种无需修改现有类的方式,透明地添加功能。这种模式提高了代码的灵活性和可扩展性。

代理模式(Proxy)

代理模式(Proxy Pattern)是一种结构型设计模式,它为另一个对象提供一个代替或占位符对象以控制对它的访问。代理可以在不直接与实际对象交互的情况下,提供对目标对象的间接访问。这在多种情况下非常有用,例如:

  • 当需要对目标对象的访问进行控制或监控时。
  • 当目标对象的创建成本很高,需要按需创建时。
  • 当需要为远程或虚拟对象提供局部代理以隐藏对象的位置透明性时。

以下是使用Java实现代理模式的一个简单示例:

首先定义一个Subject接口,它定义了代理和目标对象的共同协议:

1
2
3
public interface Subject {
void performTask();
}

然后实现一个具体的目标对象RealSubject

1
2
3
4
5
6
public class RealSubject implements Subject {
@Override
public void performTask() {
System.out.println("RealSubject is performing the task.");
}
}

接下来定义Proxy类,它实现了与目标对象相同的接口,并持有目标对象的引用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Proxy implements Subject {
private RealSubject realSubject;

public Proxy() {
this.realSubject = null;
}

public void performTask() {
if (realSubject == null) {
realSubject = new RealSubject();
}
realSubject.performTask();
}
}

在这个示例中,Proxy类提供了一个方法performTask(),它与RealSubject类中的实现相同。然而,在代理类中,我们可以添加额外的逻辑,例如访问控制或延迟初始化。

最后,客户端代码可以这样使用代理模式:

1
2
3
4
5
6
public class ProxyPatternDemo {
public static void main(String[] args) {
Subject subject = new Proxy();
subject.performTask();
}
}

在这个示例中,客户端代码通过代理对象Proxy来访问目标对象的功能。由于代理对象实现了与目标对象相同的接口,因此客户端代码可以像使用目标对象一样使用代理对象,而不需要知道实际对象的具体细节。

代理模式的关键优点包括:

  • 降低了对象之间的耦合度,因为对象不需要直接相互通信。
  • 可以控制对目标对象的访问,增加额外的功能,如访问控制、延迟初始化等。
  • 可以为不同类型的对象提供统一的接口。

代理模式的缺点可能包括:

  • 可能会引入额外的复杂性,因为需要维护额外的代理类。
  • 如果代理对象和目标对象的接口不匹配,可能需要进行额外的适配工作。

外观模式(Facade)

外观模式(Facade Pattern)是一种结构型设计模式,它提供了一个统一的高层接口来访问子系统中的一组接口。这种模式定义了一个协调多个子系统类的接口,使得子系统更容易使用。外观模式隐藏了子系统内部的复杂性,让子系统与客户端之间通过一个简单的接口进行通信。

以下是使用Java实现外观模式的一个简单示例:

假设我们有一个简单的银行系统,它包括账户管理、贷款管理和存款管理三个子系统。

首先,定义各个子系统的接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface AccountManagement {
void createAccount();
void closeAccount();
}

public interface LoanManagement {
void applyForLoan();
void repayLoan();
}

public interface DepositManagement {
void depositMoney();
void withdrawMoney();
}

然后实现具体的子系统类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class ConcreteAccountManagement implements AccountManagement {
@Override
public void createAccount() {
System.out.println("Creating an account.");
}

@Override
public void closeAccount() {
System.out.println("Closing an account.");
}
}

public class ConcreteLoanManagement implements LoanManagement {
@Override
public void applyForLoan() {
System.out.println("Applying for a loan.");
}

@Override
public void repayLoan() {
System.out.println("Repaying a loan.");
}
}

public class ConcreteDepositManagement implements DepositManagement {
@Override
public void depositMoney() {
System.out.println("Depositing money.");
}

@Override
public void withdrawMoney() {
System.out.println("Withdrawing money.");
}
}

接下来,创建外观类BankSystemFacade,它将作为客户端与子系统之间的接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class BankSystemFacade {
private AccountManagement accountManagement;
private LoanManagement loanManagement;
private DepositManagement depositManagement;

public BankSystemFacade() {
accountManagement = new ConcreteAccountManagement();
loanManagement = new ConcreteLoanManagement();
depositManagement = new ConcreteDepositManagement();
}

public void performAccountOperations() {
accountManagement.createAccount();
accountManagement.closeAccount();
}

public void performLoanOperations() {
loanManagement.applyForLoan();
loanManagement.repayLoan();
}

public void performDepositOperations() {
depositManagement.depositMoney();
depositManagement.withdrawMoney();
}
}

最后,客户端代码通过外观类BankSystemFacade来访问子系统的功能:

1
2
3
4
5
6
7
8
9
public class FacadePatternDemo {
public static void main(String[] args) {
BankSystemFacade bankSystemFacade = new BankSystemFacade();

bankSystemFacade.performAccountOperations();
bankSystemFacade.performLoanOperations();
bankSystemFacade.performDepositOperations();
}
}

在这个示例中,BankSystemFacade外观类提供了三个方法,每个方法都封装了子系统中的一系列操作。客户端代码通过调用这些方法来执行相应的操作,而不需要直接与各个子系统类交互。

外观模式的优点包括:

  • 简化了客户端与子系统的交互,客户端不需要了解子系统内部的复杂性。
  • 降低了客户端与子系统之间的耦合度。
  • 提供了一个高层次的接口,使得子系统更容易使用。

外观模式的缺点可能包括:

  • 如果外观类过于复杂,它可能会变得难以维护。
  • 外观类可能会在一定程度上违背开闭原则,因为添加新的子系统功能可能需要修改外观类。

桥接模式(Bridge)

桥接模式(Bridge Pattern)是一种结构型设计模式,用于将一个类的抽象部分与它的实现部分分离开来,以便它们可以独立地变化。这种模式是一种对象结构化手段,它通过将一个类的操作集合与该类的具体实现分离,使得可以动态地将一个集合的行为绑定到该类的一个对象上。

桥接模式由两个主要部分组成:抽象层(Abstraction)和实现层(Implementor)。抽象层定义了客户端使用的接口,而实现层提供了具体的实现。

以下是使用Java实现桥接模式的一个简单示例:

首先定义实现层的接口:

1
2
3
public interface Implementor {
void operate();
}

然后实现具体操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class ConcreteImplementorA implements Implementor {
@Override
public void operate() {
System.out.println("ConcreteImplementorA operates.");
}
}

public class ConcreteImplementorB implements Implementor {
@Override
public void operate() {
System.out.println("ConcreteImplementorB operates.");
}
}

接下来定义抽象层的接口:

1
2
3
4
5
6
7
8
9
public abstract class Abstraction {
protected Implementor implementor;

public Abstraction(Implementor implementor) {
this.implementor = implementor;
}

public abstract void request();
}

然后实现具体的抽象类:

1
2
3
4
5
6
7
8
9
10
11
public class RefinedAbstraction extends Abstraction {
public RefinedAbstraction(Implementor implementor) {
super(implementor);
}

@Override
public void request() {
System.out.println("Handling request with:");
implementor.operate();
}
}

最后,客户端代码通过桥接模式来使用不同的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class BridgePatternDemo {
public static void main(String[] args) {
RefinedAbstraction abstraction;
Implementor implementor;

// 使用实现A
implementor = new ConcreteImplementorA();
abstraction = new RefinedAbstraction(implementor);
abstraction.request();

// 使用实现B
implementor = new ConcreteImplementorB();
abstraction = new RefinedAbstraction(implementor);
abstraction.request();
}
}

在这个示例中,Implementor接口定义了实现层的行为。ConcreteImplementorAConcreteImplementorB是具体的实现类。Abstraction类是抽象层的基类,它包含一个Implementor类型的成员变量,并定义了一个抽象方法request()RefinedAbstraction是抽象层的具体实现类,它实现了request()方法,并调用了Implementoroperate()方法。

客户端代码通过创建RefinedAbstractionImplementor的实例,并将它们关联起来,来使用桥接模式。这样,抽象层和实现层就可以独立变化,增加新的实现或抽象层的变化不会影响到对方。

桥接模式的优点包括:

  • 分离抽象与实现,使得它们可以独立地扩展。
  • 减少系统中类的数量。
  • 提高系统的灵活性。

桥接模式的缺点可能包括:

  • 增加系统的复杂性,因为需要维护两个层次的类。
  • 对于简单的系统,使用桥接模式可能是一种过度设计。

组合模式(Composite)

组合模式(Composite Pattern)是一种结构型设计模式,它允许你将对象组合成树状结构,以表示“部分-整体”的层次结构。这种模式使得用户可以一致地对待单个对象和对象组合。

组合模式非常适合于管理具有层次结构的场景,其中树形结构中的节点可以是叶子节点(没有子节点的节点)和容器节点(包含子节点的节点)。

以下是使用Java实现组合模式的一个简单示例:

首先定义组件接口,这里以Component表示:

1
2
3
public interface Component {
void operation();
}

然后实现叶子节点Leaf

1
2
3
4
5
6
7
8
9
10
11
12
public class Leaf implements Component {
private String name;

public Leaf(String name) {
this.name = name;
}

@Override
public void operation() {
System.out.println("Leaf " + name + " is performing the operation.");
}
}

接下来实现容器节点Composite

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Composite implements Component {
private List<Component> children = new ArrayList<>();

public void add(Component component) {
children.add(component);
}

public void remove(Component component) {
children.remove(component);
}

@Override
public void operation() {
for (Component child : children) {
child.operation();
}
}
}

最后,客户端代码可以这样使用组合模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class CompositePatternDemo {
public static void main(String[] args) {
// 创建叶子节点
Component leaf1 = new Leaf("Leaf A");
Component leaf2 = new Leaf("Leaf B");

// 创建复合节点
Composite composite1 = new Composite();
composite1.add(leaf1);
composite1.add(leaf2);

// 将复合节点添加到另一个复合节点中
Composite composite2 = new Composite();
composite2.add(composite1);

// 执行操作
composite2.operation();
}
}

在这个示例中,Component接口定义了组合中所有对象的一致操作方式。Leaf类实现了Component接口,表示树中的叶节点,它没有子节点。Composite类也实现了Component接口,并持有子节点的集合,它可以包含LeafComposite类型的子节点。

客户端代码通过创建LeafComposite对象,并将它们组合成树状结构来使用组合模式。Composite类提供了添加和删除子节点的方法,以及执行操作的方法,该方法递归地调用其子节点的操作。

组合模式的关键优点包括:

  • 简化客户端代码,客户端可以统一地使用组合结构中的所有对象。
  • 使得更容易增加新的组件类型。

组合模式的缺点可能包括:

  • 对于某些情况下可能过于复杂,特别是当组合结构变得非常深或动态时。

享元模式(Flyweight)

享元模式(Flyweight Pattern)是一种结构型设计模式,旨在通过共享来高效地支持大量细粒度的对象。这种模式非常适合用于当对象的创建成本很高,或者当需要创建大量对象时。享元模式通过共享对象来减少创建对象的数量,从而降低内存占用和提高性能。

享元模式包含两个主要概念:

  1. 内部状态(Intrinsic State):不会随环境改变而改变的,可以共享的状态。
  2. 外部状态(Extrinsic State):依赖于环境或变化的,不可以共享的状态。

以下是使用Java实现享元模式的一个简单示例:

首先定义一个享元接口:

1
2
3
public interface Flyweight {
void doOperation(String extrinsicState);
}

然后实现具体的享元类:

1
2
3
4
5
6
7
8
9
10
11
12
public class ConcreteFlyweight implements Flyweight {
private String intrinsicState;

public ConcreteFlyweight(String intrinsicState) {
this.intrinsicState = intrinsicState;
}

@Override
public void doOperation(String extrinsicState) {
System.out.println("Intrinsic State: " + intrinsicState + ", Extrinsic State: " + extrinsicState);
}
}

接下来创建一个享元工厂,用于管理享元对象的创建和共享:

1
2
3
4
5
6
7
8
9
10
public class FlyweightFactory {
private static Map<String, Flyweight> flyweights = new HashMap<>();

public static Flyweight getFlyweight(String intrinsicState) {
if (!flyweights.containsKey(intrinsicState)) {
flyweights.put(intrinsicState, new ConcreteFlyweight(intrinsicState));
}
return flyweights.get(intrinsicState);
}
}

最后,客户端代码通过享元工厂来获取享元对象,并执行操作:

1
2
3
4
5
6
7
8
9
10
11
12
public class FlyweightPatternDemo {
public static void main(String[] args) {
Flyweight flyweight1 = FlyweightFactory.getFlyweight("State-A");
flyweight1.doOperation("State-X");

Flyweight flyweight2 = FlyweightFactory.getFlyweight("State-A");
flyweight2.doOperation("State-Y");

Flyweight flyweight3 = FlyweightFactory.getFlyweight("State-B");
flyweight3.doOperation("State-Z");
}
}

在这个示例中,Flyweight接口定义了享元对象的操作方法。ConcreteFlyweight类实现了Flyweight接口,并持有内部状态。FlyweightFactory是一个享元工厂,它维护了一个享元对象的集合,并提供了获取享元对象的方法。当请求一个享元对象时,如果工厂中不存在该对象,则创建一个新的享元对象并存储在工厂中,否则直接返回现有的享元对象。

客户端代码通过调用FlyweightFactorygetFlyweight()方法来获取享元对象,并调用其doOperation()方法执行操作,同时传递外部状态。

享元模式的优点包括:

  • 显著减少对象的数量,降低内存占用。
  • 提高了对象处理的效率。

享元模式的缺点可能包括:

  • 增加了系统的复杂性,需要维护享元工厂和享元对象。
  • 需要区分内部状态和外部状态,这可能会使得系统设计更加复杂。

行为型模式(Behavioral Patterns)

策略模式(Strategy)

策略模式(Strategy Pattern)是一种行为型设计模式,它定义了一组算法,并将每一个算法封装起来,使它们可以互换。策略模式让算法的变化独立于使用算法的客户。

这种模式非常适合用于以下几种情况:

  • 许多相关的算法可以用于解决同一个问题。
  • 需要在运行时根据不同的条件选择使用哪一个算法。

以下是使用Java实现策略模式的一个简单示例:

首先定义一个策略接口,这里以Strategy表示:

1
2
3
public interface Strategy {
void execute();
}

然后实现具体的策略:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class ConcreteStrategyA implements Strategy {
@Override
public void execute() {
System.out.println("Executing ConcreteStrategyA");
}
}

public class ConcreteStrategyB implements Strategy {
@Override
public void execute() {
System.out.println("Executing ConcreteStrategyB");
}
}

接下来定义环境类Context,它将使用策略接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Context {
private Strategy strategy;

public Context(Strategy strategy) {
this.strategy = strategy;
}

public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}

public void executeStrategy() {
strategy.execute();
}
}

最后,客户端代码可以这样使用策略模式:

1
2
3
4
5
6
7
8
9
10
11
public class StrategyPatternDemo {
public static void main(String[] args) {
// 创建环境对象,并使用策略A
Context context = new Context(new ConcreteStrategyA());
context.executeStrategy(); // 执行策略A

// 更换策略为策略B
context.setStrategy(new ConcreteStrategyB());
context.executeStrategy(); // 执行策略B
}
}

在这个示例中,Strategy接口定义了所有支持的算法的公共接口。ConcreteStrategyAConcreteStrategyB是具体的策略实现类,它们实现了Strategy接口。

Context类是环境类,它维护一个Strategy对象,并定义了一个executeStrategy()方法来执行当前策略。客户端可以通过创建Context对象并注入不同的策略来使用策略模式。通过调用setStrategy()方法,客户端可以在运行时改变Context所使用的策略。

策略模式的优点包括:

  • 算法的变化独立于使用算法的客户。
  • 可以轻松地增加新的算法或策略。
  • 使得客户可以透明地使用不同的算法。

策略模式的缺点可能包括:

  • 客户端必须知道所有的策略类,至少要知道它们的接口。
  • 如果有大量的策略,可能会导致系统中策略类的数量急剧增加。

模板方法模式(Template Method)

模板方法模式(Template Method Pattern)是一种行为型设计模式,它在方法中定义了一个算法的框架,将一些步骤的实现延迟到子类中。这种模式让子类在不改变算法结构的前提下,重新定义算法的某些特定步骤。

模板方法模式非常适合以下情况:

  • 多个子类存在相同的方法,并且这些方法有可以抽象出来的公共行为。
  • 需要通过子类来扩展或修改某个特定步骤的行为,而不影响整个算法的流程。

以下是使用Java实现模板方法模式的一个简单示例:

首先定义一个抽象类AbstractClass,它将实现算法的框架:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public abstract class AbstractClass {
// 模板方法,定义算法的框架
public final void templateMethod() {
stepOne();
stepTwo();
stepThree();
stepFour();
}

// 钩子方法,可以被子类重写
protected void hookMethod() {
// 默认行为
}

// 抽象方法,必须被子类实现
protected abstract void stepOne();

// 具体方法,也可以被子类重写
protected void stepTwo() {
System.out.println("Executing step two in abstract class.");
}

// 具体方法,也可以被子类重写
protected void stepThree() {
System.out.println("Executing step three in abstract class.");
}

// 具体方法,也可以被子类重写
private void stepFour() {
System.out.println("Executing step four in abstract class.");
}
}

然后实现具体的子类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class ConcreteClassA extends AbstractClass {
@Override
protected void stepOne() {
System.out.println("Executing step one in ConcreteClassA.");
}

@Override
protected void stepTwo() {
System.out.println("Executing step two in ConcreteClassA.");
}

@Override
protected void stepThree() {
System.out.println("Executing step three in ConcreteClassA.");
}
}

public class ConcreteClassB extends AbstractClass {
@Override
protected void stepOne() {
System.out.println("Executing step one in ConcreteClassB.");
}

// 可以不重写stepTwo,使用父类实现
@Override
protected void stepThree() {
System.out.println("Executing step three in ConcreteClassB.");
}
}

最后,客户端代码可以这样使用模板方法模式:

1
2
3
4
5
6
7
8
9
public class TemplateMethodPatternDemo {
public static void main(String[] args) {
AbstractClass concreteClassA = new ConcreteClassA();
concreteClassA.templateMethod();

AbstractClass concreteClassB = new ConcreteClassB();
concreteClassB.templateMethod();
}
}

在这个示例中,AbstractClass是一个抽象类,它定义了算法的框架方法templateMethod()。它还定义了多个步骤方法,包括抽象的stepOne()和具体的stepTwo()stepThree()以及私有的stepFour()hookMethod()是一个钩子方法,提供了一个可以被子类覆盖的默认实现。

ConcreteClassAConcreteClassB是具体的子类,它们继承自AbstractClass并实现了stepOne()方法。子类可以选择重写其他步骤方法,以改变算法的特定行为。

客户端代码通过创建AbstractClass的实例,并调用templateMethod()来执行算法。由于算法的框架在AbstractClass中定义,所以算法的结构保持不变,而特定步骤的行为可以通过子类来定制。

模板方法模式的优点包括:

  • 保证了算法的固定结构,同时允许子类定制特定步骤的行为。
  • 提高了代码的复用性。
  • 分离了算法的框架和具体实现,降低了代码间的耦合度。

模板方法模式的缺点可能包括:

  • 如果算法的步骤经常变化,可能会导致需要频繁修改抽象类及其子类。

观察者模式(Observer)

观察者模式(Observer Pattern)是一种行为型设计模式,它定义了对象间的一种一对多的依赖关系,这样一来,当一个对象改变状态时,所有依赖于它的对象都会得到通知并自动更新。

这种模式非常适合于以下几个场景:

  • 当对象间的操作是相互独立的,但是需要将一个对象的状态或行为结果传递给多个对象时。
  • 当想要构建一个分布式的事件处理系统时。

以下是使用Java实现观察者模式的一个简单示例:

首先定义观察者接口:

1
2
3
public interface Observer {
void update(String message);
}

然后定义具体观察者类:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class ConcreteObserverA implements Observer {
@Override
public void update(String message) {
System.out.println("ConcreteObserverA: " + message);
}
}

public class ConcreteObserverB implements Observer {
@Override
public void update(String message) {
System.out.println("ConcreteObserverB: " + message);
}
}

接下来定义主题接口:

1
2
3
4
5
public interface Subject {
void registerObserver(Observer o);
void removeObserver(Observer o);
void notifyObservers();
}

然后实现具体的主题类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class ConcreteSubject implements Subject {
private List<Observer> observers = new ArrayList<>();
private String state;

@Override
public void registerObserver(Observer o) {
observers.add(o);
}

@Override
public void removeObserver(Observer o) {
observers.remove(o);
}

@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(state);
}
}

public void setState(String state) {
this.state = state;
notifyObservers();
}

public String getState() {
return state;
}
}

最后,客户端代码可以这样使用观察者模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ObserverPatternDemo {
public static void main(String[] args) {
ConcreteSubject subject = new ConcreteSubject();

Observer observerA = new ConcreteObserverA();
Observer observerB = new ConcreteObserverB();

subject.registerObserver(observerA);
subject.registerObserver(observerB);

// 当状态改变时,注册的观察者都会收到通知
subject.setState("New State");
}
}

在这个示例中,Observer接口定义了观察者必须实现的update()方法。ConcreteObserverAConcreteObserverB是具体的观察者类,它们实现了Observer接口。

Subject接口定义了主题必须实现的方法,如注册观察者、移除观察者和通知观察者。ConcreteSubject是具体的主题类,它维护了一个观察者列表,并在状态改变时通知所有观察者。

客户端代码通过创建ConcreteSubjectObserver的实例,并将观察者注册到主题上。当主题的状态发生改变时,它会自动通知所有注册的观察者。

观察者模式的优点包括:

  • 降低了对象间的耦合度,主题与观察者之间是松散耦合的。
  • 增加了对象的可扩展性,可以动态地添加或移除观察者。

观察者模式的缺点可能包括:

  • 当观察者对象很多时,通知的效率可能会降低。
  • 如果观察者和主题之间的依赖关系过于复杂,可能会导致系统难以理解和维护。

迭代器模式(Iterator)

迭代器模式(Iterator Pattern)是一种行为型设计模式,它允许顺序访问一个聚合对象中的各个元素而不需要暴露其内部的表示。迭代器模式定义了一种方法,通过该方法可以访问一个容器对象中的所有元素,同时保持容器对象的封装性。

迭代器模式通常包含以下角色:

  • 迭代器(Iterator):定义了访问和遍历元素的接口。
  • 具体迭代器(Concrete Iterator):实现了迭代器接口,跟踪当前遍历的位置。
  • 聚合(Aggregate):定义了创建迭代器的方法。
  • 具体聚合(Concrete Aggregate):实现了创建具体迭代器的方法。

以下是使用Java实现迭代器模式的一个简单示例:

首先定义迭代器接口:

1
2
3
4
public interface Iterator {
boolean hasNext();
Object next();
}

然后实现具体迭代器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class ConcreteIterator implements Iterator {
private ConcreteAggregate aggregate;
private int position;

public ConcreteIterator(ConcreteAggregate aggregate) {
this.aggregate = aggregate;
this.position = 0;
}

@Override
public boolean hasNext() {
return position < aggregate.size();
}

@Override
public Object next() {
if (hasNext()) {
Object item = aggregate.get(position);
position++;
return item;
}
return null;
}
}

接下来定义聚合接口:

1
2
3
public interface Aggregate {
Iterator createIterator();
}

然后实现具体聚合:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ConcreteAggregate implements Aggregate {
private List<Object> items = new ArrayList<>();

public void add(Object item) {
items.add(item);
}

public Object get(int index) {
return items.get(index);
}

public int size() {
return items.size();
}

@Override
public Iterator createIterator() {
return new ConcreteIterator(this);
}
}

最后,客户端代码可以这样使用迭代器模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class IteratorPatternDemo {
public static void main(String[] args) {
ConcreteAggregate aggregate = new ConcreteAggregate();
aggregate.add("Item 1");
aggregate.add("Item 2");
aggregate.add("Item 3");

Iterator iterator = aggregate.createIterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}

在这个示例中,Iterator接口定义了迭代器的基本操作,ConcreteIterator实现了这个接口,并维护了一个指向当前遍历元素的索引。Aggregate接口定义了创建迭代器的方法,ConcreteAggregate实现了这个接口,并提供了添加元素和获取元素的方法。

客户端代码通过调用ConcreteAggregatecreateIterator()方法来获取迭代器,并使用迭代器遍历所有元素。

迭代器模式的优点包括:

  • 它支持聚合对象的多种遍历方式,可以有多个不同的迭代器实现。
  • 它不暴露聚合对象的内部结构,符合封装原则。
  • 它提供了一个统一的接口来遍历所有类型的聚合对象。

迭代器模式的缺点可能包括:

  • 需要为每个聚合类型都实现一个迭代器类,这可能会增加代码量。
  • 迭代器模式可能会使得简单的聚合操作变得复杂,特别是对于只需要一次遍历的简单场景。

责任链模式(Chain of Responsibility)

责任链模式(Chain of Responsibility Pattern)是一种行为型设计模式,它允许多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。每个对象包含对另一个对象的引用,构成一条链,并且每个对象要么处理请求,要么将请求转发到链上的下一个对象。

责任链模式通常包含以下角色:

  • 处理者(Handler):定义了一个处理请求的接口。
  • 具体处理者(Concrete Handler):实现了处理者接口,真正处理请求或将请求传递给链上的下一个处理者。
  • 请求(Request):需要处理的信息。

以下是使用Java实现责任链模式的一个简单示例:

首先定义处理者接口:

1
2
3
public interface Handler {
void handleRequest(Request request);
}

然后实现具体处理者:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class ConcreteHandlerA implements Handler {
private Handler nextHandler;

public ConcreteHandlerA(Handler nextHandler) {
this.nextHandler = nextHandler;
}

@Override
public void handleRequest(Request request) {
if (nextHandler != null) {
nextHandler.handleRequest(request);
} else {
System.out.println("No more handlers to process the request.");
}
}

public void setNextHandler(Handler nextHandler) {
this.nextHandler = nextHandler;
}
}

public class ConcreteHandlerB implements Handler {
@Override
public void handleRequest(Request request) {
System.out.println("Handler B processing the request: " + request);
}
}

接下来定义请求类:

1
2
3
4
5
6
7
8
9
10
11
public class Request {
private String info;

public Request(String info) {
this.info = info;
}

public String getInfo() {
return info;
}
}

最后,客户端代码可以这样使用责任链模式:

1
2
3
4
5
6
7
8
9
10
public class ChainOfResponsibilityPatternDemo {
public static void main(String[] args) {
Handler handlerA = new ConcreteHandlerA(null);
Handler handlerB = new ConcreteHandlerB();
handlerA.setNextHandler(handlerB);

Request request = new Request("This is a request.");
handlerA.handleRequest(request);
}
}

在这个示例中,Handler接口定义了处理请求的方法。ConcreteHandlerAConcreteHandlerB是具体处理者,它们实现了Handler接口。ConcreteHandlerA可以设置下一个处理者,并且在接收到请求时,可以选择将请求传递给链上的下一个处理者。ConcreteHandlerB直接处理请求。

客户端代码通过创建具体处理者对象,并通过setNextHandler()方法将它们链接起来,构成责任链。然后,客户端创建一个请求,并将其传递给链的第一个处理者。

责任链模式的优点包括:

  • 降低了对象之间的耦合度,每个处理者只需关注自己的职责。
  • 增强了系统的可扩展性,可以灵活地增加新的处理者。
  • 增强了代码的可维护性,每个处理者独立实现自己的逻辑。

责任链模式的缺点可能包括:

  • 对于复杂的系统,责任链可能会变得难以追踪和理解。
  • 如果链上的处理者数量很多,可能会导致性能问题。

命令模式(Command)

命令模式(Command Pattern)是一种行为型设计模式,它将请求或操作封装为一个对象。这种模式允许用户使用不同的请求、队列或日志请求来参数化其他对象,并支持可撤销的操作。

命令模式包含以下几个关键角色:

  • 命令(Command):定义命令接口,声明执行操作的方法。
  • 具体命令(Concrete Command):实现命令接口,对应具体操作的实现。
  • 请求者(Invoker):要求执行请求,它持有命令对象并触发命令的执行。
  • 接收者(Receiver):知道如何实施与执行一个请求相关的操作。
  • 客户端(Client):创建具体命令对象,设置接收者,并配置命令。

以下是使用Java实现命令模式的一个简单示例:

首先定义命令接口:

1
2
3
public interface Command {
void execute();
}

然后实现具体命令:

1
2
3
4
5
6
7
8
9
10
11
12
public class ConcreteCommand implements Command {
private Receiver receiver;

public ConcreteCommand(Receiver receiver) {
this.receiver = receiver;
}

@Override
public void execute() {
receiver.action();
}
}

接下来定义接收者:

1
2
3
4
5
public class Receiver {
public void action() {
System.out.println("Action is performed.");
}
}

然后定义请求者:

1
2
3
4
5
6
7
8
9
10
11
public class Invoker {
private Command command;

public void setCommand(Command command) {
this.command = command;
}

public void trigger() {
command.execute();
}
}

最后,客户端代码可以这样使用命令模式:

1
2
3
4
5
6
7
8
9
10
public class CommandPatternDemo {
public static void main(String[] args) {
Receiver receiver = new Receiver();
Command command = new ConcreteCommand(receiver);
Invoker invoker = new Invoker();

invoker.setCommand(command);
invoker.trigger(); // 执行命令
}
}

在这个示例中,Command接口定义了执行操作的方法。ConcreteCommand实现了Command接口,持有Receiver对象,并在execute()方法中调用Receiveraction()方法。

Receiver类是实际执行操作的类。Invoker类持有一个Command对象,并提供一个trigger()方法来执行命令。

客户端代码通过创建ReceiverCommandInvoker对象,将命令设置给请求者,然后触发请求。

命令模式的优点包括:

  • 降低系统各部分之间的耦合度。
  • 增加新的命令很容易,符合开闭原则。
  • 可以容易地实现请求的排队、记录和撤销。

命令模式的缺点可能包括:
-可能会导致系统设计变得复杂,特别是命令有许多附加属性时。
-命令模式可能会导致一些循环依赖,因为请求者可能需要知道命令的所有细节。

备忘录模式(Memento)

备忘录模式(Memento Pattern)是一种行为型设计模式,用于在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这种模式通常用于实现功能如撤销(Undo)操作。

备忘录模式包含以下几个关键角色:

  • 发起人(Originator):负责创建备忘录,并使用它来恢复自己的状态。
  • 备忘录(Memento):存储发起人对象的内部状态,并提供一个接口供恢复者访问。
  • 恢复者(Caretaker):负责保存备忘录,并防止备忘录之外的其它对象访问它。
  • 客户端(Client):负责创建和保存备忘录,以及指示发起人恢复状态。

以下是使用Java实现备忘录模式的一个简单示例:

首先定义备忘录接口:

1
2
3
4
public interface Memento {
// 用于恢复状态的方法
void restore();
}

然后实现具体备忘录:

1
2
3
4
5
6
7
8
9
10
11
12
public class ConcreteMemento implements Memento {
private String state;

public ConcreteMemento(String state) {
this.state = state;
}

@Override
public void restore() {
// 恢复状态逻辑
}
}

接下来定义发起人:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Originator {
private String state;

public void setState(String state) {
this.state = state;
}

public String getState() {
return state;
}

public Memento saveStateToMemento() {
return new ConcreteMemento(state);
}

public void getStateFromMemento(Memento memento) {
if (memento instanceof ConcreteMemento) {
state = ((ConcreteMemento) memento).state;
}
}
}

然后定义恢复者:

1
2
3
4
5
6
7
8
9
10
11
public class Caretaker {
private Memento memento;

public void setMemento(Memento memento) {
this.memento = memento;
}

public Memento getMemento() {
return memento;
}
}

最后,客户端代码可以这样使用备忘录模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class MementoPatternDemo {
public static void main(String[] args) {
Originator originator = new Originator();
Caretaker caretaker = new Caretaker();

// 改变发起人的状态
originator.setState("State 1");
caretaker.setMemento(originator.saveStateToMemento());

// 更改状态
originator.setState("State 2");

// 恢复到先前的状态
originator.getStateFromMemento(caretaker.getMemento());
System.out.println("Restored State: " + originator.getState());
}
}

在这个示例中,Originator类负责创建备忘录,并提供方法来保存和恢复状态。ConcreteMemento类实现了Memento接口,存储了Originator的状态。

Caretaker类负责保存备忘录,并通过其方法对外提供访问。客户端代码通过创建OriginatorCaretaker对象,并通过saveStateToMementogetStateFromMemento方法来保存和恢复状态。

备忘录模式的优点包括:

  • 可以捕获一个对象的内部状态,并在该对象之外进行保存。
  • 不破坏封装性,使状态恢复操作更加安全。

备忘录模式的缺点可能包括:

  • 如果发起人的状态包含大量数据,备忘录可能会消耗大量内存。
  • 增加了系统的复杂性,需要引入备忘录和恢复者角色。

状态模式(State)

状态模式(State Pattern)是一种行为型设计模式,它允许一个对象在其内部状态改变时改变其行为,看起来好像是改变了其类。这种模式通过将状态相关的操作封装到不同的子类中来实现状态的切换,从而使得对象在不同状态下的行为表现保持一致。

状态模式通常包含以下几个关键角色:

  • 状态接口(State):定义了一个或多个状态相关的方法。
  • 具体状态(Concrete State):实现状态接口,包含具体的状态行为。
  • 上下文(Context):包含状态属性,并负责与状态相关的方法调用。
  • 客户端(Client):触发状态变化和上下文操作的代码。

以下是使用Java实现状态模式的一个简单示例:

首先定义状态接口:

1
2
3
public interface State {
void handle(Context context);
}

然后实现具体状态:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ConcreteStateA implements State {
@Override
public void handle(Context context) {
System.out.println("Handling in ConcreteStateA");
// 根据需要改变状态
context.setState(new ConcreteStateB());
}
}

public class ConcreteStateB implements State {
@Override
public void handle(Context context) {
System.out.println("Handling in ConcreteStateB");
// 可以根据需要继续切换状态
}
}

接下来定义上下文:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Context {
private State state;

public void setState(State state) {
this.state = state;
}

public State getState() {
return state;
}

public void request() {
state.handle(this);
}
}

最后,客户端代码可以这样使用状态模式:

1
2
3
4
5
6
7
8
9
public class StatePatternDemo {
public static void main(String[] args) {
Context context = new Context();
context.setState(new ConcreteStateA());
context.request(); // 将输出 ConcreteStateA 的行为

context.request(); // 状态可能已经改变,将输出新状态的行为
}
}

在这个示例中,State接口定义了所有状态必须实现的方法。ConcreteStateAConcreteStateB是具体的状态类,它们实现了State接口,并定义了在该状态下的行为。

Context类是上下文类,它包含一个State类型的对象,并提供request()方法来请求当前状态对象处理请求。状态对象可以在其行为中更改上下文的内部状态。

客户端代码通过创建Context对象,并设置初始状态,然后调用request()方法来触发状态处理。

状态模式的优点包括:

  • 将与特定状态相关的行为局部化到特定的类中,遵循单一职责原则。
  • 允许状态转换逻辑与状态行为逻辑分离,使得状态转换更加灵活。

状态模式的缺点可能包括:

  • 增加设计复杂性,需要为每个状态创建一个状态类。
  • 如果状态很多,系统中类的数量将急剧增加。

访问者模式(Visitor)

访问者模式(Visitor Pattern)是一种行为型设计模式,它用于为一个对象结构(如组合结构)增加新能力,而不需要修改该结构本身的类。此模式通过将操作封装在独立的访问者类中来实现。访问者模式非常适合于处理对象结构中的对象集合,并对这些对象执行不同的操作。

访问者模式包含以下几个关键角色:

  • 访问者(Visitor):定义了对每一个元素类的访问操作。
  • 具体访问者(Concrete Visitor):实现对各个元素类的访问和操作。
  • 元素(Element):定义了接受访问者的方法。
  • 具体元素(Concrete Element):实现接受访问者的方法。
  • 对象结构(Object Structure):包含多个元素,可以遍历和访问这些元素。

以下是使用Java实现访问者模式的一个简单示例:

首先定义访问者接口:

1
2
3
4
public interface Visitor {
void visitConcreteElementA(ConcreteElementA element);
void visitConcreteElementB(ConcreteElementB element);
}

然后实现具体访问者:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class ConcreteVisitor implements Visitor {
@Override
public void visitConcreteElementA(ConcreteElementA element) {
System.out.println("访问 ConcreteElementA");
// 访问 ConcreteElementA 的特定操作
}

@Override
public void visitConcreteElementB(ConcreteElementB element) {
System.out.println("访问 ConcreteElementB");
// 访问 ConcreteElementB 的特定操作
}
}

接下来定义元素接口:

1
2
3
public interface Element {
void accept(Visitor visitor);
}

然后实现具体元素:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ConcreteElementA implements Element {
@Override
public void accept(Visitor visitor) {
visitor.visitConcreteElementA(this);
}

// ConcreteElementA 的其他方法和属性
}

public class ConcreteElementB implements Element {
@Override
public void accept(Visitor visitor) {
visitor.visitConcreteElementB(this);
}

// ConcreteElementB 的其他方法和属性
}

接下来定义对象结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import java.util.ArrayList;
import java.util.List;

public class ObjectStructure {
private List<Element> elements = new ArrayList<>();

public void addElement(Element element) {
elements.add(element);
}

public void removeElement(Element element) {
elements.remove(element);
}

public void accept(Visitor visitor) {
for (Element element : elements) {
element.accept(visitor);
}
}
}

最后,客户端代码可以这样使用访问者模式:

1
2
3
4
5
6
7
8
9
10
public class VisitorPatternDemo {
public static void main(String[] args) {
ObjectStructure objectStructure = new ObjectStructure();
objectStructure.addElement(new ConcreteElementA());
objectStructure.addElement(new ConcreteElementB());

Visitor visitor = new ConcreteVisitor();
objectStructure.accept(visitor);
}
}

在这个示例中,Visitor接口定义了对不同元素的访问操作。ConcreteVisitor实现了Visitor接口,并定义了访问具体元素类时的行为。

Element接口定义了接受访问者的方法。ConcreteElementAConcreteElementB是具体元素类,它们实现了Element接口,并提供了接受访问者的方法。

ObjectStructure类是对象结构类,它维护了一个元素集合,并提供了添加、删除元素和接受访问者的方法。

客户端代码通过创建ObjectStructure和元素对象,将元素添加到对象结构中,然后创建访问者对象,并调用对象结构的accept()方法来遍历所有元素并执行相应的操作。

访问者模式的优点包括:

  • 增加新的操作很容易,无需修改现有代码。
  • 可以对对象结构中的元素进行操作,而不需要了解其内部结构。

访问者模式的缺点可能包括:

  • 增加新的元素类很困难,因为它需要修改访问者接口和所有访问者类。
  • 访问者模式可能会导致系统更加复杂,特别是当对象结构很大时。

浅谈设计模式
http://example.com/2024/06/12/浅谈设计模式/
作者
crush
发布于
2024年6月12日
更新于
2024年6月12日
许可协议