设计模式专题

主要是代码复用、简洁 ,经过前辈总结的通用 的一套方法设计模版,分为 创建着模式、行为模式、结构模式

创建者模式: 简单工厂、工厂方法、抽象工厂、单例、建造者模式、原型模式

行为模式:观察者模式、迭代器模式、命令模式、状态模式、策略模式、责任链模式、备忘录模式 、解释器模式、*中介者模式

结构模式: 代理模式、装饰模式、门面模式、组合模式、适配器模式、享元模式、桥接模式

创建型模式(Creational Patterns)

单例模式(Singleton):

  • 优点:在整个应用生命周期中确保类的唯一实例。
  • 缺点:可能引入全局状态,降低可测试性。

简单工厂(Simple Factory)

以下是一个简单工厂模式(Simple Factory Pattern)的 Java 示例,用于展示简单工厂模式的核心原理。

首先,我们创建一个名为 Animal 的接口:

1
2
3
public interface Animal {
void speak();
}

接下来,我们创建两个实现 Animal 接口的类,分别为 DogCat

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Dog implements Animal {
@Override
public void speak() {
System.out.println("Woof!");
}
}

public class Cat implements Animal {
@Override
public void speak() {
System.out.println("Meow!");
}
}

然后,我们创建一个名为 AnimalFactory 的工厂类,用于根据传入的参数创建不同类型的 Animal 实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class AnimalFactory {
public enum AnimalType {
DOG, CAT
}

public static Animal createAnimal(AnimalType type) {
switch (type) {
case DOG:
return new Dog();
case CAT:
return new Cat();
default:
throw new IllegalArgumentException("Invalid animal type!");
}
}
}

最后,创建一个测试类来展示简单工厂模式的用法:

1
2
3
4
5
6
7
8
9
public class SimpleFactoryDemo {
public static void main(String[] args) {
Animal dog = AnimalFactory.createAnimal(AnimalFactory.AnimalType.DOG);
dog.speak();

Animal cat = AnimalFactory.createAnimal(AnimalFactory.AnimalType.CAT);
cat.speak();
}
}

在这个简化的示例中,简单工厂模式用于将 Animal 类的创建逻辑封装在 AnimalFactory
类中,从而将对象的创建和客户端代码解耦。客户端只需要知道所需的 AnimalType(例如 DOGCAT
),然后调用 AnimalFactory.createAnimal() 方法来创建相应类型的 Animal 实例。

运行测试类,你会看到如下输出:

1
2
Woof!
Meow!

这个例子展示了简单工厂模式的基本用法,它可以简化客户端代码并将对象创建过程集中管理。

工厂方法模式(Factory Method):

  • 优点:抽象出对象创建过程,解耦了客户端和具体产品类。
  • 缺点:可能引入更多类和复杂性。

抽象工厂模式(Abstract Factory):

  • 优点:支持一组相关产品的创建,而无需指定具体实现类。
  • 缺点:添加新产品可能需要修改接口,违反开闭原则。

建造者模式(Builder):

  • 优点:让可选、有默认值的参数组合更易管理。
  • 缺点:可能引入更多类和复杂性。

组合模式允许你构建对象的树形结构,并以统一的方式处理单一对象和组合对象。以下是一个简单的 Java
代码示例,演示如何使用组合模式构建一个表示文件和文件夹的层次结构。

首先,创建一个 Component 抽象类:

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
import java.util.List;
import java.util.ArrayList;

public abstract class Component {
protected String name;

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

public void add(Component component) {
throw new UnsupportedOperationException("Unsupported operation.");
}

public void remove(Component component) {
throw new UnsupportedOperationException("Unsupported operation.");
}

public List<Component> getChildren() {
throw new UnsupportedOperationException("Unsupported operation.");
}

public String getName() {
return name;
}

public abstract void display(int depth);
}

然后创建 FileDirectory 类,分别继承 Component 类:

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
48
public class File extends Component {

public File(String name) {
super(name);
}

@Override
public void display(int depth) {
for (int i = 0; i < depth; i++) {
System.out.print("-");
}
System.out.println("File: " + getName());
}
}

public class Directory extends Component {
private List<Component> children = new ArrayList<>();

public Directory(String name) {
super(name);
}

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

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

@Override
public List<Component> getChildren() {
return children;
}

@Override
public void display(int depth) {
for (int i = 0; i < depth; i++) {
System.out.print("-");
}
System.out.println("Directory: " + getName());
for (Component child : children) {
child.display(depth + 2);
}
}
}

最后,创建一个测试类来演示组合模式的用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class CompositePatternDemo {
public static void main(String[] args) {
Directory root = new Directory("root");
Directory dir1 = new Directory("dir1");
Directory dir2 = new Directory("dir2");
File file1 = new File("file1");
File file2 = new File("file2");
File file3 = new File("file3");

root.add(dir1);
root.add(file1);
dir1.add(dir2);
dir1.add(file2);
dir2.add(file3);

root.display(0);
}
}

浏览组合结构的输出如下:

1
2
3
4
5
6
Directory: root
--Directory: dir1
----Directory: dir2
------File: file3
----File: file2
--File: file1

在这个示例中,Component 抽象类表示文件和文件夹的共同接口。FileDirectory 类分别表示文件和文件夹对象。通过将 File
Directory 作为 Component 的子类,可以统一地处理文件和文件夹,并轻松地构建树形结构。

  1. 原型模式(Prototype):
    • 优点:通过克隆来创建新对象,避免了复杂的初始化操作。
    • 缺点:需要实现克隆方法,不能与单例模式结合使用。

原型模式是一种创建型设计模式,用于通过克隆现有对象来创建新对象。以下是一个简单的 Java
示例,展示了如何使用原型模式创建一个 Sheep 类,该类实现了 Cloneable 接口:

