跳至主要內容

设计模式

程序员李某某大约 46 分钟

设计模式

六大原则

  • 开闭原则(OCP原则):对扩展开放,对修改关闭
  • 里氏代换原则:子类重写父类方法,结果应该与期望的是一致的(多实现类结果符合父类定义)
  • 依赖倒转原则:针对接口编程,依赖抽象,不依赖具体
  • 接口隔离原则:是两个功能就不要写到一个接口中
  • 迪米特法则(最少知道原则):一个实体中尽量少的与其他实体相互作用
  • 合成复用原则:多组合,少继承

创建型(4)

工厂模式(Factory)

简单工厂模式

  • 简单工厂模式又称静态工厂方法模式
  • 目的:定义一个用于创建对象的接口
  • 组成
    • 工厂类角色:这是本模式的核心,含有一定的商业逻辑和判断逻辑。在java中它往往由一个具体类实现
    • 抽象产品角色:它一般是具体产品继承的父类或者实现的接口。在java中由接口或者抽象类来实现。
    • 具体产品角色:工厂类所创建的对象就是此角色的实例
  • 缺点:工厂违背开闭原则(扩展开放,修改封闭)
//抽象
public interface Car{
  void drive();
}

//具体1
public class Benz implements Car{
  public void drive(){
    System.out.println("开奔驰")}
}
//具体2
public class Bwm implements Car{
  public void drive(){
    System.out.println("开宝马")}
}
//再有新的具体类,添加一个,实现接口或抽象类

//工厂
public class Driver{
  public static Car driveCar(String s)throws Exception{
    if(s.equalsIgnoreCase("Benz")) return new Benz();  
		else if(s.equalsIgnoreCase("Bmw")) return new Bmw();  
    ......  
    else throw new Exception();
  }
}

//测试类
public class Test{
  public static void main(String[] args){
    try{
      Car car = Driver.driveCar("benz");
      car.drive();
    }
  }
}

工厂方法模式

  • 去掉了简单工厂模式中工厂方法的静态属性,使得它可以被子类继承

  • 组成

    • 抽象工厂角色: 这是工厂方法模式的核心,它与应用程序无关。是具体工厂角色必须实现的接口或者必须继承的父类
    • 具体工厂角色:它含有和具体业务逻辑有关的代码。由应用程序调用以创建对应的具体产品的对象
    • 抽象产品角色:它是具体产品继承的父类或者是实现的接口
    • 具体产品角色:具体工厂角色所创建的对象就是此角色的实例
  • 缺点:对象双倍增加,一个产品对象对应一个工厂对象

    • 可以考虑使 用简单工厂模式与工厂方法模式相结合的方式来减少工厂类:即对于产品树上类似的种类(一般是树的叶子中互为兄弟的)使用简单工厂模式来实现。
  • 下面情况可以考虑使用工厂方法模式:

    1. 当客户程序不需要知道要使用对象的创建过程。
    2. 客户程序使用的对象存在变动的可能,或者根本就不知道使用哪一个具体的对象。
  • 简单工厂模式与工厂方法模式真正的避免了代码的改动了?没有。在简单工厂模式中,新产品的加入要修改工厂角色中的判断语句;而在工厂方法模式中,要么将判断逻辑留在抽象工厂角色中,要么在客户程序中将具体工厂角色写死(就象上面的例子一样)。而且产品对象创建条件的改变必然会引起工厂角色的修改。

    面对这种情况,Java 的反射机制与配置文件的巧妙结合突破了限制——这在 Spring 中完美的体现了出来。
    
//抽象工厂
public interface Driver{
  Car driveCar();
}
//具体工厂1
public class BenzDriver implements Driver{
  public Car driveCar(){
    return new Benz();
  }
}
//具体工厂2
public class BmwDriver implements Driver{
  public Car driveCar(){
    return new Bmw();
  }
}
//产品相关的同简单工厂

//抽象产品
public interface Car{
  void drive();
}

//具体产品1
public class Benz implements Car{
  public void drive(){
    System.out.println("开奔驰")}
}
//具体产品2
public class Bwm implements Car{
  public void drive(){
    System.out.println("开宝马")}
}
//再有新的具体类,添加一个工厂一个产品,实现接口或抽象类
//测试类
public class Test{
  public static void main(String[] args){
    try{
      Driver driver = new BenzDriver();
      Car car = driver.derveCar();
      car.drive();
    }
  }
}

抽象工厂模式

  • 产品族: 位于不同产品等级结构中,功能相关联的产品组成的家族

    • BmwCar 和 BenzCar 就是两个产品树(产品层次结构)
    • BenzSportsCar 和 BmwSportsCar 就是一个产品族。他们都可以放到跑车家族中,因此功能有所关联
    • 同理 BmwBussinessCar 和 BenzBussinessCar 也是一个产品族。
    image-20220801155626921
    image-20220801155626921
  • 目的:给客户端提供一个接口,可以创建多个产品族中的产品对象

  • 条件

    • 系统中有多个产品族,而系统一次只可能消费其中一族产品。
    • 同属于同一个产品族的产品以其使用。
  • 组成:抽象工厂、具体工厂、抽象产品、具体产品

    image-20220801160636745
    image-20220801160636745

单例模式(Singleton)

单例对象(Singleton)是一种常用的设计模式。在 Java 应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在。这样的模式有几个好处:

	1、某些类创建比较频繁,对于一些大型的对象,这是一笔很大的系统开销。
	2、省去了  new 操作符,降低了系统内存的使用频率,减轻  GC 压力。
	3、有些类如交易所的核心交易引擎,控制着交易流程,如果该类可以创建多个的话,系统完全乱了。(比如一个军队出现了多个司令员同时指挥,肯定会乱成一团),所以只有使用单例模式,才能保证核心交易服务器独立控制整个流程。

饿汉式:加载时间长,但线程安全

懒汉式:延迟对象的创建

1.私有化构造器(不能直接创建对象,只能通过静态属性调用
2.内部提供当前类的实例(内部创建对象
3.提供公共静态方法,返回当前类的对
4.此实例必须静态化,才能被直接调用

饿汉式

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

懒汉式

class Singleton{
	//1.私有化构造器
	private Singleton(){}
	//2.内部提供当前类的实例(内部创建对象)
	//4.此实例必须静态化,才能被直接调用
 	private static Singleton singleton = null;
    //3.提供公共静态方法,返回当前类的对象
    public static Singleton getSingleton(){
        if(Singleton == null){
            syschronized(Singleton.class){
                if(Singleton == null){
                  singleton = new Singleton;
                }
            }
        }
    	return singleton;
  	}
}

枚举

enum Singleton {  
	INSTANCE; 
}

内部类


建造者模式(Builder)

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

适用于那些需要逐步构建一个复杂对象,并且希望构造过程和表示分离的场景。

建造者模式的核心在于将复杂的构建步骤封装起来,使得客户端不需要知道具体的构建细节,从而降低了系统的耦合度。

核心角色

  • 产品(Product):这是被构建的复杂对象。它通常包含多个属性,这些属性在构建过程中被逐步设置。
  • 抽象建造者(Builder):这是一个接口或抽象类,它定义了一个创建产品对象的抽象步骤,但并不提供具体的实现。这为创建各种具体的产品提供了统一的接口。
  • 具体建造者(Concrete Builder):实现了抽象建造者接口,提供了具体的构建步骤。每个具体建造者都对应一种具体的产品构建方式。
  • 指挥者(Director):负责组织具体建造者的构建步骤,指导具体建造者创建产品。它隔离了客户端与具体建造者之间的耦合,客户端只需与指挥者交互,不必关心具体的构建过程。

应用场景

当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。 当构造过程必须允许被构造的对象有不同表示时。 当需要通过一步一步地构造一个复杂的对象,且构造过程允许被参数化时。

优点

  • 封装性好:将产品的构建过程和表示分离,使得构建过程更加清晰,易于理解。
  • 灵活性高:用户只需要指定具体建造者就可以得到不同的产品表现,增加新的具体建造者无须修改原有代码。
  • 易于控制复杂对象的创建:通过逐步构建的方式,可以更精细地控制复杂对象的产生过程。

缺点:产品内部发生变化时,可能需要修改建造者类的代码,这违反了“开闭原则”。

使用示例

假设我们要构建一个复杂的计算机对象,包含CPU、内存、硬盘等组件,可以定义一个Computer作为产品,一个ComputerBuilder作为抽象建造者,以及几个具体建造者如DesktopBuilder和LaptopBuilder,最后定义一个ComputerDirector作为指挥者来组织构建过程。

产品类

@Data
public class Computer {
    private Component cpu;
    private Component memory;
    private Component hardDisk;

    public Computer(CPU cpu, Memory memory, HardDisk hardDisk) {
        this.cpu = cpu;
        this.memory = memory;
        this.hardDisk = hardDisk;
    }
}
// 组件接口
interface Component {
    void build();
}

// 具体组件类
@Data
class CPU implements Component {
    @Override
    public void build() {
        System.out.println("Building CPU...");
    }
}
@Data
class Memory implements Component {
    @Override
    public void build() {
        System.out.println("Building Memory...");
    }
}
@Data

class HardDisk implements Component {
    @Override
    public void build() {
        System.out.println("Building Hard Disk...");
    }
}

抽象建造者

interface ComputerBuilder {
    void setCPU(Component cpu);

    void setMemory(Component memory);

    void setHardDisk(Component hardDisk);

    Computer build();
}

具体建造者

// 具体建造者1
class DesktopBuilder implements ComputerBuilder {
    private Computer computer = new Computer(null, null, null);

    @Override
    public void setCPU(Component cpu) {
        computer.setCpu(cpu);
    }

    @Override
    public void setMemory(Component memory) {
        computer.setMemory(memory);
    }

    @Override
    public void setHardDisk(Component hardDisk) {
        computer.setHardDisk(hardDisk);
    }

    @Override
    public Computer build() {
        return computer;
    }
}
// 具体建造者2
class LaptopBuilder implements ComputerBuilder {
    private Computer computer = new Computer(null, null, null);

    @Override
    public void setCPU(Component cpu) {
        computer.setCpu(cpu);
    }

    @Override
    public void setMemory(Component memory) {
        computer.setMemory(memory);
    }

    @Override
    public void setHardDisk(Component hardDisk) {
        computer.setHardDisk(hardDisk);
    }

    @Override
    public Computer build() {
        return computer;
    }
}

指挥者

// 指挥者
class ComputerDirector {
    private ComputerBuilder builder;

    public ComputerDirector(ComputerBuilder builder) {
        this.builder = builder;
    }

    public Computer constructComputer() {
        builder.setCPU(new CPU());
        builder.setMemory(new Memory());
        builder.setHardDisk(new HardDisk());
        return builder.build();
    }
}

测试

public class MainTest {
    public static void main(String[] args) {
        ComputerDirector director = new ComputerDirector(new DesktopBuilder());
        Computer desktop = director.constructComputer();
        System.out.println(desktop);

        director = new ComputerDirector(new LaptopBuilder());
        Computer laptop = director.constructComputer();
        System.out.println(laptop);
    }
}

框架示例

lombok

swagger

spring mvc ---- UriComponentsBuilder

public class UriComponentsBuilder {
    // 静态工厂方法,开始构建过程
    public static UriComponentsBuilder fromHttpUrl(String uriString) {...}
    public static UriComponentsBuilder fromUriString(String uriString) {...}

    // 添加路径、查询参数等
    public UriComponentsBuilder pathSegment(String... segments) {...}
    public UriComponentsBuilder queryParam(String name, Object... values) {...}

    // 构建UriComponents
    public UriComponents build() {...}
    public UriComponents build(boolean encode) {...}
}

原型模式(Prototype )

原型模式用于快速创建复杂对象,Java 中 clone方法提供了快速创建对象的功能,

clone()方法相对于new关键字来说,通常会更快一些,因为它是通过内存拷贝的方式创建新对象,省去了对象初始化的过程

但是该方法默认情况下是浅拷贝。如果需要深拷贝,需要在实现clone()方法时进行处理

Cloneable这个接口只是标记,clone方法在Object中

浅拷贝

@Data
public class User implements Cloneable {
    private String name;
    private Integer age;
    private User teacher;

    @Override
    public User clone() throws CloneNotSupportedException {
        return (User) super.clone();
    }
}

测试

public class MainTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        User zhangsan = new User();
        zhangsan.setAge(18);
        zhangsan.setName("zhangsan");
        User tc = new User();
        tc.setName("tc");
        zhangsan.setTeacher(tc);
        User clone = zhangsan.clone();

        System.out.println(clone == zhangsan);	// false
        System.out.println(clone.getTeacher() == zhangsan.getTeacher());	// true
    }
}

