设计模式使用Java案例
代码设计要有可维护性,可复用性,可扩展性,灵活性,所有要使用设计模式进行灵活设计代码
创建型
简单工厂模式(Simple Factory)
简单工厂模式(Simple Factory Pattern)是一种创建型设计模式,它通过一个工厂类来创建对象,而不是直接在客户端代码中使用 new
关键字。简单工厂模式将对象的创建逻辑集中在一个工厂类中,使客户端代码与具体实现解耦。
1. 简单工厂模式的结构
简单工厂模式包含以下角色:
- 工厂类(Factory):负责创建对象。
- 抽象产品类(Product):定义产品的接口或抽象类。
- 具体产品类(Concrete Product):实现抽象产品类的具体产品。
2. UML 类图
以下是简单工厂模式的 UML 类图:
+---------------------+ +---------------------+
| Factory | | Product |
+---------------------+ +---------------------+
| + createProduct() | — — ————>| + use(): void |
+---------------------+ +---------------------+
△
|
|
+-----------------------------+
| |
+---------------------+ +---------------------+
| ConcreteProductA | | ConcreteProductB |
+---------------------+ +---------------------+
| + use(): void | | + use(): void |
+---------------------+ +---------------------+
说明:
- Factory:工厂类,负责创建具体产品。
- Product:抽象产品类,定义产品的接口。
- ConcreteProductA 和 ConcreteProductB:具体产品类,实现
Product
接口。
3. 代码实现
以下是一个简单的 Java 实现示例:
(1) 抽象产品类(Product)
public interface Product {
void use();
}
(2) 具体产品类(ConcreteProductA 和 ConcreteProductB)
public class ConcreteProductA implements Product {
@Override
public void use() {
System.out.println("Using Product A");
}
}
public class ConcreteProductB implements Product {
@Override
public void use() {
System.out.println("Using Product B");
}
}
(3) 工厂类(Factory)
public class Factory {
public static Product createProduct(String type) {
if (type.equals("A")) {
return new ConcreteProductA();
} else if (type.equals("B")) {
return new ConcreteProductB();
} else {
throw new IllegalArgumentException("Unknown product type");
}
}
}
(4) 客户端代码
public class Client {
public static void main(String[] args) {
Product productA = Factory.createProduct("A");
productA.use(); // 输出: Using Product A
Product productB = Factory.createProduct("B");
productB.use(); // 输出: Using Product B
}
}
4. 优点
- 解耦:将对象的创建与使用分离,客户端代码无需关心具体产品的创建细节。
- 集中管理:对象的创建逻辑集中在工厂类中,便于维护和扩展。
5. 缺点
- 违反开闭原则:如果需要添加新的产品类型,必须修改工厂类的代码。
- 工厂类职责过重:如果产品类型过多,工厂类的代码会变得复杂。
6. 适用场景
- 对象的创建逻辑比较简单。
- 客户端不需要关心对象的创建细节。
- 产品类型较少,且不会频繁变化。
7. 总结
- 简单工厂模式:通过工厂类集中管理对象的创建逻辑。
- 优点:解耦、集中管理。
- 缺点:违反开闭原则、工厂类职责过重。
- 适用场景:对象创建逻辑简单、产品类型较少。
抽象工厂模式(Abstract Factory)
抽象工厂模式(Abstract Factory Pattern)是一种创建型设计模式,它提供了一种方式来创建一系列相关或相互依赖的对象,而无需指定它们的具体类。抽象工厂模式的核心思想是将对象的创建与使用分离,使得系统可以在不修改代码的情况下切换整个产品族。
1. 抽象工厂模式的结构
抽象工厂模式包含以下角色:
-
抽象工厂(Abstract Factory):
- 定义创建一系列产品对象的接口。
- 包含多个工厂方法,每个方法用于创建一个具体的产品对象。
-
具体工厂(Concrete Factory):
- 实现抽象工厂的接口,负责创建具体的产品对象。
- 每个具体工厂对应一个产品族。
-
抽象产品(Abstract Product):
- 定义产品对象的接口。
-
具体产品(Concrete Product):
- 实现抽象产品的接口,是具体工厂创建的对象。
-
客户端(Client):
- 使用抽象工厂和抽象产品接口,无需关心具体的实现类。
2. 抽象工厂模式的 UML 图
3. 抽象工厂模式的实现
以下是一个简单的抽象工厂模式的代码示例:
(1) 抽象产品
// 抽象产品 A
interface ProductA {
void methodA();
}
// 抽象产品 B
interface ProductB {
void methodB();
}
(2) 具体产品
// 具体产品 A1
class ProductA1 implements ProductA {
@Override
public void methodA() {
System.out.println("ProductA1 methodA");
}
}
// 具体产品 B1
class ProductB1 implements ProductB {
@Override
public void methodB() {
System.out.println("ProductB1 methodB");
}
}
// 具体产品 A2
class ProductA2 implements ProductA {
@Override
public void methodA() {
System.out.println("ProductA2 methodA");
}
}
// 具体产品 B2
class ProductB2 implements ProductB {
@Override
public void methodB() {
System.out.println("ProductB2 methodB");
}
}
(3) 抽象工厂
interface AbstractFactory {
ProductA createProductA();
ProductB createProductB();
}
(4) 具体工厂
// 具体工厂 1
class ConcreteFactory1 implements AbstractFactory {
@Override
public ProductA createProductA() {
return new ProductA1();
}
@Override
public ProductB createProductB() {
return new ProductB1();
}
}
// 具体工厂 2
class ConcreteFactory2 implements AbstractFactory {
@Override
public ProductA createProductA() {
return new ProductA2();
}
@Override
public ProductB createProductB() {
return new ProductB2();
}
}
(5) 客户端
public class Client {
public static void main(String[] args) {
// 使用具体工厂 1
AbstractFactory factory1 = new ConcreteFactory1();
ProductA productA1 = factory1.createProductA();
ProductB productB1 = factory1.createProductB();
productA1.methodA();
productB1.methodB();
// 使用具体工厂 2
AbstractFactory factory2 = new ConcreteFactory2();
ProductA productA2 = factory2.createProductA();
ProductB productB2 = factory2.createProductB();
productA2.methodA();
productB2.methodB();
}
}
输出:
ProductA1 methodA
ProductB1 methodB
ProductA2 methodA
ProductB2 methodB
4. 抽象工厂模式的优点
- 解耦:将对象的创建与使用分离,客户端只需依赖抽象接口,无需关心具体实现。
- 扩展性:可以轻松扩展新的产品族,只需增加新的具体工厂和产品类。
- 一致性:确保创建的产品对象属于同一个产品族,避免不兼容的对象组合。
5. 抽象工厂模式的缺点
- 复杂性:增加了系统的复杂性,需要定义多个接口和类。
- 扩展困难:如果需要增加新的产品类型(如
ProductC
),需要修改抽象工厂和所有具体工厂的接口。
6. 适用场景
- 系统需要创建一系列相关或相互依赖的对象。
- 系统需要支持多个产品族,并且可以在运行时切换产品族。
- 系统需要确保创建的对象属于同一个产品族。
工厂方法模式(Factory Method)
工厂方法模式(Factory Method Pattern)是一种创建型设计模式,它定义了一个创建对象的接口,但由子类决定实例化哪个类。工厂方法模式将对象的创建延迟到子类,使得系统可以在不修改代码的情况下扩展新的产品类型。
1. 工厂方法模式的结构
工厂方法模式包含以下角色:
-
抽象产品(Product):
- 定义产品对象的接口。
-
具体产品(Concrete Product):
- 实现抽象产品的接口,是工厂方法创建的对象。
-
抽象工厂(Creator):
- 定义工厂方法(Factory Method),用于创建产品对象。
-
具体工厂(Concrete Creator):
- 实现工厂方法,返回具体产品的实例。
2. 工厂方法模式的 UML 图
3. 工厂方法模式的实现
以下是一个简单的工厂方法模式的代码示例:
(1) 抽象产品
// 抽象产品
interface Product {
void method();
}
(2) 具体产品
// 具体产品 A
class ConcreteProductA implements Product {
@Override
public void method() {
System.out.println("ConcreteProductA method");
}
}
// 具体产品 B
class ConcreteProductB implements Product {
@Override
public void method() {
System.out.println("ConcreteProductB method");
}
}
(3) 抽象工厂
// 抽象工厂
abstract class Creator {
// 工厂方法
public abstract Product factoryMethod();
// 其他方法
public void someOperation() {
Product product = factoryMethod();
product.method();
}
}
(4) 具体工厂
// 具体工厂 A
class ConcreteCreatorA extends Creator {
@Override
public Product factoryMethod() {
return new ConcreteProductA();
}
}
// 具体工厂 B
class ConcreteCreatorB extends Creator {
@Override
public Product factoryMethod() {
return new ConcreteProductB();
}
}
(5) 客户端
public class Client {
public static void main(String[] args) {
// 使用具体工厂 A
Creator creatorA = new ConcreteCreatorA();
creatorA.someOperation();
// 使用具体工厂 B
Creator creatorB = new ConcreteCreatorB();
creatorB.someOperation();
}
}
输出:
ConcreteProductA method
ConcreteProductB method
4. 工厂方法模式的优点
- 解耦:将对象的创建与使用分离,客户端只需依赖抽象接口,无需关心具体实现。
- 扩展性:可以轻松扩展新的产品类型,只需增加新的具体工厂和产品类。
- 单一职责:每个具体工厂只负责创建一种产品,符合单一职责原则。
5. 工厂方法模式的缺点
- 复杂性:增加了系统的复杂性,需要定义多个接口和类。
- 类的数量增加:每增加一种产品类型,都需要增加一个具体工厂类。
6. 适用场景
- 系统需要支持多种产品类型,并且可以在运行时动态切换。
- 系统需要将对象的创建与使用分离,提高灵活性和可维护性。
- 系统需要遵循开闭原则,支持扩展而不修改现有代码。
7. 工厂方法模式与简单工厂模式与抽象工厂模式的区别
- 简单工厂模式:由一个工厂类负责创建所有产品对象,不符合开闭原则。
- 工厂方法模式:将对象的创建延迟到子类,符合开闭原则。
- 抽象工厂模式:将创建一系列相关或相互依赖的对象,符合开闭原则。
建造者模式(Builder)
建造者模式(Builder Pattern)是一种创建型设计模式,它用于将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。建造者模式适用于需要分步骤创建复杂对象的场景,尤其是在对象的构建过程需要多个步骤或参数时。
1. 建造者模式的结构
建造者模式包含以下角色:
- 产品(Product):
- 表示被构建的复杂对象。
- 抽象建造者(Builder):
- 定义构建产品的各个步骤的接口。
- 具体建造者(Concrete Builder):
- 实现抽象建造者的接口,完成产品的具体构建。
- 指挥者(Director):
- 负责调用建造者的方法,控制构建过程。
- 客户端(Client):
- 使用指挥者和建造者创建产品。
2. 建造者模式的 UML 图
3. 建造者模式的实现
以下是一个简单的建造者模式的代码示例:
(1) 产品
// 产品
class Product {
private String partA;
private String partB;
public void setPartA(String partA) {
this.partA = partA;
}
public void setPartB(String partB) {
this.partB = partB;
}
@Override
public String toString() {
return "Product{partA='" + partA + "', partB='" + partB + "'}";
}
}
(2) 抽象建造者
// 抽象建造者
interface Builder {
void buildPartA();
void buildPartB();
Product getResult();
}
(3) 具体建造者
// 具体建造者
class ConcreteBuilder implements Builder {
private Product product = new Product();
@Override
public void buildPartA() {
product.setPartA("PartA");
}
@Override
public void buildPartB() {
product.setPartB("PartB");
}
@Override
public Product getResult() {
return product;
}
}
(4) 指挥者
// 指挥者
class Director {
private Builder builder;
public Director(Builder builder) {
this.builder = builder;
}
public void construct() {
builder.buildPartA();
builder.buildPartB();
}
}
(5) 客户端
public class Client {
public static void main(String[] args) {
// 创建具体建造者
Builder builder = new ConcreteBuilder();
// 创建指挥者
Director director = new Director(builder);
// 构建产品
director.construct();
// 获取产品
Product product = builder.getResult();
System.out.println(product);
}
}
输出:
Product{partA='PartA', partB='PartB'}
4. 建造者模式的优点
- 分离构建与表示:将复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。
- 灵活性:可以灵活地改变产品的内部表示,只需增加新的具体建造者。
- 控制构建过程:指挥者可以精确控制产品的构建过程。
5. 建造者模式的缺点
- 复杂性:增加了系统的复杂性,需要定义多个类。
- 适用范围有限:适用于需要分步骤构建复杂对象的场景,对于简单对象可能显得冗余。
6. 适用场景
- 需要创建复杂对象,且对象的构建过程需要多个步骤。
- 需要创建的对象具有多个组成部分,且这些组成部分可以灵活组合。
- 需要将对象的构建过程与其表示分离。
7. 建造者模式与工厂模式的区别
- 工厂模式:关注于创建单个对象,适用于创建过程简单的场景。
- 建造者模式:关注于分步骤创建复杂对象,适用于创建过程复杂的场景。
原型模式(Prototype)
原型模式(Prototype Pattern)是一种创建型设计模式,它通过复制现有对象来创建新对象,而不是通过实例化类。原型模式适用于创建成本较高的对象,或者需要动态配置对象的场景。
1. 原型模式的结构
原型模式包含以下角色:
- 抽象原型(Prototype):
- 定义克隆方法的接口。
- 具体原型(Concrete Prototype):
- 实现抽象原型的接口,完成对象的克隆。
- 客户端(Client):
- 使用原型对象创建新对象。
2. 原型模式的 UML 图
3. 原型模式的实现
以下是一个简单的原型模式的代码示例:
(1) 抽象原型
// 抽象原型
interface Prototype {
Prototype clone();
}
(2) 具体原型
// 具体原型
class ConcretePrototype implements Prototype {
private String field;
public ConcretePrototype(String field) {
this.field = field;
}
@Override
public Prototype clone() {
return new ConcretePrototype(this.field);
}
@Override
public String toString() {
return "ConcretePrototype{field='" + field + "'}";
}
}
(3) 客户端
public class Client {
public static void main(String[] args) {
// 创建原型对象
Prototype prototype = new ConcretePrototype("Original");
// 克隆对象
Prototype clone = prototype.clone();
System.out.println("Prototype: " + prototype);
System.out.println("Clone: " + clone);
}
}
输出:
Prototype: ConcretePrototype{field='Original'}
Clone: ConcretePrototype{field='Original'}
4. 原型模式的优点
- 性能优化:通过复制现有对象创建新对象,避免了重复的初始化操作,提高了性能。
- 动态配置:可以在运行时动态配置对象的属性。
- 简化创建过程:对于创建成本较高的对象,原型模式可以简化创建过程。
5. 原型模式的缺点
- 深拷贝与浅拷贝:需要正确处理对象的深拷贝和浅拷贝问题。
- 复杂性:对于包含循环引用的对象,克隆过程可能变得复杂。
6. 适用场景
- 需要创建的对象成本较高(如需要复杂的初始化过程)。
- 需要动态配置对象的属性。
- 需要避免重复创建相似对象。
7. 深拷贝与浅拷贝
- 浅拷贝:只复制对象的基本类型字段和引用类型字段的引用,不复制引用类型字段指向的对象。
- 深拷贝:复制对象的所有字段,包括引用类型字段指向的对象。
示例:深拷贝
class ConcretePrototype implements Prototype, Cloneable {
private String field;
private List<String> list;
public ConcretePrototype(String field, List<String> list) {
this.field = field;
this.list = list;
}
@Override
public Prototype clone() {
try {
ConcretePrototype clone = (ConcretePrototype) super.clone();
clone.list = new ArrayList<>(this.list); // 深拷贝
return clone;
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
@Override
public String toString() {
return "ConcretePrototype{field='" + field + "', list=" + list + "}";
}
}
8. 原型模式与工厂模式的区别
- 工厂模式:通过工厂方法创建新对象,适用于创建过程简单的场景。
- 原型模式:通过复制现有对象创建新对象,适用于创建成本较高的场景。
单例模式(Singleton)
单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点。单例模式适用于需要全局唯一对象的场景,如配置管理、线程池、日志记录等。
1. 单例模式的结构
单例模式包含以下角色:
- 单例类(Singleton):
- 定义获取唯一实例的方法。
- 确保类只有一个实例。
- 客户端(Client):
- 使用单例类的唯一实例。
2. 单例模式的 UML 图
3. 单例模式的实现
以下是几种常见的单例模式实现方式:
(1) 懒汉式(线程不安全)
class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
缺点:线程不安全,多个线程可能同时创建多个实例。
(2) 懒汉式(线程安全)
class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
缺点:每次调用 getInstance()
都会加锁,性能较差。
(3) 双重检查锁(Double-Checked Locking)
class Singleton {
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;
}
}
优点:线程安全,且只有在第一次创建实例时加锁,性能较好。
(4) 静态内部类
class Singleton {
private Singleton() {}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
优点:线程安全,且延迟加载(Lazy Initialization)。
(5) 枚举
enum Singleton {
INSTANCE;
public void doSomething() {
System.out.println("Singleton is doing something.");
}
}
优点:线程安全,且防止反射攻击。
4. 单例模式的优点
- 全局唯一:确保一个类只有一个实例。
- 节省资源:避免重复创建对象,节省系统资源。
- 全局访问点:提供一个全局访问点,方便管理。
5. 单例模式的缺点
- 扩展性差:单例类通常难以扩展。
- 测试困难:单例类的全局状态可能导致测试困难。
- 违反单一职责原则:单例类通常承担了创建和管理实例的职责。
6. 适用场景
- 需要全局唯一对象的场景,如配置管理、线程池、日志记录等。
- 需要频繁创建和销毁对象的场景,使用单例模式可以节省资源。
7. 单例模式的注意事项
- 线程安全:确保单例类在多线程环境下正常工作。
- 延迟加载:根据需求选择是否延迟加载实例。
- 防止反射攻击:通过枚举或私有构造方法防止反射创建新实例。
8. 示例代码
以下是使用双重检查锁实现的单例模式:
public class Singleton {
private static volatile Singleton instance;
private Singleton() {
// 防止反射创建新实例
if (instance != null) {
throw new RuntimeException("Use getInstance() method to get the single instance.");
}
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
public void doSomething() {
System.out.println("Singleton is doing something.");
}
}
客户端代码:
public class Client {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
instance.doSomething();
}
}
输出:
Singleton is doing something.
结构型设计模式
适配器模式
适配器模式(Adapter Pattern)是一种结构型设计模式,它允许不兼容的接口之间进行协作。适配器模式通过将一个类的接口转换成客户端期望的另一个接口,使得原本由于接口不兼容而无法一起工作的类可以一起工作。
1. 适配器模式的结构
适配器模式包含以下角色:
- 目标接口(Target):
- 客户端期望的接口。
- 适配者(Adaptee):
- 需要被适配的类。
- 适配器(Adapter):
- 实现目标接口,并包装适配者对象,使其能够与客户端协作。
- 客户端(Client):
- 使用目标接口与适配器交互。
2. 适配器模式的 UML 图
3. 适配器模式的实现
以下是一个简单的适配器模式的代码示例:
(1) 目标接口
// 目标接口
interface Target {
void request();
}
(2) 适配者
// 适配者
class Adaptee {
public void specificRequest() {
System.out.println("Adaptee specificRequest");
}
}
(3) 适配器
// 适配器
class Adapter implements Target {
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
adaptee.specificRequest();
}
}
(4) 客户端
public class Client {
public static void main(String[] args) {
// 创建适配者
Adaptee adaptee = new Adaptee();
// 创建适配器
Target target = new Adapter(adaptee);
// 使用目标接口
target.request();
}
}
输出:
Adaptee specificRequest
4. 适配器模式的优点
- 解耦:将客户端与适配者解耦,客户端只需依赖目标接口。
- 复用性:可以复用现有的类,而无需修改其代码。
- 灵活性:可以动态地切换适配器,支持多种适配者。
5. 适配器模式的缺点
- 复杂性:增加了系统的复杂性,需要定义额外的适配器类。
- 性能开销:适配器模式可能引入额外的性能开销。
6. 适用场景
- 需要使用现有的类,但其接口与系统不兼容。
- 需要创建一个可以复用的类,该类可以与其他不相关的类协作。
- 需要统一多个类的接口。
7. 适配器模式的类型
-
类适配器:
- 使用继承实现适配器。
- 适配器继承适配者,并实现目标接口。
示例:
class Adapter extends Adaptee implements Target { @Override public void request() { specificRequest(); } }
-
对象适配器:
- 使用组合实现适配器。
- 适配器包装适配者对象,并实现目标接口。
示例:
class Adapter implements Target { private Adaptee adaptee; public Adapter(Adaptee adaptee) { this.adaptee = adaptee; } @Override public void request() { adaptee.specificRequest(); } }
8. 示例代码
以下是使用对象适配器实现的适配器模式:
// 目标接口
interface Target {
void request();
}
// 适配者
class Adaptee {
public void specificRequest() {
System.out.println("Adaptee specificRequest");
}
}
// 适配器
class Adapter implements Target {
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
adaptee.specificRequest();
}
}
// 客户端
public class Client {
public static void main(String[] args) {
// 创建适配者
Adaptee adaptee = new Adaptee();
// 创建适配器
Target target = new Adapter(adaptee);
// 使用目标接口
target.request();
}
}
输出:
Adaptee specificRequest
桥接模式(Bridge)
桥接模式(Bridge Pattern)是一种结构型设计模式,它将抽象部分与实现部分分离,使它们可以独立变化。桥接模式通过组合代替继承,解决了多层继承带来的复杂性。
1. 桥接模式的结构
桥接模式包含以下角色:
- 抽象部分(Abstraction):
- 定义抽象接口,并包含一个对实现部分的引用。
- 扩展抽象部分(Refined Abstraction):
- 扩展抽象部分,增加额外的功能。
- 实现部分(Implementor):
- 定义实现部分的接口。
- 具体实现部分(Concrete Implementor):
- 实现实现部分的接口。
- 客户端(Client):
- 使用抽象部分与实现部分交互。
2. 桥接模式的 UML 图
3. 桥接模式的实现
以下是一个简单的桥接模式的代码示例:
(1) 实现部分
// 实现部分
interface Implementor {
void operationImpl();
}
(2) 具体实现部分
// 具体实现部分 A
class ConcreteImplementorA implements Implementor {
@Override
public void operationImpl() {
System.out.println("ConcreteImplementorA operationImpl");
}
}
// 具体实现部分 B
class ConcreteImplementorB implements Implementor {
@Override
public void operationImpl() {
System.out.println("ConcreteImplementorB operationImpl");
}
}
(3) 抽象部分
// 抽象部分
abstract class Abstraction {
protected Implementor implementor;
public Abstraction(Implementor implementor) {
this.implementor = implementor;
}
public abstract void operation();
}
(4) 扩展抽象部分
// 扩展抽象部分
class RefinedAbstraction extends Abstraction {
public RefinedAbstraction(Implementor implementor) {
super(implementor);
}
@Override
public void operation() {
System.out.println("RefinedAbstraction operation");
implementor.operationImpl();
}
}
(5) 客户端
public class Client {
public static void main(String[] args) {
// 使用具体实现部分 A
Implementor implementorA = new ConcreteImplementorA();
Abstraction abstractionA = new RefinedAbstraction(implementorA);
abstractionA.operation();
// 使用具体实现部分 B
Implementor implementorB = new ConcreteImplementorB();
Abstraction abstractionB = new RefinedAbstraction(implementorB);
abstractionB.operation();
}
}
输出:
RefinedAbstraction operation
ConcreteImplementorA operationImpl
RefinedAbstraction operation
ConcreteImplementorB operationImpl
4. 桥接模式的优点
- 分离抽象与实现:将抽象部分与实现部分分离,使它们可以独立变化。
- 扩展性:可以独立扩展抽象部分和实现部分,无需修改现有代码。
- 减少子类:避免了多层继承带来的复杂性。
5. 桥接模式的缺点
- 复杂性:增加了系统的复杂性,需要定义多个接口和类。
- 设计难度:需要正确识别抽象部分和实现部分,设计难度较高。
6. 适用场景
- 需要将抽象部分与实现部分分离,使它们可以独立变化。
- 需要避免多层继承带来的复杂性。
- 需要在运行时动态切换实现部分。
7. 桥接模式与适配器模式的区别
- 适配器模式:用于解决接口不兼容的问题,通常在系统设计完成后使用。
- 桥接模式:用于将抽象部分与实现部分分离,通常在系统设计阶段使用。
组合模式(Composite)
组合模式(Composite Pattern)是一种结构型设计模式,它允许你将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得客户端可以统一处理单个对象和组合对象。
1. 组合模式的结构
组合模式包含以下角色:
- 组件(Component):
- 定义组合中所有对象的通用接口。
- 叶子节点(Leaf):
- 表示组合中的叶子对象,没有子节点。
- 复合节点(Composite):
- 表示组合中的复合对象,包含子节点。
- 客户端(Client):
- 使用组件接口与组合结构交互。
2. 组合模式的 UML 图
3. 组合模式的实现
以下是一个简单的组合模式的代码示例:
(1) 组件
// 组件
interface Component {
void operation();
}
(2) 叶子节点
// 叶子节点
class Leaf implements Component {
private String name;
public Leaf(String name) {
this.name = name;
}
@Override
public void operation() {
System.out.println("Leaf " + name + " operation");
}
}
(3) 复合节点
// 复合节点
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);
}
public Component getChild(int index) {
return children.get(index);
}
@Override
public void operation() {
System.out.println("Composite operation");
for (Component component : children) {
component.operation();
}
}
}
(4) 客户端
public class Client {
public static void main(String[] args) {
// 创建叶子节点
Component leaf1 = new Leaf("Leaf1");
Component leaf2 = new Leaf("Leaf2");
Component leaf3 = new Leaf("Leaf3");
// 创建复合节点
Composite composite = new Composite();
composite.add(leaf1);
composite.add(leaf2);
// 创建另一个复合节点
Composite composite2 = new Composite();
composite2.add(leaf3);
composite2.add(composite);
// 使用组件
composite2.operation();
}
}
输出:
Composite operation
Leaf Leaf3 operation
Composite operation
Leaf Leaf1 operation
Leaf Leaf2 operation
4. 组合模式的优点
- 统一处理:客户端可以统一处理单个对象和组合对象。
- 灵活性:可以动态地添加或删除组件。
- 简化代码:减少了客户端代码的复杂性。
5. 组合模式的缺点
- 设计复杂性:需要正确识别组件、叶子节点和复合节点,设计难度较高。
- 类型检查:客户端可能需要检查组件的类型,增加了代码的复杂性。
6. 适用场景
- 需要表示“部分-整体”的层次结构。
- 需要统一处理单个对象和组合对象。
- 需要动态地添加或删除组件。
装饰模式(Decorator)
装饰模式(Decorator Pattern)是一种结构型设计模式,它允许你通过将对象放入包含行为的特殊封装对象中来为原对象动态添加新的行为。装饰模式通过组合代替继承,提供了更灵活的方式来扩展对象的功能。
1. 装饰模式的结构
装饰模式包含以下角色:
- 组件(Component):
- 定义对象的接口,可以动态地添加行为。
- 具体组件(Concrete Component):
- 实现组件接口,是被装饰的原始对象。
- 装饰器(Decorator):
- 包含一个对组件对象的引用,并实现组件接口。
- 具体装饰器(Concrete Decorator):
- 扩展装饰器,添加新的行为。
- 客户端(Client):
- 使用组件接口与装饰后的对象交互。
2. 装饰模式的 UML 图
3. 装饰模式的实现
以下是一个简单的装饰模式的代码示例:
(1) 组件
// 组件
interface Component {
void operation();
}
(2) 具体组件
// 具体组件
class ConcreteComponent implements Component {
@Override
public void operation() {
System.out.println("ConcreteComponent operation");
}
}
(3) 装饰器
// 装饰器
abstract class Decorator implements Component {
protected Component component;
public Decorator(Component component) {
this.component = component;
}
@Override
public void operation() {
component.operation();
}
}
(4) 具体装饰器
// 具体装饰器 A
class ConcreteDecoratorA extends Decorator {
public ConcreteDecoratorA(Component component) {
super(component);
}
@Override
public void operation() {
super.operation();
addedBehavior();
}
private void addedBehavior() {
System.out.println("ConcreteDecoratorA addedBehavior");
}
}
// 具体装饰器 B
class ConcreteDecoratorB extends Decorator {
public ConcreteDecoratorB(Component component) {
super(component);
}
@Override
public void operation() {
super.operation();
addedBehavior();
}
private void addedBehavior() {
System.out.println("ConcreteDecoratorB addedBehavior");
}
}
(5) 客户端
public class Client {
public static void main(String[] args) {
// 创建具体组件
Component component = new ConcreteComponent();
// 使用具体装饰器 A 装饰组件
Component decoratedComponentA = new ConcreteDecoratorA(component);
decoratedComponentA.operation();
// 使用具体装饰器 B 装饰组件
Component decoratedComponentB = new ConcreteDecoratorB(component);
decoratedComponentB.operation();
// 使用多个装饰器装饰组件
Component decoratedComponentAB = new ConcreteDecoratorB(new ConcreteDecoratorA(component));
decoratedComponentAB.operation();
}
}
输出:
ConcreteComponent operation
ConcreteDecoratorA addedBehavior
ConcreteComponent operation
ConcreteDecoratorB addedBehavior
ConcreteComponent operation
ConcreteDecoratorA addedBehavior
ConcreteDecoratorB addedBehavior
4. 装饰模式的优点
- 动态扩展:可以在运行时动态地添加或删除功能。
- 单一职责:每个装饰器只负责一个功能,符合单一职责原则。
- 灵活性:可以组合多个装饰器,提供更灵活的功能扩展。
5. 装饰模式的缺点
- 复杂性:增加了系统的复杂性,需要定义多个装饰器类。
- 调试困难:由于装饰器可以嵌套,调试可能变得困难。
6. 适用场景
- 需要动态地添加或删除功能。
- 需要扩展对象的功能,但不希望使用继承。
- 需要组合多个功能。
7. 装饰模式与继承的区别
- 继承:静态地扩展对象的功能,编译时确定。
- 装饰模式:动态地扩展对象的功能,运行时确定。
8. 组合模式与装饰器模式的区别
- 组合模式:用于表示“部分-整体”的层次结构,客户端可以统一处理单个对象和组合对象。
- 装饰器模式:用于动态地添加功能,客户端可以透明地使用装饰后的对象。
外观模式(Facade)
外观模式(Facade Pattern)是一种结构型设计模式,它提供了一个统一的接口,用于访问子系统中的一组接口。外观模式通过定义一个高层接口,简化了客户端与子系统之间的交互。
1. 外观模式的结构
外观模式包含以下角色:
- 外观(Facade):
- 提供一个统一的接口,用于访问子系统中的一组接口。
- 子系统(Subsystem):
- 包含一组类,实现子系统的功能。
- 客户端(Client):
- 使用外观接口与子系统交互。
2. 外观模式的 UML 图
3. 外观模式的实现
以下是一个简单的外观模式的代码示例:
(1) 子系统
// 子系统 A
class SubsystemA {
public void operationA() {
System.out.println("SubsystemA operationA");
}
}
// 子系统 B
class SubsystemB {
public void operationB() {
System.out.println("SubsystemB operationB");
}
}
// 子系统 C
class SubsystemC {
public void operationC() {
System.out.println("SubsystemC operationC");
}
}
(2) 外观
// 外观
class Facade {
private SubsystemA subsystemA;
private SubsystemB subsystemB;
private SubsystemC subsystemC;
public Facade() {
subsystemA = new SubsystemA();
subsystemB = new SubsystemB();
subsystemC = new SubsystemC();
}
public void operation() {
subsystemA.operationA();
subsystemB.operationB();
subsystemC.operationC();
}
}
(3) 客户端
public class Client {
public static void main(String[] args) {
// 创建外观
Facade facade = new Facade();
// 使用外观
facade.operation();
}
}
输出:
SubsystemA operationA
SubsystemB operationB
SubsystemC operationC
4. 外观模式的优点
- 简化接口:提供了一个统一的接口,简化了客户端与子系统的交互。
- 解耦:将客户端与子系统解耦,客户端只需依赖外观接口。
- 易于使用:客户端无需了解子系统的复杂性,只需调用外观接口。
5. 外观模式的缺点
- 不符合开闭原则:如果需要修改子系统的功能,可能需要修改外观类。
- 灵活性差:外观类可能变得复杂,难以扩展。
6. 适用场景
- 需要简化客户端与复杂子系统的交互。
- 需要将客户端与子系统解耦。
- 需要为子系统提供一个统一的接口。
7. 外观模式与适配器模式的区别
- 外观模式:用于简化客户端与子系统的交互,提供一个统一的接口。
- 适配器模式:用于解决接口不兼容的问题,通常在系统设计完成后使用。
享元模式(Flyweight)
享元模式(Flyweight Pattern)是一种结构型设计模式,它通过共享对象来减少内存使用和提高性能。享元模式适用于需要创建大量相似对象的场景,通过共享这些对象的内部状态,减少内存占用。
1. 享元模式的结构
享元模式包含以下角色:
- 享元(Flyweight):
- 定义共享对象的接口。
- 具体享元(Concrete Flyweight):
- 实现享元接口,包含内部状态。
- 享元工厂(Flyweight Factory):
- 创建和管理享元对象,确保共享。
- 客户端(Client):
- 使用享元对象,维护外部状态。
2. 享元模式的 UML 图
3. 享元模式的实现
以下是一个简单的享元模式的代码示例:
(1) 享元
// 享元
interface Flyweight {
void operation(String extrinsicState);
}
(2) 具体享元
// 具体享元
class ConcreteFlyweight implements Flyweight {
private String intrinsicState;
public ConcreteFlyweight(String intrinsicState) {
this.intrinsicState = intrinsicState;
}
@Override
public void operation(String extrinsicState) {
System.out.println("Intrinsic State: " + intrinsicState);
System.out.println("Extrinsic State: " + extrinsicState);
}
}
(3) 享元工厂
// 享元工厂
class FlyweightFactory {
private Map<String, Flyweight> flyweights = new HashMap<>();
public Flyweight getFlyweight(String key) {
if (!flyweights.containsKey(key)) {
flyweights.put(key, new ConcreteFlyweight(key));
}
return flyweights.get(key);
}
}
(4) 客户端
public class Client {
public static void main(String[] args) {
// 创建享元工厂
FlyweightFactory factory = new FlyweightFactory();
// 获取享元对象
Flyweight flyweight1 = factory.getFlyweight("A");
Flyweight flyweight2 = factory.getFlyweight("B");
Flyweight flyweight3 = factory.getFlyweight("A");
// 使用享元对象
flyweight1.operation("State1");
flyweight2.operation("State2");
flyweight3.operation("State3");
// 检查享元对象是否共享
System.out.println("flyweight1 == flyweight3: " + (flyweight1 == flyweight3));
}
}
输出:
Intrinsic State: A
Extrinsic State: State1
Intrinsic State: B
Extrinsic State: State2
Intrinsic State: A
Extrinsic State: State3
flyweight1 == flyweight3: true
4. 享元模式的优点
- 减少内存使用:通过共享对象,减少内存占用。
- 提高性能:减少了对象的创建和销毁,提高了性能。
- 灵活性:可以动态地添加或删除享元对象。
5. 享元模式的缺点
- 复杂性:增加了系统的复杂性,需要区分内部状态和外部状态。
- 线程安全:在多线程环境下,需要确保享元对象的线程安全。
6. 适用场景
- 需要创建大量相似对象。
- 对象的大部分状态可以外部化。
- 需要减少内存使用和提高性能。
7. 享元模式与单例模式的区别
- 单例模式:确保一个类只有一个实例,适用于全局唯一对象的场景。
- 享元模式:通过共享对象减少内存使用,适用于需要创建大量相似对象的场景。
代理模式(Proxy)
代理模式(Proxy Pattern)是一种结构型设计模式,它提供了一个代理对象,用于控制对另一个对象的访问。代理模式通常用于延迟加载、访问控制、日志记录等场景。
1. 代理模式的结构
代理模式包含以下角色:
- 抽象主题(Subject):
- 定义真实主题和代理对象的共同接口。
- 真实主题(Real Subject):
- 实现抽象主题接口,是代理对象所代表的真实对象。
- 代理(Proxy):
- 实现抽象主题接口,并包含一个对真实主题的引用,用于控制对真实主题的访问。
- 客户端(Client):
- 使用抽象主题接口与代理对象交互。
2. 代理模式的 UML 图
3. 代理模式的实现
以下是一个简单的代理模式的代码示例:
(1) 抽象主题
// 抽象主题
interface Subject {
void request();
}
(2) 真实主题
// 真实主题
class RealSubject implements Subject {
@Override
public void request() {
System.out.println("RealSubject request");
}
}
(3) 代理
// 代理
class Proxy implements Subject {
private RealSubject realSubject;
@Override
public void request() {
if (realSubject == null) {
realSubject = new RealSubject();
}
preRequest();
realSubject.request();
postRequest();
}
private void preRequest() {
System.out.println("Proxy preRequest");
}
private void postRequest() {
System.out.println("Proxy postRequest");
}
}
(4) 客户端
public class Client {
public static void main(String[] args) {
// 创建代理
Subject proxy = new Proxy();
// 使用代理
proxy.request();
}
}
输出:
Proxy preRequest
RealSubject request
Proxy postRequest
4. 代理模式的优点
- 控制访问:可以在访问真实主题之前或之后执行额外的操作。
- 延迟加载:可以延迟真实主题的创建,直到真正需要时。
- 解耦:将客户端与真实主题解耦,客户端只需依赖抽象主题接口。
5. 代理模式的缺点
- 复杂性:增加了系统的复杂性,需要定义额外的代理类。
- 性能开销:代理模式可能引入额外的性能开销。
6. 适用场景
- 需要控制对真实对象的访问。
- 需要延迟加载真实对象。
- 需要在访问真实对象之前或之后执行额外的操作。
7. 代理模式的类型
- 远程代理:
- 用于在不同的地址空间中代表对象。
- 虚拟代理:
- 用于延迟加载真实对象。
- 保护代理:
- 用于控制对真实对象的访问权限。
- 智能引用代理:
- 用于在访问真实对象时执行额外的操作,如日志记录、引用计数等。
行为设计模式
策略模式
策略模式(Strategy Pattern)是一种行为型设计模式,它定义了一系列算法,并将每个算法封装起来,使它们可以互相替换。策略模式让算法的变化独立于使用它的客户端。
1. 策略模式的结构
策略模式包含以下角色:
- 策略接口(Strategy):定义算法的接口。
- 具体策略类(Concrete Strategy):实现策略接口的具体算法。
- 上下文类(Context):持有一个策略对象的引用,并通过策略接口调用具体算法。
2. UML 类图
说明:
- Strategy:策略接口,定义算法的接口。
- ConcreteStrategyA 和 ConcreteStrategyB:具体策略类,实现
Strategy
接口。 - Context:上下文类,持有一个策略对象的引用,并通过策略接口调用具体算法。
3. 代码实现
以下是一个简单的 Java 实现示例:
(1) 策略接口(Strategy)
public interface Strategy {
void execute();
}
(2) 具体策略类(ConcreteStrategyA 和 ConcreteStrategyB)
public class ConcreteStrategyA implements Strategy {
@Override
public void execute() {
System.out.println("Executing Strategy A");
}
}
public class ConcreteStrategyB implements Strategy {
@Override
public void execute() {
System.out.println("Executing Strategy B");
}
}
(3) 上下文类(Context)
public class Context {
private Strategy strategy;
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
public void executeStrategy() {
if (strategy != null) {
strategy.execute();
} else {
System.out.println("No strategy set");
}
}
}
(4) 客户端代码
public class Client {
public static void main(String[] args) {
Context context = new Context();
// 使用策略 A
context.setStrategy(new ConcreteStrategyA());
context.executeStrategy(); // 输出: Executing Strategy A
// 使用策略 B
context.setStrategy(new ConcreteStrategyB());
context.executeStrategy(); // 输出: Executing Strategy B
}
}
4. 优点
- 开闭原则:可以在不修改上下文类的情况下添加新的策略。
- 避免条件语句:通过策略模式可以避免使用大量的条件语句。
- 算法复用:不同的上下文可以共享相同的策略。
5. 缺点
- 客户端必须了解策略:客户端需要知道所有策略类,并选择合适的策略。
- 策略类增多:如果策略类过多,系统会变得复杂。
6. 适用场景
- 需要在运行时动态选择算法。
- 有多个相似的类,仅在行为上有所不同。
- 需要避免使用大量的条件语句。
7. 总结
- 策略模式:将算法封装在独立的策略类中,使它们可以互相替换。
- 优点:开闭原则、避免条件语句、算法复用。
- 缺点:客户端必须了解策略、策略类增多。
- 适用场景:动态选择算法、避免条件语句。