java设计模式


前言

设计模式是在软件设计过程中反复出现的问题的解决方案的经验总结。Java中有许多经典的设计模式。

  1. 创建型模式
  2. 结构型模式
  3. 行为型模式

一、创建型模式

1. 单例模式(Singleton):

确保一个类只有一个实例,并提供全局访问点。单例模式在多线程环境下需要注意线程安全性,避免出现并发访问的问题。

(1) 懒汉式,通过私有化构造函数防止外部直接实例化对象,getInstance()方法通过检查instance变量是否为空来决定是创建新的实例还是返回现有实例。由于使用了synchronized关键字,该实现是线程安全的,但在高并发场景下可能会影响性能。

public class Singleton {
	private static Singleton instance;
	
	private Singleton() {
		// 私有化构造函数
	}

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

(2)饿汉式,实例对象在类加载时就被创建,因此可以保证线程安全,由于实例是在类加载时创建的,因此不存在并发访问的问题,不过这种方式可能会在程序启动时就创建实例,如果实例的创建和初始化过程较为复杂,可能会导致启动时间延长。

public class Singleton {
	private static final Singleton instance = new Singleton();	

	private Singleton() {
		// 私有构造函数
	}

	public static Singleton getInstance() {
		return instance;
	}
}

(3)双重检查锁定,在懒汉式的基础上进行了优化,通过两次检查instance变量来保证线程安全,并使用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;
	}
}

注:volatile关键字,当一个变量被声明为volatile,意味着它的值可能会被多个线程同时修改,因此需要特殊的内存可见性保证和禁止指令重排序。使用 volatile 关键字修饰的变量具备:内存可见性,当一个线程修改了 volatile 变量的值,该变量的最新值会立即被写入主内存中,同时其他线程读取该变量时会从主内存中获取最新值,而不是使用线程自己的缓存副本。这样可以确保多个线程之间对该变量的读写操作是一致的。禁止指令重排序,编译器和处理器在执行指令时可能会对指令进行重排序优化,但对于被 volatile 修饰的变量,编译器和处理器会保证指令不会被重排序到 volatile 变量的读写操作之前或之后,从而避免出现意想不到的结果。需要注意的是,虽然 volatile 关键字能够保证变量的可见性和禁止指令重排序,但它并不能保证原子性。对于复合操作(例如自增、自减等),仍然需要额外的同步手段(如锁或原子类)来保证原子性操作。在多线程编程中,使用 volatile 关键字可以确保变量的可见性,避免了数据的脏读和一致性问题。常见的应用场景包括作为状态标志、控制线程终止等。

(4) 静态内部类,利用了类加载机制和类的初始化特性来实现延迟加载和线程安全。当getInstance()方法第一次被调用时,会触发SingletonHolder类的加载,从而创建Singleton的实例,由于类加载是线程安全的,所以实现了线程安全的延迟加载。

public class Singleton {
	private Singleton() {
		// 私有构造函数
	}

	private static class SingletonHolder {
		private static final Singleton instance = new Singleton();
	}
	
	public static Singleton getInstance() {
		return SingletonHolder.instance;
	}
}

2. 工厂模式(Factory):

通过工厂类创建对象,而不是直接实例化对象。用于创建对象的过程封装和解耦,通过定义一个公共的接口或抽象类来创建对象,而不需要暴露具体的实例化逻辑给客户端。

包含以下关键角色:
(1) 抽象产品,定义了产品的共同接口或抽象类,具体产品类将实现这个接口或继承这个抽象类
(2) 具体产品,实现了抽象产品接口或继承抽象产品类,是工厂模式创建的目标对象
(3) 抽象工厂,定义了创建产品的方法,可以是接口或抽象类,通常包含一个或多个工厂方法,用于创建具体产品的对象
(4) 具体工厂,实现抽象工厂的工厂方法,负责实例化具体产品对象

// 抽象产品:汽车
public interface Car {
	void drive();
}
// 具体产品:SUV汽车
public class SuvCar implements Car {
	@Override
	public void drive(){
		System.out.println("Driving SUV car");
	}
}
// 具体产品:轿车
public class SedanCar implements Car {
	@Override
	public void drive() {
		System.out.println("Driving sedan car");
	}
}
// 抽象工厂:汽车工厂
public interface CarFactory {
	Car createCar();
}
// 具体工厂:SUV汽车工厂
public class SuvCarFactory implements CarFactory {
	@Override
	public Car createCar() {
		return new SuvCar();
	}
}
// 具体工厂:轿车工厂
public class SedanCarFactory implements CarFactory {
	@Override
	public Car createCar() {
		return new SedanCar();
	}
}

使用工厂模式的客户端代码如下:通过工厂对象的工厂方法来创建具体产品对象,而无需关心具体产品的实例化过程

public class Clinet {
	public static void main(String[] args) {
		CarFactory factory = new SuvCarFactory();
		Car car = factory.createCar();
		car.drive();// 输出:Driving SUV car

		factory = new SendanCarFactory();
		car = factory.createCar();
		car.drive();// 输出:Driving sedan car
}

3. 抽象工厂模式(Abstract Factory):

提供一个创建一系列相关或相互依赖对象的接口,而无需指定具体类。

包含关键角色:
(1) 抽象工厂:定义了创建一组相关产品对象的接口
(2) 具体工厂:实现了抽象工厂接口,负责创建具体产品对象
(3) 抽象产品:定义了一组产品对象的接口
(4) 具体产品:实现了抽象产品接口,具体工厂创建的目标产品
以一个简单的示例来说明抽象工厂模式的用法,假设有一个手机工厂,可以生产不同品牌的手机和对应的操作系统:

// 抽象产品:手机
public interface Phone {
    void makeCall();
}

// 具体产品:小米手机
public class XiaomiPhone implements Phone {
    @Override
    public void makeCall() {
        System.out.println("Making call with Xiaomi phone");
    }
}

// 具体产品:华为手机
public class HuaweiPhone implements Phone {
    @Override
    public void makeCall() {
        System.out.println("Making call with Huawei phone");
    }
}

// 抽象产品:操作系统
public interface OperatingSystem {
    void run();
}

// 具体产品:MIUI操作系统
public class MIUI implements OperatingSystem {
    @Override
    public void run() {
        System.out.println("Running MIUI operating system");
    }
}

// 具体产品:EMUI操作系统
public class EMUI implements OperatingSystem {
    @Override
    public void run() {
        System.out.println("Running EMUI operating system");
    }
}

// 抽象工厂:手机工厂
public interface PhoneFactory {
    Phone createPhone();
    OperatingSystem createOperatingSystem();
}

// 具体工厂:小米手机工厂
public class XiaomiFactory implements PhoneFactory {
    @Override
    public Phone createPhone() {
        return new XiaomiPhone();
    }