由此可见 clone方法默认是浅拷贝

深拷贝

@Data
public class User implements Cloneable {
    private String name;
    private Integer age;
    private User teacher;

    @Override
    public User clone() throws CloneNotSupportedException {
        // 首先,调用super.clone()获得一个浅拷贝对象
        User clonedUser = (User) super.clone();

        // 然后,对引用类型的成员变量进行深拷贝处理
        if (this.teacher != null) {
            // 递归调用teacher的clone方法以实现深拷贝
            clonedUser.setTeacher(this.teacher.clone());
        }

        return clonedUser;
    }
}

测试结果都为false

使用实例

游戏中的怪物👹

// 抽象怪物类,定义克隆接口
abstract class Monster implements Cloneable {
    protected int healthPoints;	// 血量健康度
    protected int attackPower;	// 攻击力

    public abstract void showStatus();

    // 克隆方法
    public abstract Monster clone() throws CloneNotSupportedException;
}

// 具体怪物类
class Goblin extends Monster {
    public Goblin(int healthPoints, int attackPower) {
        this.healthPoints = healthPoints;
        this.attackPower = attackPower;
    }

    @Override
    public void showStatus() {
        System.out.println("Goblin - Health: " + healthPoints + ", Attack Power: " + attackPower);
    }

    @Override
    public Goblin clone() throws CloneNotSupportedException {
        return (Goblin) super.clone();
    }
}

// 原型管理器类
class MonsterManager {
    private Map<String, Monster> prototypes = new HashMap<>();

    public void addPrototype(String type, Monster prototype) {
        prototypes.put(type, prototype);
    }

    public Monster getPrototype(String type) {
        try {
            return prototypes.get(type).clone();
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException("Cloning not supported.", e);
        }
    }
}

// 客户端代码
public class PrototypePatternDemo {
    public static void main(String[] args) {
        MonsterManager manager = new MonsterManager();

        // 创建原型
        Goblin goblinPrototype = new Goblin(100, 5);
        manager.addPrototype("Goblin", goblinPrototype);

        // 使用原型生成新怪物
        Goblin goblin1 = (Goblin) manager.getPrototype("Goblin");
        goblin1.showStatus();

        Goblin goblin2 = (Goblin) manager.getPrototype("Goblin");
        goblin2.showStatus();

        // 修改goblin1的属性,验证深拷贝
        goblin1.setHealthPoints(120);
        goblin1.showStatus();

        // goblin2的属性不受影响
        goblin2.showStatus();
    }
}

结构型(7)

适配器模式(Adapter)

将一个类的接口(方法)转化为另一个接口

使用示例

假设你正在开发一个音乐播放器应用程序,该程序原本设计为播放WAV文件,但现在需要扩展功能以支持播放MP3文件。

由于原有的播放器类只认识MAV格式,我们可以通过适配器模式来解决这一问题,而不必修改原有的播放器代码。

// 目标接口(Target):原有的音乐播放器接口,只认识MP3格式
interface MusicPlayer {
    void playMp3(String fileName);
}

// 原始类(Adaptee):WAV播放器,只能播放WAV格式的音乐
class WAVPlayer {
    public void playWAV(String fileName) {
        System.out.println("Playing WAV file: " + fileName);
    }
}

// 适配器类(Adapter):让WAV播放器能够被音乐播放器接口识别
class WAVPlayerAdapter implements MusicPlayer {
    private WAVPlayer wavPlayer;

    public WAVPlayerAdapter(WAVPlayer wavPlayer) {
        this.wavPlayer = wavPlayer;
    }

    @Override
    public void playMp3(String fileName) {
        // 将接收到的播放请求转换为WAV播放器能理解的格式
        String wavFileName = convertToWav(fileName);
        wavPlayer.playWAV(wavFileName);
    }

    // 简化的转换逻辑,实际应用中可能需要更复杂的转换
    // 这里是适配器的关键,适配的具体逻辑,这里只是用一个简单方法代替,实际开发中可能会很复杂
    // 比较复杂的情况下就适配封装为一个类,通过继承或者组合的方式进行调用
    private String convertToWav(String mp3FileName) {
        return mp3FileName.replace(".mp3", ".wav");
    }
}

测试

public class MainTest {
    public static void main(String[] args) {
        // 创建一个WAV播放器实例
        WAVPlayer wavPlayer = new WAVPlayer();
        // 使用适配器包装WAV播放器,使其看起来像一个支持MP3的播放器
        MusicPlayer musicPlayer = new WAVPlayerAdapter(wavPlayer);
        // 尝试播放一个"看似"是MP3的文件
        musicPlayer.playMp3("song.mp3");
    }
}

再看一个示例:

现在有一个播放器,只有中文字幕,现在想加日文字幕

public interface Player {
    String play();
}

class MoviePlayer implements Player {

    @Override
    public String play() {
        System.out.println("正在播放:李老师的宝贵时间.mp4");
        String content = "你好";
        System.out.println(content);    // 打印字幕
        return content;
    }
}

public interface Translator {
    String translate(String content);
}

class Zh_JPTranslator implements Translator {
    public String translate(String content) {
        if ("你好".equals(content)) {
            return "こんにちは";
        }
        if ("什么".equals(content)) {
            return "なに";
        }
        return "*******";
    }
}

添加一个适配器

class JPMoviePlayerAdapter extends Zh_JPTranslator implements Player {

    private Player target;

    public JPMoviePlayerAdapter(Player target) {
        this.target = target;
    }

    @Override
    public String play() {
        String content = target.play(); // 正常播放
        String translate = translate(content);// 转化字幕
        System.out.println(translate);    // 打印字幕
        return translate;
    }
}

框架示例

Tomcat 如何将Request流转为标准 Request?

