(23种设计模式详解)java中为什么要用设计模式,有什么作用,怎么实现
java常用设计模式为什么要有设计模式一、创建型模式单例模式原型模式工厂模式建造者模式
二、结构性模式代理模式JDK 动态代理CGLIB 动态代理
适配器模式组合模式装饰器模式外观模式享元模式桥接模式
三、行为型模式策略模式迭代器模式中介者模式责任链模式命令模式解释器模式观察者模式状态模式模板模式备忘录模式访问者模式
java常用设计模式
为什么要有设计模式
一些软件开发者他们发现在不同的项目中遇到了相同的问题,这个时候他们开始寻找可重用的方案,这些解决方案被称为设计模式。 GOF四人帮他们所提出的设计模式主要是基于面向对象设计原则 ,对接口编程而不是对实现编程,对接口编程而不是对实现编程,设计模式有几种类型,包括创建型、结构型和行为型模式。
创建型模式用于以灵活和高效的方式创建对象。包括Singleton(单例)模式、工厂模式和抽象工厂模式等。
结构型模式用于组合类和对象以形成更大的结构。包括适配器模式、桥接模式和装饰器模式等。
行为型模式用于处理类或对象之间的通信和控制流。包括观察者模式、策略模式和模板方法模式。 设计模式的六大原则,
单一职责原则(SRP):一个类 / 模块只负责一项职责,避免功能混杂。开放封闭原则(OCP):对扩展开放,对修改关闭。新增功能时通过扩展实现,而非修改现有代码。里氏替换原则(LSP):子类必须能替换父类,且不破坏原有逻辑。依赖倒置原则(DIP):高层模块不应该依赖于低层模块,两者都应该依赖于抽象接口;抽象接口不应该依赖于具体实现,具体实现应该依赖于抽象接口。接口隔离原则(ISP):不应该强迫一个类实现它不需要的接口,应该将接口拆分成更小和更具体的部分,以便客户端只需要知道它们感兴趣的部分。迪米特法则(LOD):一个对象应该对其他对象有尽可能少的了解,通常称为“最少知识原则”。
一、创建型模式
单例模式
单例模式属于创建类型的常用软件设计模式,其核心是确保一个类在特定范围内(如整个应用程序或一个线程上下文)只有一个实例,并提供一个全局访问点来获取该实例。 例如在打印任务中,一个系统之应该有一个打印的程序,避免多个打印作业同时输出造成混乱,保证数据一致性和状态统一,
饿汉式:
在类加载时就立即创建唯一的实例。因为实例在类加载阶段就已创建,而类加载由 JVM 保证线程安全,所以这种方式天生线程安全。但如果这个实例创建后很长时间才被使用,就会造成资源的浪费。
public class Singleton {
// 立即创建实例
private static final Singleton instance = new Singleton();
// 私有构造函数,防止外部实例化
private Singleton() {}
// 提供全局访问点
public static Singleton getInstance() {
return instance;
}
}
懒汉式(线程不安全):延迟实例化,只有在第一次调用获取实例的方法时才创建实例。这种方式在单线程环境下可以正常工作,但在多线程环境中,如果多个线程同时调用 getInstance 方法,可能会创建多个实例,因此线程不安全。
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
懒汉式(线程安全):在懒汉式基础上,给获取实例的方法加上 synchronized 关键字,保证同一时间只有一个线程可以进入该方法创建实例,从而实现线程安全。不过,每次调用 getInstance 方法都要进行同步检查,性能较低。
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
双检锁式(DCL):结合了懒加载和高效的线程安全机制。通过两次检查实例是否为 null,并在第一次创建实例时使用 synchronized 关键字同步,同时使用 volatile 关键字禁止指令重排,确保在多线程环境下只有一个实例被创建,且性能相对较高。
public class Singleton {
private volatile static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
静态内部类方式:利用类加载机制来保证线程安全和懒加载特性。外部类加载时,静态内部类不会被加载,只有当调用 getInstance 方法时,静态内部类才会被加载,此时创建实例。JVM 保证了静态内部类的加载过程是线程安全的。
public class Singleton {
private Singleton() {}
// 静态内部类
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
枚举方式:在 Java 中,枚举类本身天然就是单例的,并且这种方式简洁、线程安全,还能防止通过反射和反序列化来创建多个实例。
public enum Singleton {
INSTANCE;
// 可以在这里定义其他方法和属性
}
原型模式
在软件开发中,有些对象的创建过程可能非常复杂,涉及到大量的初始化操作、数据加载或者网络请求等。如果每次都通过常规的创建方式来生成新对象,会消耗大量的时间和系统资源。另外,有时候我们需要创建与现有对象相似的新对象,并且可能只需要对部分属性进行修改。如果从头开始创建这些对象,不仅繁琐,还容易出错。原型模式就是为了解决这些问题而出现的。
java实现
在 Java 中,要实现原型模式,需要让类实现Cloneable接口,并重写clone()方法。Cloneable接口是一个标记接口,它告诉 Java 虚拟机这个类可以被克隆。,Prototype类实现了Cloneable接口,并重写了clone()方法。在main方法中,我们创建了一个原型对象original,然后通过调用clone()方法克隆出一个新对象clone,并修改了克隆对象的属性。
浅拷贝和深拷贝:在使用原型模式时,需要注意浅拷贝和深拷贝的区别。浅拷贝只复制对象的一层属性,如果对象包含引用类型的属性,浅拷贝只会复制引用,而不会复制对象本身。深拷贝会递归地复制对象的所有属性,确保新对象和原对象完全独立。
// 实现Cloneable接口
class Prototype implements Cloneable {
private String name;
public Prototype(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// 重写clone()方法
@Override
protected Prototype clone() throws CloneNotSupportedException {
return (Prototype) super.clone();
}
}
public class PrototypePatternDemo {
public static void main(String[] args) {
try {
// 创建原型对象
Prototype original = new Prototype("Original");
// 克隆原型对象
Prototype clone = original.clone();
// 修改克隆对象的属性
clone.setName("Clone");
System.out.println("Original: " + original.getName());
System.out.println("Clone: " + clone.getName());
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
工厂模式
在软件开发中,对象的创建过程可能会比较复杂,涉及到一系列的初始化操作、依赖注入等。如果在业务代码中直接进行对象的创建,会使业务代码和对象创建代码紧密耦合在一起。当对象的创建逻辑发生变化时,就需要修改大量的业务代码,这违反了开闭原则(对扩展开放,对修改关闭)。工厂模式将对象的创建逻辑封装在工厂类中,业务代码只需要从工厂获取对象,从而实现了对象创建和使用的分离,降低了代码的耦合度。
工厂模式主要分为简单工厂模式、工厂方法模式和抽象工厂模式
简单工厂模式是工厂模式的基础版本,它定义一个工厂类,根据传入的参数决定创建哪种类型的对象。 简单工厂模式的优点是实现简单,缺点是工厂类职责过重,不符合开闭原则,当需要添加新的产品类型时,需要修改工厂类的代码。
// 产品接口
interface Product {
void use();
}
// 具体产品类A
class ConcreteProductA implements Product {
@Override
public void use() {
System.out.println("使用产品A");
}
}
// 具体产品类B
class ConcreteProductB implements Product {
@Override
public void use() {
System.out.println("使用产品B");
}
}
// 简单工厂类
class SimpleFactory {
public static Product createProduct(String type) {
if ("A".equals(type)) {
return new ConcreteProductA();
} else if ("B".equals(type)) {
return new ConcreteProductB();
}
return null;
}
}
// 测试代码
public class SimpleFactoryPattern {
public static void main(String[] args) {
Product productA = SimpleFactory.createProduct("A");
productA.use();
Product productB = SimpleFactory.createProduct("B");
productB.use();
}
}
工厂方法模式定义了一个创建对象的接口,让子类决定实例化哪个类。工厂方法使一个类的实例化。到其子类。 相当于有具体的工厂,比如做咖啡,就有咖啡响应的工厂,做蛋糕,就有蛋糕相应的工厂,工厂方法模式的优点是符合开闭原则,当需要添加新的产品类型时,只需创建新的具体工厂类和产品类,而不需要修改现有的代码。缺点是工厂子类过多时,会导致代码复杂度增加。
// 产品接口
interface Product {
void use();
}
// 具体产品类A
class ConcreteProductA implements Product {
@Override
public void use() {
System.out.println("使用产品A");
}
}
// 具体产品类B
class ConcreteProductB implements Product {
@Override
public void use() {
System.out.println("使用产品B");
}
}
// 抽象工厂类
abstract class Factory {
public abstract Product createProduct();
}
// 具体工厂类A,负责创建产品A
class ConcreteFactoryA extends Factory {
@Override
public Product createProduct() {
return new ConcreteProductA();
}
}
// 具体工厂类B,负责创建产品B
class ConcreteFactoryB extends Factory {
@Override
public Product createProduct() {
return new ConcreteProductB();
}
}
// 测试代码
public class FactoryMethodPattern {
public static void main(String[] args) {
Factory factoryA = new ConcreteFactoryA();
Product productA = factoryA.createProduct();
productA.use();
Factory factoryB = new ConcreteFactoryB();
Product productB = factoryB.createProduct();
productB.use();
}
}
抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。 举个例子,有时候我们需要创建一组相关的对象来完成某个特定的业务功能。例如,在一个游戏开发场景中,我们可能需要创建不同风格的游戏角色、武器和装备,这些对象之间存在着一定的关联,比如中世纪风格的角色应该搭配中世纪风格的武器和装备。如果直接在代码里指定具体要创建哪些对象,会让代码变得复杂且难以维护,尤其是当需要添加新的风格或者修改现有风格时,会涉及到大量代码的修改。抽象工厂模式就是为了解决这类问题而出现的。
抽象工厂模式通过提供一个抽象的创建对象的接口,将客户端代码与具体的对象类解耦,使得客户端代码可以在不指定具体类的情况下创建一系列相关或相互依赖的对象。这种模式提高了代码的可维护性和可扩展性,使得系统更容易应对变化。
简单抽象工厂示例
// 抽象工厂接口
interface GUIFactory {
Button createButton();
TextField createTextField();
ScrollBar createScrollBar();
}
具体实现代码
// 具体的Windows工厂类
class WindowsGUIFactory implements GUIFactory {
@Override
public Button createButton() {
return new WindowsButton();
}
@Override
public TextField createTextField() {
return new WindowsTextField();
}
@Override
public ScrollBar createScrollBar() {
return new WindowsScrollBar();
}
}
// 具体的MacOS工厂类
class MacOSGUIFactory implements GUIFactory {
@Override
public Button createButton() {
return new MacOSButton();
}
@Override
public TextField createTextField() {
return new MacOSTextField();
}
@Override
public ScrollBar createScrollBar() {
return new MacOSScrollBar();
}
}
// 客户端代码
public class Client {
public static void main(String[] args) {
// 使用Windows工厂
GUIFactory factory = new WindowsGUIFactory();
Button button = factory.createButton();
TextField textField = factory.createTextField();
ScrollBar scrollBar = factory.createScrollBar();
// 如果要切换到MacOS风格,只需要更换工厂类
// factory = new MacOSGUIFactory();
// button = factory.createButton();
// textField = factory.createTextField();
// scrollBar = factory.createScrollBar();
}
}
建造者模式
在软件开发过程中,当创建一个复杂对象时,该对象可能包含多个组成部分,且这些组成部分的构建过程较为复杂,构建顺序也可能有特定要求。如果直接在客户端代码中进行复杂对象的创建,会导致客户端代码变得冗长、复杂且难以维护。同时,不同的用户可能对对象的构建有不同的需求组合,如果为每种需求都编写一个创建方法,会使代码的可维护性和可扩展性变差。而建造者模式刚好解决了这个问题,可以根据需求一个一个的去创建对象,建造者模式主要包含四个角色
产品(Product):需要构建的复杂对象。
抽象建造者(Builder):定义了构建产品各个部分的抽象方法,通常还会有一个返回最终产品的方法。
具体建造者(Concrete Builder):实现了抽象建造者接口,负责具体的构建过程。
指挥者(Director):负责指挥建造者按照一定的顺序构建产品,它可以根据不同的需求调用建造者的不同方法。
// 产品类
class Computer {
private String cpu;
private String memory;
private String hardDisk;
public void setCpu(String cpu) {
this.cpu = cpu;
}
public void setMemory(String memory) {
this.memory = memory;
}
public void setHardDisk(String hardDisk) {
this.hardDisk = hardDisk;
}
@Override
public String toString() {
return "Computer{" +
"cpu='" + cpu + '\'' +
", memory='" + memory + '\'' +
", hardDisk='" + hardDisk + '\'' +
'}';
}
}
// 抽象建造者
interface ComputerBuilder {
void buildCpu();
void buildMemory();
void buildHardDisk();
Computer getComputer();
}
// 具体建造者
class GamingComputerBuilder implements ComputerBuilder {
private Computer computer;
public GamingComputerBuilder() {
this.computer = new Computer();
}
@Override
public void buildCpu() {
computer.setCpu("Intel Core i9");
}
@Override
public void buildMemory() {
computer.setMemory("32GB DDR4");
}
@Override
public void buildHardDisk() {
computer.setHardDisk("1TB SSD");
}
@Override
public Computer getComputer() {
return computer;
}
}
// 指挥者
class ComputerDirector {
private ComputerBuilder builder;
public ComputerDirector(ComputerBuilder builder) {
this.builder = builder;
}
public Computer constructComputer() {
builder.buildCpu();
builder.buildMemory();
builder.buildHardDisk();
return builder.getComputer();
}
}
// 客户端代码
public class BuilderPatternDemo {
public static void main(String[] args) {
// 创建具体建造者
ComputerBuilder builder = new GamingComputerBuilder();
// 创建指挥者
ComputerDirector director = new ComputerDirector(builder);
// 指挥者指挥建造者构建产品
Computer computer = director.constructComputer();
System.out.println(computer);
}
}
二、结构性模式
代理模式
代理对象可以对客户端的访问请求进行拦截和过滤,根据一定的规则决定是否允许客户端访问目标对象,或者对访问请求进行一些预处理。例如,在一个网络防火墙系统中,代理服务器可以对客户端的网络请求进行过滤,阻止恶意请求访问内部网络。
代理对象可以在不改变目标对象代码的情况下,为目标对象添加一些额外的功能,如日志记录、性能监控、事务管理等。例如,在一个 Web 应用程序中,可以使用代理对象在调用业务方法前后记录方法的执行时间,以便进行性能分析。
代理对象可以将目标对象的实现细节隐藏起来,客户端只需要与代理对象进行交互,不需要了解目标对象的具体实现。这样可以降低客户端代码与目标对象代码之间的耦合度,提高代码的可维护性。例如,在一个数据库访问系统中,客户端只需要通过代理对象来执行数据库操作,不需要了解数据库的连接、查询语句的执行等细节。
代理模式主要分为静态代理和动态代理,下面分别介绍他们的实现
静态代理
静态代理是指在编译时就已经确定了代理类和目标类的关系,代理类和目标类都需要实现相同的接口 。静态代理的优点是实现简单,缺点是代理类和目标类需要实现相同的接口,并且如果接口中的方法较多,代理类的代码会变得很冗长。当需要为多个不同的目标类提供代理时,需要为每个目标类都创建一个对应的代理类,代码的复用性较差。
// 定义一个接口
interface Subject {
void request();
}
// 目标类,实现接口
class RealSubject implements Subject {
@Override
public void request() {
System.out.println("RealSubject: Handling request.");
}
}
// 代理类,实现相同的接口
class ProxySubject implements Subject {
private RealSubject realSubject;
public ProxySubject(RealSubject realSubject) {
this.realSubject = realSubject;
}
@Override
public void request() {
// 在调用目标对象的方法之前可以进行一些预处理
System.out.println("ProxySubject: Before handling request.");
realSubject.request();
// 在调用目标对象的方法之后可以进行一些后处理
System.out.println("ProxySubject: After handling request.");
}
}
// 客户端代码
public class StaticProxyDemo {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
ProxySubject proxySubject = new ProxySubject(realSubject);
proxySubject.request();
}
}
动态代理
动态代理是指在运行时动态地创建代理类,不需要在编译时就确定代理类的代码。在 Java 中,动态代理主要有两种实现方式:JDK 动态代理和 CGLIB 动态代理。
JDK 动态代理的优点是不需要为每个目标类都创建一个代理类,代码的复用性较高。缺点是只能为实现了接口的目标类创建代理,对于没有实现接口的类无法使用 JDK 动态代理。
CGLIB 动态代理的优点是可以为没有实现接口的类创建代理,缺点是由于使用了继承,不能为 final 类或 final 方法创建代理。
JDK 动态代理
JDK 动态代理基于接口实现,要求目标类必须实现一个或多个接口。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 定义一个接口
interface Subject {
void request();
}
// 目标类,实现接口
class RealSubject implements Subject {
@Override
public void request() {
System.out.println("RealSubject: Handling request.");
}
}
// 实现InvocationHandler接口
class ProxyHandler implements InvocationHandler {
private Object target;
public ProxyHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在调用目标对象的方法之前可以进行一些预处理
System.out.println("ProxyHandler: Before handling request.");
Object result = method.invoke(target, args);
// 在调用目标对象的方法之后可以进行一些后处理
System.out.println("ProxyHandler: After handling request.");
return result;
}
}
// 客户端代码
public class JdkDynamicProxyDemo {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
ProxyHandler proxyHandler = new ProxyHandler(realSubject);
Subject proxySubject = (Subject) Proxy.newProxyInstance(
RealSubject.class.getClassLoader(),
RealSubject.class.getInterfaces(),
proxyHandler
);
proxySubject.request();
}
}
CGLIB 动态代理
CGLIB 动态代理基于继承实现,它可以为没有实现接口的类创建代理。
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
// 目标类
class RealSubject {
public void request() {
System.out.println("RealSubject: Handling request.");
}
}
// 实现MethodInterceptor接口
class ProxyInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// 在调用目标对象的方法之前可以进行一些预处理
System.out.println("ProxyInterceptor: Before handling request.");
Object result = proxy.invokeSuper(obj, args);
// 在调用目标对象的方法之后可以进行一些后处理
System.out.println("ProxyInterceptor: After handling request.");
return result;
}
}
// 客户端代码
public class CglibDynamicProxyDemo {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(RealSubject.class);
enhancer.setCallback(new ProxyInterceptor());
RealSubject proxySubject = (RealSubject) enhancer.create();
proxySubject.request();
}
}
适配器模式
在软件开发里,随着系统的不断发展和演化,会面临不同模块、组件或系统之间的兼容性问题。适配器模式的出现,就是为了解决这些兼容性问题,让原本因接口不兼容而无法一起工作的类能够协同合作。
在对既有系统进行升级或者扩展时,可能需要引入新的组件或者系统。但新老系统的接口设计可能不同,直接集成会面临困难。此时适配器模式就能在不修改原有系统代码的基础上,实现新老系统的兼容。比如,企业要将旧的库存管理系统与新的订单系统进行对接,而两个系统的接口不匹配,使用适配器模式就能解决这个问题。
当使用第三方提供的库或者组件时,这些库的接口可能和我们现有的系统接口不一致。要是修改第三方库的代码,不仅工作量大,还可能影响其稳定性和后续的更新。通过适配器模式,可以在现有的系统和第三方库之间建立一个适配层,使它们能够协同工作。例如,在开发一个图形绘制程序时,使用了一个第三方的图形库,但该库的绘图接口与程序现有的接口不同,这时就可以使用适配器模式来适配。
在软件的不同版本中,同一个功能模块的接口可能会发生变化。为了让新老版本的代码能够兼容,就可以使用适配器模式。比如,一个软件的旧版本使用的是某种数据格式和接口,新版本对这些进行了更新,为了让旧版本的代码在新版本的系统中也能正常工作,可以通过适配器模式来进行适配。
适配器模式的作用:适配器模式的核心作用就是将一个类的接口转换成客户端所期望的另一种接口,从而使原本因接口不兼容而无法合作的类能够一起工作。例如,将一个以公制单位进行数据处理的类,通过适配器转换成可以处理英制单位的类,以满足不同客户端的需求。
适配器的实现
类适配器模式:类适配器模式通过继承需要适配的类和实现目标接口来实现适配。 Target 是目标接口,Adaptee 是需要适配的类,ClassAdapter 是类适配器,它继承了 Adaptee 类并实现了 Target 接口,在 request 方法中调用了 Adaptee 类的 specificRequest 方法,从而实现了接口的适配。
// 目标接口
interface Target {
void request();
}
// 需要适配的类
class Adaptee {
public void specificRequest() {
System.out.println("Adaptee: Specific request.");
}
}
// 类适配器
class ClassAdapter extends Adaptee implements Target {
@Override
public void request() {
this.specificRequest();
}
}
// 客户端代码
public class ClassAdapterDemo {
public static void main(String[] args) {
Target target = new ClassAdapter();
target.request();
}
}
对象适配器模式:对象适配器模式通过组合需要适配的对象和实现目标接口来实现适配。 ObjectAdapter是对象适配器,它实现了Target接口,并在构造函数中接收一个Adaptee对象,在request方法中调用了该Adaptee对象的specificRequest` 方法,从而实现了接口的适配。
两种实现方式各有优缺点,类适配器模式由于使用了继承,可能会导致适配器类和被适配类之间的耦合度较高;而对象适配器模式使用组合,耦合度相对较低,并且更灵活,能适配多个不同的被适配类。在实际应用中,可根据具体情况选择合适的实现方式。
// 目标接口
interface Target {
void request();
}
// 需要适配的类
class Adaptee {
public void specificRequest() {
System.out.println("Adaptee: Specific request.");
}
}
// 对象适配器
class ObjectAdapter implements Target {
private Adaptee adaptee;
public ObjectAdapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
adaptee.specificRequest();
}
}
// 客户端代码
public class ObjectAdapterDemo {
public static void main(String[] args) {
Adaptee adaptee = new Adaptee();
Target target = new ObjectAdapter(adaptee);
target.request();
}
}
组合模式
在软件开发中,经常会遇到一些具有层次结构的对象,比如文件系统中的文件夹和文件、公司的组织架构(部门和员工)等。这些对象可以以树形结构进行组织,并且客户端在处理这些对象时,可能需要对单个对象和组合对象(由多个单个对象或子组合对象构成)进行统一的操作。如果不使用组合模式,客户端就需要针对单个对象和组合对象分别编写不同的处理逻辑,这会使代码变得复杂且难以维护。组合模式的出现就是为了解决这个问题,它可以让客户端以一致的方式处理单个对象和组合对象。
代码实现
import java.util.ArrayList;
import java.util.List;
// 抽象组件
interface Component {
void operation();
}
// 叶节点
class Leaf implements Component {
@Override
public void operation() {
System.out.println("Leaf: Performing operation.");
}
}
// 组合节点
class Composite implements Component {
private List
public void add(Component component) {
children.add(component);
}
public void remove(Component component) {
children.remove(component);
}
@Override
public void operation() {
System.out.println("Composite: Performing operation on children.");
for (Component child : children) {
child.operation();
}
}
}
// 客户端代码
public class CompositePatternDemo {
public static void main(String[] args) {
Leaf leaf1 = new Leaf();
Leaf leaf2 = new Leaf();
Composite composite = new Composite();
composite.add(leaf1);
composite.add(leaf2);
composite.operation();
}
}
装饰器模式
在软件开发中,有时需要在不改变现有对象结构的前提下,动态地给对象添加一些额外的功能。如果使用继承来实现这些额外功能,会导致子类数量急剧增加,代码变得复杂且难以维护。例如,一个图形类有多种不同的装饰需求(如添加边框、阴影等),如果每种装饰都通过继承来实现,会产生大量的子类。装饰器模式可以解决这个问题,它通过组合的方式来动态地给对象添加功能,避免了子类爆炸的问题
代码实现
// 抽象组件
interface Component {
void operation();
}
// 具体组件
class ConcreteComponent implements Component {
@Override
public void operation() {
System.out.println("ConcreteComponent: Performing basic operation.");
}
}
// 抽象装饰器
abstract class Decorator implements Component {
protected Component component;
public Decorator(Component component) {
this.component = component;
}
@Override
public void operation() {
component.operation();
}
}
// 具体装饰器
class ConcreteDecoratorA extends Decorator {
public ConcreteDecoratorA(Component component) {
super(component);
}
@Override
public void operation() {
super.operation();
System.out.println("ConcreteDecoratorA: Adding additional operation.");
}
}
// 客户端代码
public class DecoratorPatternDemo {
public static void main(String[] args) {
Component component = new ConcreteComponent();
Component decoratedComponent = new ConcreteDecoratorA(component);
decoratedComponent.operation();
}
}
外观模式
在一个复杂的系统中,可能包含多个子系统,每个子系统又有多个不同的类和方法。客户端在使用这些子系统时,需要了解各个子系统的细节和交互方式,这会增加客户端的使用难度和代码复杂度。外观模式的出现就是为了简化客户端与复杂系统之间的交互,它提供了一个统一的接口,客户端只需要通过这个接口来调用系统的功能,而不需要了解系统内部的具体实现。
代码实现
// 子系统类A
class SubsystemA {
public void operationA() {
System.out.println("SubsystemA: Performing operation A.");
}
}
// 子系统类B
class SubsystemB {
public void operationB() {
System.out.println("SubsystemB: Performing operation B.");
}
}
// 外观类
class Facade {
private SubsystemA subsystemA;
private SubsystemB subsystemB;
public Facade() {
this.subsystemA = new SubsystemA();
this.subsystemB = new SubsystemB();
}
public void operation() {
subsystemA.operationA();
subsystemB.operationB();
}
}
// 客户端代码
public class FacadePatternDemo {
public static void main(String[] args) {
Facade facade = new Facade();
facade.operation();
}
}
享元模式
在软件开发中,有时会创建大量的相似对象,这些对象可能会占用大量的内存资源。例如,在一个文本编辑器中,可能会有大量的字符对象,这些字符对象的很多属性(如字体、颜色等)是相同的,如果为每个字符都创建一个独立的对象,会导致内存开销过大。享元模式的出现就是为了减少对象的创建数量,共享相同的对象,从而节省内存资源。 通过上述示例可以看出,在享元模式中,当需要使用相同内部状态的享元对象时,会从享元工厂的对象池中获取,避免了重复创建对象,从而节省了内存资源。
import java.util.HashMap;
import java.util.Map;
// 抽象享元
interface Flyweight {
void operation(String extrinsicState);
}
// 具体享元
class ConcreteFlyweight implements Flyweight {
private String intrinsicState;
public ConcreteFlyweight(String intrinsicState) {
this.intrinsicState = intrinsicState;
}
@Override
public void operation(String extrinsicState) {
System.out.println("ConcreteFlyweight: Intrinsic state = " + intrinsicState + ", Extrinsic state = " + extrinsicState);
}
}
// 享元工厂
class FlyweightFactory {
private Map
public Flyweight getFlyweight(String key) {
if (!flyweights.containsKey(key)) {
flyweights.put(key, new ConcreteFlyweight(key));
}
return flyweights.get(key);
}
}
// 客户端代码
public class FlyweightPatternDemo {
public static void main(String[] args) {
FlyweightFactory factory = new FlyweightFactory();
Flyweight flyweight1 = factory.getFlyweight("A");
flyweight1.operation("State 1");
Flyweight flyweight2 = factory.getFlyweight("A");
flyweight2.operation("State 2");
Flyweight flyweight3 = factory.getFlyweight("B");
flyweight3.operation("State 3");
}
}
桥接模式
桥接模式是一种结构型设计模式,它的核心思想是将抽象部分与实现部分分离,使它们可以独立地变化。这里的 “抽象部分” 通常是指一些高层的、具有通用逻辑的类,而 “实现部分” 则是具体的实现细节。通过桥接模式,我们可以将这两部分解耦,让它们可以分别进行扩展和修改,而不会相互影响。
举个简单的例子,假如我们要设计一个图形绘制系统,图形有不同的形状(如圆形、矩形),同时这些图形又可以使用不同的颜色(如红色、蓝色)来绘制。传统的设计可能会为每种形状和颜色的组合创建一个具体的类,这样会导致类的数量急剧增加。而使用桥接模式,我们可以将形状和颜色这两个维度分离,通过组合的方式来创建不同的图形,从而减少类的数量,提高系统的可扩展性。
// 实现类接口
interface Color {
void applyColor();
}
// 具体实现类
class RedColor implements Color {
@Override
public void applyColor() {
System.out.println("Applying red color");
}
}
class BlueColor implements Color {
@Override
public void applyColor() {
System.out.println("Applying blue color");
}
}
// 抽象类
abstract class Shape {
protected Color color;
public Shape(Color color) {
this.color = color;
}
abstract void draw();
}
// 扩展抽象类
class Circle extends Shape {
public Circle(Color color) {
super(color);
}
@Override
void draw() {
System.out.print("Drawing a circle. ");
color.applyColor();
}
}
class Rectangle extends Shape {
public Rectangle(Color color) {
super(color);
}
@Override
void draw() {
System.out.print("Drawing a rectangle. ");
color.applyColor();
}
}
// 客户端代码
public class BridgePatternDemo {
public static void main(String[] args) {
// 创建红色圆形
Shape redCircle = new Circle(new RedColor());
redCircle.draw();
// 创建蓝色矩形
Shape blueRectangle = new Rectangle(new BlueColor());
blueRectangle.draw();
}
}
三、行为型模式
策略模式
在软件开发中,经常会遇到一个问题有多种不同的解决方案,并且在不同的场景下需要动态地切换这些方案。如果不使用策略模式,就可能会在代码中使用大量的条件判断语句来选择不同的解决方案,这样会使代码变得复杂、难以维护和扩展。策略模式的出现就是为了解决这个问题,它将每种解决方案封装成一个独立的策略类,使得这些策略类可以相互替换,从而避免了大量的条件判断。
// 策略接口
interface Strategy {
int doOperation(int num1, int num2);
}
// 具体策略类:加法
class AddStrategy implements Strategy {
@Override
public int doOperation(int num1, int num2) {
return num1 + num2;
}
}
// 具体策略类:减法
class SubtractStrategy implements Strategy {
@Override
public int doOperation(int num1, int num2) {
return num1 - num2;
}
}
// 上下文类
class Context {
private Strategy strategy;
public Context(Strategy strategy) {
this.strategy = strategy;
}
public int executeStrategy(int num1, int num2) {
return strategy.doOperation(num1, num2);
}
}
// 客户端代码
public class StrategyPatternDemo {
public static void main(String[] args) {
Context context = new Context(new AddStrategy());
System.out.println("10 + 5 = " + context.executeStrategy(10, 5));
context = new Context(new SubtractStrategy());
System.out.println("10 - 5 = " + context.executeStrategy(10, 5));
}
}
迭代器模式
在处理集合对象时,我们经常需要遍历集合中的元素。不同的集合可能有不同的内部结构,如数组、链表、树等,如果直接在客户端代码中实现遍历逻辑,会导致代码与集合的具体实现紧密耦合,并且当集合的实现发生变化时,客户端代码也需要相应地修改。迭代器模式的出现就是为了将集合的遍历逻辑从集合对象中分离出来,提供一种统一的方式来遍历不同类型的集合。
import java.util.ArrayList;
import java.util.List;
// 迭代器接口
interface Iterator {
boolean hasNext();
Object next();
}
// 具体迭代器类
class ConcreteIterator implements Iterator {
private List
private int index;
public ConcreteIterator(List
this.list = list;
this.index = 0;
}
@Override
public boolean hasNext() {
return index < list.size();
}
@Override
public Object next() {
if (hasNext()) {
return list.get(index++);
}
return null;
}
}
// 集合接口
interface Aggregate {
Iterator createIterator();
}
// 具体集合类
class ConcreteAggregate implements Aggregate {
private List
public void add(Object item) {
list.add(item);
}
@Override
public Iterator createIterator() {
return new ConcreteIterator(list);
}
}
// 客户端代码
public class IteratorPatternDemo {
public static void main(String[] args) {
ConcreteAggregate aggregate = new ConcreteAggregate();
aggregate.add("Item 1");
aggregate.add("Item 2");
aggregate.add("Item 3");
Iterator iterator = aggregate.createIterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
中介者模式
在一个系统中,对象之间可能存在着复杂的交互关系。如果这些对象直接相互引用和调用,会导致系统的耦合度非常高,难以维护和扩展。例如,在一个聊天室系统中,多个用户之间需要相互通信,如果每个用户都直接与其他用户进行交互,那么用户之间的关系会变得非常复杂。中介者模式的出现就是为了降低对象之间的耦合度,通过引入一个中介者对象,让对象之间的交互都通过中介者来进行。
// 中介者接口
interface Mediator {
void sendMessage(String message, Colleague colleague);
}
// 具体中介者类
class ConcreteMediator implements Mediator {
private Colleague colleague1;
private Colleague colleague2;
public void setColleague1(Colleague colleague1) {
this.colleague1 = colleague1;
}
public void setColleague2(Colleague colleague2) {
this.colleague2 = colleague2;
}
@Override
public void sendMessage(String message, Colleague colleague) {
if (colleague == colleague1) {
colleague2.receiveMessage(message);
} else {
colleague1.receiveMessage(message);
}
}
}
// 同事类
abstract class Colleague {
protected Mediator mediator;
public Colleague(Mediator mediator) {
this.mediator = mediator;
}
public abstract void sendMessage(String message);
public abstract void receiveMessage(String message);
}
// 具体同事类
class ConcreteColleague1 extends Colleague {
public ConcreteColleague1(Mediator mediator) {
super(mediator);
}
@Override
public void sendMessage(String message) {
mediator.sendMessage(message, this);
}
@Override
public void receiveMessage(String message) {
System.out.println("ConcreteColleague1 received: " + message);
}
}
class ConcreteColleague2 extends Colleague {
public ConcreteColleague2(Mediator mediator) {
super(mediator);
}
@Override
public void sendMessage(String message) {
mediator.sendMessage(message, this);
}
@Override
public void receiveMessage(String message) {
System.out.println("ConcreteColleague2 received: " + message);
}
}
// 客户端代码
public class MediatorPatternDemo {
public static void main(String[] args) {
ConcreteMediator mediator = new ConcreteMediator();
ConcreteColleague1 colleague1 = new ConcreteColleague1(mediator);
ConcreteColleague2 colleague2 = new ConcreteColleague2(mediator);
mediator.setColleague1(colleague1);
mediator.setColleague2(colleague2);
colleague1.sendMessage("Hello, colleague2!");
colleague2.sendMessage("Hi, colleague1!");
}
}
责任链模式
在软件开发中,有时一个请求可能需要经过多个处理者的处理,每个处理者都有自己的处理逻辑和处理条件。如果不使用责任链模式,可能会在代码中使用大量的条件判断语句来确定由哪个处理者来处理请求,这样会使代码变得复杂且难以维护。责任链模式的出现就是为了解决这个问题,它将多个处理者组成一个链,请求沿着这个链依次传递,直到有一个处理者能够处理该请求为止。
// 抽象处理者
abstract class Handler {
protected Handler nextHandler;
public void setNextHandler(Handler nextHandler) {
this.nextHandler = nextHandler;
}
public abstract void handleRequest(int request);
}
// 具体处理者
class ConcreteHandler1 extends Handler {
@Override
public void handleRequest(int request) {
if (request < 10) {
System.out.println("ConcreteHandler1 handled the request: " + request);
} else if (nextHandler != null) {
nextHandler.handleRequest(request);
}
}
}
class ConcreteHandler2 extends Handler {
@Override
public void handleRequest(int request) {
if (request >= 10 && request < 20) {
System.out.println("ConcreteHandler2 handled the request: " + request);
} else if (nextHandler != null) {
nextHandler.handleRequest(request);
}
}
}
class ConcreteHandler3 extends Handler {
@Override
public void handleRequest(int request) {
if (request >= 20) {
System.out.println("ConcreteHandler3 handled the request: " + request);
} else if (nextHandler != null) {
nextHandler.handleRequest(request);
}
}
}
// 客户端代码
public class ChainOfResponsibilityPatternDemo {
public static void main(String[] args) {
Handler handler1 = new ConcreteHandler1();
Handler handler2 = new ConcreteHandler2();
Handler handler3 = new ConcreteHandler3();
handler1.setNextHandler(handler2);
handler2.setNextHandler(handler3);
handler1.handleRequest(5);
handler1.handleRequest(15);
handler1.handleRequest(25);
}
}
命令模式
在软件开发中,有时需要将请求的发送者和接收者解耦,并且支持请求的排队、记录日志、撤销等操作。例如,在一个图形编辑软件中,用户的各种操作(如绘制图形、删除图形等)可以看作是一个个请求,如果直接在用户操作的代码中调用图形编辑的方法,会使代码的耦合度很高,并且难以实现撤销等功能。命令模式的出现就是为了解决这个问题,它将请求封装成一个对象,使得请求的发送者和接收者可以独立变化。
// 命令接口
interface Command {
void execute();
}
// 具体命令类
class ConcreteCommand implements Command {
private Receiver receiver;
public ConcreteCommand(Receiver receiver) {
this.receiver = receiver;
}
@Override
public void execute() {
receiver.action();
}
}
// 接收者
class Receiver {
public void action() {
System.out.println("Receiver is performing an action.");
}
}
// 调用者
class Invoker {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void executeCommand() {
command.execute();
}
}
// 客户端代码
public class CommandPatternDemo {
public static void main(String[] args) {
Receiver receiver = new Receiver();
Command command = new ConcreteCommand(receiver);
Invoker invoker = new Invoker();
invoker.setCommand(command);
invoker.executeCommand();
}
}
解释器模式
在软件开发中,有时需要对一些特定的语言或表达式进行解释和执行。例如,在一个简单的计算器程序中,需要对用户输入的数学表达式进行解析和计算。如果不使用解释器模式,可能会使用复杂的正则表达式或递归算法来实现表达式的解析和计算,这样会使代码变得复杂且难以维护。解释器模式的出现就是为了解决这个问题,它定义了一种语言的文法表示,并提供了一个解释器来解释和执行该语言的表达式。
import java.util.HashMap;
import java.util.Map;
// 抽象表达式
interface Expression {
int interpret(Map
}
// 终结符表达式
class VariableExpression implements Expression {
private String variable;
public VariableExpression(String variable) {
this.variable = variable;
}
@Override
public int interpret(Map
return context.getOrDefault(variable, 0);
}
}
// 非终结符表达式:加法表达式
class AddExpression implements Expression {
private Expression left;
private Expression right;
public AddExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret(Map
return left.interpret(context) + right.interpret(context);
}
}
// 客户端代码
public class InterpreterPatternDemo {
public static void main(String[] args) {
// 上下文
Map
context.put("a", 5);
context.put("b", 3);
// 表达式:a + b
Expression expression = new AddExpression(
new VariableExpression("a"),
new VariableExpression("b")
);
// 解释执行表达式
int result = expression.interpret(context);
System.out.println("Result: " + result);
}
}
观察者模式
import java.util.ArrayList;
import java.util.List;
// 主题接口
interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
// 具体主题类
class ConcreteSubject implements Subject {
private List
private int state;
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
notifyObservers();
}
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(state);
}
}
}
// 观察者接口
interface Observer {
void update(int state);
}
// 具体观察者类
class ConcreteObserver implements Observer {
@Override
public void update(int state) {
System.out.println("Observer received new state: " + state);
}
}
// 客户端代码
public class ObserverPatternDemo {
public static void main(String[] args) {
ConcreteSubject subject = new ConcreteSubject();
ConcreteObserver observer1 = new ConcreteObserver();
ConcreteObserver observer2 = new ConcreteObserver();
subject.registerObserver(observer1);
subject.registerObserver(observer2);
subject.setState(10);
}
}
状态模式
在软件开发中,有些对象的行为会随着其内部状态的变化而变化。如果不使用状态模式,可能会在代码中使用大量的条件判断语句来根据对象的状态来执行不同的行为,这样会使代码变得复杂且难以维护。状态模式的出现就是为了解决这个问题,它将对象的不同状态封装成不同的状态类,使得对象的行为可以根据其状态的变化而动态地改变。
// 抽象状态类
interface State {
void switchOn(Context context);
void switchOff(Context context);
}
// 具体状态类:开启状态
class OnState implements State {
@Override
public void switchOn(Context context) {
System.out.println("Already switched on.");
}
@Override
public void switchOff(Context context) {
System.out.println("Switching off.");
context.setState(new OffState());
}
}
// 具体状态类:关闭状态
class OffState implements State {
@Override
public void switchOn(Context context) {
System.out.println("Switching on.");
context.setState(new OnState());
}
@Override
public void switchOff(Context context) {
System.out.println("Already switched off.");
}
}
// 上下文类
class Context {
private State state;
public Context() {
this.state = new OffState();
}
public void setState(State state) {
this.state = state;
}
public void switchOn() {
state.switchOn(this);
}
public void switchOff() {
state.switchOff(this);
}
}
// 客户端代码
public class StatePatternDemo {
public static void main(String[] args) {
Context context = new Context();
context.switchOn();
context.switchOff();
context.switchOff();
}
}
模板模式
在软件开发中,有些算法的步骤是固定的,但其中某些步骤的具体实现可能会因不同的情况而有所不同。例如,在一个游戏开发中,游戏的启动流程可能包括加载资源、初始化界面、开始游戏等步骤,但不同的游戏在加载资源和初始化界面的具体实现上可能会有所不同。模板模式的出现就是为了解决这个问题,它定义了一个算法的骨架,将一些步骤的具体实现延迟到子类中。
// 抽象类
abstract class Game {
// 模板方法,定义了算法的骨架
public final void play() {
initialize();
startGame();
endGame();
}
// 抽象方法,由子类实现
protected abstract void initialize();
protected abstract void startGame();
protected abstract void endGame();
}
// 具体子类:足球游戏
class FootballGame extends Game {
@Override
protected void initialize() {
System.out.println("Football Game: Initializing the field and players.");
}
@Override
protected void startGame() {
System.out.println("Football Game: Starting the match.");
}
@Override
protected void endGame() {
System.out.println("Football Game: Ending the match.");
}
}
// 具体子类:篮球游戏
class BasketballGame extends Game {
@Override
protected void initialize() {
System.out.println("Basketball Game: Setting up the court and players.");
}
@Override
protected void startGame() {
System.out.println("Basketball Game: Starting the game.");
}
@Override
protected void endGame() {
System.out.println("Basketball Game: Ending the game.");
}
}
// 客户端代码
public class TemplatePatternDemo {
public static void main(String[] args) {
Game footballGame = new FootballGame();
footballGame.play();
System.out.println();
Game basketballGame = new BasketballGame();
basketballGame.play();
}
}
备忘录模式
在软件开发中,有时需要保存对象的某个状态,以便在需要的时候可以恢复到该状态。例如,在一个文本编辑器中,用户可能会进行撤销操作,这就需要保存文本编辑器的历史状态。如果不使用备忘录模式,可能会在代码中手动保存和恢复对象的状态,这样会使代码变得复杂且容易出错。备忘录模式的出现就是为了解决这个问题,它提供了一种机制来保存和恢复对象的状态,而不破坏对象的封装性。
// 备忘录
class Memento {
private String state;
public Memento(String state) {
this.state = state;
}
public String getState() {
return state;
}
}
// 原发器
class Editor {
private String content;
public void setContent(String content) {
this.content = content;
}
public String getContent() {
return content;
}
public Memento save() {
return new Memento(content);
}
public void restore(Memento memento) {
this.content = memento.getState();
}
}
// 管理者
class History {
private java.util.Stack
public void push(Memento memento) {
mementos.push(memento);
}
public Memento pop() {
return mementos.pop();
}
}
// 客户端代码
public class MementoPatternDemo {
public static void main(String[] args) {
Editor editor = new Editor();
History history = new History();
editor.setContent("Hello");
history.push(editor.save());
editor.setContent("Hello, World");
history.push(editor.save());
editor.setContent("Hello, Java");
System.out.println("Current content: " + editor.getContent());
// 撤销操作
editor.restore(history.pop());
System.out.println("After first undo: " + editor.getContent());
editor.restore(history.pop());
System.out.println("After second undo: " + editor.getContent());
}
}
访问者模式
在软件开发中,有时需要对一个对象结构中的不同类型的对象进行不同的操作。如果不使用访问者模式,可能会在对象的类中添加大量的操作方法,这样会使对象的类变得复杂且难以维护。访问者模式的出现就是为了解决这个问题,它将对象的操作和对象的结构分离,使得可以在不修改对象结构的情况下,为对象结构中的不同对象添加新的操作。
import java.util.ArrayList;
import java.util.List;
// 抽象访问者
interface Visitor {
void visit(Circle circle);
void visit(Rectangle rectangle);
}
// 具体访问者:计算面积
class AreaVisitor implements Visitor {
@Override
public void visit(Circle circle) {
double area = Math.PI * circle.getRadius() * circle.getRadius();
System.out.println("Circle area: " + area);
}
@Override
public void visit(Rectangle rectangle) {
double area = rectangle.getWidth() * rectangle.getHeight();
System.out.println("Rectangle area: " + area);
}
}
// 抽象元素
interface Element {
void accept(Visitor visitor);
}
// 具体元素:圆形
class Circle implements Element {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
public double getRadius() {
return radius;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
// 具体元素:矩形
class Rectangle implements Element {
private double width;
private double height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
public double getWidth() {
return width;
}
public double getHeight() {
return height;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
// 对象结构
class ObjectStructure {
private List
public void addElement(Element element) {
elements.add(element);
}
public void accept(Visitor visitor) {
for (Element element : elements) {
element.accept(visitor);
}
}
}
// 客户端代码
public class VisitorPatternDemo {
public static void main(String[] args) {
ObjectStructure objectStructure = new ObjectStructure();
objectStructure.addElement(new Circle(5));
objectStructure.addElement(new Rectangle(3, 4));
Visitor areaVisitor = new AreaVisitor();
objectStructure.accept(areaVisitor);
}
}
System.out.println("Circle area: " + area);
}
@Override
public void visit(Rectangle rectangle) {
double area = rectangle.getWidth() * rectangle.getHeight();
System.out.println("Rectangle area: " + area);
}
}
// 抽象元素 interface Element { void accept(Visitor visitor); }
// 具体元素:圆形 class Circle implements Element { private double radius;
public Circle(double radius) {
this.radius = radius;
}
public double getRadius() {
return radius;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
// 具体元素:矩形 class Rectangle implements Element { private double width; private double height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
public double getWidth() {
return width;
}
public double getHeight() {
return height;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
// 对象结构 class ObjectStructure { private List elements = new ArrayList<>();
public void addElement(Element element) {
elements.add(element);
}
public void accept(Visitor visitor) {
for (Element element : elements) {
element.accept(visitor);
}
}
}
// 客户端代码 public class VisitorPatternDemo { public static void main(String[] args) { ObjectStructure objectStructure = new ObjectStructure(); objectStructure.addElement(new Circle(5)); objectStructure.addElement(new Rectangle(3, 4));
Visitor areaVisitor = new AreaVisitor();
objectStructure.accept(areaVisitor);
}
}