    @Override
    public OperatingSystem createOperatingSystem() {
        return new MIUI();
    }
}

// 具体工厂:华为手机工厂
public class HuaweiFactory implements PhoneFactory {
    @Override
    public Phone createPhone() {
        return new HuaweiPhone();
    }

    @Override
    public OperatingSystem createOperatingSystem() {
        return new EMUI();
    }
}

使用抽象工厂模式的客户端代码如下:

public class Client {
    public static void main(String[] args) {
        PhoneFactory factory = new XiaomiFactory();
        Phone phone = factory.createPhone();
        OperatingSystem os = factory.createOperatingSystem();

        phone.makeCall(); // 输出:Making call with Xiaomi phone
        os.run(); // 输出:Running MIUI operating system

        factory = new HuaweiFactory();
        phone = factory.createPhone();
        os = factory.createOperatingSystem();

        phone.makeCall(); // 输出:Making call with Huawei phone
        os.run(); // 输出:Running EMUI operating system
    }
}

客户端通过抽象工厂对象的工厂方法来创建一组相关的产品对象,从而实现了产品的簇创建,确保了产品之间的兼容性。抽象工厂模式可以很方便地扩展和切换不同的产品系列,但也增加了系统的复杂性。

4. 建造者模式(Builder):

将一个复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。目的是通过一步一步地构建一个复杂对象,将对象的构建过程封装在一个指导者(Director)类中,由指导者按照特定的构建步骤调用建造者(Builder)来构建最终的产品。通过这种方式,客户端可以通过指导者来创建一个对象,而无需了解具体的构建过程和细节。

建造者模式包含以下几个关键角色:
(1) 产品(Product):表示被构建的复杂对象。
(2)抽象建造者(Abstract Builder):定义了构建产品的抽象方法,具体建造者需要实现这些方法来构建产品的各个部分。
(3)具体建造者(Concrete Builder):实现了抽象建造者接口,具体实现了产品各个部分的构建过程。
(4)指导者(Director):负责使用建造者构建最终的产品对象,定义了具体构建过程的调用顺序和步骤。

// 产品:电脑
public class Computer {
    private String cpu;
    private String memory;
    private String hardDrive;
    
    public void setCpu(String cpu) {
        this.cpu = cpu;
    }
    
    public void setMemory(String memory) {
        this.memory = memory;
    }
    
    public void setHardDrive(String hardDrive) {
        this.hardDrive = hardDrive;
    }
    
    // 其他属性和方法...
}

// 抽象建造者:电脑建造者
public abstract class ComputerBuilder {
    protected Computer computer;
    
    public Computer getComputer() {
        return computer;
    }
    
    public void createNewComputer() {
        computer = new Computer();
    }
    
    public abstract void buildCpu();
    public abstract void buildMemory();
    public abstract void buildHardDrive();
    
    // 其他方法...
}

// 具体建造者:高配电脑建造者
public class HighEndComputerBuilder extends ComputerBuilder {
    @Override
    public void buildCpu() {
        computer.setCpu("Intel i9");
    }
    
    @Override
    public void buildMemory() {
        computer.setMemory("32GB");
    }
    
    @Override
    public void buildHardDrive() {
        computer.setHardDrive("1TB SSD");
    }
    
    // 其他方法...
}

// 具体建造者:低配电脑建造者
public class LowEndComputerBuilder extends ComputerBuilder {
    @Override
    public void buildCpu() {
        computer.setCpu("Intel i3");
    }
    
    @Override
    public void buildMemory() {
        computer.setMemory("8GB");
    }
    
    @Override
    public void buildHardDrive() {
        computer.setHardDrive("256GB SSD");
    }
    
    // 其他方法...
}

// 指导者:电脑销售员
public class ComputerSalesman {
    private ComputerBuilder computerBuilder;
    
    public void setComputerBuilder(ComputerBuilder builder) {
        computerBuilder = builder;
    }
    
    public Computer getComputer() {
        return computerBuilder.getComputer();
    }
    
    public void constructComputer() {
        computerBuilder.createNewComputer();
        computerBuilder.buildCpu();
        computerBuilder.buildMemory();
        computerBuilder.buildHardDrive();
        
        // 其他构建步骤...
    }
}


在上述示例中,Computer 是**产品类**,表示被构建的复杂对象。ComputerBuilder 是**抽象建造者类**,定义了构建产品的抽象方法。HighEndComputerBuilder 和 LowEndComputerBuilder 是**具体建造者类**,实现了抽象建造者接口,具体实现了产品各个部分的构建过程。ComputerSalesman 是**指导者**类,负责调用具体建造者来构建最终的产品对象。

使用建造者模式的客户端代码如下:

public class Client {
    public static void main(String[] args) {
        ComputerSalesman salesman = new ComputerSalesman();
        
        // 构建高配电脑
        ComputerBuilder highEndBuilder = new HighEndComputerBuilder();
        salesman.setComputerBuilder(highEndBuilder);
        salesman.constructComputer();
        Computer highEndComputer = salesman.getComputer();
        
        // 构建低配电脑
        ComputerBuilder lowEndBuilder = new LowEndComputerBuilder();
        salesman.setComputerBuilder(lowEndBuilder);
        salesman.constructComputer();
        Computer lowEndComputer = salesman.getComputer();
        
        // 使用构建好的电脑对象...
    }
}

建造者模式将复杂对象的构建过程与其表示分离,使得客户端可以通过指导者来统一构建对象,从而避免了构建过程的复杂性和重复性。同时,由于建造者模式将构建过程封装在具体建造者类中,可以方便地扩展和修改构建过程,使得建造者模式具有较高的灵活性和可维护性。

5. 原型模式(Prototype):

通过复制现有对象来创建新对象,而无需依赖显式的构造函数或工厂方法。原型模式基于对象的克隆来创建新对象,既可以是浅克隆(复制对象的字段值),也可以是深克隆(同时复制对象的引用类型字段)。

包含以下关键角色:
(1) 原型接口(Prototype):定义了克隆方法 clone,所有具体原型类都实现了该接口。
(2)具体原型类(Concrete Prototype):实现了原型接口,并提供了克隆方法的具体实现。
(3)客户端(Client):使用原型接口的克隆方法来创建新的对象。
在 Java 中,对象的克隆可以通过实现 Cloneable 接口并重写 clone 方法来实现。

// 原型接口:图形
public interface Shape extends Cloneable {
    void draw();
    Shape clone();
}

// 具体原型类:矩形
public class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a rectangle");
    }
    
    @Override
    public Shape clone() {
        try {
            return (Shape) super.clone();
        } catch (CloneNotSupportedException e) {
            return null;
        }
    }
}