Tomcat中适配器模式的一个典型运用体现在其请求处理机制上,特别是在连接器(Connector)和容器(Container)之间。Tomcat使用适配器模式来桥接HTTP请求与Servlet容器,使得不同类型的请求(如HTTP、AJP)可以被容器统一处理

  • 连接器(Connector)与协议处理器(ProtocolHandler)
    • Connector:负责监听端口、接收客户端的连接请求,并将请求转化为Tomcat内部可以处理的形式。
    • ProtocolHandler:处理特定协议(如HTTP、AJP)的细节,它将网络请求转换为Tomcat内部的请求表示。
  • CoyoteAdapter:适配器的核心
    • org.apache.catalina.connector.CoyoteAdapter是Tomcat中一个重要的适配器类,它实现了从Coyote(Tomcat的协议处理包)到Servlet容器的适配。 当一个请求到达时,ProtocolHandler(通常是org.apache.coyote.http11.Http11Processor)会处理HTTP协议层面的事务,然后调用CoyoteAdapter的service方法。
    • CoyoteAdapter.service方法接收org.apache.coyote.Request(Coyote请求对象)和org.apache.coyote.Response(Coyote响应对象),并将它们转换为javax.servlet.ServletRequest和javax.servlet.ServletResponse,以便容器内的Servlet可以处理
  • 容器(Container)与Pipeline
    • Container:处理Servlet生命周期和请求分发,包括Engine、Host、Context和Wrapper四个层次。
    • Pipeline:每个容器都有一个Pipeline,它是一个责任链模式的实现,包含多个Valve(阀门),用于处理请求的不同阶段,如认证、日志记录等

Spring MVC 中的HandlerAdpator

在Spring MVC中,HandlerAdapter是一个关键组件,它作为请求处理流程中的适配器,负责调用实际的处理器(Handler,通常是带有@Controller注解的类的方法)来处理HTTP请求。HandlerAdapter的作用是确保Spring MVC框架可以处理各种不同类型的控制器方法,即使它们的签名各异

public interface HandlerAdapter {
    // 判断该HandlerAdapter是否能处理给定的Handler
    boolean supports(Object handler);
	// 核心方法,实际调用Handler执行处理逻辑,返回ModelAndView
    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
}

HandlerAdapter的实现类通常会覆盖这些方法来提供具体的实现。例如,RequestMappingHandlerAdapter是Spring MVC中最常用的HandlerAdapter实现,它处理带有@RequestMapping注解的方法


在实际的请求处理过程中,Spring MVC会根据HandlerMapping找到合适的Handler,然后通过HandlerAdapter来调用Handler处理请求。如果一个Handler有多个HandlerAdapter可以处理,Spring会选择优先级最高的那个

supports方法正是体现适配器模式的地方, 此方法用于判断当前HandlerAdapter实例是否支持处理某个特定的Handler(通常是控制器类中的一个方法)。这相当于适配器模式中的“适配条件检查”,即判断适配器能否适应(或处理)给定的目标对象(在这里是处理器方法)。每个HandlerAdapter可能针对不同类型的处理器方法(如不同的注解、参数类型等),通过重写supports方法,框架可以决定哪个HandlerAdapter最适合处理某个特定的请求处理器。

在Spring MVC的上下文中,不同的控制器方法可能有不同的需求,比如有的需要数据绑定,有的需要特殊的消息转换等。HandlerAdapter通过适配不同类型的处理器方法,提供了统一的接口来调用这些方法,确保了框架的灵活性和扩展性。即使未来新增了不同类型的处理器,只需增加相应的HandlerAdapter实现即可,无需修改框架的其他部分

桥接模式(Bridge)

核心目的是将抽象部分(Abstraction)与它的实现部分(Implementation)分离,让它们可以独立地变化

抽象部分可以理解为一个东西, 还没有实现的细节, 比如图形, 功能就是画图

实现部分可以理解为这个东西的一些维度, 比如形状是一种实现, 颜色是一种实现

桥接模式就是将抽象和实现解耦, 通过组合等方式进行桥接

这种分离通过创建一个桥接接口来实现,该接口将抽象和实现连接起来,使得两者可以灵活地独立发展,从而提高了系统的可扩展性和可维护性

应用场景

  • 当一个类存在两个独立变化的维度,且这两个维度都需要进行扩展时
  • 需要设计一个可以有多重表现形式的类,同时这些表现形式可以自由切换
  • 避免使用多重继承来解决类的扩展问题,减少类的复杂度和继承层次

使用示例

假设我们正在设计一个图形编辑器,其中图形(圆形、矩形等)可以有不同的颜色(红色、蓝色等)。这里,图形种类和颜色是两个独立变化的维度

  • 抽象类:可以定义一个Shape接口,包含绘制图形的抽象方法
  • 修正(扩展)抽象类:具体形状类如Circle和Rectangle继承自Shape,并实现具体形状的绘制逻辑
  • 实现接口:定义一个Color接口,包含颜色相关的操作
  • 具体实现类:具体颜色类如RedColor和BlueColor实现Color接口,提供颜色的实现
// 抽象部分 - Shape
interface Shape {
    void draw(IColor color);
}

// 修正抽象类 - 具体形状
class Circle implements Shape {
    @Override
    public void draw(IColor color) {
        System.out.println("Drawing a circle with " + color.getColor());
    }
}

class Rectangle implements Shape {
    @Override
    public void draw(IColor color) {
        System.out.println("Drawing a rectangle with " + color.getColor());
    }
}

// 实现接口 - Color
interface IColor {
    String getColor();
}

// 具体实现类 - 颜色
class RedColor implements IColor {
    @Override
    public String getColor() {
        return "red";
    }
}

class BlueColor implements IColor {
    @Override
    public String getColor() {
        return "blue";
    }
}

// 客户端代码
public class BridgePatternDemo {
    public static void main(String[] args) {
        Shape circle = new Circle();
        Shape rectangle = new Rectangle();

        IColor red = new RedColor();
        IColor blue = new BlueColor();

        circle.draw(red); // Drawing a circle with red
        circle.draw(blue); // Drawing a circle with blue

        rectangle.draw(red); // Drawing a rectangle with red
        rectangle.draw(blue); // Drawing a rectangle with blue
    }
}

添加一个位置坐标维度

  • 这里实现类扩展是通过组合的形式, Color也可以通过组合方式进行扩展进来
// 新增 - Position接口
interface Position {
    void setPosition(int x, int y);
}

// 新增 - 具体位置实现
class CoordinatePosition implements Position {
    private int x;
    private int y;

    public void setPosition(int x, int y) {
        this.x = x;
        this.y = y;
        System.out.println("Setting position to (" + x + ", " + y + ")");
    }
}

// 修改 - Shape接口
interface Shape {
    void draw(IColor color);
    void setPosition(Position pos);
}

// 修改 - Circle和Rectangle类
class Circle implements Shape {
    private Position pos;

    public void setPosition(Position pos) {
        this.pos = pos;
    }

    @Override
    public void draw(IColor color) {
        System.out.println("Drawing a circle with " + color.getColor() + " at " + pos);
    }
}

class Rectangle implements Shape {
    private Position pos;

    public void setPosition(Position pos) {
        this.pos = pos;
    }

    @Override
    public void draw(IColor color) {
        System.out.println("Drawing a rectangle with " + color.getColor() + " at " + pos);
    }
}

// 客户端代码
public class BridgePatternDemoWithPosition {
    public static void main(String[] args) {
        Shape circle = new Circle();
        Shape rectangle = new Rectangle();

        IColor red = new RedColor();
        IColor blue = new BlueColor();

        Position coordinatePos1 = new CoordinatePosition();
        Position coordinatePos2 = new CoordinatePosition();

        coordinatePos1.setPosition(10, 20);
        coordinatePos2.setPosition(30, 40);

        circle.setPosition(coordinatePos1);
        rectangle.setPosition(coordinatePos2);

        circle.draw(red); // Drawing a circle with red at (10, 20)
        circle.draw(blue); // Drawing a circle with blue at (10, 20)

        rectangle.draw(red); // Drawing a rectangle with red at (30, 40)
        rectangle.draw(blue); // Drawing a rectangle with blue at (30, 40)
    }
}

装饰器模式(Decorator/Wrapper)

装饰器模式和适配器模式很像,

  • 装饰器目的是增强现有功能, 适配器目的是接口兼容转换

使用示例

我们有一个简单的文本处理程序,它可以读取文本文件并对其进行格式化输出。我们可以使用装饰器模式来添加额外的功能,比如加粗、斜体或下划线。

// 基础接口(Component)
public interface Text {
    String getContent(); // 返回文本内容
}

// 具体组件(ConcreteComponent)
class PlainText implements Text {
    private String content;

    public PlainText(String content) {
        this.content = content;
    }

    @Override
    public String getContent() {
        return content;
    }
}
package com.jd.starlink.designPatterns.decorator;

// 装饰者抽象类(Decorator)
public abstract class TextDecorator implements Text {
    protected Text decoratedText;	// 被装饰的对象

    public TextDecorator(Text decoratedText) {
        this.decoratedText = decoratedText;
    }

    public String getContent() {
        return decoratedText.getContent();
    }
}

// 具体装饰者(ConcreteDecorator)
class BoldText extends TextDecorator {
    public BoldText(Text decoratedText) {
        super(decoratedText);
    }

    @Override
    public String getContent() {
        return "<b>" + super.getContent() + "</b>";
    }
}

class ItalicText extends TextDecorator {
    public ItalicText(Text decoratedText) {
        super(decoratedText);
    }

    @Override
    public String getContent() {
        return "<i>" + super.getContent() + "</i>";
    }
}

class UnderlineText extends TextDecorator {
    public UnderlineText(Text decoratedText) {
        super(decoratedText);
    }

    @Override
    public String getContent() {
        return "<u>" + super.getContent() + "</u>";
    }
}

测试