首先,创建一个名为 Sheep 的对象,它将克隆自身来创建新对象:

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 Sheep implements Cloneable {
private String name;

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

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

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

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

然后,创建一个测试类来展示原型模式的用法:

1
2
3
4
5
6
7
8
9
10
11
12
public class PrototypePatternDemo {
public static void main(String[] args) {
Sheep originalSheep = new Sheep("Dolly");
System.out.println("Original sheep: " + originalSheep);

Sheep clonedSheep = originalSheep.clone();
System.out.println("Cloned sheep: " + clonedSheep);

System.out.println("Are originalSheep and clonedSheep the same instance? "
+ (originalSheep == clonedSheep));
}
}

在这个示例中,我们创建了一个名为 Sheep 的类,它实现了 Cloneable 接口。clone 方法从父类 Object
继承而来,我们需要重写该方法,使其返回 Sheep 类型的实例。

main 方法创建了一个名为 originalSheep 的原始实例。接下来,我们使用 clone 方法创建了一个名为 clonedSheep
的新实例。请注意,新实例与原始实例具有相同的属性,但它们是两个不同的对象。

原型模式在对象的创建过程成本较高时非常有用,例如大对象的创建或引用其他类库的创建。通过简单地克隆现有对象,可以避免即时的构造过程,从而减轻创建新对象的负担。

结构型模式(Structural Patterns)

适配器模式(Adapter):

- 优点:使具有不兼容接口的类可以相互协作。
- 缺点:如果过多使用,将降低系统结构的透明度。

首先,创建一个名为 Charger 的接口:

1
2
3
public interface Charger {
void charge();
}

接着,创建一个名为 USCharger 的类实现 Charger 接口:

1
2
3
4
5
6
public class USCharger implements Charger {
@Override
public void charge() {
System.out.println("Charging with US charger.");
}
}

此外,我们新创建一个名为 EUCharger 的接口供适配:

1
2
3
public interface EUCharger {
void chargeWithEUCharger();
}

现在我们创建一个名为 ChargerAdapter 的适配器类,用于将 EUCharger 适配为 Charger

1
2
3
4
5
6
7
8
9
10
11
12
public class ChargerAdapter implements Charger {
private EUCharger euCharger;

public ChargerAdapter(EUCharger euCharger) {
this.euCharger = euCharger;
}

@Override
public void charge() {
euCharger.chargeWithEUCharger();
}
}

最后,创建一个名为 EUSocket 的类,实现 EUCharger 接口:

1
2
3
4
5
6
public class EUSocket implements EUCharger {
@Override
public void chargeWithEUCharger() {
System.out.println("Charging with EU charger.");
}
}

然后,创建一个测试类来展示适配器模式的用法:

1
2
3
4
5
6
7
8
9
10
public class AdapterPatternDemo {
public static void main(String[] args) {
USCharger usCharger = new USCharger();
usCharger.charge();

EUSocket euSocket = new EUSocket();
Charger euChargerAdapter = new ChargerAdapter(euSocket);
euChargerAdapter.charge();
}
}

在这个简化的示例中,适配器模式使一个 Charger 接口与一个 EUCharger 接口兼容。ChargerAdapter
是一个适配器类,它实现 Charger 接口并接受一个 EUCharger 实例。通过调用适配器的 charge() 方法可以使用 EUCharger
类型的实例。

运行测试类,你会看到如下输出:

1
2
Charging with US charger.
Charging with EU charger.

建造者模式(Builder):

- 优点:将抽象部分与实现部分分离,提高系统的可扩展性。
- 缺点:增加类的数量,增加系统的复杂度。

桥接模式将抽象和实现分离,使两者可以独立地变化。以下是一个简单的 Java 代码示例,演示如何使用桥接模式定义一个跨平台的图形绘制应用。

首先,创建 DrawAPI 接口,作为实现部分:

1
2
3
public interface DrawAPI {
void drawCircle(int radius, int x, int y);
}

然后,创建两个不同的实现类:RedCircleGreenCircle,分别表示两种颜色的圆形:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class RedCircle implements DrawAPI {
@Override
public void drawCircle(int radius, int x, int y) {
System.out.println("Drawing red circle [radius: " + radius + ", x: " + x + ", y:" + y + "]");
}
}

public class GreenCircle implements DrawAPI {
@Override
public void drawCircle(int radius, int x, int y) {
System.out.println("Drawing green circle [radius: " + radius + ", x: " + x + ", y:" + y + "]");
}
}

接下来,创建抽象类 Shape,用于表示抽象部分:

1
2
3
4
5
6
7
8
9
public abstract class Shape {
protected DrawAPI drawAPI;

protected Shape(DrawAPI drawAPI) {
this.drawAPI = drawAPI;
}

public abstract void draw();
}

创建具体的 Circle 类,继承自 Shape 类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Circle extends Shape {
private int x, y, radius;

public Circle(int x, int y, int radius, DrawAPI drawAPI) {
super(drawAPI);
this.x = x;
this.y = y;
this.radius = radius;
}

@Override
public void draw() {
drawAPI.drawCircle(radius, x, y);
}
}

最后,创建一个演示类,用于展示桥接模式的用法:

1
2
3
4
5
6
7
8
9
public class BridgePatternDemo {
public static void main(String[] args) {
Shape redCircle = new Circle(100, 100, 10, new RedCircle());
Shape greenCircle = new Circle(100, 100, 10, new GreenCircle());

redCircle.draw();
greenCircle.draw();
}
}
  1. 组合模式(Composite):

- 优点:统一对待组合对象和单一对象,简化客户端代码。
- 缺点:设计过程中需要有明确的叶子对象和组合对象的区分。

组合模式允许你构建对象的树形结构,并以统一的方式处理单一对象和组合对象。以下是一个简单的 Java
代码示例,演示如何使用组合模式构建一个表示文件和文件夹的层次结构。

首先,创建一个 Component 抽象类:

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
import java.util.List;
import java.util.ArrayList;

public abstract class Component {
protected String name;

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

public void add(Component component) {
throw new UnsupportedOperationException("Unsupported operation.");
}

public void remove(Component component) {
throw new UnsupportedOperationException("Unsupported operation.");
}

public List<Component> getChildren() {
throw new UnsupportedOperationException("Unsupported operation.");
}

public String getName() {
return name;
}

public abstract void display(int depth);
}

然后创建 FileDirectory 类,分别继承 Component 类:

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
48
public class File extends Component {

public File(String name) {
super(name);
}

@Override
public void display(int depth) {
for (int i = 0; i < depth; i++) {
System.out.print("-");
}
System.out.println("File: " + getName());
}
}

public class Directory extends Component {
private List<Component> children = new ArrayList<>();

public Directory(String name) {
super(name);
}

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

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

@Override
public List<Component> getChildren() {
return children;
}

@Override
public void display(int depth) {
for (int i = 0; i < depth; i++) {
System.out.print("-");
}
System.out.println("Directory: " + getName());
for (Component child : children) {
child.display(depth + 2);
}
}
}

最后,创建一个测试类来演示组合模式的用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class CompositePatternDemo {
public static void main(String[] args) {
Directory root = new Directory("root");
Directory dir1 = new Directory("dir1");
Directory dir2 = new Directory("dir2");
File file1 = new File("file1");
File file2 = new File("file2");
File file3 = new File("file3");

root.add(dir1);
root.add(file1);
dir1.add(dir2);
dir1.add(file2);
dir2.add(file3);

root.display(0);
}
}

浏览组合结构的输出如下:

1
2
3
4
5
6
Directory: root
--Directory: dir1
----Directory: dir2
------File: file3
----File: file2
--File: file1

在这个示例中,Component 抽象类表示文件和文件夹的共同接口。FileDirectory 类分别表示文件和文件夹对象。通过将 File
Directory 作为 Component 的子类,可以统一地处理文件和文件夹,并轻松地构建树形结构。

装饰器模式(Decorator):

- 优点:通过装饰器类,动态地为对象添加新功能,符合开闭原则。
- 缺点:可能导致大量小型类,使系统变得复杂。

以下是一个简单的装饰器模式的 Java 示例,用于展示装饰器模式的核心原理。

首先,创建一个名为 Beverage 的接口:

1
2
3
4
public interface Beverage {
String getDescription();
double cost();
}

然后,创建一个实现 Beverage 接口的具体组件(ConcreteComponent)类,名为 Coffee

1
2
3
4
5
6
7
8
9
10
11
public class Coffee implements Beverage {
@Override
public String getDescription() {
return "Coffee";
}

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

接下来,创建一个抽象装饰类(Decorator)来实现 Beverage 接口,并包含一个 Beverage 类型的组件引用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public abstract class BeverageDecorator implements Beverage {
private Beverage beverage;

public BeverageDecorator(Beverage beverage) {
this.beverage = beverage;
}

@Override
public String getDescription() {
return beverage.getDescription();
}

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

然后,创建一个具体装饰器(ConcreteDecorator)类来扩展 BeverageDecorator,名为 Milk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Milk extends BeverageDecorator {
public Milk(Beverage beverage) {
super(beverage);
}

@Override
public String getDescription() {
return super.getDescription() + ", Milk";
}

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

最后,创建一个测试类来展示装饰器模式的用法:

1
2
3
4
5
6
7
8
9
public class DecoratorPatternDemo {
public static void main(String[] args) {
Beverage coffee = new Coffee();
System.out.println(coffee.getDescription() + " $" + coffee.cost());

Beverage coffeeWithMilk = new Milk(coffee);
System.out.println(coffeeWithMilk.getDescription() + " $" + coffeeWithMilk.cost());
}
}

在这个简化的示例中,装饰器模式用于为现有的类(Coffee)动态地添加新的功能(Milk),而不是通过继承来实现。BeverageDecorator
是一个抽象的装饰类,它实现了 Beverage 接口并包含一个 Beverage 引用。具体装饰器类 Milk 扩展了 BeverageDecorator
,并可以修改 getDescription()cost() 方法来实现额外的功能。

运行测试类,你会看到如下输出:

1
2
Coffee \$1.5
Coffee, Milk \$1.8

外观模式(Facade):

- 优点:简化外部操作,降低模块间耦合。
- 缺点:过度使用可能限制用户选项。

首先,我们创建三个简单的类,分别为 ClassAClassBClassC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ClassA {
public void operationA() {
System.out.println("Operation A");
}
}

public class ClassB {
public void operationB() {
System.out.println("Operation B");
}
}

public class ClassC {
public void operationC() {
System.out.println("Operation C");
}
}

接下来,创建一个名为 Facade 的类,用于简化这三个类的使用:

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 Facade {
private ClassA classA;
private ClassB classB;
private ClassC classC;

public Facade() {
classA = new ClassA();
classB = new ClassB();
classC = new ClassC();
}

public void doSomethingA() {
classA.operationA();
}

public void doSomethingB() {
classB.operationB();
}

public void doSomethingC() {
classC.operationC();
}
}

最后,创建一个测试类来展示外观模式的用法:

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

facade.doSomethingA();
facade.doSomethingB();
facade.doSomethingC();
}
}

在这个简化的示例中,外观模式用于将 ClassAClassBClassC 的操作封装在一个简单的接口(Facade
)中。客户端不需要关心这些具体的组件是如何实现和协作的,而只需要和 Facade 类进行交互。这样不仅简化了客户端的使用,还降低了客户端和各个子系统之间的耦合。

运行测试类,你会看到如下输出:

1
2
3
Operation A
Operation B
Operation C

这个例子展示了如何使用外观模式来简化复杂系统的使用,并且可以将子系统的实现细节隐藏在外观类的背后。

享元模式(Flyweight):

- 优点:复用对象,降低系统中大量小对象的开销。
- 缺点:实现较复杂,需要区分内部状态和外部状态。

首先,创建一个 Shape 接口:

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

然后,创建具体的 Circle 类实现 Shape 接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Circle implements Shape {
private String color;
private int x;
private int y;

public Circle(String color) {
this.color = color;
}

public void setX(int x) {
this.x = x;
}

public void setY(int y) {
this.y = y;
}

@Override
public void draw() {
System.out.println("Drawing circle [color: " + color + ", x: " + x + ", y: " + y + "]");
}
}

接下来,创建一个 ShapeFactory 类,负责按需创建新的 Circle 对象或复用现有的对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.util.HashMap;

public class ShapeFactory {
private static final HashMap<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("Creating a new circle of color: " + color);
}
return circle;
}
}