// 客户端
public class Client {
    public static void main(String[] args) {
        Shape rectangle = new Rectangle();
        Shape clonedRectangle = rectangle.clone();
        
        rectangle.draw(); // 输出:Drawing a rectangle
        clonedRectangle.draw(); // 输出:Drawing a rectangle
    }
}
在上述示例中,Shape 是原型接口,定义了 draw 方法和 clone 方法。Rectangle 是具体原型类,实现了 Shape 接口,并在 clone 方法中通过调用 super.clone() 来实现克隆。客户端通过调用原型对象的 clone 方法来创建新的对象。

Cloneable与clone():
(1) Cloneable 接口是 Java 中的一个标记接口(marker interface),它表明一个类的对象可以被克隆。该接口没有任何方法,只是作为一个标记,用于告诉 Java 运行时系统该类可以进行克隆操作。
(2) Cloneable 接口本身没有定义 clone 方法。实际上,clone 方法是定义在 Object 类中的,但是它是受保护的方法,因此需要在具体的类中进行重写。
(3)在实现类中,需要重写 clone 方法,并将其访问修饰符改为 public。重写的 clone 方法必须调用 super.clone() 方法来执行对象的浅克隆,然后根据需要进行深克隆。
(4)clone 方法返回的是一个 Object 类型的对象,因此在具体的实现类中,需要将返回类型强制转换为该类的类型。
(5)在使用 clone 方法进行对象克隆时,可以通过调用 super.clone() 实现浅克隆,也可以根据需要对引用类型进行深克隆。

二、结构型模式

1.适配器模式(Adapter):

将一个类的接口转换成客户希望的另一个接口。适配器模式使得原本由于接口不兼容而无法一起工作的类能够协同工作。
包含以下关键角色:
(1)目标接口:客户端所期望使用的接口,适配器将被适配者适配成该接口。
(2)适配者类:需要被适配的类,它已经实现了客户端不需要的接口。
(3)适配器类:将适配者适配成目标接口的类,它实现了目标接口,并且持有适配者的实例。

使用适配器模式的关键在于适配器类的实现。适配器类需要将适配者的接口转换成目标接口,以满足客户端的需求。在适配器类中,可以通过适配者的实例来调用适配者的方法,并在适配器类的目标接口方法中进行适配处理。

// 目标接口:中国插座
public interface ChinaSocket {
    void powerSupply();
}

// 适配者类:英国插座
public class UKSocket {
    public void powerOn() {
        System.out.println("Power is on in the UK");
    }
}

// 适配器类:适配英国插座为中国插座
public class SocketAdapter implements ChinaSocket {
    private UKSocket ukSocket;
    
    public SocketAdapter(UKSocket ukSocket) {
        this.ukSocket = ukSocket;
    }
    
    @Override
    public void powerSupply() {
        ukSocket.powerOn(); // 调用适配者类的方法
    }
}

// 客户端
public class Client {
    public static void main(String[] args) {
        UKSocket ukSocket = new UKSocket();
        ChinaSocket chinaSocket = new SocketAdapter(ukSocket);
        
        chinaSocket.powerSupply(); // 输出:Power is on in the UK
    }
}
ChinaSocket 是目标接口,定义了中国插座的供电方法 powerSupply。UKSocket 是适配者类,实现了英国插座的供电方法 powerOn。SocketAdapter 是适配器类,实现了目标接口 ChinaSocket,并在 powerSupply 方法中调用了适配者类的方法。客户端通过创建适配器对象,并调用目标接口的方法来实现对适配者的适配。

适配器模式可以帮助解决接口不兼容的问题,使得原本无法协同工作的类能够一起工作。它可以在不修改适配者类的情况下实现适配,保持原有代码的稳定性和兼容性。适配器模式常用于集成不同系统、框架或第三方库的场景中,用于简化接口调用和提高系统的灵活性。

2.装饰器模式(Decorator):

动态地给对象添加额外的责任,同时又不改变其结构。通过将对象包装在一个装饰器类中,来给对象添加新的功能或责任,同时保持原有对象的接口不变。
包含角色:
(1)抽象构件(Component):定义了原始对象和装饰器对象的共同接口,可以是一个接口或抽象类
(2)具体构件(Concrete Component):实现了抽象构件接口,是被装饰的原始对象
(3)抽象装饰器(Decorator):继承自抽象构件,持有一个抽象构件类型的引用,定义了装饰器的共同接口
(4)具体装饰器(Concrete Decorator):继承自抽象装饰器,实现了具体的装饰逻辑,可以在被装饰对象的行为前后添加额外的功能。

使用装饰器模式时,可以通过创建不同的具体装饰器来动态地给对象添加新的功能。装饰器类可以嵌套使用,形成一条装饰器链,每个装饰器在原有功能的基础上添加自己的功能。

// 抽象构件:咖啡
public interface Coffee {
    String getDescription();
    double getCost();
}