public class MainTest {
    public static void main(String[] args) {
        Text text = new PlainText("Hello, world!");

        // 加粗
        text = new BoldText(text);
        System.out.println("Bold: " + text.getContent());

        // 斜体
        text = new ItalicText(text);
        System.out.println("Bold and Italic: " + text.getContent());

        // 下划线
        text = new UnderlineText(text);
        System.out.println("Bold, Italic, and Underline: " + text.getContent());
    }
}

框架示例

代理模式(Proxy)

静态代理

被代理类想实现接口的方法,但前期需要很多步骤,委托代理类实现

public class StaticProxyTest {

	public static void main(String[] args) {
		Star s = new Proxy(new RealStar());
		s.confer();
		s.signContract();
		s.bookTicket();
		s.sing();
		s.collectMoney();
	}
}

//接口定义整个流程
interface Star {
	void confer();// 面谈
	void signContract();// 签合同
	void bookTicket();// 订票
	void sing();// 唱歌
	void collectMoney();// 收钱
}

//被代理类实现只能自己完成的方法
class RealStar implements Star {

	public void confer() {}
	public void signContract() {}
	public void bookTicket() {}
	public void sing() {
		System.out.println("明星:歌唱~~~");
	}
	public void collectMoney() {}
}

//代理类实现可代理的方法
class Proxy implements Star {
	//调用时传入被代理对象,有需要亲力亲为的内部调用下
 	private Star real;
	public Proxy(Star real) {
		this.real = real;
	}

	public void confer() {
		System.out.println("经纪人面谈");
	}

	public void signContract() {
		System.out.println("经纪人签合同");
	}

	public void bookTicket() {
		System.out.println("经纪人订票");
	}
  
	//亲力亲为,内部调用
	public void sing() {
		real.sing();
	}

	public void collectMoney() {
		System.out.println("经纪人收钱");
	}
}

再来一个示例: 假设我们有一个Image接口,代表图片加载功能,RealImage是真实主题,实现了图片加载,而ImageProxy是静态代理,用于缓存图片,减少重复加载

// 图片接口
public interface Image {
    void display();
}

// 真实主题:实际加载图片
class RealImage implements Image {
    private String fileName;

    public RealImage(String fileName) {
        this.fileName = fileName;
        loadFromDisk(fileName); // 加载图片
    }

    @Override
    public void display() {
        System.out.println("Displaying " + fileName);
    }

    private void loadFromDisk(String fileName) {
        System.out.println("Loading " + fileName);
    }
}

class ImageProxy implements Image {
    private RealImage realImage;
    private String fileName;

    public ImageProxy(String fileName) {
        this.fileName = fileName;
    }

    @Override
    public void display() {
        if (realImage == null) {
            realImage = new RealImage(fileName);
        }
        realImage.display();
    }
}

测试

public class MainTest {
    public static void main(String[] args) {
        Image image = new ImageProxy("test.jpg");
        image.display(); // 第一次显示,会加载图片
        image.display(); // 第二次显示,直接从缓存中获取,不再加载
    }
}

静态代理和装饰器也非常类似,

  • 静态代理目的是通过代理对象增强或替换原本的逻辑
  • 装饰器目的就是增强
  • 实现上, 都是和原始类实现同一个接口, 但装饰器有一个抽象接口或抽象类, 可以不同形式的增强实现

JDK动态代理

下面给出一种万能的动态代理, 若要实现上述静态代理缓存的逻辑, 在invoke 方法中增加一些前置逻辑即可

class JdkDynamicProxy<T> implements InvocationHandler {
    private T obj;

    public ImageDynamicProxy(T obj) {
        this.obj = obj;
    }


    public static <T> T getProxy(T obj) {
        return (T) Proxy.newProxyInstance(
                obj.getClass().getClassLoader(),
                obj.getClass().getInterfaces(),
                new ImageDynamicProxy(obj));
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //
        System.out.println("执行被代理对象前");
        Object invoke = method.invoke(obj, args);
        System.out.println("执行被代理对象后");
        return invoke;
    }
}

测试

public static void main(String[] args) {
    JdkDynamicProxy.getProxy((Image) new RealImage("test.jpg")).display();
}

CGlib动态代理

无需实现接口

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version> <!-- 根据最新版本调整 -->
</dependency>

必须提供空参构造器

class CglibDynamicProxy<T> implements MethodInterceptor {
    private final T obj;

    public CglibDynamicProxy(T obj) {
        this.obj = obj;
    }

    public static <T> T getProxy(T obj) {
        return (T) Enhancer.create(
                obj.getClass(),
                new CglibDynamicProxy<>(obj));
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("执行被代理对象前");
        Object invoke = methodProxy.invoke(obj, objects);
        System.out.println("执行被代理对象后");
        return invoke;
    }
}

测试

public static void main(String[] args) {
    CglibDynamicProxy.getProxy(new RealImage("test.jpg")).display();
}

外观模式(Facade)

又叫门面模式

通过组合的方式, 提供了一个统一的接口,用来访问子系统中的一组接口, 降低客户端与子系统的耦合度

使用示例

假设我们有一个家庭影院系统,包括投影仪、音响系统和DVD播放器,我们想要提供一个简单的接口来控制整个家庭影院系统

// 子系统:投影仪
class Projector {
    public void on() {
        System.out.println("投影仪已启动");
    }

    public void off() {
        System.out.println("投影仪已关闭");
    }

    public void setInput(String input) {
        System.out.println("投影仪输入设置为:" + input);
    }
}

// 子系统:音响系统
class SoundSystem {
    public void on() {
        System.out.println("音响系统已启动");
    }

    public void off() {
        System.out.println("音响系统已关闭");
    }

    public void setVolume(int volume) {
        System.out.println("音量设置为:" + volume);
    }
}

// 子系统:DVD播放器
class DVDPlayer {
    public void on() {
        System.out.println("DVD播放器已启动");
    }

    public void off() {
        System.out.println("DVD播放器已关闭");
    }

    public void play(String movie) {
        System.out.println("正在播放电影:" + movie);
    }
}

门面

// 外观类:家庭影院
class HomeTheaterFacade {
    private Projector projector;
    private SoundSystem soundSystem;
    private DVDPlayer dvdPlayer;

    public HomeTheaterFacade(Projector projector, SoundSystem soundSystem, DVDPlayer dvdPlayer) {
        this.projector = projector;
        this.soundSystem = soundSystem;
        this.dvdPlayer = dvdPlayer;
    }

    public void watchMovie(String movie) {
        System.out.println("准备观看电影...");
        projector.on();
        soundSystem.on();
        dvdPlayer.on();
        dvdPlayer.play(movie);
    }

    public void endMovie() {
        System.out.println("电影结束,关闭家庭影院设备...");
        dvdPlayer.off();
        soundSystem.off();
        projector.off();
    }
}

测试

public class MainTest {
    public static void main(String[] args) {
        Projector projector = new Projector();
        SoundSystem soundSystem = new SoundSystem();
        DVDPlayer dvdPlayer = new DVDPlayer();

        HomeTheaterFacade homeTheater = new HomeTheaterFacade(projector, soundSystem, dvdPlayer);

        // 观看电影
        homeTheater.watchMovie("The Matrix");

        // 电影结束
        homeTheater.endMovie();
    }
}

框架示例

组合模式(Composite)

使用示例

构建一个文件系统结构,其中包含文件(叶子)和目录(组合)

// Component: 定义所有对象共有的接口
interface FileComponent {
    void display(int depth);

    default void printCurrent(int depth, String name) {
        StringBuilder sb = new StringBuilder();

        for (int i = 0; i < depth; i++) {
            sb.append("\t");
            if (i == depth - 1) {
                sb.append("|");
            }
        }
        for (int i = 0; i < depth; i++) {
            sb.append("-");
        }
        sb.append(name).append('\n');
        System.out.print(sb);
    }
}

// Leaf: 实现叶子节点,这里是文件
class File implements FileComponent {
    private String name;

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

    @Override
    public void display(int depth) {
        // 使用StringBuilder进行字符串拼接
        printCurrent(depth, name);
    }
}

// Composite: 实现组合节点,这里是目录
class Directory implements FileComponent {
    private String name;
    private List<FileComponent> components = new ArrayList<>();

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

    public void add(FileComponent component) {
        components.add(component);
    }

    public void remove(FileComponent component) {
        components.remove(component);
    }

    @Override
    public void display(int depth) {
        printCurrent(depth, name);

        for (FileComponent component : components) {
            component.display(depth + 1);
        }
    }

}

测试

// 客户端代码
public class FileSystemDemo {
    public static void main(String[] args) {
        Directory rootDir = new Directory("根目录");
        Directory docDir = new Directory("文档");
        Directory imgDir = new Directory("图片");

        File file1 = new File("报告.docx");
        File file2 = new File("照片.jpg");

        rootDir.add(docDir);
        rootDir.add(imgDir);
        docDir.add(file1);
        imgDir.add(file2);

        rootDir.display(0);
    }
}

框架示例

享元模式(Flyweight)

池化技术主要就是享元模式

使用示例

文本编辑器中的字符对象。假设我们正在开发一个支持多种字体、大小和颜色的文本编辑器,每个字符都有字体、大小和颜色等属性。在编辑器中,用户可能输入大量相同的字符,如空格、逗号、句号等。如果为每个字符创建一个完整的对象,内存消耗将会非常高。 现在,我们可以使用享元模式来优化这个问题。首先,我们将字符的字体、大小和颜色等属性视为外部状态,因为它们可以随上下文(每个字符的位置)而变化。而字符本身(如字母"A")是内部状态,是共享的,不随上下文改变。