最后,创建一个演示类,利用 ShapeFactory 来绘制不同颜色的圆形:

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
import java.util.Random;

public class FlyweightPatternDemo {
private static final String[] colors = {"Red", "Green", "Blue", "White", "Black"};

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

在这个例子中,ShapeFactory 通过维护一个颜色到 Circle 对象的映射,确保每种颜色只创建一次 Circle
。当需要绘制相同颜色的圆时,直接从工厂中获取现有的对象。这样,即使应用中绘制了多个圆形,实际创建的 Circle 对象数量也被显著减少。

代理模式(Proxy):

- 优点:代理类实现原对象附加功能,如:访问控制、延迟计算等。
- 缺点:可能增加延迟,并降低代码透明度

动态代理

首先,我们创建一个名为 Greeting 的接口:

1
2
3
public interface Greeting {
void sayHello(String name);
}

然后,创建一个实现 Greeting 接口的 GreetingImpl 类:

1
2
3
4
5
6
public class GreetingImpl implements Greeting {
@Override
public void sayHello(String name) {
System.out.println("Hello, " + name);
}
}

接下来创建一个实现了 java.lang.reflect.InvocationHandlerGreetingInvocationHandler 类,用于处理代理实例上的方法调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class GreetingInvocationHandler implements InvocationHandler {
private final Object target;

public GreetingInvocationHandler(Object target) {
this.target = target;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before method call: " + method.getName());
Object result = method.invoke(target, args);
System.out.println("After method call: " + method.getName());
return result;
}
}

最后,我们创建一个测试类来展示动态代理的用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.lang.reflect.Proxy;

public class DynamicProxyDemo {
public static void main(String[] args) {
GreetingImpl greetingImpl = new GreetingImpl();
GreetingInvocationHandler invocationHandler = new GreetingInvocationHandler(greetingImpl);

Greeting greetingProxy = (Greeting) Proxy.newProxyInstance(
greetingImpl.getClass().getClassLoader(),
greetingImpl.getClass().getInterfaces(),
invocationHandler);

greetingProxy.sayHello("JDK Dynamic Proxy");
}
}

静态代理:

首先,我们创建一个名为 Database 的接口:

1
2
3
public interface Database {
void executeQuery(String query);
}

接下来,我们创建实现 Database 接口的 RealDatabase 类:

1
2
3
4
5
6
public class RealDatabase implements Database {
@Override
public void executeQuery(String query) {
System.out.println("Executing query: " + query);
}
}

然后,我们创建一个实现 Database 接口的代理类 DatabaseProxy

1
2
3
4
5
6
7
8
9
10
11
12
13
public class DatabaseProxy implements Database {
private RealDatabase database;

public DatabaseProxy() {
database = new RealDatabase();
}

@Override
public void executeQuery(String query) {
System.out.println("Proxy checking access...");
database.executeQuery(query);
}
}

最后,我们创建一个测试类来展示代理模式的用法:

1
2
3
4
5
6
7
public class ProxyPatternDemo {
public static void main(String[] args) {
Database database = new DatabaseProxy();

database.executeQuery("SELECT * FROM users;");
}
}

行为型模式(Behavioral Patterns)

职责链模式(Chain of Responsibility):

- 优点:解耦请求者与处理者,提高灵活性。
- 缺点:不保证请求被处理,可能会增加延迟。

先创建一个抽象校验器类(Validator),它包含指向下一个校验器的引用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public abstract class Validator {
protected Validator nextValidator;
protected boolean valid;

public void setNextValidator(Validator nextValidator) {
this.nextValidator = nextValidator;
}

public abstract void validate(String data);

public boolean isValid() {
return valid;
}
}

接下来,创建两个具体的校验器类(LengthValidatorCharacterValidator),分别从抽象校验器类 Validator 继承:

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 LengthValidator extends Validator {
@Override
public void validate(String data) {
if (data.length() >= 5) {
System.out.println("LengthValidator passed.");
valid = true;
if (nextValidator != null) {
nextValidator.validate(data);
valid = nextValidator.isValid();
}
} else {
System.out.println("LengthValidator failed.");
valid = false;
}
}
}

public class CharacterValidator extends Validator {
@Override
public void validate(String data) {
if (data.matches("[a-zA-Z]+")) {
System.out.println("CharacterValidator passed.");
valid = true;
} else {
System.out.println("CharacterValidator failed.");
valid = false;
}
}
}

最后,创建一个测试类来展示责任链模式在校验场景中的应用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class ValidationChainDemo {
public static void main(String[] args) {
Validator lengthValidator = new LengthValidator();
Validator characterValidator = new CharacterValidator();

lengthValidator.setNextValidator(characterValidator);

String[] inputs = {"abc", "abcdef", "abc123", "abc&^%"};

for (String input : inputs) {
System.out.println("Input: " + input);
lengthValidator.validate(input);
if (!lengthValidator.isValid()) {
// 如果长度校验器校验失败,跳过后续校验器
System.out.println("Skipping next validators...");
}
System.out.println("Validation result: " + lengthValidator.isValid());
System.out.println("------");
}
}
}

在这个修改后的示例中,遇到一个 false 结果就停止接下来的校验。现在,如果 LengthValidator
失败,它不会传递给下一个校验器 CharacterValidator。### 命令模式(Command):

- 优点:将请求和处理分离,增加系统的灵活性。
- 缺点:可能引入较多的具体命令类,增加复杂性。

命令模式(Command Pattern)用于将操作封装成一个对象,从而解耦请求者和实际操作的执行者。以下是一个简单的 Java 示例:

  1. 创建一个 Command 接口,定义命令操作:
1
2
3
public interface Command {
void execute();
}
  1. 创建一个具体的命令类(例如 PrintCommand),实现 Command 接口:
1
2
3
4
5
6
7
8
9
10
11
12
public class PrintCommand implements Command {
private String message;

public PrintCommand(String message) {
this.message = message;
}

@Override
public void execute() {
System.out.println(message);
}
}
  1. 创建一个请求者类 Invoker,用于接收和执行命令:
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 executeCommand() {
command.execute();
}
}
  1. 最后创建一个测试类来演示命令模式的用法:
1
2
3
4
5
6
7
8
9
10
11
12
13
public class CommandPatternDemo {
public static void main(String[] args) {
Invoker invoker = new Invoker();
Command printHello = new PrintCommand("Hello, World!");
Command printGoodbye = new PrintCommand("Goodbye, World!");

invoker.setCommand(printHello);
invoker.executeCommand();

invoker.setCommand(printGoodbye);
invoker.executeCommand();
}
}

在这个简单的示例中,我们创建了一个命令接口 Command 和一个具体的命令实现类 PrintCommandInvoker
类将命令对象与实际操作的执行者解耦。我们可以轻松地更换 Command 对象,而不会影响到 Invoker
的实现。在此示例中,我们创建了两个不同的 PrintCommand 对象并分别通过 Invoker 对象执行它们。