// 具体构件:浓缩咖啡
public class Espresso implements Coffee {
    @Override
    public String getDescription() {
        return "Espresso";
    }
    
    @Override
    public double getCost() {
        return 1.99;
    }
}

// 抽象装饰器:咖啡配料
public abstract class CoffeeDecorator implements Coffee {
    protected Coffee coffee;
    
    public CoffeeDecorator(Coffee coffee) {
        this.coffee = coffee;
    }
    
    @Override
    public String getDescription() {
        return coffee.getDescription();
    }
    
    @Override
    public double getCost() {
        return coffee.getCost();
    }
}

// 具体装饰器:牛奶
public class MilkDecorator extends CoffeeDecorator {
    public MilkDecorator(Coffee coffee) {
        super(coffee);
    }
    
    @Override
    public String getDescription() {
        return super.getDescription() + ", Milk";
    }
    
    @Override
    public double getCost() {
        return super.getCost() + 0.5;
    }
}

// 具体装饰器:糖浆
public class SyrupDecorator extends CoffeeDecorator {
    public SyrupDecorator(Coffee coffee) {
        super(coffee);
    }
    
    @Override
    public String getDescription() {
        return super.getDescription() + ", Syrup";
    }
    
    @Override
    public double getCost() {
        return super.getCost() + 0.3;
    }
}

// 客户端
public class Client {
    public static void main(String[] args) {
        Coffee espresso = new Espresso();
        Coffee milkCoffee = new MilkDecorator(espresso);
        Coffee syrupMilkCoffee = new SyrupDecorator(milkCoffee);
        
        System.out.println(syrupMilkCoffee.getDescription()); // 输出: Espresso, Milk, Syrup
        System.out.println(syrupMilkCoffee.getCost()); // 输出: 2.79
    }
}

在上述示例中,Coffee 是抽象构件接口,定义了咖啡的描述和价格。Espresso 是具体构件类,实现了抽象构件接口,表示浓缩咖啡。CoffeeDecorator 是抽象装饰器类,继承自抽象构件接口,持有一个抽象构件类型的引用,并实现了共同的装饰逻辑。MilkDecorator 和 SyrupDecorator 是具体装饰器类,继承自抽象装饰器类,分别添加了牛奶和糖浆的额外功能。客户端可以根据需要选择不同的装饰器来包装原始的咖啡对象,从而得到带有不同配料的咖啡描述和价格。

装饰器模式允许在不改变现有对象结构的情况下,动态地给对象添加功能。它提供了一种灵活的方式来扩展对象的功能,并且遵循开闭原则,使得系统更易于维护和扩展。

3.代理模式(Proxy):

通过代理对象控制对真实对象的访问,并可以在访问前后进行一些额外操作。使用代理模式时,客户端并不直接访问实际对象,而是通过代理对象来间接访问。代理对象可以隐藏实际对象的具体实现细节,还可以在访问前后添加一些额外的逻辑,例如权限验证、缓存、延迟加载等。通过代理模式,可以实现对实际对象的控制和扩展,同时将客户端与实际对象解耦。包含以下几个关键角色:
(1)抽象主题(Subject):定义了真实主题和代理主题的共同接口,客户端通过该接口访问实际对象
(2)真实主题(Real Subject):实际的业务对象,客户端最终想要访问的对象
(3)代理(Proxy):代理对象,持有对真实主题对象的引用,并实现了抽象主题接口,在客户端访问时,代理对象可以在调用真实主题之前或之后进行一些额外的操作

// 抽象主题
interface Image {
    void display();
}

// 真实主题
class RealImage implements Image {
    private String filename;
    
    public RealImage(String filename) {
        this.filename = filename;
        loadFromDisk();
    }
    
    private void loadFromDisk() {
        System.out.println("Loading image from disk: " + filename);
    }
    
    @Override
    public void display() {
        System.out.println("Displaying image: " + filename);
    }
}

// 代理
class ProxyImage implements Image {
    private String filename;
    private RealImage realImage;
    
    public ProxyImage(String filename) {
        this.filename = filename;
    }
    
    @Override
    public void display() {
        if (realImage == null) {
            realImage = new RealImage(filename);
        }
        realImage.display();
    }
}

// 客户端
public class Client {
    public static void main(String[] args) {
        Image image1 = new ProxyImage("image1.jpg");
        Image image2 = new ProxyImage("image2.jpg");
        
        image1.display(); // 实际对象被延迟加载
        image1.display(); // 实际对象已加载,直接显示
        image2.display(); // 实际对象被延迟加载
    }
}
在上述示例中,Image 是抽象主题接口,定义了图片显示的方法 display()。RealImage 是真实主题类,实现了抽象主题接口,表示实际的图片加载和显示。ProxyImage 是代理类,也实现了抽象主题接口,它在调用真实主题的 display() 方法之前先判断是否已经加载过实际对象,如果没有则创建实际对象并加载,然后调用实际对象的 display() 方法。

在客户端中,通过创建代理对象来访问图片,第一次访问时代理对象会创建实际对象并加载图片,后续访问则直接显示已加载的图片。代理对象实现了延迟加载的功能,将实际对象的创建和加载延迟到实际需要访问时才执行,从而提高了系统的性能和效率。

代理模式可以应用于许多场景,例如远程代理、虚拟代理、安全代理等,以实现不同的功能和控制。

4.外观模式(Facade):

它提供了一个统一的接口,用于访问一组复杂子系统的功能。外观模式通过定义一个高层接口,封装了子系统的复杂性,使得客户端可以通过简单的接口与子系统进行交互,而不需要了解子系统的内部实现细节。包含以下几个关键角色:
(1)外观(Facade):外观对象是外部客户端访问子系统的入口点,它知道哪些子系统负责处理客户端请求,将客户端的请求转发给相应的子系统处理。
(2)子系统(Subsystem):子系统是实际处理客户端请求的组件或模块,外观对象将客户端的请求委派给相应的子系统进行处理。
外观模式的优点包括:
(1)简化客户端代码:外观模式提供了一个简单的接口,客户端可以通过该接口与子系统进行交互,无需了解子系统的复杂性和细节。
(2)解耦客户端和子系统:外观模式将客户端与子系统之间的依赖关系解耦,客户端只需要依赖外观对象即可,不需要直接依赖子系统的组件。
(3)提供了灵活性和扩展性:外观模式通过外观对象作为中间层,使得子系统的变化对客户端的影响降到最低,同时也方便对外观对象进行扩展,以满足不同的需求。