// 享元接口
interface MyCharacter {
    void display(Font font, int size, Color color);
}

// 具体享元(内部状态)
class ConcreteCharacter implements MyCharacter {
    private final char value;

    public ConcreteCharacter(char value) {
        this.value = value;
    }

    @Override
    public void display(Font font, int size, Color color) {
        // 实际的显示逻辑,如绘制字符
        System.out.println("Displaying character: " + value
                + " with Font: " + font.getName()
                + ", Size: " + size + ", Color: " + color);
    }
}

// 外部状态(非享元)
class Context {
    private final char character;
    private final Font font;
    private final Color color;

    public Context(char character, Font font, Color color) {
        this.character = character;
        this.font = font;
        this.color = color;
    }

    public void display() {
        // 获取享元对象并传入外部状态
        MyCharacter flyweight = getFlyweight(character);
        flyweight.display(font, font.getSize(), color);
    }

    // 享元工厂
    private static final Map<Character, MyCharacter> flyweights = new HashMap<>();

    private static MyCharacter getFlyweight(char character) {
        if (!flyweights.containsKey(character)) {
            flyweights.put(character, new ConcreteCharacter(character));
        }
        return flyweights.get(character);
    }
}

测试

public class FlyweightPatternDemo {
    public static void main(String[] args) {
        Context context1 = new Context('A', new Font("Arial", Font.PLAIN, 12), Color.BLACK);
        context1.display();

        Context context2 = new Context('A', new Font("CourierNew", Font.PLAIN, 16), Color.BLUE);
        context2.display();

        Context context3 = new Context('B', new Font("TimesNewRoman", Font.PLAIN, 18), Color.RED);
        context3.display();
    }
}

框架示例

Boolean,Byte,Short,Integer,Long,Character 等包装类提供了 valueOf 方法,例如 Long 的 valueOf 会缓存 -128~127 之间的 Long 对象,在这个范围之间会重用对象,大于这个范围,才会新建 Long 对象

行为型(11)

模板方法模式(TemplateMethod)

使用示例

  • 整体步骤,固定的、通用的,抽象出父类,易变部分抽象出方法,供不同子类重写实现

  • 应用于

    • 数据库访问的封装
    • Junit单元测试
    • Hibernate中模板程序
    • HibernateTemplate
    • JavaWeb的Servlet中关于doGet/doPost方法调用
    • Spring中JDBCTemlate等

比如,银行业务办理流程,如下

abstract class BankTemplateMethod {
  // 1.取号排队
  public void takeNumber() {
		System.out.println("取号排队");
  }

  //2.办理业务,要被重写
  public abstract void transact();

  //3.反馈评分
  public void evaluate() {
      System.out.println("反馈评分");
  }

  // 模板,不被重写
  public final void process() {
      this.takeNumber();
      this.transact();
      this.evaluate();
  }
} 
  
  //具体业务1
class DrawMoney extends BankTemplateMethod {
    public void transact() {
        System.out.println("我要取款!!!");
    }
}

  //具体业务2
class ManageMoney extends BankTemplateMethod {
    public void transact() {
        System.out.println("我要理财!我这里有2000万美元!!");
    }
}

框架示例

策略模式(Strategy)

使用示例

假设我们有一个文本编辑器,用户可以选择不同的文字排列方式,如左对齐、右对齐和居中。我们可以为每种排列方式创建一个策略类,然后在上下文中使用这些策略

// 策略接口
interface TextAlignmentStrategy {
    void alignText(String text);
}

// 具体策略
class LeftAlignStrategy implements TextAlignmentStrategy {
    @Override
    public void alignText(String text) {
        System.out.println(text);
    }
}

class RightAlignStrategy implements TextAlignmentStrategy {
    @Override
    public void alignText(String text) {
        StringBuilder spaces = new StringBuilder();
        for (int i = 0; i < text.length(); i++) {
            spaces.append(" ");
        }
        System.out.println(spaces + text);
    }
}

class CenterAlignStrategy implements TextAlignmentStrategy {
    @Override
    public void alignText(String text) {
        int padding = (text.length() % 2 == 0) ? (text.length() / 2) : (text.length() / 2 + 1);
        StringBuilder leftSpaces = new StringBuilder();
        StringBuilder rightSpaces = new StringBuilder();
        for (int i = 0; i < padding; i++) {
            leftSpaces.append(" ");
        }
        for (int i = 0; i < text.length() - padding; i++) {
            rightSpaces.append(" ");
        }
        System.out.println(leftSpaces + text + rightSpaces);
    }
}

// 上下文
@Setter
class TextEditor {
    private TextAlignmentStrategy alignmentStrategy;

    public TextEditor(TextAlignmentStrategy strategy) {
        this.alignmentStrategy = strategy;
    }

    public void displayText(String text) {
        alignmentStrategy.alignText(text);
    }
}

测试

// 测试
public class StrategyPatternDemo {
    public static void main(String[] args) {
        TextEditor editor = new TextEditor(new LeftAlignStrategy());
        editor.displayText("Hello, World!");
        editor.setAlignmentStrategy(new RightAlignStrategy());
        editor.displayText("Hello, World!");
        editor.setAlignmentStrategy(new CenterAlignStrategy());
        editor.displayText("Hello, World!");
    }
}

框架示例

ThreadPoolExecutor 中的拒绝策略

// 策略接口
public interface RejectedExecutionHandler {
    void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}
// 具体策略
// AbortPolicy(默认):抛出RejectedExecutionException异常,阻止新任务的提交
public static class AbortPolicy implements RejectedExecutionHandler {

    public AbortPolicy() { }

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        throw new RejectedExecutionException("Task " + r.toString() +
                                             " rejected from " +
                                             e.toString());
    }
}
// CallerRunsPolicy:调用执行任务的当前线程(通常是调用execute方法的线程)来直接执行任务,这可能会阻塞调用者线程。
public static class CallerRunsPolicy implements RejectedExecutionHandler {

    public CallerRunsPolicy() { }

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) {
            r.run();
        }
    }
}
// DiscardPolicy:直接丢弃任务,不执行也不抛出异常
public static class DiscardPolicy implements RejectedExecutionHandler {

    public DiscardPolicy() {}

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    }
}
// DiscardOldestPolicy:丢弃队列中最旧的任务(也就是即将被执行的任务),然后尝试重新提交被拒绝的任务
public static class DiscardOldestPolicy implements RejectedExecutionHandler {

    public DiscardOldestPolicy() { }

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) {
            e.getQueue().poll();
            e.execute(r);
        }
    }
}

Spring 中很多地方用到了策略模式

  • AOP的切面实现: Spring AOP允许用户自定义通知(Advice),如Before、After、Around等。这些通知可以看作是策略,Spring在运行时根据配置选择合适的策略执行。例如,MethodBeforeAdvice、AfterReturningAdvice等接口定义了不同的通知策略。

  • Bean的初始化策略: Spring允许用户定义Bean的初始化策略,如init-method属性,或者通过实现InitializingBean接口的afterPropertiesSet()方法。这些不同的初始化方式可以被视为不同的策略。

  • ResourceLoader和Resource: Spring的Resource接口定义了资源访问的通用策略,而具体的实现(如ClassPathResource、UrlResource等)则提供了不同的访问策略。 ApplicationContext的事件发布: Spring允许注册事件监听器,这些监听器可以处理特定类型的事件,形成了事件处理的策略。例如,ApplicationListener接口定义了事件处理策略。

  • WebApplicationContext的MVC处理器: 在Spring MVC中,HandlerMapping和HandlerAdapter是策略模式的典型应用。它们分别负责映射请求到处理器和适配处理器到具体的处理方法。不同的实现类提供了不同的映射和适配策略。

    例如,HandlerMapping的接口定义了映射策略,而SimpleUrlHandlerMapping、BeanNameUrlHandlerMapping等类提供了具体的映射策略。同样,HandlerAdapter接口定义了处理策略,HttpRequestHandlerAdapter、SimpleControllerHandlerAdapter等类提供了不同的处理器适配策略

  • ConversionService: Spring的ConversionService允许注册自定义转换器,从而可以根据需要选择不同的转换策略

状态模式(State)

和策略模式比较类似, 状态模式存在状态的流转, 而策略模式无需改变状态

都包括三个角色

  • 环境(上下文)
  • 抽象状态(策略)
  • 具体状态(策略)

使用示例

假设我们有一个游戏中的角色,角色可以处于三种状态:正常状态、受伤状态和死亡状态。根据角色的状态,角色会有不同的行为和表现。

package com.jd.starlink.designPatterns.state;

import lombok.Setter;

// 状态接口
interface State {
    void handle();
}

// 具体状态:正常状态
class NormalState implements State {
    @Override
    public void handle() {
        System.out.println("角色处于正常状态,可以自由移动和攻击");
    }
}

// 具体状态:受伤状态
class InjuredState implements State {
    @Override
    public void handle() {
        System.out.println("角色受伤了,移动速度减慢,但仍可攻击");
    }
}

// 具体状态:死亡状态
class DeadState implements State {
    @Override
    public void handle() {
        System.out.println("角色已经死亡,无法移动和攻击");
    }
}

// 环境类:角色
@Setter
class Character {
    private State state;

    public Character() {
        // 默认状态为正常状态
        this.state = new NormalState();
    }

    public void performAction() {
        state.handle();
    }
}