解释器模式(Interpreter):

- 优点:便于为文法规则添加新的解释表示。
- 缺点:当文法规则复杂时,效率较低。

解释器模式(Interpreter Pattern)通常用于解析和处理具有良好结构的文本或者其他形式的数据。以下是一个简化的 Java
示例,用于理解解释器模式。

我们将创建一个简单的解释器,用于将输入的数字(1-7)转换为星期的名称。

  1. 定义 Expression 接口,表示抽象表达式:
1
2
3
public interface Expression {
String interpret(int number);
}
  1. 创建一个具体的解释器类 DayOfWeekInterpreter,实现 Expression 接口:
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 DayOfWeekInterpreter implements Expression {
@Override
public String interpret(int number) {
switch (number) {
case 1:
return "Sunday";
case 2:
return "Monday";
case 3:
return "Tuesday";
case 4:
return "Wednesday";
case 5:
return "Thursday";
case 6:
return "Friday";
case 7:
return "Saturday";
default:
return "Invalid day number";
}
}
}
  1. 创建一个测试类来演示解释器模式的用法:
1
2
3
4
5
6
7
8
9
10
public class InterpreterPatternDemo {
public static void main(String[] args) {
Expression dayOfWeekInterpreter = new DayOfWeekInterpreter();
int[] dayNumbers = {1, 3, 5, 7, 8};

for (int dayNumber : dayNumbers) {
System.out.println("Day " + dayNumber + " is " + dayOfWeekInterpreter.interpret(dayNumber));
}
}
}

在这个简化的示例中,我们创建了一个 Expression
接口和一个具体的解释器实现类 DayOfWeekInterpreterDayOfWeekInterpreter
根据输入的数字返回星期的名称。实际应用中,解释器模式的使用场景可以复杂得多,例如处理各种具体语法规则、语法分析器等。这个例子只是为了简化理解。

迭代器模式(Iterator):

- 优点:为容器提供统一的访问接口,降低遍历复杂性。
- 缺点:对于简单的容器,接口可能显得冗余。

迭代器模式(Iterator Pattern)提供一种遍历容器内元素的统一方法,同时不暴露容器的内部表示。以下是一个简化的 Java
示例,用于理解迭代器模式。

  1. 定义迭代器接口 Iterator
1
2
3
4
public interface Iterator<E> {
boolean hasNext();
E next();
}
  1. 定义需要遍历的容器接口 Container
1
2
3
public interface Container<E> {
Iterator<E> getIterator();
}
  1. 创建具体的容器实现类 ListContainer
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
import java.util.ArrayList;
import java.util.List;

public class ListContainer<E> implements Container<E> {
private List<E> elements;

public ListContainer() {
this.elements = new ArrayList<>();
}

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

@Override
public Iterator<E> getIterator() {
return new ListIterator();
}

private class ListIterator implements Iterator<E> {
private int index = 0;

@Override
public boolean hasNext() {
return index < elements.size();
}

@Override
public E next() {
if (hasNext()) {
return elements.get(index++);
}
return null;
}
}
}
  1. 创建一个测试类来演示迭代器模式的用法:
1
2
3
4
5
6
7
8
9
10
11
12
13
public class IteratorPatternDemo {
public static void main(String[] args) {
ListContainer<String> names = new ListContainer<>();
names.addElement("Alice");
names.addElement("Bob");
names.addElement("Charlie");

Iterator<String> iterator = names.getIterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}

在这个简化的示例中,我们定义了一个 Iterator 接口和一个需要遍历的容器接口 ContainerListContainer 实现了 Container
接口,并通过内部类 ListIterator 提供了实际的迭代器。测试类展示了如何使用迭代器遍历容器内的数据。

这个简洁的例子展示了迭代器模式的核心思想。实际应用中,Java 提供了现成的 Iterator 接口和许多实现了 Iterable
接口的集合类(如 ArrayListHashSet 等),因此无需手动创建这些接口和实现类。

中介者模式(Mediator):

- 优点:减少对象之间的直接调用,降低耦合。
- 缺点:中介者可能变得庞大且复杂。

中介者模式(Mediator Pattern)用于降低系统组件之间的耦合度,使得组件可以通过一个中介对象进行相互通信。以下是一个简化的 Java
示例,以方便理解中介者模式。

假设我们有两个按钮,它们需要相互协调来解锁或锁定一个门。