// 子系统A
class SubsystemA {
    public void operationA() {
        System.out.println("Subsystem A operation");
    }
}

// 子系统B
class SubsystemB {
    public void operationB() {
        System.out.println("Subsystem B operation");
    }
}

// 外观
class Facade {
    private SubsystemA subsystemA;
    private SubsystemB subsystemB;
    
    public Facade() {
        subsystemA = new SubsystemA();
        subsystemB = new SubsystemB();
    }
    
    public void operation() {
        subsystemA.operationA();
        subsystemB.operationB();
    }
}

// 客户端
public class Client {
    public static void main(String[] args) {
        Facade facade = new Facade();
        facade.operation();
    }
}
在上述示例中,SubsystemA 和 SubsystemB 分别表示两个子系统,它们具有不同的功能和实现。Facade 是外观对象,负责将客户端的请求转发给子系统的相应组件。客户端只需要与外观对象进行交互,调用外观对象的 operation() 方法,而无需直接与子系统的组件进行交互。

5.桥接模式(Bridge):

将抽象部分与它的实现部分分离,使它们可以独立地变化。桥接模式的目的是在不同维度上进行解耦,将抽象和实现之间建立一个桥梁以便它们可以独立地进行变化和扩展。包含以下关键角色:
(1)抽象类(Abstraction):抽象类定义了抽象部分的接口,并维护了一个对实现类的引用。抽象类可以是抽象类、接口或具体类
(2)具体类(Concrete Abstraction):具体类继承或实现了抽象类,并实现了抽象部分的接口。
(3)实现类接口(Implementor):实现类接口定义了实现部分的接口,它不依赖于抽象部分的接口。
(4)具体实现类(Concrete Implementor):具体实现类实现了实现类接口,并提供了具体的实现。
使用桥接模式时,抽象部分和实现部分可以独立地进行变化和扩展。抽象类通过引用实现类接口的方式来委托实现部分的操作。这样,抽象部分和实现部分可以独立地扩展和变化,而且它们之间的关系通过桥接的方式连接起来。

// 实现类接口
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 Client {
    public static void main(String[] args) {
        Shape redCircle = new Circle(new RedColor());
        redCircle.draw();
        
        Shape blueRectangle = new Rectangle(new BlueColor());
        blueRectangle.draw();
    }
}

在上述示例中,Color 是实现类接口,定义了颜色的操作方法。RedColor 和 BlueColor 是具体实现类,实现了颜色的具体操作。Shape 是抽象类,它引用了 Color 接口,并定义了绘制形状的抽象方法 draw()。Circle 和 Rectangle 是具体类,它们继承自抽象类 Shape,并实现了绘制形状的具体方法。在绘制形状时,它们调用了 Color 接口的 applyColor() 方法,实现了形状和颜色的组合。

三、行为型模式

1.观察者模式(Observer):

定义了一种一对多的依赖关系,使得当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。观察者模式包含以下几个关键角色:
(1)主题(Subject):主题是被观察的对象,它维护了一个观察者列表,并提供了用于添加、删除和通知观察者的方法。
(2)观察者(Observer):观察者是依赖于主题的对象,它定义了一个更新的方法,在接收到主题通知时进行相应的处理。
使用观察者模式时,主题对象和观察者对象之间是松耦合的,它们之间通过订阅和通知的方式进行交互。主题对象负责管理观察者对象的注册和通知,当主题对象的状态发生变化时,它会遍历观察者列表,依次通知每个观察者进行更新。

import java.util.ArrayList;
import java.util.List;

// 主题接口
interface Subject {
    void registerObserver(Observer observer);
    void removeObserver(Observer observer);
    void notifyObservers();
}

// 观察者接口
interface Observer {
    void update(String message);
}

// 具体主题
class ConcreteSubject implements Subject {
    private List<Observer> observers = new ArrayList<>();
    private String message;
    
    @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(message);
        }
    }
    
    public void setMessage(String message) {
        this.message = message;
        notifyObservers();
    }
}

// 具体观察者
class ConcreteObserver implements Observer {
    private String name;
    
    public ConcreteObserver(String name) {
        this.name = name;
    }
    
    @Override
    public void update(String message) {
        System.out.println(name + " received message: " + message);
    }
}

// 客户端
public class Client {
    public static void main(String[] args) {
        ConcreteSubject subject = new ConcreteSubject();
        
        Observer observer1 = new ConcreteObserver("Observer 1");
        Observer observer2 = new ConcreteObserver("Observer 2");
        
        subject.registerObserver(observer1);
        subject.registerObserver(observer2);
        
        subject.setMessage("Hello, observers!");
        
        subject.removeObserver(observer1);
        
        subject.setMessage("Observer 1 is removed");
    }
}

在上述示例中,Subject 是主题接口,定义了注册、移除和通知观察者的方法。Observer 是观察者接口,定义了更新的方法。ConcreteSubject 是具体主题类,实现了主题接口,并维护了观察者列表。在状态变化时,它通过调用观察者的 update() 方法来通知观察者进行更新。ConcreteObserver 是具体观察者类,实现了观察者接口,并在收到主题通知时输出相应的消息。

2.策略模式(Strategy):

定义一系列算法,将每个算法都封装起来,并使它们之间可以互换。运行时根据需要选择不同的策略来执行特定的任务,从而使算法的变化独立于使用算法的客户端。包含以下关键角色:
(1)策略接口:定义了一个共同的方法或行为,所有具体策略类都必须实现该接口
(2)具体策略类:实现了策略接口,提供了具体的算法实现
(3)上下文:包含一个对策略接口的引用,提供了设置和获取策略的方法,上下文对象可以根据需要选择不同的策略来执行特定的任务。

// 策略接口
interface SortingStrategy {
    void sort(int[] array);
}