// 客户端代码
public class Main {
    public static void main(String[] args) {
        Character character = new Character();

        // 角色处于正常状态
        character.performAction();

        // 角色受伤了
        character.setState(new InjuredState());
        character.performAction();

        // 角色死亡
        character.setState(new DeadState());
        character.performAction();
    }
}

购物车的状态,购物车可以有不同的状态,如空、添加商品、结算等

// 抽象状态类
abstract class ShoppingCartState {
    abstract void addItem(Item item, ShoppingCart cart);	// 添加商品
    abstract void removeItem(Item item, ShoppingCart cart);	// 移除商品
    abstract void checkout(ShoppingCart cart);				// 结算
}

// 具体状态类:空购物车
class EmptyCartState extends ShoppingCartState {
    @Override
    void addItem(Item item, ShoppingCart cart) {
        System.out.println("Adding item: " + item.getName());
        cart.getItems().add(item);
        cart.setState(new ItemAddedState(cart));
    }

    @Override
    void removeItem(Item item, ShoppingCart cart) {
        System.out.println("Cannot remove an item from an empty cart.");
    }

    @Override
    void checkout(ShoppingCart cart) {
        System.out.println("Cannot checkout an empty cart.");
    }
}

// 具体状态类:已添加商品
class ItemAddedState extends ShoppingCartState {
    public ItemAddedState(ShoppingCart cart) {
        cart.setState(this);
    }

    @Override
    void addItem(Item item, ShoppingCart cart) {
        System.out.println("Adding item: " + item.getName());
        cart.getItems().add(item);
    }

    @Override
    void removeItem(Item item, ShoppingCart cart) {
        System.out.println("Removing item: " + item.getName());
        cart.getItems().remove(item);
        if (cart.isEmpty()) {
            cart.setState(new EmptyCartState());
        }
    }

    @Override
    void checkout(ShoppingCart cart) {
        System.out.println("Checking out...");
        // 实现结账逻辑
        // ...
    }
}

// 上下文类:购物车
class ShoppingCart {
    @Getter
    private final List<Item> items;
    @Setter
    private ShoppingCartState state;

    public ShoppingCart() {
        items = new ArrayList<>();
        state = new EmptyCartState();
    }

    public void addItem(Item item) {
        state.addItem(item, this);
    }

    public void removeItem(Item item) {
        state.removeItem(item, this);
    }

    public void checkout() {
        state.checkout(this);
    }

    public boolean isEmpty() {
        return items.isEmpty();
    }
}

// 商品类
class Item {
    private String name;

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

    public String getName() {
        return name;
    }
}

测试


// 主程序
public class ShoppingCartStateMachine {
    public static void main(String[] args) {
        ShoppingCart cart = new ShoppingCart();
        cart.addItem(new Item("Product A"));
        cart.addItem(new Item("Product B"));
        cart.removeItem(new Item("Product A"));
        cart.checkout();
    }
}

框架示例

  • 用户认证和授权系统:在处理用户登录状态、权限状态时,如Spring Security框架中的认证流程,不同的认证状态(未认证、已认证、认证过期等)可以视为不同的状态,通过状态模式管理这些状态转换。
  • 工作流引擎:如Activiti、Camunda等BPMN工作流引擎,工作流中的任务节点根据不同的业务规则和条件进行状态迁移(如待办、处理中、已完成等),这种场景非常适合应用状态模式。
  • 网络编程中的连接状态管理:如Netty框架中,可以利用状态模式管理Socket连接的不同状态(连接建立、数据传输、断开连接等)。
  • 游戏开发中的角色状态:正如你提供的示例代码所示,游戏中角色的状态变化(正常、受伤、死亡等)非常符合状态模式的应用场景。
  • GUI应用程序:Swing或JavaFX等GUI框架中,组件的状态变化(如按钮的按下、释放状态,窗口的最小化、最大化状态等)也可以通过状态模式设计。
  • 线程池管理:在实现自定义线程池时,线程的生命周期管理(新建、就绪、运行、阻塞、死亡等)可以利用状态模式简化设计。

中介者模式(Mediator)

中介者模式可以讲网状结构变为星状结构

讲中介组合到同事类中

他和门面(外观)模式有点像

使用示例

飞机与塔台

package com.jd.starlink.designPatterns.mediator;

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

// 中介者接口
interface ATCMediator {
    void sendMessage(String message, Aircraft aircraft);
}

// 具体中介者:塔台
class AirTrafficControl implements ATCMediator {
    private final List<Aircraft> aircrafts;

    public AirTrafficControl() {
        this.aircrafts = new ArrayList<>();
    }

    @Override
    public void sendMessage(String message, Aircraft aircraft) {
        // 广播消息给所有其他飞机
        for (Aircraft a : aircrafts) {
            if (a != aircraft) {
                a.receiveMessage(message);
            }
        }
    }

    public void addAircraft(Aircraft aircraft) {
        aircrafts.add(aircraft);
    }
}

// 抽象同事类:飞机
abstract class Aircraft {
    protected ATCMediator mediator;
    protected String callSign;

    public Aircraft(ATCMediator mediator, String callSign) {
        this.mediator = mediator;
        this.callSign = callSign;
    }

    public abstract void sendMessage(String message);

    public abstract void receiveMessage(String message);
}

// 具体同事类:客机
class Airliner extends Aircraft {
    public Airliner(ATCMediator mediator, String callSign) {
        super(mediator, callSign);
    }

    @Override
    public void sendMessage(String message) {
        System.out.println(callSign + " 发送消息: " + message);
        mediator.sendMessage(message, this);
    }

    @Override
    public void receiveMessage(String message) {
        System.out.println(callSign + " 收到消息: " + message);
    }
}

// 具体同事类:军用飞机
class MilitaryAircraft extends Aircraft {
    public MilitaryAircraft(ATCMediator mediator, String callSign) {
        super(mediator, callSign);
    }

    @Override
    public void sendMessage(String message) {
        System.out.println(callSign + " 发送消息: " + message);
        mediator.sendMessage(message, this);
    }

    @Override
    public void receiveMessage(String message) {
        System.out.println(callSign + " 收到消息: " + message);
    }
}

// 客户端代码
public class Main {
    public static void main(String[] args) {
        AirTrafficControl atc = new AirTrafficControl();

        Aircraft airliner = new Airliner(atc, "AAL123");
        Aircraft militaryAircraft = new MilitaryAircraft(atc, "MAF456");

        atc.addAircraft(airliner);
        atc.addAircraft(militaryAircraft);

        airliner.sendMessage("请确认降落许可");
        militaryAircraft.sendMessage("请求飞行区域确认");
    }
}

框架示例

在Spring MVC中,DispatcherServlet 可以视为一个类似中介者的角色,它负责接收HTTP请求,然后根据请求信息(如URL、请求方法等)分发到合适的处理器(如控制器Controller)

观察者模式(Observer)

使用示例

主播与粉丝

// 抽象: 抖音主播
interface AbstractTicToker {
    // 添加粉丝
    void addFans(AbstractFans fans);

    // 通知粉丝
    void notifyFans(String msg);

}

// 具体被观察者: 美女主播
class MMTikToker implements AbstractTicToker {

    List<AbstractFans> fansList = new ArrayList<>();

    void startSell() {
        System.out.println("美女主播,开始卖货......");
        notifyFans("美女主播,开始卖货......");
    }

    void endSell() {
        System.out.println("美女下播了......");
        notifyFans("美女主播,停止卖货......");
    }

    @Override
    public void addFans(AbstractFans fans) {
        fansList.add(fans);
    }

    @Override
    public void notifyFans(String msg) {
        for (AbstractFans fans : fansList) {
            fans.acceptMsg(msg);
        }
    }
}

// 抽象观察者: 粉丝
interface AbstractFans {
    void acceptMsg(String msg);

    default void follow(AbstractTicToker ticToker) {
        ticToker.addFans(this);
    }

}
// 具体观察者
class HumanFans implements AbstractFans {
    @Override
    public void acceptMsg(String msg) {
        System.out.println("真实粉丝收到消息:" + msg);
    }
}

class RobotFans implements AbstractFans {
    @Override
    public void acceptMsg(String msg) {
        System.out.println("僵尸粉收到消息:" + msg);
    }
}

测试

public class MainTest {
    public static void main(String[] args) {
        MMTikToker tikToker = new MMTikToker();
        tikToker.addFans(new HumanFans());
        // 或
        new HumanFans().follow(tikToker);
        tikToker.addFans(new RobotFans());
        // 或
        new RobotFans().follow(tikToker);

        tikToker.startSell();
        tikToker.endSell();
    }
}

框架示例

备忘录模式(Memento)

  • Originator(发起者):发起者是创建备忘录的对象,它负责创建一个备忘录对象,这个对象包含其内部状态的副本。

    • 发起者通常有一个方法来创建备忘录,如createMemento(),并且有一个方法来恢复之前的状态,如restoreFromMemento()。
  • Memento(备忘录):备忘录对象存储发起者的内部状态,但不应提供修改这些状态的途径。它通常包含一组私有成员变量,用于保存发起者的状态。

    • 为了保护内部状态,备忘录类可能有私有的构造函数,仅由发起者访问。
  • CareTaker(管理者):管理者对象负责保存和管理备忘录,但它不应该了解备忘录的具体内容,只负责存储和提供备忘录。

    • 管理者通常有一个备忘录列表,用于存储多个状态点的备忘录。

使用示例

假设一个打游戏的场景, 实现存档和载入

// 备忘录
@Data
class GameRecord {
    private Integer id;
    private String name;
    private Date saveTime;
    private Integer score;
    private Integer level;
    private Integer money;
}