  1. 创建 Colleague 抽象类来表示组件:
1
2
3
4
5
6
7
8
9
public abstract class Colleague {
protected Mediator mediator;

public Colleague(Mediator mediator) {
this.mediator = mediator;
}

public abstract void toggle();
}
  1. 创建两个具体的 Colleague 类:ButtonAButtonB
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 ButtonA extends Colleague {
public ButtonA(Mediator mediator) {
super(mediator);
}

@Override
public void toggle() {
System.out.println("Button A toggled");
mediator.changeState(this);
}
}

public class ButtonB extends Colleague {
public ButtonB(Mediator mediator) {
super(mediator);
}

@Override
public void toggle() {
System.out.println("Button B toggled");
mediator.changeState(this);
}
}
  1. 创建 Mediator 接口来表示中介者:
1
2
3
public interface Mediator {
void changeState(Colleague colleague);
}
  1. 创建具体的中介者类 DoorMediator
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 DoorMediator implements Mediator {
private boolean is_unlocked;
private ButtonA buttonA;
private ButtonB buttonB;

public DoorMediator(ButtonA buttonA, ButtonB buttonB) {
this.buttonA = buttonA;
this.buttonB = buttonB;
is_unlocked = false;
}

@Override
public void changeState(Colleague colleague) {
if (colleague instanceof ButtonA) {
is_unlocked = !is_unlocked;
}

if (colleague instanceof ButtonB) {
if (is_unlocked) {
System.out.println("Door has been locked");
is_unlocked = false;
} else {
System.out.println("Door cannot be locked before unlocking");
}
}
}
}
  1. 创建一个测试类来演示中介者模式的用法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MediatorPatternDemo {
public static void main(String[] args) {
DoorMediator mediator = new DoorMediator(null, null);
ButtonA buttonA = new ButtonA(mediator);
ButtonB buttonB = new ButtonB(mediator);

mediator = new DoorMediator(buttonA, buttonB);

buttonA.toggle();
buttonB.toggle();

buttonA.toggle(); // Attempt to lock the door without unlocking it
buttonB.toggle(); // Attempt to lock the door while it's already locked
}
}

在这个简化示例中,我们创建了一个 Colleague 抽象类,两个具体的组件 ButtonAButtonB,以及一个 Mediator
接口和一个具体的中介者类 DoorMediator。通过让按钮与中介者进行交互,而非直接相互沟通,我们降低了按钮 ButtonA
ButtonB 之间的耦合。

在实际项目中,中介者模式可以应用于更复杂的系统,以减轻各个组件间的通信压力。

  1. 备忘录模式(Memento):

- 优点:允许在不破坏封装性的前提下保存和恢复对象的状态。
- 缺点:如果需要保存的对象状态较大,会占用较多的内存资源。

忘录模式(Memento Pattern)用于捕获对象的内部状态,并在以后将对象恢复到此状态。以下是一个简化的 Java 示例,用于理解备忘录模式。

假设我们正在编写一个非常简单的撤销功能的文本编辑器。

  1. 创建 Memento 类来存储文本编辑器的状态:
1
2
3
4
5
6
7
8
9
10
11
public class Memento {
private String state;

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

public String getState() {
return state;
}
}
  1. 创建需要保存状态的 Originator 类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Originator {
private String state;

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

public String getState() {
return state;
}

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

public void getStateFromMemento(Memento memento) {
state = memento.getState();
}
}
  1. 创建一个负责保存备忘录的 Caretaker 类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.util.ArrayList;
import java.util.List;

public class Caretaker {
private List<Memento> mementoList = new ArrayList<>();

public void add(Memento state) {
mementoList.add(state);
}

public Memento get(int index) {
return mementoList.get(index);
}
}
  1. 创建一个测试类来演示备忘录模式的用法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class MementoPatternDemo {
public static void main(String[] args) {
Originator originator = new Originator();
Caretaker caretaker = new Caretaker();

originator.setState("State #1");
caretaker.add(originator.saveStateToMemento());

originator.setState("State #2");
caretaker.add(originator.saveStateToMemento());

originator.setState("State #3");

// Restoring to previous saved state
originator.getStateFromMemento(caretaker.get(1));
System.out.println("Restored state: " + originator.getState()); // Should print 'State #2'
}
}

在这个简化示例中,我们创建了一个 Memento 类,用于存储文本编辑器的状态。Originator
类负责设置和获取状态以及将状态保存到实例化的 Memento 对象外部。Caretaker 类负责保存Memento 实例,并在需要时提供恢复状态的功能。

这个简洁的例子展示了备忘录模式的核心思想,用于保存和恢复对象的内部状态。实际应用中,备忘录模式可用于实现撤销和重做操作、程序快照等功能。

观察者模式(Observer):

- 优点:支持发布-订阅模式,降低对象间耦合度。
- 缺点:通知顺序不固定,可能导致死循环。