// 具体策略类
class BubbleSortStrategy implements SortingStrategy {
    @Override
    public void sort(int[] array) {
        System.out.println("Sorting using bubble sort");
        // 实现具体的冒泡排序算法
    }
}

// 具体策略类
class QuickSortStrategy implements SortingStrategy {
    @Override
    public void sort(int[] array) {
        System.out.println("Sorting using quick sort");
        // 实现具体的快速排序算法
    }
}

// 上下文
class SortContext {
    private SortingStrategy strategy;
    
    public void setStrategy(SortingStrategy strategy) {
        this.strategy = strategy;
    }
    
    public void performSort(int[] array) {
        strategy.sort(array);
    }
}

// 客户端
public class Client {
    public static void main(String[] args) {
        SortContext context = new SortContext();
        
        int[] array = {5, 2, 8, 1, 9};
        
        SortingStrategy bubbleSortStrategy = new BubbleSortStrategy();
        context.setStrategy(bubbleSortStrategy);
        context.performSort(array);
        
        SortingStrategy quickSortStrategy = new QuickSortStrategy();
        context.setStrategy(quickSortStrategy);
        context.performSort(array);
    }
}
在上述示例中,SortingStrategy 是策略接口,定义了排序算法的方法。BubbleSortStrategy 和 QuickSortStrategy 是具体策略类,它们实现了策略接口,并提供了具体的排序算法实现。SortContext 是上下文类,它包含一个对策略接口的引用,并提供了设置策略和执行排序的方法。

3.命令模式(Command):

将一个请求封装成一个对象,从而使得请求的发送者和接收者解耦,命令模式允许请求的发送者与具体的请求接收者无需直接交互,而是通过调用命令对象来间接执行请求,包含角色:
(1)命令接口:定义了执行请求的方法,所有具体命令类都需实现该接口
(2)具体命令类:实现了命令接口,并封装了具体的请求操作。它包含了一个对请求接收者的引用,通过调用请求接收者的方法来执行请求。
(3)请求接收者:是执行实际请求操作的对象,定义了具体的操作方法。
(4)调用者:持有一个命令对象,并在需要执行请求时调用命令对象的执行方法。

命令模式的优点包括:
(1)解耦请求发送者和接收者:命令模式将请求封装在命令对象中,使得请求的发送者和接收者解耦,它们不需要直接交互,降低了系统的耦合性。
(2)容易扩展新的命令:可以通过添加新的具体命令类来扩展系统的功能,而无需修改已有的代码。

// 命令接口
interface Command {
    void execute();
}

// 具体命令类
class LightOnCommand implements Command {
    private Light light;
    
    public LightOnCommand(Light light) {
        this.light = light;
    }
    
    @Override
    public void execute() {
        light.turnOn();
    }
}

// 请求接收者
class Light {
    public void turnOn() {
        System.out.println("Light is turned on");
    }
    
    public void turnOff() {
        System.out.println("Light is turned off");
    }
}

// 调用者
class RemoteControl {
    private Command command;
    
    public void setCommand(Command command) {
        this.command = command;
    }
    
    public void pressButton() {
        command.execute();
    }
}

// 客户端
public class Client {
    public static void main(String[] args) {
        RemoteControl remoteControl = new RemoteControl();
        
        Light light = new Light();
        Command lightOnCommand = new LightOnCommand(light);
        
        remoteControl.setCommand(lightOnCommand);
        remoteControl.pressButton();
    }
}
在上述示例中,Command 是命令接口,定义了执行命令的方法。LightOnCommand 是具体命令类,实现了命令接口,并封装了打开灯的操作。Light 是请求接收者,它定义了具体的灯的操作方法。RemoteControl 是调用者,它持有一个命令对象,并在需要执行命令时调用命令对象的执行方法。

4.模板方法模式(Template Method):

定义一个操作中的算法框架,而将一些步骤延迟到子类中实现。包含以下关键角色:
(1)抽象类:定义了一个模板方法,包含算法的骨架,一部分步骤的具体实现由子类提供。抽象类还可以定义一些抽象方法,由子类实现具体的行为。
(2)具体子类:继承自抽象类,并实现了抽象方法中的具体行为。它可以重写部分步骤的行为,以实现算法的个性化定制。

// 抽象类
abstract class Beverage {
    public final void prepareBeverage() {
        boilWater();
        brew();
        pourIntoCup();
        addCondiments();
    }
    
    public void boilWater() {
        System.out.println("Boiling water");
    }
    
    public abstract void brew();
    
    public void pourIntoCup() {
        System.out.println("Pouring into cup");
    }
    
    public abstract void addCondiments();
}

// 具体子类
class Coffee extends Beverage {
    @Override
    public void brew() {
        System.out.println("Brewing coffee");
    }
    
    @Override
    public void addCondiments() {
        System.out.println("Adding sugar and milk");
    }
}

// 具体子类
class Tea extends Beverage {
    @Override
    public void brew() {
        System.out.println("Steeping tea");
    }
    
    @Override
    public void addCondiments() {
        System.out.println("Adding lemon");
    }
}

// 客户端
public class Client {
    public static void main(String[] args) {
        Beverage coffee = new Coffee();
        coffee.prepareBeverage();
        
        System.out.println();
        
        Beverage tea = new Tea();
        tea.prepareBeverage();
    }
}

在上述示例中,Beverage 是抽象类,定义了一个模板方法prepareBeverage(),其中包含了煮饮料的算法骨架。抽象类还定义了一些抽象方法,如 brew() 和 addCondiments(),由具体子类实现具体的行为。Coffee 和 Tea 是具体子类,它们继承自抽象类,并根据需要重写抽象方法,以实现具体的行为。客户端通过创建具体子类的对象,调用模板方法 prepareBeverage(),即可按照模板定义的算法骨架进行煮咖啡和煮茶的过程。具体步骤的实现由具体子类完成,实现了算法的个性化定制。

5.迭代器模式(Iterator):