// 管理者
class GameServer {

    private final Map<Integer, GameRecord> history = new LinkedHashMap<>();
    private int i = 1;

    // 保存记录
    public void saveGameRecord(GameRecord gameRecord) {
        gameRecord.setSaveTime(new Date());
        if (StringUtils.isBlank(gameRecord.getName())) {
            gameRecord.setName(gameRecord.getSaveTime().toString());
        }
        history.put(i++, gameRecord);
    }

    // 获取记录
    public GameRecord getGameRecord(Integer id) {
        return history.get(id);
    }

    // 获取最后一次记录
    public GameRecord getLastGameRecord() {
        return history.get(history.size());
    }
}

// 发起者
@Data
class Gamer {
    private Integer score;
    private Integer level;
    private Integer money;

    private GameServer gameServer = new GameServer();

    // 存档
    void saveGameRecord() {
        System.out.println("保存记录......");
        GameRecord record = new GameRecord();
        BeanUtils.copyProperties(this, record);
        gameServer.saveGameRecord(record);
    }

    // 载入
    GameRecord loadGameRecord(Integer id) {
        System.out.println("加载历史记录......");
        return gameServer.getGameRecord(id);
    }

    // 继续
    GameRecord continueGame() {
        return gameServer.getLastGameRecord();
    }

    // 开始
    void playGame() {
        System.out.println("游戏进行中......");
        score = new Random().nextInt();
        level = new Random().nextInt();
        money = new Random().nextInt();
        System.out.println(this);
    }

    // 退出
    void exitGame() {
        System.out.println("退出游戏......存档......");
        saveGameRecord();
    }

}

测试

public class GameTest {
    public static void main(String[] args) {
        Gamer gamer = new Gamer();
        gamer.playGame();
        gamer.saveGameRecord();
        gamer.playGame();
        gamer.saveGameRecord();

        gamer.exitGame();
        GameRecord record = gamer.loadGameRecord(1);
        BeanUtils.copyProperties(record, gamer);
        System.out.println(gamer);
        record = gamer.continueGame();
        BeanUtils.copyProperties(record, gamer);
        System.out.println(gamer);

    }
}

框架示例

解释器模式(Interpreter)

解释器模式在一些解释器中大量使用, 它允许你定义一种语言的文法,并提供一个解释器来解释该语言中的句子。这种模式常用于简单的语言解析,例如配置文件、表达式计算等场景

使用示例

实现一个简单的四则运算表达式解释器,包括加法、减法、乘法和除法运算。

// 抽象表达式接口
interface Expression {
    int interpret();
}

// 终结表达式
class OperatorExpression implements Expression {
    @Getter
    private final char operator;
    @Setter
    private Expression left;
    @Setter
    private Expression right;

    public OperatorExpression(char op, Expression left, Expression right) {
        this.operator = op;
        this.left = left;
        this.right = right;
    }

    @Override
    public int interpret() {
        if (operator == '+') {
            return left.interpret() + right.interpret();
        } else if (operator == '-') {
            return left.interpret() - right.interpret();
        } else if (operator == '*') {
            return left.interpret() * right.interpret();
        } else if (operator == '/') {
            return left.interpret() / right.interpret();
        }
        throw new IllegalArgumentException("Unsupported operator");
    }
}

// 非终结表达式
class NumberExpression implements Expression {
    private final int number;

    public NumberExpression(int number) {
        this.number = number;
    }

    @Override
    public int interpret() {
        return number;
    }
}


class Calculator {

    public static int calculate(String expression) {
        Tokenizer tokenizer = new Tokenizer(expression);
        List<String> tokensList = tokenizer.tokensList();
        Stack<NumberExpression> numberStack = new Stack<>();// 数字
        Stack<OperatorExpression> operatorStack = new Stack<>();// 运算符
        for (String token : tokensList) { // 使用增强for循环遍历tokens
            if (Character.isDigit(token.charAt(0))) {
                numberStack.push(new NumberExpression(Integer.parseInt(token)));
            } else if (token.equals("(")) {
                operatorStack.push(new OperatorExpression('(', null, null));
            } else if (token.equals(")")) {

                calculateInParentheses(numberStack, operatorStack);
                // 左括号直接弹出
                operatorStack.pop();

            } else {
                operatorStack.push(new OperatorExpression(token.charAt(0), null, null));
            }
        }

        calculateInParentheses(numberStack, operatorStack);

        return numberStack.pop().interpret();
    }

    private static void calculateInParentheses(Stack<NumberExpression> numberStack, Stack<OperatorExpression> operatorStack) {
        while (!operatorStack.isEmpty() && !String.valueOf(operatorStack.peek().getOperator()).equals("(")) {
            OperatorExpression op = operatorStack.pop();    // 弹出一个运算符
             if (!operatorStack.isEmpty() && !String.valueOf(operatorStack.peek().getOperator()).equals("(")) {
                // 比较优先级
                OperatorExpression op2 = operatorStack.pop();
                if (precedence(op.getOperator()) < precedence(op2.getOperator())) {
                    // 优先级低
                    NumberExpression tmp = numberStack.pop();
                    op2.setRight(numberStack.pop());
                    op2.setLeft(numberStack.pop());
                    numberStack.push(new NumberExpression(op2.interpret()));
                    numberStack.push(tmp);
                    operatorStack.push(op);
                } else {
                    // 优先级高
                    op.setRight(numberStack.pop());
                    op.setLeft(numberStack.pop());
                    numberStack.push(new NumberExpression(op.interpret()));
                    operatorStack.push(op2);
                }
            } else {
                op.setRight(numberStack.pop());
                op.setLeft(numberStack.pop());
                numberStack.push(new NumberExpression(op.interpret()));
            }


        }
    }

    // 计算优先级
    private static int precedence(char op) {
        switch (op) {
            case '+':
            case '-':
                return 1;
            case '*':
            case '/':
                return 2;
            case '(':
                return 0;
            default:
                throw new IllegalArgumentException("Invalid operator: " + op);
        }
    }

    // 分词器
    public static class Tokenizer {
        private final String expression;

        public Tokenizer(String expression) {
            this.expression = expression;
        }

        public List<String> tokensList() {
            return Arrays.stream(expression.split("(?=[-+*/()])|(?<=[-+*/()])"))
                    .filter(StringUtils::isNotBlank)
                    .map(String::trim)
                    .collect(Collectors.toList());
        }

    }

}

public class InterpreterDemo {
    public static void main(String[] args) {
        System.out.println(Calculator.calculate("(20-(2*3 + 5)) * 2"));
    }
}

框架示例

命令模式(Command)

它将请求封装为一个对象,从而使得你可以通过不同的请求对客户端进行参数化,同时将发出请求的对象与知道如何实现该请求的对象解耦

命令模式我们天天在用,就像SpringMVC中Controller,Service,Dao的关系一样

想象一下电视遥控器的例子。遥控器(Invoker)上有多个按钮(每个按钮关联一个具体命令),按下按钮就是向遥控器发送一个请求。每个按钮背后关联的命令对象知道如何操作电视(Receiver),比如打开、关闭、换台等。这样,即使添加新的电视频道或功能(即新的命令),只需添加对应的新按钮和命令类,而无需改动遥控器的结构

使用示例

餐厅点餐系统。在这个例子中,我们可以将各个角色与命令模式的组件对应起来:

  • 客户(Customer):作为请求者(Invoker),客户选择菜品并下单。

  • 菜单(Menu):包含了各种菜品,相当于命令的接口,定义了所有的命令(如炒饭Command、炒菜Command等)。

  • 厨师(Cook):作为接收者(Receiver),厨师负责烹饪菜品。

  • 点菜命令(OrderCommand):这是具体命令(Concrete Command),它实现了菜单中的命令接口,负责将客户的订单传递给厨师进行烹饪。每个点菜命令对应一个菜品,如炒饭命令会调用厨师的炒饭方法。

  • 服务员(Waiter):服务员接收到客户的订单后,将订单转化为点菜命令,并将命令传递给厨师(调用execute方法)。

当客户(请求者)下单时,他会告诉服务员(Invoker)他要点什么菜。服务员创建相应的点菜命令(Concrete Command),然后调用命令的execute方法,将请求传递给厨师(Receiver)。厨师接到命令后,执行相应的烹饪操作。 如果要增加新的菜品,只需添加新的点菜命令类,不会影响现有的系统结构。同样,如果需要记录点菜历史,可以将命令对象保存下来,以便后续查看或撤销操作。

package com.jd.starlink.designPatterns.command;

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

// 菜单接口,即命令接口
interface Menu {
    void execute();
}

// 厨师类似DAO,负责执行烹饪任务,即接收者
class Cook {
    public void makeFriedRice() {
        System.out.println("正在烹饪炒饭...");
    }

    public void makeStirFry() {
        System.out.println("正在烹饪炒菜...");
    }
}

// 点菜命令类似Service,实现了菜单接口
class OrderFriedRice implements Menu {
    private final Cook cook;

    public OrderFriedRice(Cook cook) {
        this.cook = cook;
    }

    @Override
    public void execute() {
        cook.makeFriedRice();
    }
}

class OrderStirFry implements Menu {
    private final Cook cook;

    public OrderStirFry(Cook cook) {
        this.cook = cook;
    }

    @Override
    public void execute() {
        cook.makeStirFry();
    }
}

// 服务员类似Controller,负责接收客户订单并传达给厨师
class Waiter {
    private final List<Menu> orders;