  1. 使用以下简化版的观察者(Observer)和主题(Subject)接口:
1
2
3
4
5
6
7
8
9
public interface Observer {
void update(String topic);
}

public interface Subject {
void addObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
  1. 创建一个 Lecturer 类作为 Subject:
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
import java.util.ArrayList;
import java.util.List;

public class Lecturer implements Subject {
private List<Observer> students;
private String topic;

public Lecturer() {
students = new ArrayList<>();
}

@Override
public void addObserver(Observer observer) {
students.add(observer);
}

@Override
public void removeObserver(Observer observer) {
students.remove(observer);
}

@Override
public void notifyObservers() {
for (Observer student : students) {
student.update(topic);
}
}

public void setTopic(String topic) {
this.topic = topic;
notifyObservers();
}
}
  1. 创建一个 Student 类作为具体的观察者:
1
2
3
4
5
6
7
8
9
10
11
12
public class Student implements Observer {
private String name;

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

@Override
public void update(String topic) {
System.out.println(name + " received the new topic: " + topic);
}
}
  1. 创建一个测试类来演示观察者模式的用法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class SimpleLectureExample {
public static void main(String[] args) {
Lecturer lecturer = new Lecturer();

Student student1 = new Student("Alice");
Student student2 = new Student("Bob");

lecturer.addObserver(student1);
lecturer.addObserver(student2);

lecturer.setTopic("Design Patterns");
lecturer.setTopic("Big Data");
}
}

这个简单的例子展示了一对多依赖关系的核心概念:当主讲人的课题发生变化时,所有订阅学生都会及时收到通知。

状态模式(State):

- 优点:封装内部状态行为,降低外部调用复杂性。
- 缺点:增加了类的数量,增加系统负担。

假设我们有一个简单的交通信号灯,只有红、黄、绿三种状态。首先我们创建一个状态接口:

1
2
3
public interface TrafficLightState {
void change();
}

然后为红、黄、绿三种状态创建具体的状态类:

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
public class RedState implements TrafficLightState {
private TrafficLight trafficLight;

public RedState(TrafficLight trafficLight) {
this.trafficLight = trafficLight;
}

@Override
public void change() {
System.out.println("Red light turns to Green");
trafficLight.setState(trafficLight.getGreenState());
}
}

public class YellowState implements TrafficLightState {
private TrafficLight trafficLight;

public YellowState(TrafficLight trafficLight) {
this.trafficLight = trafficLight;
}

@Override
public void change() {
System.out.println("Yellow light turns to Red");
trafficLight.setState(trafficLight.getRedState());
}
}

public class GreenState implements TrafficLightState {
private TrafficLight trafficLight;

public GreenState(TrafficLight trafficLight) {
this.trafficLight = trafficLight;
}

@Override
public void change() {
System.out.println("Green light turns to Yellow");
trafficLight.setState(trafficLight.getYellowState());
}
}

创建一个 TrafficLight 类,用于管理状态转换:

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 TrafficLight {
private TrafficLightState redState;
private TrafficLightState yellowState;
private TrafficLightState greenState;

private TrafficLightState currentState;

public TrafficLight() {
redState = new RedState(this);
yellowState = new YellowState(this);
greenState = new GreenState(this);

currentState = redState;
}

public void change() {
currentState.change();
}

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

public TrafficLightState getRedState() {
return redState;
}

public TrafficLightState getYellowState() {
return yellowState;
}

public TrafficLightState getGreenState() {
return greenState;
}
}

策略模式(Strategy):

- 优点:将一组算法封装,提高算法的可互换性和复用性。
- 缺点:客户端必须了解具体策略的区别。

个完整的策略模式包括策略接口、具体策略实现、上下文类以及它们的使用。以下是一个关于支付方式的完整的策略模式示例:

  1. 策略接口 - PaymentStrategy
1
2
3
public interface PaymentStrategy {
void pay(double amount);
}
  1. 具体策略实现
  • CreditCardStrategy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class CreditCardStrategy implements PaymentStrategy {
private String name;
private String cardNumber;
private String cvv;
private String dateOfExpiry;

public CreditCardStrategy(String name, String cardNumber, String cvv, String dateOfExpiry) {
this.name = name;
this.cardNumber = cardNumber;
this.cvv = cvv;
this.dateOfExpiry = dateOfExpiry;
}

@Override
public void pay(double amount) {
System.out.println(amount + " paid with credit/debit card");
}
}
  • PaypalStrategy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class PaypalStrategy implements PaymentStrategy {
private String email;
private String password;

public PaypalStrategy(String email, String password) {
this.email = email;
this.password = password;
}

@Override
public void pay(double amount) {
System.out.println(amount + " paid using PayPal.");
}
}
  1. 上下文类 - ShoppingCart
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
import java.util.ArrayList;
import java.util.List;

public class ShoppingCart {
private List<Item> items;

public ShoppingCart() {
this.items = new ArrayList<>();
}

public void addItem(Item item) {
items.add(item);
}

public void removeItem(Item item) {
items.remove(item);
}

public double calculateTotal() {
double sum = 0;
for (Item item : items) {
sum += item.getPrice();
}
return sum;
}

public void pay(PaymentStrategy paymentStrategy) {
double amount = calculateTotal();
paymentStrategy.pay(amount);
}
}
  1. 数据类 - Item(产品)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Item {
private String code;
private double price;

public Item(String code, double price) {
this.code = code;
this.price = price;
}

public String getCode() {
return code;
}

public double getPrice() {
return price;
}
}
  1. 策略模式的使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ShoppingCartTest {
public static void main(String[] args) {
ShoppingCart cart = new ShoppingCart();
Item item1 = new Item("item1", 100);
Item item2 = new Item("item2", 150);

cart.addItem(item1);
cart.addItem(item2);

// 支付通过信用卡
cart.pay(new CreditCardStrategy("John Doe", "1234567890", "123", "01/2025"));

// 支付通过Paypal
cart.pay(new PaypalStrategy("johndoe@example.com", "mypassword"));
}
}

在这个完整的策略模式示例中,我们创建了一个支付策略接口,以及两个不同的具体支付策略实现:信用卡支付和PayPal支付。通常,这两种支付方式的处理过程和API可能有很大差异,但从客户(购物车)的角度来看,它们具有相同的行为:支付金额。购物车充当上下文,并在运行时接收一个支付策略对象来完成支付。这使得购物车及其支付逻辑具有更高的灵活性,阅读代码时可通过不同支付接口而无需更改其实现。

模板方法模式(Template Method):

- 优点:将不变的行为定义在父类,将可变的行为定义在子类。
- 缺点:可能导致过多的子类。

模板方法模式是一种行为设计模式,它定义了算法的基本步骤,在父类中提供通用实现。子类可以根据需要覆写这些方法,从而实现特定行为。以下是一个关于制作三明治的完整模板方法模式示例:

  1. 抽象类 - Sandwich
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
public abstract class Sandwich {
// 模板方法
public final void make() {
cutBread();
addMeat();
addCondiments();
addVegetables();
if (customerWantsCheese()) {
addCheese();
}
wrapSandwich();
}

void cutBread() {
System.out.println("Bread is sliced.");
}

abstract void addMeat();

abstract void addCondiments();

void addVegetables() {
System.out.println("Adding vegetables.");
}

// 钩子方法
boolean customerWantsCheese() {
return true;
}

void addCheese() {
System.out.println("Adding Cheese.");
}

void wrapSandwich() {
System.out.println("Wrapping the sandwich.");
}
}
  1. 具体类
  • HamSandwich
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class HamSandwich extends Sandwich {
@Override
void addMeat() {
System.out.println("Adding ham.");
}

@Override
void addCondiments() {
System.out.println("Adding mayo and mustard.");
}

@Override
boolean customerWantsCheese() {
return false;
}
}
  • TurkeySandwich
1
2
3
4
5
6
7
8
9
10
11
public class TurkeySandwich extends Sandwich {
@Override
void addMeat() {
System.out.println("Adding turkey.");
}

@Override
void addCondiments() {
System.out.println("Adding ketchup.");
}
}
  1. 使用模板方法模式
1
2
3
4
5
6
7
8
9
10
11
12
public class SandwichMaker {
public static void main(String[] args) {
Sandwich hamSandwich = new HamSandwich();
System.out.println("Making Ham Sandwich:");
hamSandwich.make();
System.out.println();

Sandwich turkeySandwich = new TurkeySandwich();
System.out.println("Making Turkey Sandwich:");
turkeySandwich.make();
}
}

在此示例中,Sandwich 抽象类定义了制作三明治的基本步骤。该类中实现了一些方法,如 cutBreadwrapSandwich
,它们对所有子类都是通用的。同时,一些抽象方法,如 addMeataddCondiments
,需要具体子类去实现。我们还提供了一个钩子方法 customerWantsCheese,允许子类覆盖默认行为。

我们创建了两个具体三明治子类:HamSandwichTurkeySandwich
。这些子类实现了抽象方法,以便对特定类型的三明治调整步骤。我们在 SandwichMaker 类中实例化这些子类并调用 make
方法。make 方法在父类 Sandwich 中定义,并依次调用基本步骤。子类可以根据需要覆写这些方法,从而实现特定行为。

访问者模式(Visitor):

- 优点:将操作与数据结构分离,增加新操作不修改数据结构。
- 缺点:增加新的数据结构需要修改现有的操作类,违反开闭原则。

访问者模式是一种行为设计模式,允许你在不改变类的前提下定义关于这些类的新操作。下面是一个以计算机硬件组件为例的完整访问者模式示例:

  1. 元素接口 - ComputerPart
1
2
3
public interface ComputerPart {
void accept(ComputerPartVisitor computerPartVisitor);
}
  1. 具体元素类
  • Keyboard
1
2
3
4
5
6
public class Keyboard implements ComputerPart {
@Override
public void accept(ComputerPartVisitor computerPartVisitor) {
computerPartVisitor.visit(this);
}
}
  • Monitor
1
2
3
4
5
6
public class Monitor implements ComputerPart {
@Override
public void accept(ComputerPartVisitor computerPartVisitor) {
computerPartVisitor.visit(this);
}
}
  • Mouse
1
2
3
4
5
6
public class Mouse implements ComputerPart {
@Override
public void accept(ComputerPartVisitor computerPartVisitor) {
computerPartVisitor.visit(this);
}
}
  • Computer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Computer implements ComputerPart {
ComputerPart[] parts;

public Computer() {
parts = new ComputerPart[] { new Mouse(), new Keyboard(), new Monitor() };
}

@Override
public void accept(ComputerPartVisitor computerPartVisitor) {
for (int i = 0; i < parts.length; i++) {
parts[i].accept(computerPartVisitor);
}
computerPartVisitor.visit(this);
}
}
  1. 访问者接口 - ComputerPartVisitor
1
2
3
4
5
6
public interface ComputerPartVisitor {
void visit(Computer computer);
void visit(Mouse mouse);
void visit(Keyboard keyboard);
void visit(Monitor monitor);
}
  1. 具体访问者类 - ComputerPartDisplayVisitor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class ComputerPartDisplayVisitor implements ComputerPartVisitor {
@Override
public void visit(Computer computer) {
System.out.println("Displaying Computer.");
}

@Override
public void visit(Mouse mouse) {
System.out.println("Displaying Mouse.");
}

@Override
public void visit(Keyboard keyboard) {
System.out.println("Displaying Keyboard.");
}

@Override
public void visit(Monitor monitor) {
System.out.println("Displaying Monitor.");
}
}
  1. 使用访问者模式
1
2
3
4
5
6
public class VisitorPatternDemo {
public static void main(String[] args) {
ComputerPart computer = new Computer();
computer.accept(new ComputerPartDisplayVisitor());
}
}

在上面的示例中,我们定义了一个 ComputerPart 接口以及几个具体的组件类。这些组件实现了 ComputerPart
接口,它仅包含一个 accept 方法,用于接受访问者对象。

接下来,我们定义了 ComputerPartVisitor 接口,它有四个不同的 visit
方法,分别处理不同类型的组件。然后创建一个具体的访问者类 ComputerPartDisplayVisitor,它实现了 ComputerPartVisitor
接口。在这个类中,我们可以针对不同的计算机组件执行不同的操作,这些操作与组件类本身是解耦的。

最后,在 VisitorPatternDemo 中,我们创建了一个计算机元素对象,并用具体的访问者 ComputerPartDisplayVisitor
来访问它。我们可以随时创建更多的具体访问者类来处理组件对象,而无需修改组件类的实现。这使得添加新功能更加灵活,且不会破坏现有代码的封装性。

__END__