提供一种方法顺序访问一个聚合对象(列表、集合、数组等)中的各个元素,而又不暴露其内部的表示。通过迭代器模式,客户端可以使用统一的方式遍历不同类型的聚合对象,提高了代码的灵活性和可复用性。包含角色:
(1)迭代器接口:定义了访问和遍历聚合对象元素的方法,包括获取当前元素、判断是否还有下一个元素、获取下一个元素等。
(2)具体迭代器类:实现了迭代器接口,负责实现具体的遍历逻辑,记录当前位置和状态并提供访问和遍历聚合对象元素的方法
(3)聚合对象接口:定义了创建迭代器的方法,用于返回一个与该聚合对象关联的迭代器对象
(4)具体聚合对象类:实现了聚合对象接口,负责创建具体迭代器对象,并提供访问聚合对象元素的方法

客户端通过聚合对象获取一个迭代器对象,然后使用迭代器对象来遍历聚合对象的元素。客户端不需要关心聚合对象的具体实现细节,只需要通过迭代器接口来访问和遍历元素。

// 迭代器接口
interface Iterator<T> {
    boolean hasNext();
    T next();
}

// 具体迭代器类
class ListIterator<T> implements Iterator<T> {
    private List<T> list;
    private int position;
    
    public ListIterator(List<T> list) {
        this.list = list;
        this.position = 0;
    }
    
    @Override
    public boolean hasNext() {
        return position < list.size();
    }
    
    @Override
    public T next() {
        T item = list.get(position);
        position++;
        return item;
    }
}

// 聚合对象接口
interface Aggregate<T> {
    Iterator<T> createIterator();
}

// 具体聚合对象类
class ListAggregate<T> implements Aggregate<T> {
    private List<T> list;
    
    public ListAggregate(List<T> list) {
        this.list = list;
    }
    
    @Override
    public Iterator<T> createIterator() {
        return new ListIterator<>(list);
    }
}

// 客户端
public class Client {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("Apple");
        list.add("Banana");
        list.add("Orange");
        
        Aggregate<String> aggregate = new ListAggregate<>(list);
        Iterator<String> iterator = aggregate.createIterator();
        
        while (iterator.hasNext()) {
            String item = iterator.next();
            System.out.println(item);
        }
    }
}
在上述示例中,Iterator 是迭代器接口,定义了访问和遍历聚合对象元素的方法。ListIterator 是具体迭代器类,实现了迭代器接口,负责实现遍历列表的具体逻辑。Aggregate 是聚合对象接口,定义了创建迭代器的方法。ListAggregate 是具体聚合对象类,实现了聚合对象接口,负责创建具体迭代器对象。客户端通过创建具体聚合对象,并调用其 createIterator() 方法来获取一个迭代器对象。然后,使用迭代器对象的 hasNext() 和 next() 方法来遍历聚合对象的元素,打印出每个元素的值。

6.责任链模式(Chain of Responsibility):

使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。责任链模式通过构建一个链式的处理对象,依次传递请求,直到有一个对象能够处理该请求为止。包含角色:
(1)抽象处理者(Handler):定义了处理请求的接口并维护一个指向下一个处理者的引用,它可以决定是否将请求传递给下一个处理者。
(2)具体处理者:继承自抽象处理者,实现了处理请求的具体逻辑。它负责判断自己是否能处理请求,如果可以处理则进行处理,否则将请求传递给下一个处理者。

// 抽象处理者
abstract class Approver {
    protected Approver nextApprover;
    
    public void setNextApprover(Approver nextApprover) {
        this.nextApprover = nextApprover;
    }
    
    public abstract void processRequest(Request request);
}

// 具体处理者
class TeamLeader extends Approver {
    @Override
    public void processRequest(Request request) {
        if (request.getLeaveDays() <= 2) {
            System.out.println("Team Leader approves the leave request.");
        } else if (nextApprover != null) {
            nextApprover.processRequest(request);
        }
    }
}

class Manager extends Approver {
    @Override
    public void processRequest(Request request) {
        if (request.getLeaveDays() <= 5) {
            System.out.println("Manager approves the leave request.");
        } else if (nextApprover != null) {
            nextApprover.processRequest(request);
        }
    }
}

class Director extends Approver {
    @Override
    public void processRequest(Request request) {
        if (request.getLeaveDays() <= 10) {
            System.out.println("Director approves the leave request.");
        } else {
            System.out.println("Leave request denied.");
        }
    }
}

// 请求类
class Request {
    private int leaveDays;
    
    public Request(int leaveDays) {
        this.leaveDays = leaveDays;
    }
    
    public int getLeaveDays() {
        return leaveDays;
    }
}

// 客户端
public class Client {
    public static void main(String[] args) {
        Approver teamLeader = new TeamLeader();
        Approver manager = new Manager();
        Approver director = new Director();
        
        teamLeader.setNextApprover(manager);
        manager.setNextApprover(director);
        
        Request request1 = new Request(1);
        teamLeader.processRequest(request1);
        
        Request request5 = new Request(5);
        teamLeader.processRequest(request5);
        
        Request request10 = new Request(10);
        teamLeader.processRequest(request10);
        
        Request request15 = new Request(15);
        teamLeader.processRequest(request15);
    }
}
在上述示例中,Approver 是抽象处理者,定义了处理请求的接口,并维护一个指向下一个处理者的引用。TeamLeader、Manager 和 Director 是具体处理者,继承自抽象处理者,实现了处理请求的具体逻辑。Request 是请求类,包含了请假天数信息。客户端通过创建具体处理者对象,并设置其下一个处理者的引用,构建了一个处理请求的责任链。客户端将请假申请发送给第一个处理者 teamLeader,然后由处理者依次判断请假天数是否在自己的处理范围内,如果是,则进行处理;否则将请求传递给下一个处理者。

7.状态模式(State):

允许对象在其内部状态改变时改变它的行为。通过将状态封装成独立的类,并将行为委托给当前状态对象来实现状态的切换和行为的变化。关键角色:
(1)环境类:定义了一个维护状态对象的引用,并将行为委托给当前状态对象来处理。它可以维护一个状态对象的引用,并根据内部状态的改变调用相应的方法。
(2)抽象状态类:定义了一个接口,用于封装与环境类的一个特定状态相关的行为,它可以有多个具体状态类继承自它。
(3)具体状态类:继承自抽象状态类,实现了与特定状态相关的行为。
状态模式的优点包括:
(1)封装了状态:状态模式将状态封装成独立的类,使得状态的变化对于环境类来说是透明的,客户端无需关心状态的切换和行为的变化。
(2)简化了条件语句:状态模式将状态的判断逻辑从客户端代码中移除,使得代码更加清晰、简洁,避免了大量的条件语句。