    public Waiter() {
        this.orders = new ArrayList<>();
    }

    public void takeOrder(Menu order) {
        orders.add(order);
        order.execute();
    }
}

public class CommandPatternDemo {
    public static void main(String[] args) {
        // 创建接收者
        Cook cook = new Cook();

        // 创建服务员
        Waiter waiter = new Waiter();

        // 客户点餐
        Menu friedRiceOrder = new OrderFriedRice(cook);
        Menu stirFryOrder = new OrderStirFry(cook);

        // 服务员接收并执行订单
        waiter.takeOrder(friedRiceOrder);
        waiter.takeOrder(stirFryOrder);
    }
}

框架示例

迭代器模式

它提供了一种访问聚合对象(如集合、列表等)内元素的方式,而无需了解聚合对象的底层结构。迭代器模式使得遍历操作与聚合对象的内部结构分离,提高了聚合的灵活性和复用性

使用示例

动物园游览。在这个场景中,动物园(聚合)包含多个动物(元素),游客(客户端)希望按特定顺序参观这些动物,而不需要知道动物的存储方式或者动物园的布局

package com.jd.starlink.designPatterns.iterator;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;

// 动物园接口,定义创建迭代器的方法
interface Zoo {
    Iterator<Animal> createIterator();
    void addAnimal(Animal animal);
}

// 动物接口
interface Animal {
    String getName();
}

// 具体动物
class Lion implements Animal {
    @Override
    public String getName() {
        return "Lion";
    }
}

class Monkey implements Animal {
    @Override
    public String getName() {
        return "Monkey";
    }
}


// 具体动物区,哺乳动物区
class MammalsZone implements Zoo {
    private final List<Animal> mammals = new ArrayList<>();

    public void addAnimal(Animal animal) {
        mammals.add(animal);
    }

    @Override
    public Iterator<Animal> createIterator() {
        return new MammalsZoneIterator(mammals);
    }
}

// 哺乳动物区迭代器
class MammalsZoneIterator implements Iterator<Animal> {
    private final List<Animal> mammals;
    private int currentIndex = 0;

    public MammalsZoneIterator(List<Animal> mammals) {
        this.mammals = mammals;
    }

    @Override
    public boolean hasNext() {
        return currentIndex < mammals.size();
    }

    @Override
    public Animal next() {
        if (hasNext()) {
            return mammals.get(currentIndex++);
        } else {
            throw new NoSuchElementException();
        }
    }
}

// 游客
public class Visitor {
    public static void main(String[] args) {
        Zoo zoo = new MammalsZone();
        Iterator<Animal> iterator = zoo.createIterator();

        zoo.addAnimal(new Lion());
        zoo.addAnimal(new Monkey());
        while (iterator.hasNext()) {
            Animal animal = iterator.next();
            System.out.println(animal.getName());
        }
    }
}

框架示例

责任链模式

使用示例

公司的请假审批流程, 员工提交请假申请后,会依次经过不同级别的经理进行审批。每个经理可以批准或拒绝申请,并将申请传递给下一个级别(如果有的话)000

package com.jd.starlink.designPatterns.chain;

import lombok.Getter;
import lombok.Setter;

// 抽象审批者
@Setter
abstract class Approver {
    protected Approver successor;

    public abstract void approveLeaveRequest(LeaveRequest request);
    public abstract void rejectLeaveRequest(LeaveRequest request);
}

// 具体审批者:部门经理
class DepartmentManager extends Approver {
    @Override
    public void approveLeaveRequest(LeaveRequest request) {
        if (request.getDaysOff() <= 3) {
            System.out.println("Department Manager approved the request.");
        } else if (successor != null) {
            successor.approveLeaveRequest(request);
        } else {
            System.out.println("Department Manager rejected the request as no higher approver is available.");
        }
    }

    @Override
    public void rejectLeaveRequest(LeaveRequest request) {
        System.out.println("Department Manager rejected the request.");
    }
}

// 具体审批者:总监
class Director extends Approver {
    @Override
    public void approveLeaveRequest(LeaveRequest request) {
        if (request.getDaysOff() <= 7) {
            System.out.println("Director approved the request.");
        } else if (successor != null) {
            successor.approveLeaveRequest(request);
        } else {
            System.out.println("Director rejected the request as no higher approver is available.");
        }
    }

    @Override
    public void rejectLeaveRequest(LeaveRequest request) {
        System.out.println("Director rejected the request.");
    }
}


// 新的审批者:总裁
class President extends Approver {
    @Override
    public void approveLeaveRequest(LeaveRequest request) {
        if (request.getDaysOff() > 7) {
            System.out.println("President approved the request.");
        } else {
            System.out.println("President rejected the request as it should be handled by lower approvers.");
        }
    }

    @Override
    public void rejectLeaveRequest(LeaveRequest request) {
        System.out.println("President rejected the request.");
    }
}
// 请假申请
@Getter
class LeaveRequest {
    private final Employee employee;
    private final int daysOff;

    public LeaveRequest(Employee employee, int daysOff) {
        this.employee = employee;
        this.daysOff = daysOff;
    }

}

// 员工
@Getter
class Employee {
    private final String name;

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

}
// 客户端
public class LeaveApprovalDemo {
    public static void main(String[] args) {
        Employee employee = new Employee("Alice");
        LeaveRequest request = new LeaveRequest(employee, 10);

        Approver departmentManager = new DepartmentManager();
        Approver director = new Director();
        Approver president = new President();
        departmentManager.setSuccessor(director);
        director.setSuccessor(president);

        departmentManager.approveLeaveRequest(request);
    }
}

框架示例

访问者模式

允许在不修改对象结构的情况下,为对象结构中的元素添加新的操作。这种模式主要用于处理具有复杂对象结构的情况,使得访问者可以访问结构中的每一个元素,并执行特定的操作

  • 访问者(Visitor):定义了一个访问和操作元素的接口,每个访问者都对应一个访问操作。
  • 具体访问者(Concrete Visitor):实现了访问者接口,为每个元素提供具体的访问操作实现。
  • 元素接口(Element):定义了一个接受访问者的接口,通常包含一个 accept 方法,该方法接受一个访问者对象作为参数。
  • 具体元素(Concrete Element):实现了元素接口,提供了 accept 方法的实现,将访问者对象传递给访问者,并允许访问者访问和操作自己。
  • 对象结构(Object Structure):通常是一个容器或集合,可以存储各种元素,并支持遍历其包含的所有元素,允许访问者访问这些元素。

适用场景:

  • 当需要在不改变元素类的前提下,为元素增加新的操作。

  • 当元素类层次结构的静态结构不变,但经常需要在此基础上增加新操作。

  • 当需要对一批对象进行相同的操作,但这些对象属于不同的类,且这些类不具备统一的超类。

优点:

  • 增加新的操作很容易:只需创建一个新的访问者类,而无需修改元素类。
  • 结构独立:访问者与元素之间的耦合度较低,访问者只关注如何访问,不关心元素的内部实现。

缺点:

  • 违反了“开闭原则”:如果增加新的元素类,可能需要修改访问者接口,这违背了“对扩展开放,对修改关闭”的原则。
  • 结构复杂:当对象结构复杂时,访问者模式可能会导致代码过于复杂,难以理解和维护。
  • 限制扩展:访问者模式限制了对象结构的扩展,如果对象结构需要改变,可能需要修改大量的访问者类。

使用示例

假设我们有一个简单的对象结构,包含两种类型的形状(圆形和矩形),现在我们需要为这些形状计算周长和面积

// 访问者接口
interface ShapeVisitor {
    void visit(Circle circle);
    void visit(Rectangle rectangle);
}

// 元素接口
interface Shape {
    void accept(ShapeVisitor visitor);
}

// 具体元素:圆形
@Data
class Circle implements Shape {
    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    @Override
    public void accept(ShapeVisitor visitor) {
        visitor.visit(this);
    }
}

// 具体元素:矩形
@Data
class Rectangle implements Shape {
    private double width;
    private double height;

    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }

    @Override
    public void accept(ShapeVisitor visitor) {
        visitor.visit(this);
    }

    // 其他属性和方法...
}

// 具体访问者:计算周长
class PerimeterCalculator implements ShapeVisitor {
    @Override
    public void visit(Circle circle) {
        System.out.println("Circle perimeter: " + 2 * Math.PI * circle.getRadius());
    }

    @Override
    public void visit(Rectangle rectangle) {
        System.out.println("Rectangle perimeter: " + 2 * (rectangle.getWidth() + rectangle.getHeight()));
    }
}

// 具体访问者:计算面积
class AreaCalculator implements ShapeVisitor {
    @Override
    public void visit(Circle circle) {
        System.out.println("Circle area: " + Math.PI * Math.pow(circle.getRadius(), 2));
    }

    @Override
    public void visit(Rectangle rectangle) {
        System.out.println("Rectangle area: " + rectangle.getWidth() * rectangle.getHeight());
    }
}

// 客户端代码
public class VisitorPatternDemo {
    public static void main(String[] args) {
        Shape circle = new Circle(5);
        Shape rectangle = new Rectangle(4, 3);

        ShapeVisitor perimeterCalculator = new PerimeterCalculator();
        circle.accept(perimeterCalculator);
        rectangle.accept(perimeterCalculator);

        ShapeVisitor areaCalculator = new AreaCalculator();
        circle.accept(areaCalculator);
        rectangle.accept(areaCalculator);
    }
}

框架示例

上次编辑于:
贡献者: ext.liyuanhao3,liyuanhao,李元昊