前言:
此文主要内容为
- 面向对象七大设计原则(OOD Principle)
- GOF(Gang Of Four)23种设计模式
- 拓展的两个设计模式
- 简单工厂模式(Simple Factory Pattern)
- 空对象模式(Null Object Pattern)
- 以及工厂模式与策略模式(Factory And Strategy Pattern)之间的区别,两个设计模式较为相似,细节上需要分清
每个设计模式都分为了八步介绍
- 先对设计模式进行简单介绍,尽量一句话概述,方便记忆
- 再以现实生活中的场景举例,抛出问题
- 使用对应的设计模式解决问题
- 提供了方便理解的类关系图
- 然后指出适用于当前设计模式的场景
- 当前设计模式编码时的易错点
- 当前设计模式的优缺点
- 最后再次对当前设计模式进行总结
使用代码案例时,可复制整个代码块,先粘贴至一个类中,再拆分
如有疑问添加文章封面联系方式
GOF(Gang Of Four)是四位计算机科学家(Erich Gamma、Richard Helm、Ralph Johnson和John Vlissides)在1994年出版的书籍《设计模式:可复用面向对象软件的基础》中提出的一种设计模式分类和解决方案。该书介绍了23种常用的设计模式,分为创建型、结构型和行为型三大类。这本书的社会认可度非常高,被公认为设计模式领域的经典之作。
GOF的设计模式在软件开发领域得到了广泛的认可和应用,成为了许多软件开发人员必备的知识和工具之一。它为软件设计和架构提供了有力的指导,帮助开发人员构建高质量、可扩展的软件系统。
序号 | 中文名称 | 英文名称 | 所用设计原则 |
---|---|---|---|
1 | 工厂方法模式 | Factory Method Pattern | SRP、OCP、LSP、DIP |
2 | 抽象工厂模式 | Abstract Factory Pattern | SRP、OCP、LSP、DIP |
3 | 单例模式 | Singleton Pattern | 无直接关联设计原则 |
4 | 原型模式 | Prototype Pattern | SRP、OCP、LSP、DIP |
5 | 建造者模式 | Builder Pattern | SRP、OCP、LSP、DIP |
序号 | 中文名称 | 英文名称 | 所用设计原则 |
---|---|---|---|
6 | 适配器模式 | Adapter Pattern | SRP、OCP、LSP、ISP、DIP |
7 | 桥接模式 | Bridge Pattern | SRP、OCP、LSP、ISP、DIP |
8 | 组合模式 | Composite Pattern | SRP、OCP、LSP、ISP、DIP |
9 | 装饰模式 | Decorator Pattern | SRP、OCP、LSP、ISP、DIP |
10 | 外观模式 | Facade Pattern | SRP、OCP、LSP、ISP、DIP |
11 | 享元模式 | Flyweight Pattern | SRP、OCP、LSP、ISP、DIP |
12 | 代理模式 | Proxy Pattern | SRP、OCP、LSP、ISP、DIP |
序号 | 中文名称 | 英文名称 | 所用设计原则 |
---|---|---|---|
13 | 责任链模式 | Chain of Responsibility Pattern | SRP、OCP、LSP、ISP、DIP |
14 | 命令模式 | Command Pattern | SRP、OCP、LSP、ISP、DIP |
15 | 解释器模式 | Interpreter Pattern | SRP、OCP、LSP、ISP、DIP |
16 | 迭代器模式 | Iterator Pattern | SRP、OCP、LSP、ISP、DIP |
17 | 中介者模式 | Mediator Pattern | SRP、OCP、LSP、ISP、DIP |
18 | 备忘录模式 | Memento Pattern | SRP、OCP、LSP、ISP、DIP |
19 | 观察者模式 | Observer Pattern | SRP、OCP、LSP、ISP、DIP |
20 | 状态模式 | State Pattern | SRP、OCP、LSP、ISP、DIP |
21 | 策略模式 | Strategy Pattern | SRP、OCP、LSP、ISP、DIP |
22 | 模板方法模式 | Template Method Pattern | SRP、OCP、LSP、ISP、DIP |
23 | 访问者模式 | Visitor Pattern | SRP、OCP、LSP、ISP、DIP |
面向对象设计原则(OODP)Object-Oriented Design Principle
单一职责原则(SRP)Single Responsibility Principle
开闭原则(OCP)Open/Closed Principle
里氏替换原则(LSP)Liskov Substitution Principle
接口隔离原则(ISP) Interface Segregation Principle
依赖倒置原则(DIP)Dependency Inversion Principle
迪米特法则(LoD)Law Of Demeter
合成复用原则(CRP)Composite Reuse Principle
创建型模式是设计模式中的一类,主要关注如何创建对象以及对象的实例化过程。它们提供了一种灵活、可复用的方式来创建对象,同时隐藏了对象创建的细节,从而降低了系统的耦合性。
这些创建型模式各自适用于不同的场景,可以根据具体需求来选择合适的设计模式来实现对象的创建和初始化。它们的共同目标是降低对象的创建和使用之间的耦合,提供更加灵活和可扩展的代码结构。
在学习工厂方法模式之前建议先学习文章最后一章《必要拓展》中的简单工厂模式(Simple Factory Pattern)
工厂方法模式(Factory Method Pattern)定义一个创建对象的接口,但由子类决定具体实例化哪个类。客户端只需要知道工厂接口,而不关心具体的产品类。
假设你是一家汽车制造公司,你有多个型号的汽车需要生产,每种型号的汽车有不同的配置和特性。你需要一个灵活的方式来生产不同型号的汽车,并且在将来能够轻松添加新的汽车型号。
// 产品接口:汽车
interface Car {
void assemble();
}
// 具体产品:SUV 汽车
class SUVCar implements Car {
@Override
public void assemble() {
System.out.println("组装SUV汽车");
}
}
// 具体产品:轿车
class SedanCar implements Car {
@Override
public void assemble() {
System.out.println("组装轿车");
}
}
// 工厂接口:汽车工厂
interface CarFactory {
Car createCar();
}
// 具体工厂:SUV 汽车工厂
class SUVCarFactory implements CarFactory {
@Override
public Car createCar() {
return new SUVCar();
}
}
// 具体工厂:轿车工厂
class SedanCarFactory implements CarFactory {
@Override
public Car createCar() {
return new SedanCar();
}
}
// 调用代码
public class XZ {
public static void main(String[] args) {
System.out.println("===========SUV===========");
Car suvCar = new SUVCarFactory().createCar();
suvCar.assemble();
System.out.println("===========Sedan===========");
Car sedanCar = new SedanCarFactory().createCar();
sedanCar.assemble();
}
}
优点
缺点
工厂方法模式是一种创建型设计模式,通过将产品的创建与使用解耦,让子类决定实例化哪个类。这样可以提高代码的灵活性和可扩展性,使得系统的演化和维护更加容易。虽然增加了类的个数,但对于复杂系统的开发来说,这是值得的权衡。在需要动态地选择创建对象,或者有多个类似产品等级结构的情况下,工厂方法模式是一个很好的选择。
抽象工厂模式(Abstract Factory Pattern)提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。抽象工厂可以创建多个不同类型的产品。
假设你是一家家具制造公司,你有多种类型的家具,如沙发、床、桌子等,每种类型的家具有不同的风格和材料。你需要一种灵活的方式来生产不同类型和风格的家具,并且在将来能够轻松添加新的家具类型和风格。
// 产品接口:沙发
interface Sofa {
void sitOn();
}
// 产品接口:床
interface Bed {
void sleepOn();
}
// 具体产品:现代风格沙发
class ModernSofa implements Sofa {
@Override
public void sitOn() {
System.out.println("坐在现代风格的沙发上");
}
}
// 具体产品:现代风格床
class ModernBed implements Bed {
@Override
public void sleepOn() {
System.out.println("睡在现代风格的床上");
}
}
// 具体产品:古典风格沙发
class ClassicalSofa implements Sofa {
@Override
public void sitOn() {
System.out.println("坐在古典风格的沙发上");
}
}
// 具体产品:古典风格床
class ClassicalBed implements Bed {
@Override
public void sleepOn() {
System.out.println("睡在古典风格的床上");
}
}
// 抽象工厂接口:家具工厂
interface FurnitureFactory {
Sofa createSofa();
Bed createBed();
}
// 具体工厂:现代风格家具工厂
class ModernFurnitureFactory implements FurnitureFactory {
@Override
public Sofa createSofa() {
return new ModernSofa();
}
@Override
public Bed createBed() {
return new ModernBed();
}
}
// 具体工厂:古典风格家具工厂
class ClassicalFurnitureFactory implements FurnitureFactory {
@Override
public Sofa createSofa() {
return new ClassicalSofa();
}
@Override
public Bed createBed() {
return new ClassicalBed();
}
}
// 调用代码
public class XZ {
public static void main(String[] args) {
System.out.println("===========现代家具===========");
ModernFurnitureFactory modernFurnitureFactory = new ModernFurnitureFactory();
Bed modernBed = modernFurnitureFactory.createBed();
modernBed.sleepOn();
Sofa modernSofa = modernFurnitureFactory.createSofa();
modernSofa.sitOn();
System.out.println("======古典家具========");
ClassicalFurnitureFactory classicalFurnitureFactory = new ClassicalFurnitureFactory();
Bed classicaBed = classicalFurnitureFactory.createBed();
classicaBed.sleepOn();
Sofa classicaSofa = classicalFurnitureFactory.createSofa();
classicaSofa.sitOn();
}
}
优点
缺点
抽象工厂模式是一种创建型设计模式,通过提供一个接口来创建一组相关或相互依赖的对象,将一系列产品封装在一起,使得客户端不需要知道具体产品的类名,只需使用抽象接口即可。抽象工厂模式适用于创建一组相关的产品,且希望系统能够轻松支持新的产品族或产品等级结构的场景。在使用抽象工厂模式时,需要注意在添加新的产品族或产品等级结构时,可能需要修改较多的代码。
单例模式(Singleton Pattern)确保一个类只有一个实例,并提供一个全局访问点。这样可以控制对象的创建和访问,通常用于管理共享资源或全局配置。
在现实世界中,我们可能会遇到这样的问题:希望某个类在整个应用程序中只能拥有一个实例,不论在何处访问该类,始终获取到相同的唯一实例。
静态内部类单例模式是一种创建单例对象的方法,它通过使用静态内部类来实现懒加载和线程安全。
// 静态内部类单例
public class StaticInnerClassSingleton {
private static class StaticInnerClass {
private static final StaticInnerClassSingleton staticInnerClassSingleton = new StaticInnerClassSingleton();
}
public static StaticInnerClassSingleton getInstance() {
return StaticInnerClass.staticInnerClassSingleton;
}
}
// 调用代码
public class XZ {
public static void main(String[] args) {
// 获取懒汉式单例实例
StaticInnerClassSingleton instance1 = StaticInnerClassSingleton.getInstance();
StaticInnerClassSingleton instance2 = StaticInnerClassSingleton.getInstance();
// 验证是否为同一实例
System.out.println(instance1 == instance2);
// Output: true
}
}
懒汉式是指在首次使用时才创建实例。解决方案为在类内部定义一个私有静态变量作为该类的唯一实例,并提供一个公共静态方法来获取该实例。
// 懒汉式单例
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {}
// 私有构造方法,防止其他类通过 new 创建实例
public static synchronized LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
// 调用代码
public class XZ {
public static void main(String[] args) {
// 获取懒汉式单例实例
LazySingleton instance1 = LazySingleton.getInstance();
LazySingleton instance2 = LazySingleton.getInstance();
// 验证是否为同一实例
System.out.println(instance1 == instance2);
// Output: true
}
}
饿汉式是指在类加载时就创建实例。解决方案为在类定义时直接创建一个私有静态实例,并提供一个公共静态方法来获取该实例。
// 饿汉式单例
public class EagerSingleton {
private static final EagerSingleton instance = new EagerSingleton();
private EagerSingleton() {}
// 私有构造方法,防止其他类通过 new 创建实例
public static EagerSingleton getInstance() {
return instance;
}
}
// 调用代码
public class XZ {
public static void main(String[] args) {
// 获取饿汉式单例实例
EagerSingleton instance1 = EagerSingleton.getInstance();
EagerSingleton instance2 = EagerSingleton.getInstance();
// 验证是否为同一实例
System.out.println(instance1 == instance2);
// Output: true
}
}
双重校验锁单例是一种在懒汉式基础上进行改进的解决方案,旨在减少不必要的同步开销。
// 双重校验锁单例
public class DoubleCheckedSingleton {
private volatile static DoubleCheckedSingleton instance;
private DoubleCheckedSingleton() {}
// 私有构造方法,防止其他类通过 new 创建实例
public static DoubleCheckedSingleton getInstance() {
if (instance == null) {
synchronized (DoubleCheckedSingleton.class) {
if (instance == null) {
instance = new DoubleCheckedSingleton();
}
}
}
return instance;
}
}
// 调用代码
public class XZ {
public static void main(String[] args) {
// 获取双重校验锁单例实例
DoubleCheckedSingleton instance1 = DoubleCheckedSingleton.getInstance();
DoubleCheckedSingleton instance2 = DoubleCheckedSingleton.getInstance();
// 验证是否为同一实例
System.out.println(instance1 == instance2);
// Output: true
}
}
枚举单例是一种简洁且线程安全的单例模式解决方案。在Java中,枚举类型是天然的单例,保证在任何情况下都只有一个实例。
// 枚举单例
public enum EnumSingleton {
INSTANCE;
public static EnumSingleton getInstance() {
return INSTANCE;
}
}
// 调用代码
public class XZ {
public static void main(String[] args) {
// 获取枚举单例实例
EnumSingleton instance1 = EnumSingleton.getInstance();
EnumSingleton instance2 = EnumSingleton.getInstance();
// 验证是否为同一实例
System.out.println(instance1 == instance2);
// Output: true
}
}
在每个示例中,我们都通过相应的静态方法获取单例实例,并通过比较引用地址验证是否为同一实例。由于单例模式保证在整个应用程序中只有一个实例,所以输出结果都应该是
true
。
静态内部类单例模式
优点
缺点
懒汉式单例模式
优点
缺点
饿汉式单例模式
优点
缺点
双重校验锁单例模式
优点
缺点
枚举单例模式
优点
缺点
选择哪种单例模式应该根据具体需求来决定。静态内部类单例模式通常是一个很好的选择,因为它兼具了懒加载和线程安全的优点,而且实现简单。如果需要更高级的特性,如防止反射攻击,可以考虑使用枚举单例模式。
原型模式(Prototype Pattern)通过复制现有对象来创建新的对象,避免了使用常规构造函数来创建对象,从而提高性能和灵活性。
在现实世界中,我们可能会遇到这样的问题:希望创建一个对象的副本,并且可以根据原对象进行快速的复制和修改。
原型模式是一种创建型设计模式,它通过复制现有对象来创建新对象。Java中可以通过实现Cloneable接口和重写clone()方法来实现原型模式。
// 原型对象类
public class Prototype implements Cloneable {
private String data;
public Prototype(String data) {
this.data = data;
}
// 重写clone方法,实现浅拷贝
@Override
public Prototype clone() throws CloneNotSupportedException {
return (Prototype) super.clone();
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
}
// 调用代码的类
public class XZ {
public static void main(String[] args) throws CloneNotSupportedException {
Date date = new Date();
// 创建原型对象
Prototype originalPrototype = new Prototype("玄子", date);
// 克隆原型对象
Prototype clonedPrototype = originalPrototype.clone();
System.out.println("源对象:" + originalPrototype.hashCode() + "," + originalPrototype.getName() + "," + originalPrototype.getBirthday());
System.out.println("克隆对象:" + clonedPrototype.hashCode() + "," + clonedPrototype.getName() + "," + clonedPrototype.getBirthday());
System.out.println("================================");
date.setTime(22222222);
System.out.println("源对象:" + originalPrototype.hashCode() + "," + originalPrototype.getName() + "," + originalPrototype.getBirthday());
System.out.println("克隆对象:" + clonedPrototype.hashCode() + "," + clonedPrototype.getName() + "," + clonedPrototype.getBirthday());
// 虽然克隆出来的是两个内存地址的对象
// 但修改数据后,源对象与克隆对象的数据都发生了改变
// 数据引用的是同一内存地址
}
/*
源对象:990368553,玄子,Sun Sep 24 02:50:52 CST 2023
克隆对象:396873410,玄子,Sun Sep 24 02:50:52 CST 2023
================================
源对象:990368553,玄子,Thu Jan 01 14:10:22 CST 1970
克隆对象:396873410,玄子,Thu Jan 01 14:10:22 CST 1970
*/
}
// 重写clone方法与属性,实现深拷贝
@Override
public Prototype clone() throws CloneNotSupportedException {
Prototype clone = (Prototype) super.clone();
clone.birthday = (Date) this.birthday.clone();
return clone;
}
// 实现深克隆仅需要,在源对象的 clone() 方法内对属性进行单独克隆
/*
源对象:990368553,玄子,Sun Sep 24 02:50:52 CST 2023
克隆对象:396873410,玄子,Sun Sep 24 02:50:52 CST 2023
================================
源对象:990368553,玄子,Thu Jan 01 14:10:22 CST 1970
克隆对象:396873410,玄子,Sun Sep 24 03:00:40 CST 2023
*/
// 原型对象类,实现 Serializable 接口
public class Prototype implements Serializable {
private String name;
private Date birthday;
public Prototype(String name, Date birthday) {
this.name = name;
this.birthday = birthday;
}
// 使用序列化和反序列化实现深拷贝
public Prototype deepClone() throws IOException, ClassNotFoundException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
// 序列化当前对象
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
// 反序列化为新对象
return (Prototype) ois.readObject();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
// 调用代码的类
public class XZ {
public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {
Date date = new Date();
// 创建原型对象
Prototype originalPrototype = new Prototype("玄子", date);
// 克隆原型对象
Prototype clonedPrototype = originalPrototype.deepClone();
System.out.println("源对象:" + originalPrototype.hashCode() + "," + originalPrototype.getName() + "," + originalPrototype.getBirthday());
System.out.println("克隆对象:" + clonedPrototype.hashCode() + "," + clonedPrototype.getName() + "," + clonedPrototype.getBirthday());
System.out.println("================================");
date.setTime(22222222);
System.out.println("源对象:" + originalPrototype.hashCode() + "," + originalPrototype.getName() + "," + originalPrototype.getBirthday());
System.out.println("克隆对象:" + clonedPrototype.hashCode() + "," + clonedPrototype.getName() + "," + clonedPrototype.getBirthday());
}
/*
源对象:990368553,玄子,Sun Sep 24 02:50:52 CST 2023
克隆对象:396873410,玄子,Sun Sep 24 02:50:52 CST 2023
================================
源对象:990368553,玄子,Thu Jan 01 14:10:22 CST 1970
克隆对象:396873410,玄子,Thu Jan 01 14:10:22 CST 1970
*/
}
浅拷贝问题:默认情况下,clone()方法执行的是浅拷贝,即只复制基本类型的字段和引用类型的地址,而不复制引用类型对象本身。如果对象中包含引用类型字段,需要注意处理深拷贝问题。
优点
缺点
原型模式是一种创建型设计模式,通过复制现有对象来创建新对象。在Java中,可以通过实现Cloneable接口和重写clone()方法来实现原型模式。原型模式适用于需要创建复杂对象或大量相似对象的场景,可以提高对象的创建效率,并方便快速创建和修改对象副本。需要注意处理引用类型字段时的浅拷贝问题,并根据具体情况考虑是否需要实现深拷贝。
建造者模式(Builder Pattern)将一个复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。
在现实世界中,我们可能会遇到这样的问题:希望构建一个复杂的对象,该对象有多个可选属性,并且在创建过程中可以灵活组合这些属性。
建造者模式是一种创建型设计模式,它通过将对象的构建过程和表示分离,使得同样的构建过程可以创建不同的表示。建造者模式通常包含一个Builder
接口和一个ConcreteBuilder
实现类,以及一个Director
类来指导构建过程。
// 建造者接口
public interface Builder {
void buildPart1();
void buildPart2();
void buildPart3();
Product getResult();
}
// 具体建造者
public class ConcreteBuilder implements Builder {
private final Product product = new Product();
public void buildPart1() {
product.setPart1("Part1");
}
public void buildPart2() {
product.setPart2("Part2");
}
public void buildPart3() {
product.setPart3("Part3");
}
public Product getResult() {
return product;
}
}
// 产品类
public class Product {
private String part1;
private String part2;
private String part3;
public String getPart1() {
return part1;
}
public void setPart1(String part1) {
this.part1 = part1;
}
public String getPart2() {
return part2;
}
public void setPart2(String part2) {
this.part2 = part2;
}
public String getPart3() {
return part3;
}
public void setPart3(String part3) {
this.part3 = part3;
}
@Override
public String toString() {
return "Product{" +
"part1='" + part1 + '\'' +
", part2='" + part2 + '\'' +
", part3='" + part3 + '\'' +
'}';
}
}
// 指导者类
public class Director {
public void construct(Builder builder) {
builder.buildPart1();
builder.buildPart2();
builder.buildPart3();
}
}
// 调用代码
public class XZ {
public static void main(String[] args) {
// 创建指导者和建造者对象
Director director = new Director();
Builder builder = new ConcreteBuilder();
// 指导建造过程并获取产品对象
director.construct(builder);
Product product = builder.getResult();
// 输出产品信息,产品信息顺序固定
System.out.println(product);
// Output: Product{part1='Part1', part2='Part2', part3='Part3'}
}
}
Director
)// 建造者接口
public interface Builder {
Builder buildPart1(String part);
Builder buildPart2(String part);
Builder buildPart3(String part);
Product getResult();
}
// 具体建造者
public class ConcreteBuilder implements Builder {
private final Product product = new Product();
public Builder buildPart1(String part) {
product.setPart1(part);
return this;
}
public Builder buildPart2(String part) {
product.setPart2(part);
return this;
}
public Builder buildPart3(String part) {
product.setPart3(part);
return this;
}
public Product getResult() {
return product;
}
}
// 产品类
public class Product {
private String part1 = "part1";
private String part2 = "part1";
private String part3 = "part1";
public String getPart1() {
return part1;
}
public void setPart1(String part1) {
this.part1 = part1;
}
public String getPart2() {
return part2;
}
public void setPart2(String part2) {
this.part2 = part2;
}
public String getPart3() {
return part3;
}
public void setPart3(String part3) {
this.part3 = part3;
}
@Override
public String toString() {
return "Product{" +
"part1='" + part1 + '\'' +
", part2='" + part2 + '\'' +
", part3='" + part3 + '\'' +
'}';
}
}
// 调用代码
public class XZ {
public static void main(String[] args) {
// 创建指导者和建造者对象
ConcreteBuilder concreteBuilder = new ConcreteBuilder();
Product product = concreteBuilder.getResult();
// 输出产品信息
System.out.println(product);
// Output: Product{part1='Part1', part2='Part2', part3='Part3'}
System.out.println("============================");
// 创建指导者和建造者对象
ConcreteBuilder concreteBuilder2 = new ConcreteBuilder();
// 链式编程:在原来的基础上,可自由组合,若不组合,则默认
Product product2 = concreteBuilder2.buildPart1("part2").buildPart2("part1").buildPart3("part3").getResult();
// 输出产品信息,产品信息顺序可自由搭配
System.out.print(product2);
// Output: Product{part1='part2', part2='part1', part3='part3'}
}
}
优点
缺点
建造者模式是一种创建型设计模式,它通过将对象的构建过程和表示分离,使得同样的构建过程可以创建不同的表示。在Java中,可以通过定义建造者接口和具体建造者类来实现建造者模式,并通过指导者类指导建造过程。建造者模式适用于需要创建复杂对象,且对象的构建过程相对稳定但表示可以灵活组合的场景。需要注意定义建造者接口和具体建造者类,确保建造过程的正确性和灵活性。同时,建造者模式也应避免过度使用,以免造成代码冗余。
结构型模式(Structural Patterns)是设计模式中的一类,它主要关注对象和类的组合,以实现更大的结构,以及改变或简化类之间的交互方式。这些模式使得不同类和对象之间的关系更加灵活,同时也降低了系统的耦合度,使系统更易于维护和扩展。
结构型模式在软件开发中起到了重要的作用,它们帮助我们在设计阶段选择合适的模式,提高系统的设计质量和性能,并促进代码的重用和维护。不同的结构型模式适用于不同的场景和需求,通过合理使用结构型模式,可以让系统更加灵活、可扩展和易于理解。
适配器模式(Adapter Pattern)将一个类的接口转换成客户端所期望的另一个接口,使得不兼容的类可以协同工作。
假设我们现在有一台笔记本,需要插入网线来上网,但是笔记本并没有网线接口,只有一个 USB 接口,网线与 USB 接口并不兼容,我们无法直接在笔记本上插入网线,需要使用转接器把 USB 接口转换为网线接口
适配器模式是一种结构型设计模式,它通过创建一个适配器类来转换一个类的接口为客户端所期望的另一个接口。适配器模式主要包含三个角色:目标接口(Adapter
)、被适配者(Computer
)和适配器类(AdapterNetwork
)。
// 被适配者类
public class Computer {
public void surf(Adapter adapter) {
adapter.adapter();
System.out.println(this.getClass().getSimpleName() + ":电脑上网");
}
}
// 转换器接口
public interface Adapter {
public void adapter();
}
// 适配器类
public class AdapterNetwork implements Adapter {
private final Network network;
public AdapterNetwork(Network network) {
this.network = network;
}
@Override
public void adapter() {
network.getNet();
System.out.println(this.getClass().getSimpleName() + ":将网口转为USB");
}
}
// 网口
public class Network {
public void getNet() {
System.out.println(this.getClass().getSimpleName() + ":联网");
}
}
// 调用代码
public class XZ {
public static void main(String[] args) {
// 创建被适配者对象,电脑需要联网
Computer computer = new Computer();
// 创建网口
Network network = new Network();
// 使用适配器将,USB口转换为网口
Adapter adapter = new AdapterNetwork(network);
// 电脑插上转换后的网口
computer.surf(adapter);
}
/*
Network:联网
AdapterNetwork:将网口转为USB
Computer:电脑上网
*/
}
优点
缺点
适配器模式是一种结构型设计模式,它通过创建一个适配器类来转换一个类的接口为客户端所期望的另一个接口。适配器模式适用于将不兼容的类或接口转换为可用的接口,提高代码的复用性和灵活性,避免修改现有代码。需要注意适配器类实现目标接口,并正确调用被适配者类的方法。同时,适配器模式也应避免过度使用,以免引入过多的适配器类增加代码复杂性。
桥接模式(Bridge Pattern):将抽象部分和实现部分分离,使它们可以独立变化,从而增加系统的灵活性。
假设我们要开发一个绘图软件,支持绘制不同的图形(如圆形、矩形、椭圆等)和填充不同的颜色(如红色、绿色、蓝色等)。如果直接使用继承来实现每种图形和颜色的组合,将会导致类爆炸,即需要创建大量的类来覆盖所有的组合可能性,这会导致代码的复杂性增加。我们希望能够将图形和颜色的绘制过程解耦,使得每种图形和颜色可以独立地变化而不影响其他部分。
桥接模式是一种结构型设计模式,它将抽象部分和实现部分分离,使它们可以独立变化,从而增加系统的灵活性。桥接模式主要包含两个层次结构:抽象化(Abstraction
)和实现化(Implementation
)。
// 抽象化类
public abstract class Shape {
protected Color color;
public Shape(Color color) {
this.color = color;
}
public abstract void draw();
}
// 具体抽象化类 - 圆形
public class Circle extends Shape {
public Circle(Color color) {
super(color);
}
public void draw() {
System.out.print("绘制圆形:");
color.applyColor();
}
}
// 具体抽象化类 - 矩形
public class Rectangle extends Shape {
public Rectangle(Color color) {
super(color);
}
public void draw() {
System.out.print("绘制矩形:");
color.applyColor();
}
}
// 实现化接口
public interface Color {
void applyColor();
}
// 具体实现化类 - 红色
public class RedColor implements Color {
public void applyColor() {
System.out.println("使用红色");
}
}
// 具体实现化类 - 绿色
public class GreenColor implements Color {
public void applyColor() {
System.out.println("使用绿色");
}
}
// 调用代码的类
public class XZ {
public static void main(String[] args) {
// 创建具体实现化对象
Color redColor = new RedColor();
Color greenColor = new GreenColor();
// 创建具体抽象化对象
Shape redCircle = new Circle(redColor);
Shape greenRectangle = new Rectangle(greenColor);
// 绘制图形
redCircle.draw();
// Output: 绘制圆形:使用红色
greenRectangle.draw();
// Output: 绘制矩形:使用绿色
}
}
优点
缺点
桥接模式是一种结构型设计模式,它将抽象部分和实现部分分离,使它们可以独立变化,增加了系统的灵活性和可扩展性。通过桥接模式,可以将一个类的多个维度的变化进行解耦,避免了类的爆炸现象。需要注意在设计类的继承结构时,合理选择抽象化和实现化的关系,以确保系统的灵活性和可维护性。桥接模式适用于需要将抽象部分和实现部分分离的场景,特别是当一个类存在多个维度的变化时,可以使用桥接模式来简化类的继承关系。
组合模式(Composite Pattern)将对象组合成树形结构以表示“整体-部分”层次关系,使得客户端对单个对象和组合对象的使用具有一致性。
假设我们正在开发一个文件系统,文件系统中包含文件和文件夹两种类型的元素。我们希望能够以树形结构来表示文件系统中的所有元素,并且能够对整个文件系统进行统一的操作,比如查找文件、删除文件、获取文件大小等。同时,我们还希望能够在文件夹中包含其他文件夹,形成一个递归的结构。
组合模式是一种结构型设计模式,它允许将对象组合成树形结构来表示“整体-部分”层次关系。组合模式主要包含两个角色:组合对象(Composite
)和叶子对象(Leaf
)。组合对象可以包含叶子对象或其他组合对象,形成递归的结构。
// 抽象组件类
public abstract class FileComponent {
protected String name;
public FileComponent(String name) {
this.name = name;
}
public abstract void display(int indentLevel);
}
// 叶子对象类 - 文件
public class File extends FileComponent {
public final int size;
public File(String name, int size) {
super(name);
this.size = size;
}
public void display(int indentLevel) {
StringBuilder indent = new StringBuilder();
for (int i = 0; i < indentLevel; i++) {
indent.append(" ");
// 每级缩进两个空格
}
System.out.println(indent + "File: " + name + ", Size: " + size + " KB");
}
}
// 组合对象类 - 文件夹
public class Folder extends FileComponent {
public final List<FileComponent> components;
public Folder(String name) {
super(name);
components = new ArrayList<>();
}
public void add(FileComponent component) {
components.add(component);
}
public void remove(FileComponent component) {
components.remove(component);
}
public void display(int indentLevel) {
StringBuilder indent = new StringBuilder();
for (int i = 0; i < indentLevel; i++) {
indent.append(" "); // 每级缩进两个空格
}
System.out.println(indent + "Folder: " + name);
for (FileComponent component : components) {
component.display(indentLevel + 1);
// 递归调用,增加缩进级别
}
}
}
public class XZ {
public static void main(String[] args) {
// 创建文件和文件夹
FileComponent file1 = new File("File1.txt", 100);
FileComponent file2 = new File("File2.txt", 50);
Folder folder1 = new Folder("Folder1");
folder1.add(file1);
folder1.add(file2);
FileComponent file3 = new File("File3.txt", 80);
FileComponent file4 = new File("File4.txt", 120);
Folder folder2 = new Folder("Folder2");
folder2.add(file3);
folder2.add(file4);
folder2.add(folder1);
// 显示文件系统的结构
folder2.display(0);
}
/*
Folder: Folder2
File: File3.txt, Size: 80 KB
File: File4.txt, Size: 120 KB
Folder: Folder1
File: File1.txt, Size: 100 KB
File: File2.txt, Size: 50 KB
*/
}
优点
缺点
组合模式是一种结构型设计模式,它允许将对象组合成树形结构来表示“整体-部分”层次关系。通过组合模式,可以将叶子对象和组合对象统一表示,使得客户端无需关心具体的对象类型。需要注意确保组合对象和叶子对象具有一致的接口,以便客户端可以统一操作。组合模式适用于需要表示复杂的对象结构的场景,同时需要对整个对象结构进行统一操作的情况。
装饰模式(Decorator Pattern)动态地给对象添加额外的职责,是继承的替代方案,提供更加灵活的扩展能力。
假设我们正在开发一个咖啡店的点单系统。系统中有多种咖啡可以选择,并且用户可以根据个人口味选择添加不同的配料,比如牛奶、糖、巧克力等。每一种配料都有不同的价格,而且用户还可以选择多种配料的组合。我们希望能够以灵活的方式计算出点单的总价,并且可以在不修改原有咖啡类的情况下增加新的配料选项。
装饰模式是一种结构型设计模式,它允许将对象的功能动态地添加到对象中,同时不改变其接口。装饰模式主要包含三个角色:抽象组件(Component
)、具体组件(ConcreteComponent
)和装饰器(Decorator
)。
// 抽象组件类 - 咖啡
public interface Coffee {
double getCost();
String getDescription();
}
// 装饰器类 - 咖啡装饰器
public abstract class CoffeeDecorator implements Coffee {
protected Coffee decoratedCoffee;
public CoffeeDecorator(Coffee decoratedCoffee) {
this.decoratedCoffee = decoratedCoffee;
}
public double getCost() {
return decoratedCoffee.getCost();
}
public String getDescription() {
return decoratedCoffee.getDescription();
}
}
// 具体组件类 - 简单咖啡
public class SimpleCoffee implements Coffee {
public double getCost() {
return 2.0;
}
public String getDescription() {
return "基础咖啡";
}
}
// 具体装饰器类 - 牛奶装饰器
public class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee decoratedCoffee) {
super(decoratedCoffee);
}
public double getCost() {
return super.getCost() + 1.5;
}
public String getDescription() {
return super.getDescription() + ", 牛奶";
}
}
// 具体装饰器类 - 巧克力装饰器
public class ChocolateDecorator extends CoffeeDecorator {
public ChocolateDecorator(Coffee decoratedCoffee) {
super(decoratedCoffee);
}
public double getCost() {
return super.getCost() + 2.0;
}
public String getDescription() {
return super.getDescription() + ", 巧克力";
}
}
// 调用代码的类
public class XZ {
public static void main(String[] args) {
// 创建简单咖啡对象
Coffee coffee = new SimpleCoffee();
// 添加牛奶和巧克力
coffee = new MilkDecorator(coffee);
coffee = new ChocolateDecorator(coffee);
coffee = new MilkDecorator(coffee);
// 显示点单地描述和总价
System.out.println("商品:" + coffee.getDescription());
System.out.println("花费:" + coffee.getCost());
}
/*
商品:基础咖啡, 牛奶, 巧克力, 牛奶
花费:7.0
*/
}
优点
缺点
装饰模式是一种结构型设计模式,它允许将对象的功能动态地添加到对象中,同时不改变其接口。通过装饰模式,可以在不修改原有代码的情况下增加新的功能,使得功能的增加更加灵活和动态。装饰模式适用于需要动态地给对象添加功能的场景,以及有多个可选功能的情况。需要注意在设计装饰器类时,合理地组织装饰器类之间的继承关系,确保功能的正确组合。
外观模式(Facade Pattern)为复杂子系统提供一个简单的接口,使得子系统更易于使用。
假设我们正在开发一个家庭影院系统。家庭影院包含多个设备,比如投影仪、音响、DVD播放器等。每次观影时,需要打开投影仪、打开音响、打开DVD播放器等一系列操作,而且每个设备的控制接口可能都不相同,这会导致操作繁琐。我们希望能够提供一个简单的接口,让用户只需调用一个方法,就可以启动家庭影院系统并开始观影。
外观模式是一种结构型设计模式,它为子系统提供了一个统一的接口,使得子系统更加易于使用。外观模式主要包含一个外观类(Facade
)和多个子系统类(SubSystem
)。
// 子系统类 - 投影仪
public class Projector {
public void turnOn() {
System.out.println("投影仪开启");
}
public void turnOff() {
System.out.println("投影仪开启关闭");
}
}
// 子系统类 - 音响系统
public class AudioSystem {
public void turnOn() {
System.out.println("音响系统开启");
}
public void turnOff() {
System.out.println("音响系统关闭");
}
}
// 子系统类 - DVD播放器
public class DVDPlayer {
public void turnOn() {
System.out.println("DVD播放器开启");
}
public void turnOff() {
System.out.println("DVD播放器关闭");
}
}
// 外观类 - 家庭影院外观
public class HomeTheaterFacade {
private final Projector projector;
private final AudioSystem audioSystem;
private final DVDPlayer dvdPlayer;
public HomeTheaterFacade(Projector projector, AudioSystem audioSystem, DVDPlayer dvdPlayer) {
this.projector = projector;
this.audioSystem = audioSystem;
this.dvdPlayer = dvdPlayer;
}
public void watchMovie() {
projector.turnOn();
audioSystem.turnOn();
dvdPlayer.turnOn();
System.out.println("电影开始播放");
}
public void endMovie() {
projector.turnOff();
audioSystem.turnOff();
dvdPlayer.turnOff();
System.out.println("电影结束播放");
}
}
// 调用代码的类
public class XZ {
public static void main(String[] args) {
// 创建子系统对象
Projector projector = new Projector();
AudioSystem audioSystem = new AudioSystem();
DVDPlayer dvdPlayer = new DVDPlayer();
// 创建家庭影院外观对象
HomeTheaterFacade homeTheater = new HomeTheaterFacade(projector, audioSystem, dvdPlayer);
// 启动家庭影院并观影
homeTheater.watchMovie();
System.out.println("=============");
// 结束观影
homeTheater.endMovie();
}
/*
投影仪开启
音响系统开启
DVD播放器开启
电影开始播放
=============
投影仪开启关闭
音响系统关闭
DVD播放器关闭
电影结束播放
*/
}
优点
外观模式提供了一个简单的接口,使得客户端更加易于使用子系统。
外观模式将子系统的复杂性隐藏起来,使得客户端不需要了解子系统的实现细节。
缺点
外观模式是一种结构型设计模式,它为子系统提供了一个统一的接口,使得子系统更加易于使用。通过外观模式,可以将复杂的子系统封装起来,提供一个简单的接口给客户端调用。外观模式适用于需要与多个子系统交互或需要简化复杂子系统的场景。需要注意确保外观类中包含了所有子系统的功能,同时避免在外观类中添加过多的业务逻辑。
享元模式(Flyweight Pattern)共享对象以减少内存占用,提高性能。
假设我们正在构建一个简单的绘图应用程序,用户可以在画布上绘制圆形和矩形。用户可以选择不同的颜色,位置和大小来绘制这些形状。由于用户可以创建许多相似的形状,我们需要一种方式来有效地共享相同颜色的形状对象,以减少内存使用。
享元模式是一种结构型设计模式,它通过共享相同类型的对象来减少内存的使用。享元模式主要包含两个角色:享元工厂(FlyweightFactory
)和享元对象(Flyweight
)。
// 享元对象接口 - 图形
public interface Shape {
void draw();
}
// 具体享元对象类 - 圆形
public class Circle implements Shape {
// 包含可以被共享的状态
private final String color;
public Circle(String color) {
this.color = color;
}
public void draw() {
// 绘制圆形的代码
System.out.println("绘制圆形-颜色:" + color);
}
}
// 具体享元对象类 - 矩形
public class Rectangle implements Shape {
// 包含可以被共享的状态
private final String color;
public Rectangle(String color) {
this.color = color;
}
public void draw() {
// 绘制矩形的代码
System.out.println("绘制矩形-颜色:" + color);
}
}
// 享元工厂类
public class ShapeFactory {
private static final Map<String, Shape> shapeMap = new HashMap<>();
public static Shape getShape(String color) {
Shape shape = shapeMap.get(color);
if (shape == null) {
// 如果不存在具有相同颜色的对象,则创建一个新对象
if ("red".equalsIgnoreCase(color)) {
shape = new Circle("red");
} else if ("green".equalsIgnoreCase(color)) {
shape = new Circle("green");
} else if ("blue".equalsIgnoreCase(color)) {
shape = new Circle("blue");
} else {
shape = new Rectangle("black");
}
// 将新对象存储在享元工厂中,以备后续共享
shapeMap.put(color, shape);
}
return shape;
}
}
// 调用代码的类
public class XZ {
public static void main(String[] args) {
// 获取并绘制图形
Shape redCircle = ShapeFactory.getShape("red");
redCircle.draw();
Shape greenCircle = ShapeFactory.getShape("green");
greenCircle.draw();
Shape blueCircle = ShapeFactory.getShape("blue");
blueCircle.draw();
Shape blackRectangle1 = ShapeFactory.getShape("black");
blackRectangle1.draw();
Shape blackRectangle2 = ShapeFactory.getShape("black");
blackRectangle2.draw();
}
/*
绘制圆形-颜色:red
绘制圆形-颜色:green
绘制圆形-颜色:blue
绘制矩形-颜色:black
绘制矩形-颜色:black
*/
}
当系统中存在大量相同或相似的对象,并且这些对象可以被共享时,可以使用享元模式来减少内存占用。
当需要频繁地创建和销毁对象时,可以使用享元模式来提高性能。
优点
缺点
享元模式是一种结构型设计模式,它通过共享相同类型的对象来减少内存的使用。通过享元模式,可以减少重复对象的创建,提高系统的性能。享元模式适用于存在大量相同或相似的对象,并且这些对象可以被共享的场景。在使用享元模式时,需要注意正确管理对象的状态,以避免出现不正确的结果。
代理模式(Proxy Pattern)为其他对象提供一种代理以控制对这个对象的访问。
这里我们直接使用实际业务发开中的场景,现有一个查询学生信息的 getStudentInfo() 方法。我们需要在方法之执行前后,分别执行相关的业务代码操作,以满足开发需求。
代理模式是一种结构型设计模式,它通过创建一个代理对象来控制对真实对象的访问。代理对象可以在真实对象的操作前后加入自己的逻辑,从而实现对真实对象的控制。代理模式主要包含三个角色:抽象主题(Subject
)、真实主题(RealSubject
)和代理(Proxy
)。
// 业务接口
public interface StudentService {
public void getStudentInfo();
}
// 业务接口实现类
public class StudentServiceImpl implements StudentService {
@Override
public void getStudentInfo() {
System.out.println("获取学生信息");
}
}
// 静态代理对象
public class StudentServiceImplStaticProxy implements StudentService {
private final StudentService studentService;
public StudentServiceImplStaticProxy(StudentService studentService) {
this.studentService = studentService;
}
@Override
public void getStudentInfo() {
before();
studentService.getStudentInfo();
after();
}
public void before() {
System.out.println("方法执行前操作");
}
public void after() {
System.out.println("方法执行后操作");
}
}
// 调用代码的类
public class XZ {
public static void main(String[] args) {
// 创建代理对象
StudentService studentService = new StudentServiceImpl();
StudentServiceImplStaticProxy proxy = new StudentServiceImplStaticProxy(studentService);
proxy.getStudentInfo();
}
// 方法执行前操作
// 获取学生信息
// 方法执行后操作
}
// 业务接口
public interface StudentService {
public void getStudentInfo();
}
// 业务接口实现类
public class StudentServiceImpl implements StudentService {
@Override
public void getStudentInfo() {
System.out.println("获取学生信息");
}
}
// 动态代理对象
public class StudentServiceImplDynamicProxy implements InvocationHandler {
private final Object studentService;
public StudentServiceImplDynamicProxy(StudentService studentService) {
this.studentService = studentService;
}
public StudentService getProxyInstance() {
return (StudentService) Proxy.newProxyInstance(studentService.getClass().getClassLoader(), studentService.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object invoke = method.invoke(studentService, args);
after();
return invoke;
}
public void before() {
System.out.println("方法执行前操作");
}
public void after() {
System.out.println("方法执行后操作");
}
}
// 调用代码的类
public class XZ {
public static void main(String[] args) {
// 创建代理对象
StudentService studentService = new StudentServiceImpl();
StudentService studentServiceProxy = new StudentServiceImplDynamicProxy(studentService).getProxyInstance();
studentServiceProxy.getStudentInfo();
}
// 方法执行前操作
// 获取学生信息
// 方法执行后操作
}
// 业务接口
public interface StudentService {
public void getStudentInfo();
}
// 业务接口实现类
public class StudentServiceImpl implements StudentService {
@Override
public void getStudentInfo() {
System.out.println("获取学生信息");
}
}
// 动态代理对象
public class StudentServiceImplCglibDynamicProxy implements MethodInterceptor {
private final StudentService studentService;
public StudentServiceImplCglibDynamicProxy(StudentService studentService) {
this.studentService = studentService;
}
// 用来获取代理对象(创建一个代理对象)
public StudentService getProxyInstance() {
//可以通过Enhancer对象中的create()方法可以去生成一个类,用于生成代理对象
Enhancer enhancer = new Enhancer();
//设置父类(将目标类作为代理类的父类)
enhancer.setSuperclass(studentService.getClass());
//设置拦截器(回调对象为本身对象)
enhancer.setCallback(this);
//生成一个代理类对象并返回给调用着
return (StudentService) enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
before();
Object result = methodProxy.invokeSuper(o, objects);
after();
return result;
}
public void before() {
System.out.println("方法执行前操作");
}
public void after() {
System.out.println("方法执行后操作");
}
}
// 调用代码的类
public class XZ {
public static void main(String[] args) {
// 创建代理对象
StudentService studentService = new StudentServiceImpl();
StudentService proxy = new StudentServiceImplCglibDynamicProxy(studentService).getProxyInstance();
proxy.getStudentInfo();
}
// 方法执行前操作
// 获取学生信息
// 方法执行后操作
}
// 这里注意JDK版本需为1.8
当访问一个对象存在复杂的过程或需要控制访问对象时,可以使用代理模式来控制对对象的访问。
当需要在访问真实对象前后加入自己的逻辑时,可以使用代理模式来实现增强。
确保代理对象实现了抽象主题接口,并正确实现代理的功能。
确保在真实对象初始化前不执行真实对象的操作,以提升性能。
优点
缺点
代理模式是一种结构型设计模式,它通过创建一个代理对象来控制对真实对象的访问。通过代理模式,可以控制对真实对象的访问,实现对真实对象的控制和增强。代理模式适用于需要在访问真实对象前后加入自己的逻辑,或者需要控制对真实对象的访问的场景。需要注意在代理对象中正确实现代理的功能,并确保真实对象的操作在真实对象初始化前不执行,以提升性能。
行为型模式(Behavioral Patterns)是设计模式的一种分类,它关注对象之间的交互和责任分配。行为型模式主要用于描述对象之间的通信方式和协作方式,以及对象如何相互影响和完成各自的任务。在行为型模式中,关注的是对象的行为和算法的分配,而不是对象的结构。
每种行为型模式都解决了特定类型的问题,可以根据实际的需求选择合适的模式来设计和实现软件系统。这些模式在软件开发中具有广泛的应用,能够提高代码的可维护性和扩展性,降低代码的耦合度,使得系统更加灵活和易于维护。
责任链模式(Chain of Responsibility Pattern)将请求的发送者和接收者解耦,使得多个对象都有机会处理请求,将请求沿着处理链传递,直到有一个对象处理它为止。
假设你是一个客户服务中心的员工,你需要处理客户的问题和请求。有不同类型的问题,有些你可以处理,有些你需要转给你的主管,而有些问题则需要进一步转给高级管理层。你需要一种灵活的方式来处理这些问题,并且在将来能够轻松添加新的处理者。
在这个示例中,Request
类表示客户的请求,Handler
是抽象处理者类,具有设置下一个处理者的方法和处理请求的抽象方法。LowLevelEmployee
、MiddleManager
和 TopManagement
是具体处理者类,分别表示低级员工、中级主管和高级管理层。它们分别判断自己是否能够处理请求,如果能,则处理;如果不能,则将请求传递给链上的下一个处理者。
// 请求类
class Request {
private String requestType;
public Request(String requestType) {
this.requestType = requestType;
}
public String getRequestType() {
return requestType;
}
}
// 抽象处理者类
abstract class Handler {
protected Handler nextHandler;
public void setNextHandler(Handler nextHandler) {
this.nextHandler = nextHandler;
}
public abstract void handleRequest(Request request);
}
// 具体处理者类:低级员工
class LowLevelEmployee extends Handler {
@Override
public void handleRequest(Request request) {
if (request.getRequestType().equals("简单问题")) {
System.out.println("低级员工处理简单问题");
} else {
if (nextHandler != null) {
nextHandler.handleRequest(request);
}
}
}
}
// 具体处理者类:中级主管
class MiddleManager extends Handler {
@Override
public void handleRequest(Request request) {
if (request.getRequestType().equals("一般问题")) {
System.out.println("中级主管处理一般问题");
} else {
if (nextHandler != null) {
nextHandler.handleRequest(request);
}
}
}
}
// 具体处理者类:高级管理层
class TopManagement extends Handler {
@Override
public void handleRequest(Request request) {
if (request.getRequestType().equals("复杂问题")) {
System.out.println("高级管理层处理复杂问题");
} else {
System.out.println("无人能处理该问题");
}
}
}
// 调用代码的类
public class XZ {
public static void main(String[] args) {
// 创建处理者对象
Handler lowLevelEmployee = new LowLevelEmployee();
Handler middleManager = new MiddleManager();
Handler topManagement = new TopManagement();
// 设置处理者之间的关系
lowLevelEmployee.setNextHandler(middleManager);
middleManager.setNextHandler(topManagement);
// 创建请求
Request request1 = new Request("简单问题");
Request request2 = new Request("一般问题");
Request request3 = new Request("复杂问题");
Request request4 = new Request("其他问题");
// 处理请求
lowLevelEmployee.handleRequest(request1);
lowLevelEmployee.handleRequest(request2);
lowLevelEmployee.handleRequest(request3);
lowLevelEmployee.handleRequest(request4);
}
/*
低级员工处理简单问题
中级主管处理一般问题
高级管理层处理复杂问题
无人能处理该问题
*/
}
优点
缺点
责任链模式是一种行为型设计模式,允许多个对象依次处理请求,形成一个链式结构。每个处理者负责判断自己是否能够处理请求,如果能,则处理;如果不能,则将请求传递给链上的下一个处理者。责任链模式适用于多个对象可以处理同一个请求的场景,且每个处理者只需关注自己能否处理请求。在使用责任链模式时,需要注意正确设置处理者的顺序和下一个处理者,以确保请求能够正确地沿着链传递。
命令模式(Command Pattern)将一个请求封装成一个对象,使得可以用不同的请求对客户端进行参数化。
假设你正在开发一个文本编辑器应用程序,你需要实现多个编辑操作,如撤销、重做、剪切、复制等。你需要一种灵活的方式来封装这些编辑操作,使得它们能够被单独执行、撤销和重做,并且能够轻松地添加新的编辑操作。
在这个示例中,Command
接口是命令接口,包含执行和撤销操作的抽象方法。CutCommand
和 CopyCommand
是具体命令类,实现了剪切和复制操作的具体逻辑。Editor
类是接收者,表示文本编辑器,包含实际的编辑操作。CommandExecutor
类是调用者,负责执行命令。
// 命令接口:编辑操作
interface Command {
void execute();
void undo();
}
// 具体命令:剪切操作
class CutCommand implements Command {
private Editor editor;
private String text;
public CutCommand(Editor editor, String text) {
this.editor = editor;
this.text = text;
}
@Override
public void execute() {
editor.cut(text);
}
@Override
public void undo() {
editor.undoCut(text);
}
}
// 具体命令:复制操作
class CopyCommand implements Command {
private Editor editor;
private String text;
public CopyCommand(Editor editor, String text) {
this.editor = editor;
this.text = text;
}
@Override
public void execute() {
editor.copy(text);
}
@Override
public void undo() {
editor.undoCopy(text);
}
}
// 接收者:文本编辑器
class Editor {
private String content = "我是玄子今年18";
public void cut(String text) {
content = content.replace(text, "");
System.out.println("剪切:" + text);
}
public void copy(String text) {
content += text;
System.out.println("复制:" + text);
}
public void undoCut(String text) {
content += text;
System.out.println("撤销剪切:" + text);
}
public void undoCopy(String text) {
content = content.substring(0, content.length() - text.length());
System.out.println("撤销复制");
}
public void display() {
System.out.println("当前内容:" + content);
}
}
// 调用者:命令执行者
class CommandExecutor {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void executeCommand() {
command.execute();
}
public void undoCommand() {
command.undo();
}
}
// 调用代码的类
public class XZ {
public static void main(String[] args) {
// 创建文本编辑器和命令执行者
Editor editor = new Editor();
editor.display();
CommandExecutor executor = new CommandExecutor();
// 创建剪切命令并执行
Command cutCommand = new CutCommand(editor, "今年18");
executor.setCommand(cutCommand);
executor.executeCommand();
editor.display();
// 撤销剪切操作
executor.undoCommand();
editor.display();
// 创建复制命令并执行
Command copyCommand = new CopyCommand(editor, "我是玄子");
executor.setCommand(copyCommand);
executor.executeCommand();
editor.display();
// 撤销复制操作
executor.undoCommand();
editor.display();
}
/*
当前内容:我是玄子今年18
剪切:今年18
当前内容:我是玄子
撤销剪切:今年18
当前内容:我是玄子今年18
复制:我是玄子
当前内容:我是玄子今年18我是玄子
撤销复制
当前内容:我是玄子今年18
*/
}
优点
缺点
命令模式是一种行为型设计模式,它将请求或操作封装成一个对象,实现请求的参数化、传递、撤销和重做,使得请求发送者和接收者解耦。命令模式适用于需要灵活组合和扩展命令的场景,或者需要实现请求的撤销和重做的场景。在使用命令模式时,需要注意正确设计命令接口和具体命令类,确保请求可以正确地传递和执行。
解释器模式(Interpreter Pattern)定义一种语言的文法,并解释语言中的句子。
假设你正在开发一个文本处理程序,你需要实现一个功能,将特定格式的字符串解释为相应的操作或行为。例如,用户输入一段字符串 “COPY file.txt TO folder/”,你需要将它解释为将 “file.txt” 复制到 “folder/” 目录下。
在这个示例中,Expression
接口是抽象表达式,定义了一个解释方法。TerminalExpression
是终结符表达式,表示解释特定的终结符,如 “file.txt”。OrExpression
是非终结符表达式,表示解释复杂的非终结符,如 “COPY” 和 “TO”。InterpreterContext
是解释器环境,封装了一个解释器,负责解释给定的上下文。
// 抽象表达式:解释器接口
interface Expression {
boolean interpret(String context);
}
// 终结符表达式:解释特定的终结符
class TerminalExpression implements Expression {
private final String data;
public TerminalExpression(String data) {
this.data = data;
}
@Override
public boolean interpret(String context) {
return context.contains(data);
}
}
// 非终结符表达式:解释复杂的非终结符
class OrExpression implements Expression {
private final Expression expr1;
private final Expression expr2;
public OrExpression(Expression expr1, Expression expr2) {
this.expr1 = expr1;
this.expr2 = expr2;
}
@Override
public boolean interpret(String context) {
return expr1.interpret(context) || expr2.interpret(context);
}
}
// 解释器环境:上下文
class InterpreterContext {
private final Expression expression;
public InterpreterContext(Expression expression) {
this.expression = expression;
}
public boolean interpret(String context) {
return expression.interpret(context);
}
}
// 调用代码的类
public class XZ {
public static void main(String[] args) {
Expression expression1 = new TerminalExpression("COPY");
Expression expression2 = new TerminalExpression("TO");
Expression expression = new OrExpression(expression1, expression2);
InterpreterContext interpreterContext = new InterpreterContext(expression);
System.out.println(interpreterContext.interpret("COPY file.txt TO folder/"));
}
// true
}
优点
缺点
解释器模式是一种行为型设计模式,用于解释特定语法规则或表达式。它将语言的解释与表达式解耦,使得解释过程更加灵活和可扩展。解释器模式适用于需要解释复杂表达式的场景,可以将一个语言表示为解释器类的层次结构。在使用解释器模式时,需要根据语言的语法规则,定义相应的终结符和非终结符表达式,确保表达式可以正确地解释。
迭代器模式(Iterator Pattern)提供一种顺序访问集合对象元素的方法,而不用暴露其内部结构。
假设你正在开发一个音乐播放器应用程序,你需要管理用户的播放列表。播放列表是一个包含多首音乐的集合,你需要一种方法来遍历这个播放列表,播放其中的音乐,并且能够灵活地切换遍历方式。
在这个示例中,Iterator
接口是迭代器接口,定义了迭代器的两个方法 hasNext()
和 next()
。IterableCollection
接口是集合接口,定义了一个创建迭代器的方法 createIterator()
。MusicPlaylistIterator
是具体迭代器类,实现了遍历音乐播放列表的逻辑。MusicPlaylist
是具体集合类,包含了音乐播放列表的数据,并实现了创建迭代器的方法。
// 迭代器接口
interface Iterator<T> {
boolean hasNext();
T next();
}
// 集合接口
interface IterableCollection<T> {
Iterator<T> createIterator();
}
// 具体集合:音乐播放列表
class MusicPlaylist implements IterableCollection<String> {
private final String[] playlist;
public MusicPlaylist(String[] playlist) {
this.playlist = playlist;
}
@Override
public Iterator<String> createIterator() {
return new MusicPlaylistIterator(playlist);
}
}
// 具体迭代器
class MusicPlaylistIterator implements Iterator<String> {
private final String[] playlist;
private int currentIndex = 0;
public MusicPlaylistIterator(String[] playlist) {
this.playlist = playlist;
}
@Override
public boolean hasNext() {
return currentIndex < playlist.length;
}
@Override
public String next() {
return playlist[currentIndex++];
}
}
// 调用代码的类
public class XZ {
public static void main(String[] args) {
String[] playlist = {"《Slow Down》", "《Shadow Of The Sun》", "《Normal No More 》", "《There For You》", "《STAY》"};
MusicPlaylist musicPlaylist = new MusicPlaylist(playlist);
Iterator<String> iterator = musicPlaylist.createIterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
/*
《Slow Down》
《Shadow Of The Sun》
《Normal No More 》
《There For You》
《STAY》
*/
}
优点
缺点
迭代器模式是一种行为型设计模式,用于提供一种顺序访问集合中各个元素的方法,而不需要暴露其内部表示。迭代器模式适用于需要遍历一个集合,而又不想暴露其内部结构的场景。它将集合的遍历操作封装在迭代器中,使得客户端可以逐个访问集合的元素,并且能够灵活地切换遍历方式。在使用迭代器模式时,需要注意确保迭代器能够正确遍历集合,并且集合类能够正确创建迭代器对象。
中介者模式(Mediator Pattern)用一个中介对象来封装一系列对象之间的交互,使得对象之间不再直接相互引用,降低耦合性。
假设你正在开发一个多用户聊天室应用程序,用户可以在聊天室中发送消息。每个用户可以看到其他用户发送的消息,并且可以回复消息。你需要一种方式来管理用户之间的通信,并确保消息能够正确地传递给目标用户。
在这个示例中,ChatRoomMediator
接口是中介者接口,定义了一个发送消息的方法 sendMessage()
。ChatRoom
是具体中介者类,表示聊天室,它实现了发送消息的逻辑。User
是用户类,每个用户都有一个名字和一个中介者,当用户发送消息时,调用中介者的 sendMessage()
方法来发送消息。
// 中介者接口
interface ChatRoomMediator {
void sendMessage(String message, User user);
}
// 具体中介者:聊天室
class ChatRoom implements ChatRoomMediator {
@Override
public void sendMessage(String message, User user) {
System.out.println(user.getName() + " 发送消息:" + message);
}
}
// 用户类
class User {
private String name;
private ChatRoomMediator mediator;
public User(String name, ChatRoomMediator mediator) {
this.name = name;
this.mediator = mediator;
}
public String getName() {
return name;
}
public void sendMessage(String message) {
mediator.sendMessage(message, this);
}
}
// 调用代码的类
public class XZ {
public static void main(String[] args) {
// 创建聊天室中介者
ChatRoomMediator chatRoom = new ChatRoom();
// 创建用户
User user1 = new User("User1", chatRoom);
User user2 = new User("User2", chatRoom);
User user3 = new User("User3", chatRoom);
// 用户发送消息
user1.sendMessage("Hello everyone!");
user2.sendMessage("Hi User1!");
user3.sendMessage("Nice to meet you all!");
}
/*
User1 发送消息:Hello everyone!
User2 发送消息:Hi User1!
User3 发送消息:Nice to meet you all!
*/
}
优点
缺点
中介者模式是一种行为型设计模式,用于封装一组对象之间的交互。它定义了一个中介者对象,用于管理对象之间的通信,减少对象之间的耦合性,并且使得系统更易于维护和扩展。中介者模式适用于多个对象之间存在复杂的交互关系,且对象之间不直接相互交互的场景。在使用中介者模式时,需要注意设计中介者接口,确保中介者能够正确地传递消息给目标对象。
备忘录模式(Memento Pattern)在不破坏封装性的前提下,捕获一个对象的内部状态,以便在需要时恢复该状态。
假设你正在开发一个文本编辑器应用程序,用户可以在编辑器中输入和编辑文本。用户可能会频繁地对文本进行修改,但是有时候可能会误操作或者想要撤销之前的修改。你需要一种方法来保存编辑器的历史状态,以便用户可以随时撤销到之前的状态。
在这个示例中,EditorMemento
是备忘录类,用于保存文本编辑器的状态。TextEditor
是发起人类,表示文本编辑器,它包含了编辑器的内容和保存状态、恢复状态的方法。EditorHistory
是管理者类,用于保存和恢复编辑器的历史状态。
// 备忘录:保存编辑器的状态
class EditorMemento {
private String content;
public EditorMemento(String content) {
this.content = content;
}
public String getContent() {
return content;
}
}
// 发起人:文本编辑器
class TextEditor {
private String content="";
public void write(String text) {
content += text;
}
public String getContent() {
return content;
}
public EditorMemento save() {
return new EditorMemento(content);
}
public void restore(EditorMemento memento) {
content = memento.getContent();
}
}
// 管理者:保存和恢复编辑器状态
class EditorHistory {
private Stack<EditorMemento> history = new Stack<>();
public void save(EditorMemento memento) {
history.push(memento);
}
public EditorMemento pop() {
return history.pop();
}
}
// 调用代码的类
public class XZ {
public static void main(String[] args) {
// 创建文本编辑器与备忘录
TextEditor editor = new TextEditor();
EditorHistory history = new EditorHistory();
// 编辑并保存文本
editor.write("第一行文字\n");
history.save(editor.save());
editor.write("第二行文字\n");
history.save(editor.save());
editor.write("第三行文字\n");
// 恢复最后一次保存的状态
editor.restore(history.pop());
// 输出结果:第一行文字\n第二行文字\n
System.out.println(editor.getContent());
}
}
优点
缺点
备忘录模式是一种行为型设计模式,用于在不破坏封装性的前提下,保存和恢复对象的内部状态。备忘录模式允许在后续的时间点将对象恢复到之前保存的状态,提供了撤销或回滚操作的能力。它适用于需要保存历史状态,以便在后续操作中恢复状态的场景。在使用备忘录模式时,需要注意备忘录对象的封装性,确保状态可以被外部访问,但不能被直接修改。
观察者模式(Observer Pattern)定义对象之间的一种一对多的依赖关系,当一个对象状态发生变化时,所有依赖它的对象都会得到通知并自动更新。
假设你正在开发一个天气预报应用程序,你需要实现一个功能,让用户可以订阅并接收实时的天气信息。用户可以选择订阅多个城市的天气信息,并在天气发生变化时及时收到通知。
在这个示例中,Subject
是主题接口,定义了添加观察者、移除观察者和通知观察者的方法。Observer
是观察者接口,定义了更新状态的方法。WeatherForecast
是具体主题类,表示天气预报,它维护了一个观察者列表,并在状态发生变化时通知观察者。User
是具体观察者类,表示用户,当收到通知时,更新天气预报。
// 主题接口:被观察者
interface Subject {
void addObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
// 观察者接口
interface Observer {
void update(String message);
}
// 具体主题:天气预报
class WeatherForecast implements Subject {
private List<Observer> observers = new ArrayList<>();
private String weatherMessage;
@Override
public void addObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(weatherMessage);
}
}
public void setWeatherMessage(String weatherMessage) {
this.weatherMessage = weatherMessage;
notifyObservers();
}
}
// 具体观察者:用户
class User implements Observer {
private String name;
public User(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println(name + " 收到天气预报:" + message);
}
}
// 调用代码的类
public class XZ {
public static void main(String[] args) {
// 创建天气预报对象
WeatherForecast weatherForecast = new WeatherForecast();
// 创建观察者对象
User user1 = new User("张三");
User user2 = new User("李四");
// 添加观察者
weatherForecast.addObserver(user1);
weatherForecast.addObserver(user2);
// 设置天气预报信息,触发通知
weatherForecast.setWeatherMessage("明天有雨");
// 输出结果
// 张三 收到天气预报:明天有雨
// 李四 收到天气预报:明天有雨
// 移除观察者user2
weatherForecast.removeObserver(user2);
// 设置天气预报信息,触发通知
weatherForecast.setWeatherMessage("后天晴朗");
// 输出结果
// 张三 收到天气预报:后天晴朗
}
}
优点
缺点
观察者模式是一种行为型设计模式,用于实现一种一对多的依赖关系,当一个对象的状态发生变化时,其依赖对象(观察者)会自动收到通知并更新。观察者模式允许主题(被观察者)和观察者之间解耦,使得主题可以独立地改变状态而不影响观察者。它适用于需要通知多个依赖对象的场景,如天气预报、事件处理等。在实现观察者模式时,需要注意主题对象维护观察者列表,并在状态发生变化时通知观察者,同时确保观察者和主题之间解耦。
状态模式(State Pattern)允许对象在内部状态改变时改变其行为,使得对象看起来像是修改了其类。
假设你正在开发一个电梯控制系统,电梯有多个状态,包括开门状态、关门状态、运行状态和停止状态。在不同的状态下,电梯会有不同的行为,比如在运行状态下,电梯可以响应楼层按钮,而在停止状态下,电梯只能等待用户操作。
在这个示例中,ElevatorState
是状态接口,定义了电梯的四种状态:开门、关门、上行和下行。OpenState
、CloseState
、RunningState
和 StoppedState
是具体状态类,分别表示电梯的四种状态,它们实现了状态接口的方法。Elevator
是环境类,表示电梯,它维护了当前的状态,并提供了可以改变状态的方法。
// 状态接口
interface ElevatorState {
void openDoor();
void closeDoor();
void goUp();
void goDown();
}
// 具体状态:开门状态
class OpenState implements ElevatorState {
@Override
public void openDoor() {
System.out.println("电梯已经是开门状态");
}
@Override
public void closeDoor() {
System.out.println("电梯关门");
}
@Override
public void goUp() {
System.out.println("电梯开始上行");
}
@Override
public void goDown() {
System.out.println("电梯开始下行");
}
}
// 具体状态:关门状态
class CloseState implements ElevatorState {
@Override
public void openDoor() {
System.out.println("电梯开门");
}
@Override
public void closeDoor() {
System.out.println("电梯已经是关门状态");
}
@Override
public void goUp() {
System.out.println("电梯开始上行");
}
@Override
public void goDown() {
System.out.println("电梯开始下行");
}
}
// 具体状态:运行状态
class RunningState implements ElevatorState {
@Override
public void openDoor() {
System.out.println("电梯不能在运行时开门");
}
@Override
public void closeDoor() {
System.out.println("电梯不能在运行时关门");
}
@Override
public void goUp() {
System.out.println("电梯继续上行");
}
@Override
public void goDown() {
System.out.println("电梯继续下行");
}
}
// 具体状态:停止状态
class StoppedState implements ElevatorState {
@Override
public void openDoor() {
System.out.println("电梯开门");
}
@Override
public void closeDoor() {
System.out.println("电梯关门");
}
@Override
public void goUp() {
System.out.println("电梯开始上行");
}
@Override
public void goDown() {
System.out.println("电梯开始下行");
}
}
// 环境类:电梯
class Elevator {
private ElevatorState currentState;
public Elevator() {
currentState = new StoppedState();
}
public void setState(ElevatorState state) {
currentState = state;
}
public void openDoor() {
currentState.openDoor();
}
public void closeDoor() {
currentState.closeDoor();
}
public void goUp() {
currentState.goUp();
}
public void goDown() {
currentState.goDown();
}
}
// 调用代码的类
public class XZ {
public static void main(String[] args) {
// 创建电梯对象
Elevator elevator = new Elevator();
// 设置电梯状态为打开
elevator.setState(new OpenState());
elevator.openDoor(); // 电梯已经是开门状态
// 设置电梯状态为关闭
elevator.setState(new CloseState());
elevator.closeDoor(); // 电梯关门
// 设置电梯状态为运行
elevator.setState(new RunningState());
elevator.goUp(); // 电梯继续上行
// 设置电梯状态为停止
elevator.setState(new StoppedState());
elevator.openDoor(); // 电梯开门
}
}
优点
缺点
状态模式是一种行为型设计模式,它允许一个对象在其内部状态改变时改变其行为。状态模式将对象的行为封装在不同的状态类中,当对象的状态发生改变时,它的行为也会随之改变。状态模式适用于当对象的行为随着内部状态的改变而改变的场景,比如电梯控制系统、游戏角色状态等。在实现状态模式时,需要确保环境类正确维护当前的状态,并在状态改变时能够正确地调用相应状态的方法。
策略模式(Strategy Pattern)定义一系列算法,并使其可以相互替换,使得算法的变化独立于使用算法的客户端。
假设你正在开发一个电商网站,用户在购买商品时可以选择不同的支付方式,比如支付宝、微信支付、信用卡支付等。你需要设计一个灵活的支付系统,使得可以根据用户选择的支付方式来执行相应的支付逻辑。
在这个示例中,PaymentStrategy
是支付策略接口,定义了支付的方法 pay()
。AlipayStrategy
和 WechatPayStrategy
是具体支付策略类,表示支付宝支付和微信支付,它们实现了支付策略接口的方法。PaymentContext
是上下文类,表示支付系统,它包含了一个支付策略的引用,并提供了设置支付策略和执行支付的方法。
// 支付策略接口
interface PaymentStrategy {
void pay(double amount);
}
// 具体支付策略:支付宝支付
class AlipayStrategy implements PaymentStrategy {
@Override
public void pay(double amount) {
System.out.println("使用支付宝支付:" + amount + "元");
}
}
// 具体支付策略:微信支付
class WechatPayStrategy implements PaymentStrategy {
@Override
public void pay(double amount) {
System.out.println("使用微信支付:" + amount + "元");
}
}
// 上下文类:支付系统
class PaymentContext {
private final PaymentStrategy strategy;
public PaymentContext(PaymentStrategy strategy) {
this.strategy = strategy;
}
public void executePayment(double amount) {
strategy.pay(amount);
}
}
// 调用代码的类
public class XZ {
public static void main(String[] args) {
PaymentContext alipay = new PaymentContext(new AlipayStrategy());
alipay.executePayment(12);
PaymentContext wechatPay = new PaymentContext(new WechatPayStrategy());
wechatPay.executePayment(24);
}
/*
使用支付宝支付:12.0元
使用微信支付:24.0元
*/
}
优点
缺点
策略模式是一种行为型设计模式,它定义了一系列算法族,并将每个算法封装在独立的策略类中,使得算法可以互相替换。策略模式适用于当有多个算法可以选择,并且需要在运行时根据不同的条件选择合适的算法的场景。在实现策略模式时,需要确保策略类实现了相同的接口,以便在上下文类中可以统一使用。
模板方法模式(Template Method Pattern)定义一个算法的骨架,将一些步骤的实现延迟到子类中。
假设你正在开发一个咖啡店点单系统,该系统需要支持不同类型的咖啡,如美式咖啡、拿铁咖啡和卡布奇诺等。不同类型的咖啡在制作过程中可能有一些不同的步骤,但整体制作流程是相同的。你希望设计一个通用的咖啡制作模板,使得每种类型的咖啡可以共享通用的制作流程,并且允许子类来实现特定类型咖啡的特殊步骤。
在这个示例中,CoffeeTemplate
是咖啡制作模板,定义了咖啡的制作流程,其中 makeCoffee()
方法是模板方法,包含了制作咖啡的通用流程,而 boilWater()
、brewCoffee()
、pourIntoCup()
和 addCondiments()
是具体步骤,其中 brewCoffee()
和 addCondiments()
是抽象方法,由子类具体实现。AmericanoCoffee
和 LatteCoffee
是具体咖啡类,分别表示美式咖啡和拿铁咖啡,它们继承自咖啡制作模板,并实现了特定类型咖啡的特殊步骤。
// 咖啡制作模板
abstract class CoffeeTemplate {
public final void makeCoffee() {
boilWater();
brewCoffee();
pourIntoCup();
addCondiments();
}
protected void boilWater() {
System.out.println("烧水");
}
protected abstract void brewCoffee();
protected void pourIntoCup() {
System.out.println("倒入杯中");
}
protected abstract void addCondiments();
}
// 具体咖啡:拿铁咖啡
class LatteCoffee extends CoffeeTemplate {
@Override
protected void brewCoffee() {
System.out.println("冲泡拿铁咖啡");
}
@Override
protected void addCondiments() {
System.out.println("加入牛奶");
}
}
// 具体咖啡:美式咖啡
class AmericanoCoffee extends CoffeeTemplate {
@Override
protected void brewCoffee() {
System.out.println("冲泡美式咖啡");
}
@Override
protected void addCondiments() {
// 美式咖啡不加调料
}
}
// 调用代码的类
public class XZ {
public static void main(String[] args) {
CoffeeTemplate americanoCoffee = new AmericanoCoffee();
americanoCoffee.makeCoffee();
System.out.println("===========");
CoffeeTemplate latteCoffee = new LatteCoffee();
latteCoffee.makeCoffee();
}
/*
烧水
冲泡美式咖啡
倒入杯中
===========
烧水
冲泡拿铁咖啡
倒入杯中
加入牛奶
*/
}
final
或者不能被子类覆盖,以防止子类改变算法的骨架。优点
缺点
模板方法模式是一种行为型设计模式,它定义了一个算法的骨架,并将一些步骤延迟到子类中实现。模板方法模式适用于当有一个算法的流程固定,但其中某些步骤可能会有所不同的场景。在实现模板方法模式时,需要确保模板方法是 final
或者不能被子类覆盖,以防止子类改变算法的骨架。
访问者模式(Visitor Pattern)在不改变对象结构的前提下,定义作用于某个对象结构中的各个元素的操作。
假设你正在开发一个旅游景点导览系统,该系统需要展示不同类型的景点,如博物馆、动物园和公园等。每种类型的景点都有不同的展示方式,比如博物馆需要展示历史文物,动物园需要展示各种动物,公园需要展示美丽的花园。你希望设计一个通用的展示系统,使得可以根据不同类型的景点来展示不同的内容。
在这个示例中,Element
是元素接口,定义了接受访问者的方法 accept(Visitor visitor)
。Museum
、Zoo
和 Park
是具体元素类,表示博物馆、动物园和公园,它们实现了元素接口的方法,并在 accept
方法中将自身作为参数传递给访问者。Visitor
是访问者接口,定义了访问者的方法 visit(Museum museum)
、visit(Zoo zoo)
和 visit(Park park)
。DisplayVisitor
是具体访问者类,表示展示访问者,它实现了访问者接口的方法,并在每个方法中根据具体元素的类型展示相应的内容。
// 元素接口
interface Element {
void accept(Visitor visitor);
}
// 具体元素:博物馆
class Museum implements Element {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
// 博物馆特有方法
public String getExhibition() {
return "历史文物展览";
}
}
// 具体元素:动物园
class Zoo implements Element {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
// 动物园特有方法
public String getAnimals() {
return "狮子、大象、长颈鹿等";
}
}
// 具体元素:公园
class Park implements Element {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
// 公园特有方法
public String getGarden() {
return "美丽的花园";
}
}
// 访问者接口
interface Visitor {
void visit(Museum museum);
void visit(Zoo zoo);
void visit(Park park);
}
// 具体访问者:展示访问者
class DisplayVisitor implements Visitor {
@Override
public void visit(Museum museum) {
System.out.println("展示博物馆,展览内容:" + museum.getExhibition());
}
@Override
public void visit(Zoo zoo) {
System.out.println("展示动物园,动物:" + zoo.getAnimals());
}
@Override
public void visit(Park park) {
System.out.println("展示公园,花园:" + park.getGarden());
}
}
// 调用代码的类
public class XZ {
public static void main(String[] args) {
// 创建元素对象
Element museum = new Museum();
Element zoo = new Zoo();
Element park = new Park();
// 创建访问者对象
Visitor displayVisitor = new DisplayVisitor();
// 对元素应用访问者
museum.accept(displayVisitor);
zoo.accept(displayVisitor);
park.accept(displayVisitor);
}
/*
展示博物馆,展览内容:历史文物展览
展示动物园,动物:狮子、大象、长颈鹿等
展示公园,花园:美丽的花园
*/
}
优点
缺点
访问者模式是一种行为型设计模式,它允许将算法从数据结构中分离出来,使得可以在不改变数据结构的情况下定义新的算法。访问者模式适用于当有一个数据结构中包含不同类型的元素,并且希望根据不同的元素执行不同的操作的场景。在实现访问者模式时,需要确保每个具体元素类都正确实现了接受访问者的方法,并在其中调用访问者的对应方法。
简单工厂模式(Simple Factory Pattern)虽然它不在 GoF 的列表中,但在实际开发中,简单工厂模式是一种经常被使用的设计模式。它简化了对象的创建过程,提高了代码的可维护性和扩展性。
假设你正在开发一个计算器应用程序,用户可以输入两个操作数和运算符,然后应用程序会根据输入的运算符进行相应的计算,并返回结果。不同的运算符对应不同的计算方法,例如加法、减法、乘法和除法等。你希望设计一个通用的计算器工厂,根据用户输入的运算符,生产出相应的计算器对象,从而实现不同运算的计算功能。
// 运算器接口
interface Operator {
double calculate(double operand1, double operand2);
}
// 具体运算器:加法运算器
class AdditionOperator implements Operator {
@Override
public double calculate(double operand1, double operand2) {
return operand1 + operand2;
}
}
// 具体运算器:减法运算器
class SubtractionOperator implements Operator {
@Override
public double calculate(double operand1, double operand2) {
return operand1 - operand2;
}
}
// 具体运算器:乘法运算器
class MultiplicationOperator implements Operator {
@Override
public double calculate(double operand1, double operand2) {
return operand1 * operand2;
}
}
// 具体运算器:除法运算器
class DivisionOperator implements Operator {
@Override
public double calculate(double operand1, double operand2) {
if (operand2 == 0) {
throw new IllegalArgumentException("除数不能为零");
}
return operand1 / operand2;
}
}
// 简单工厂类
class CalculatorFactory {
public static Operator createOperator(String operatorType) {
switch (operatorType) {
case "+":
return new AdditionOperator();
case "-":
return new SubtractionOperator();
case "*":
return new MultiplicationOperator();
case "/":
return new DivisionOperator();
default:
throw new IllegalArgumentException("不支持的运算符:" + operatorType);
}
}
}
// 调用代码
public class XZ {
public static void main(String[] args) {
Operator operator1 = CalculatorFactory.createOperator("+");
System.out.println(operator1.calculate(1, 2));
Operator operator2 = CalculatorFactory.createOperator("-");
System.out.println(operator2.calculate(1, 2));
Operator operator3 = CalculatorFactory.createOperator("*");
System.out.println(operator3.calculate(1, 2));
Operator operator4 = CalculatorFactory.createOperator("/");
System.out.println(operator4.calculate(1, 2));
// Operator operator5 = CalculatorFactory.createOperator("%");
// System.out.println(operator5.calculate(1, 2));
// 执行没有的 Operator 抛出异常
}
}
在这个示例中,Operator
是运算器接口,定义了一个 calculate()
方法来进行运算。AdditionOperator
、SubtractionOperator
、MultiplicationOperator
和 DivisionOperator
是具体运算器类,分别表示加法运算器、减法运算器、乘法运算器和除法运算器,它们都实现了运算器接口的方法。CalculatorFactory
是简单工厂类,它通过 createOperator()
方法来根据用户输入的运算符类型,创建相应的运算器对象。
优点
缺点
简单工厂模式是一种创建型设计模式,它通过一个工厂类来创建不同类型的对象,使得客户端不需要关心对象的具体创建过程。简单工厂模式适用于当需要根据不同的条件创建不同类型的对象时。在实现简单工厂模式时,需要注意工厂类的职责,它应该负责根据条件创建相应的对象,而不应该承担过多的业务逻辑。虽然简单工厂模式有一些优点,但也有一些缺点,例如如果需要添加新的对象类型,可能需要修改工厂类的代码,这可能违反了开闭原则。因此,在应用简单工厂模式时需要权衡考虑,选择合适的设计方案。
空对象模式(Null Object Pattern)虽然它不在 GoF 的列表中,但在实际开发中,我们通常使用空对象模式(Null Object Pattern)来替代传统的null值或者抛出异常的做法。空对象模式是一种创建一个默认对象的设计模式,这个默认对象包含了类似于对null的处理,但是避免了空指针异常的出现。如果有NULL对象被调用,就会返回一个默认的对象而不是null值。
在一个电商网站中,我们需要对商品进行分类、下单、支付等操作。当有用户查询或者购买不存在的商品时,我们需要能够处理该异常情况,确保不会出现空指针异常等错误,同时也需要保证程序正常运行。
// 商品抽象类
public abstract class Product {
protected String name;
protected double price;
public abstract String getName();
public abstract double getPrice();
}
// 商品类
public class ConcreteProduct extends Product {
public ConcreteProduct(String name, double price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public double getPrice() {
return price;
}
}
// 空商品类
public class NullProduct extends Product {
public NullProduct() {
this.name = "null";
this.price = 0.0;
}
public String getName() {
return name;
}
public double getPrice() {
return price;
}
}
// 商品工厂类
public class ProductFactory {
private static final Map<String, Product> products = new HashMap<>();
static {
products.put("product1", new ConcreteProduct("Product 1", 100.0));
products.put("product2", new ConcreteProduct("Product 2", 200.0));
}
public static Product getProduct(String productName) {
Product product = products.get(productName);
if (product != null) {
return product;
}
return new NullProduct();
}
}
// 调用代码的类
public class XZ {
public static void main(String[] args) {
Product product1 = ProductFactory.getProduct("product1");
System.out.println("product 1 name: " + product1.getName() + ", price: " + product1.getPrice());
Product product3 = ProductFactory.getProduct("product3");
System.out.println("product 3 name: " + product3.getName() + ", price: " + product3.getPrice());
}
}
优点
缺点
空对象模式是一种非常实用的设计模式,可以有效地解决空值的问题,并且避免了空指针异常的出现。在项目开发中,我们可以根据实际需要,使用空对象模式对代码进行优化和重构,使之更加健壮、易读和易于维护。
工厂模式是一种创建对象的设计模式。它通过定义一个公共的接口或抽象类,并由具体的工厂类来创建对象实例。工厂模式将对象的创建与使用分离,客户端只需要与工厂进行交互,而无需直接创建对象。
主要特点
适用场景
策略模式是一种行为型设计模式,它定义了一系列算法,并将每个算法封装成单独的类,使它们可以相互替换。策略模式使得算法的变化独立于使用算法的客户端。
主要特点
适用场景
设计模式是一种用于解决特定问题的通用解决方案,它们是经过验证的、被广泛接受的最佳实践。除了常见的设计模式,还有一些拓展和衍生的设计模式,以及一些设计原则和编程技巧可以进一步优化和扩展设计模式的应用。
这些拓展和衍生的设计模式都是在特定场景下为了解决特定问题而产生的,它们丰富了设计模式的应用范围,并提供了更多的解决方案供开发者选择。在实际应用中,我们需要根据具体的问题和需求选择合适的设计模式,或者结合多种设计模式来构建出更复杂、更灵活的系统。同时,也应该注意不要滥用设计模式,只有在合适的场景下使用合适的设计模式才能真正发挥它们的优势。
玄子Share 设计模式 GOF 全23种 + 七大设计原则 9.25