// 抽象状态类
interface ElevatorState {
    void openDoor();
    void closeDoor();
    void goUp();
    void goDown();
}

// 具体状态类
class OpenState implements ElevatorState {
    @Override
    public void openDoor() {
        System.out.println("The door is already open.");
    }
    
    @Override
    public void closeDoor() {
        System.out.println("Closing the door.");
    }
    
    @Override
    public void goUp() {
        System.out.println("Going up.");
    }
    
    @Override
    public void goDown() {
        System.out.println("Cannot go down while the door is open.");
    }
}

class ClosedState implements ElevatorState {
    @Override
    public void openDoor() {
        System.out.println("Opening the door.");
    }
    
    @Override
    public void closeDoor() {
        System.out.println("The door is already closed.");
    }
    
    @Override
    public void goUp() {
        System.out.println("Going up.");
    }
    
    @Override
    public void goDown() {
        System.out.println("Going down.");
    }
}

// 环境类
class Elevator {
    private ElevatorState currentState;
    
    public Elevator() {
        currentState = new ClosedState();
    }
    
    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 Client {
    public static void main(String[] args) {
        Elevator elevator = new Elevator();
        
        elevator.openDoor();
        elevator.closeDoor();
        elevator.goUp();
        elevator.openDoor();
        elevator.closeDoor();
        elevator.goDown();
    }
}
在上述示例中,ElevatorState 是抽象状态类,定义了与电梯状态相关的行为接口。OpenState 和 ClosedState 是具体状态类,继承自抽象状态类,实现了与具体状态相关的行为。Elevator 是环境类,维护了一个当前状态对象的引用,并根据当前状态调用相应的方法来处理请求。客户端通过创建环境对象 Elevator,并调用相应的方法来控制电梯的行为。当调用方法时,环境对象会委托给当前状态对象来处理。根据不同的状态,电梯的行为会有所不同。通过状态模式,可以将电梯的状态和行为进行有效地封装和管理,使得代码更加灵活、可扩展,并且易于维护和理解。

8.访问者模式(Visitor):

封装某些作用于某个对象结构中的各个元素的操作,可以在不改变这个对象结构的前提下定义作用于这些元素的新操作。将数据结构与操作分离,使得操作可以独立于数据结构的变化而变化,包含角色:
(1)访问者:定义了要访问的每个元素的操作,即访问者所具有的行为
(2)具体访问者,实现了访问者接口的具体类,实现了对元素的具体访问操作
(3)元素:定义了一个接收访问者的接口,即被访问的对象所具有的行为
(4)具体元素:实现了元素接口的具体类,提供了接收访问者的具体实现
(5)结构对象:是一个或多个元素的集合,提供了接收访问者的方法,并能够遍历其中的元素
优点包括:
(1)增加新的操作:可以通过增加新的访问者来实现新的操作,无需修改元素类的代码,符合开闭原则
(2)统一的接口:将相关的操作封装在访问者接口中,提供了一个统一的接口供元素对象进行访问,使得操作更加一致和灵活

// 访问者接口
interface Visitor {
    void visit(Book book);
    void visit(Phone phone);
}

// 具体访问者
class PriceCalculator implements Visitor {
    private double totalPrice = 0;
    
    @Override
    public void visit(Book book) {
        totalPrice += book.getPrice();
    }
    
    @Override
    public void visit(Phone phone) {
        totalPrice += phone.getPrice();
    }
    
    public double getTotalPrice() {
        return totalPrice;
    }
}

// 元素接口
interface Element {
    void accept(Visitor visitor);
}

// 具体元素
class Book implements Element {
    private String name;
    private double price;
    
    public Book(String name, double price) {
        this.name = name;
        this.price = price;
    }
    
    public double getPrice() {
        return price;
    }
    
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

class Phone implements Element {
    private String brand;
    private double price;
    
    public Phone(String brand, double price) {
        this.brand = brand;
        this.price = price;
    }
    
    public double getPrice() {
        return price;
    }
    
    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
}

// 结构对象
class ShoppingCart {
    private List<Element> items = new ArrayList<>();
    
    public void addItem(Element item) {
        items.add(item);
    }
    
    public void calculateTotalPrice(Visitor visitor) {
        for (Element item : items) {
            item.accept(visitor);
        }
    }
}

// 客户端
public class Client {
    public static void main(String[] args) {
        ShoppingCart cart = new ShoppingCart();
        
        Book book1 = new Book("Java Programming", 39.99);
        Phone phone1 = new Phone("iPhone", 999.99);
        Book book2 = new Book("Design Patterns", 49.99);
        
        cart.addItem(book1);
        cart.addItem(phone1);
        cart.addItem(book2);
        
        PriceCalculator calculator = new PriceCalculator();
        cart.calculateTotalPrice(calculator);
        
        double totalPrice = calculator.getTotalPrice();
        System.out.println("Total Price: $" + totalPrice);
    }
}
在上述示例中,Visitor 是访问者接口,定义了访问不同元素的方法。PriceCalculator 是具体访问者,实现了访问者接口,计算每个元素的价格并累加到总价中。Element 是元素接口,定义了接受访问者的方法。Book 和 Phone 是具体元素,实现了元素接口,并根据自身类型调用访问者的相应方法。ShoppingCart 是结构对象,存储了一组元素,并提供了计算总价的方法。客户端通过创建结构对象 ShoppingCart 和具体访问者 PriceCalculator,并将访问者传递给结构对象的方法来实现对元素的访问和操作。在访问过程中,访问者根据元素的类型执行相应的操作,最终得到总价。

总结

以上仅是一些常见的设计模式,还有其他的设计模式,每个设计模式都有其特定的用途和优缺点。在实际开发中,根据具体的需求和情况选择适合的设计模式可以提高代码的可维护性和可扩展性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值