模式组合应用-装饰器模式

写在前面

Hello,我是易元,这篇文章是我学习设计模式时的笔记和心得体会。如果其中有错误,欢迎大家留言指正!


装饰器模式

定义

结构型设计模式, 旨在动态地给一个对象添加额外的职责。在不改变原有对象结构的基础上, 通过包装的方式, 为对象提供新的功能。

通过引入一个抽象的装饰器类和具体的装饰器实现, 将核心功能和附加功能解耦, 从而实现功能的灵活组合和扩展。

角色

装饰器模式的核心思想是 透明地动态扩展对象功能, 它将核心功能和附加功能拆分为两个独立的层次结构: 组件和装饰器。

  • 抽象组件: 定义一个接口或抽象类, 是具体组件和装饰器的共同父类。定义了客户端可以调用的基本操作。
  • 具体组件: 实现抽象组件接口, 是原始的、未被装饰的对象。实现了抽象组件定义的基本操作。
  • 抽象装饰器: 实现抽象组件接口, 并持有一个抽象组件的引用。它是所有具体装饰器的抽象父类。
  • 具体装饰器: 继承抽象装饰器, 并实现特定的装饰功能。每个具体装饰器都代表一个要添加的新职责。

使用时主要思考点

  • 当需要动态地给一个对象添加功能, 且这些功能可以独立地被添加或移除时, 可以选择装饰器模式, 若功能是静态的, 或者只有少数几种组合, 那么 继承可以更简单。
  • 装饰器和被装饰器的对象都实现了相同的接口或继承相同的抽象类, 确保了客户端代码可以无差别的调用同一个接口。
  • 明确哪些是对象的基本、核心职责, 哪些是可以在运行时动态添加的附加职责。核心功能应由具体组件实现, 附加功能由装饰器实现。

装饰器模式回顾

场景

假设我们正在开发一个在线咖啡店的订单系统, 顾客可以点不同种类的咖啡, 并且可以添加各种配料, 如牛奶、糖、巧克力等。每种咖啡和配料都有基 础价格, 配料的价格会叠加到咖啡的总价上。 包含以下几种咖啡: 浓缩咖啡(10元)、美式咖啡(12元) 可以添加配料如下: 牛奶(2元)、糖(1元)、巧克力(3元)

实现思路

将咖啡和配料分别抽象为不同的类:

  • 咖啡种类作为具体组件, 实现统一的咖啡接口
  • 配料作为装饰器, 继承自抽象装饰器类, 并通过组合方式包装咖啡对象。
代码内容

根据装饰器模式的角色进行代码划分 并对代码进行统一的解释,不需要在代码中增加注释,在每个小节后进行解释。

抽象组件
csharp 复制代码
public interface Coffee {

    String getDescription();

    double getCost();

}
  • 接口定义了咖啡的基本行为, 包含两个方法获取描述( getDescription() )及价格( getCost ), 所有具体咖啡和装饰器都必须实现该接口。
  • 作为顶层接口, 保证装饰器和组件之间的兼容性。
具体组件
typescript 复制代码
// 美式咖啡
public class Americano implements Coffee {

    @Override
    public String getDescription() {
        return "美式咖啡";
    }

    @Override
    public double getCost() {
        return 12.0;
    }
}
public class Espresso implements Coffee {

    @Override
    public String getDescription() {
        return "浓缩咖啡";
    }

    @Override
    public double getCost() {
        return 10.0;
    }
}
  • AmericanoEspresso是具体组件类, 分别表示一种基础咖啡类型, 实现了Coffee接口中定义的方法, 提供咖啡的基础行为。
  • 作为被装饰的基础对象, 通过装饰器扩展功能。
抽象装饰器
java 复制代码
public abstract class CondimentDecorator implements Coffee {

    protected Coffee coffee;

    public CondimentDecorator(Coffee coffee) {
        this.coffee = coffee;
    }

    @Override
    public abstract String getDescription();

    @Override
    public abstract double getCost();
}
  • CondimentDecorator 是抽象装饰器类, 也实现了 Coffee 接口, 并持有一个 Coffee 类型的引用。子类通过该引用调用被装饰对象的方 法, 并在其基础上添加新的行为。
  • 通过构造函数注入被装饰对象, 实现装饰器链的嵌套。
具体装饰器
java 复制代码
public class MilkDecorator extends CondimentDecorator {

    public MilkDecorator(Coffee coffee) {
        super(coffee);
    }

    @Override
    public String getDescription() {
        return coffee.getDescription() + ", 加牛奶";
    }

    @Override
    public double getCost() {
        return coffee.getCost() + 2.0;
    }
}

public class SugarDecorator extends CondimentDecorator {

    public SugarDecorator(Coffee coffee) {
        super(coffee);
    }

    @Override
    public String getDescription() {
        return coffee.getDescription() + ", 加糖";
    }

    @Override
    public double getCost() {
        return coffee.getCost() + 1.0;
    }
}

public class ChocolateDecorator extends CondimentDecorator {

    public ChocolateDecorator(Coffee coffee) {
        super(coffee);
    }

    @Override
    public String getDescription() {
        return coffee.getDescription() + ", 加巧克力";
    }

    @Override
    public double getCost() {
        return coffee.getCost() + 3.0;
    }
}
  • MilkDecoratorSugarDecoratorChocolateDecorator 是具体装饰器类, 分别表示不同的配料。
  • 通过构造函数接收一个 Coffee 对象, 并在其基础上增加相应的描述和价格。
测试类
csharp 复制代码
public class DecoratorCoffeeTest {

    @Test
    public void test_coffee() {

        Coffee coffee1 = new Espresso();
        System.out.println("订单1: " + coffee1.getDescription() + ", 价格: " + coffee1.getCost() + "元");

        Coffee coffee2 = new MilkDecorator(new Espresso());
        System.out.println("订单2: " + coffee2.getDescription() + ", 价格: " + coffee2.getCost() + "元");

        Coffee coffee3 = new SugarDecorator(new MilkDecorator(new Americano()));
        System.out.println("订单3: " + coffee3.getDescription() + ", 价格: " + coffee3.getCost() + "元");

    }

}
运行结果
makefile 复制代码
订单1: 浓缩咖啡, 价格: 10.0元
订单2: 浓缩咖啡, 加牛奶, 价格: 12.0元
订单3: 美式咖啡, 加牛奶, 加糖, 价格: 15.0元

Process finished with exit code 0

装饰器模式+工厂方法模式

工厂方法模式(创建型)

定义一个用于创建对象的接口, 但让子类决定实例化哪一个, 工厂方法让类的实例化延迟到子类。它的核心思想是将对象的创建与使用分离, 从而使得系统在增加新产品时, 无需修改客户端代码, 只需要增加新的具体产品类和对应的具体工厂类。

案例

准备开发一个在线游戏角色系统, 玩家可以选择不同的基础角色(如战士、法师), 并且可以为这些角色穿戴各种装备(如盔甲、武器、魔法卷轴), 每种装备都能为角色提供额外的属性加成或特殊能力。随着游戏的发展, 会不断退出新的角色类型和新的装备。我们希望能够动态的创建角色, 并为它们动态穿戴装备, 同事避免在客户端代码中之间处理复杂的对象创建逻辑和装备组合逻辑。

模式职责

  • 装饰器模式: 负责动态地为游戏角色添加状态和属性加成, 从而避免修改基础角色类的代码。每件装备都可以抽象为一个装饰器。
  • 工厂方法模式: 负责创建不同类型的游戏角色对象。提供一个统一的接口来创建角色, 具体的角色创建逻辑由不同的具体工厂子类来实现。这样, 当有新的角色类型或新的装备组合方式时, 只需添加新的工厂子类, 而无需修改客户端代码。

代码内容

装饰器模式部分
抽象组件
csharp 复制代码
public interface GameCharacter {

    String getName();

    int getAttack();

    int getDefense();

    String getDescription();

}
  • 定义游戏角色的基本行为, 包括获取名称( getName() )、攻击力( getAttack() )、防御力( getDefense() )和描述( getDescription() )。
具体组件
typescript 复制代码
public class Warrior implements GameCharacter {

    @Override
    public String getName() {
        return "战士";
    }

    @Override
    public int getAttack() {
        return 100;
    }

    @Override
    public int getDefense() {
        return 80;
    }

    @Override
    public String getDescription() {
        return "英勇的战士";
    }
}
public class Mage implements GameCharacter {

    @Override
    public String getName() {
        return "法师";
    }

    @Override
    public int getAttack() {
        return 120;
    }

    @Override
    public int getDefense() {
        return 50;
    }

    @Override
    public String getDescription() {
        return "神秘的法师";
    }
}
  • 实现了 GameCharacter 接口, 代表基础的游戏角色: 战士( Warrior ) 和 法师( Mage )
  • 提供了未被任何装备装饰的原始角色对象
抽象装饰器
typescript 复制代码
public class EquipmentDecorator implements GameCharacter {

    protected GameCharacter character;

    public EquipmentDecorator(GameCharacter character) {
        this.character = character;
    }

    @Override
    public String getName() {
        return character.getName();
    }

    @Override
    public int getAttack() {
        return character.getAttack();
    }

    @Override
    public int getDefense() {
        return character.getDefense();
    }

    @Override
    public String getDescription() {
        return character.getDescription();
    }
}
  • 实现了 GameCharacter 接口, 并持有一个 GameCharacter 类型的 character 成员变量
  • 构造函数接收一个 GameCharacter 对象, 代表要装饰的对象。
  • 默认转发所有 GameCharacter 接口的方法调用给其内部持有的 character 对象, 子类可以重写这些方法来添加新的行为或修改原有行为。
具体装饰器
java 复制代码
public class ArmorDecorator extends EquipmentDecorator {

    public ArmorDecorator(GameCharacter character) {
        super(character);
    }

    @Override
    public int getDefense() {
        return super.getDefense() + 50;
    }

    @Override
    public String getDescription() {
        return super.getDescription() + ", 穿戴盔甲";
    }

}
public class WeaponDecorator extends EquipmentDecorator {

    public WeaponDecorator(GameCharacter character) {
        super(character);
    }

    @Override
    public int getAttack() {
        return super.getAttack() + 30;
    }

    @Override
    public String getDescription() {
        return super.getDescription() + ", 装备武器";
    }
}
public class ScrollDecorator extends EquipmentDecorator {

    public ScrollDecorator(GameCharacter character) {
        super(character);
    }

    @Override
    public int getAttack() {
        return super.getAttack() + 10;
    }

    @Override
    public int getDefense() {
        return super.getDefense() + 10;
    }

    @Override
    public String getDescription() {
        return super.getDescription() + ", 学习魔法卷轴";
    }
}
  • 继承 EquipmentDecorator, 它们分别代表了盔甲( ArmorDecorator )、武器( WeaponDecorator )和魔法卷轴( ScrollDecorator )三种装备。
  • 每个装饰器都重写各自所具有的行为, 在调用被装饰对象的相应方法的基础上, 添加自己的额外功能(如: 增加防御、增加攻击、修改描述)
工厂方法模式部分
抽象工厂
csharp 复制代码
public interface CharacterFactory {

    GameCharacter createCharacter();

}
  • 定义了一个工厂方法 createCharacter(), 用于创建 GameCharacter 对象
  • 将对象的创建过程抽象化, 使得客户端无需关心具体创建哪个角色以及如何创建
具体工厂
csharp 复制代码
public class WarriorFactory implements CharacterFactory {
    @Override
    public GameCharacter createCharacter() {

        System.out.println("战士工厂: 正在创建基础战士...");
        System.out.println("战士工厂: 为战士穿戴默认盔甲和武器...");
        return new WeaponDecorator(new ArmorDecorator(new Warrior()));
    }
}
public class MageFactory implements CharacterFactory {
    @Override
    public GameCharacter createCharacter() {

        System.out.println("法师工厂: 正在创建基础法师...");
        System.out.println("法师工厂: 为法师学习魔法卷轴...");
        return new ScrollDecorator(new Mage());
    }
}
  • 实现了 CharacterFactory 接口, 分别负责创建战士和法师
  • createCharacter() 方法中, 不仅实例化了基础的 WarriorMage 对象, 还利用装饰器模式, 为基础角色默认添加了特定的装备, 封装了装饰器模式。
测试类
csharp 复制代码
public class GameCharacterTest {

    @Test
    public void test_warrior() {

        System.out.println("\n--- 通过战士工厂创建角色 ---");
        CharacterFactory warriorFactory = new WarriorFactory();
        GameCharacter warrior = warriorFactory.createCharacter();
        System.out.println("创建完成的角色: "
                + warrior.getName()
                + ", 描述: "
                + warrior.getDescription()
                + ", 攻击力: "
                + warrior.getAttack()
                + ", 防御力: "
                + warrior.getDefense()
        );

    }

    @Test
    public void test_mage() {

        System.out.println("\n--- 通过法师工厂创建角色 ---");
        CharacterFactory mageFactory = new MageFactory();
        GameCharacter mage = mageFactory.createCharacter();
        System.out.println("创建完成的角色: "
                + mage.getName()
                + ", 描述: "
                + mage.getDescription()
                + ", 攻击力: "
                + mage.getAttack()
                + ", 防御力: "
                + mage.getDefense()
        );

    }

}
运行结果
makefile 复制代码
--- 通过法师工厂创建角色 ---
法师工厂: 正在创建基础法师...
法师工厂: 为法师学习魔法卷轴...
创建完成的角色: 法师, 描述: 神秘的法师, 学习魔法卷轴, 攻击力: 130, 防御力: 60

--- 通过战士工厂创建角色 ---
战士工厂: 正在创建基础战士...
战士工厂: 为战士穿戴默认盔甲和武器...
创建完成的角色: 战士, 描述: 英勇的战士, 穿戴盔甲, 装备武器, 攻击力: 130, 防御力: 130

Process finished with exit code 0

组合优势

  • 工厂方法模式将创建复杂对象的逻辑封装, 客户端无需了解角色如何被装饰创建, 只需要通过工厂方法获取所需角色, 降低客户端的耦合度。
  • 新增角色类型或新的默认装备组合时, 只需要增加新的具体工厂类和相应的装饰器类, 无需修改现有代码。

装饰器模式+策略模式

策略模式(行为型)

定义了一系列算法, 将每个算法封装起来, 并使它们可以互相替换。 策略模式让算法独立于使用它的客户端而变化,核心思想是将算法的实现与使用算法的客户端分离, 使得客户端可以在运行时选择不同的算法, 而无需修改客户端代码, 使得算法可以独立地演化和扩展。

案例

假设需要实现一个在线支付系统, 用户可以选择不同的支付方式(如: 信用卡支付、借记卡支付、第三方支付), 每种支付方式都有其独特的处理逻辑,此外, 支付过程可能还需要额外的安全措施, 例如支付钱进行风险评估、支付后进行积分计算等。用户希望能够灵活地选择支付方式, 并能动态地为支付过程添加或移除这些安全或增值服务, 而无需修改核心支付逻辑。

模式职责

  • 装饰器模式: 负责动态地位支付策略添加额外的处理逻辑, 如风险评估、日志记录、积分计算等
  • 策略模式: 负责封装不同的支付算法, 它定义了一个统一的支付接口, 并提供多种具体的支付实现, 使得客户端可以在运行时选择并切换不同的支付方式。

代码内容

抽象策略/抽象组件
csharp 复制代码
public interface PaymentStrategy {

    void pay(double amount);

    String getDescription();

}
  • 定义了具体支付策略必须实现的接口
  • pay(double amount) 定义支付接口, 传入 amount 支付金额。
  • getDescription() 定义获取当前支付方式的描述。
具体策略/具体组件
typescript 复制代码
public class CreditCardPayment implements PaymentStrategy {

    private String cardNumber;

    public CreditCardPayment(String cardNumber) {
        this.cardNumber = cardNumber;
    }

    @Override
    public void pay(double amount) {
        System.out.println("使用信用卡 " + cardNumber + " 支付 " + amount + " 元。");
    }

    @Override
    public String getDescription() {
        return "信用卡支付";
    }
}
public class DebitCardPayment implements PaymentStrategy {

    private String accountNumber;

    public DebitCardPayment(String accountNumber) {
        this.accountNumber = accountNumber;
    }

    @Override
    public void pay(double amount) {
        System.out.println("使用借记卡 " + accountNumber + " 支付 " + amount + " 元。");
    }

    @Override
    public String getDescription() {
        return "借记卡支付";
    }
}
public class ThirdPartyPayment implements PaymentStrategy {

    private String platformName;

    public ThirdPartyPayment(String platformName) {
        this.platformName = platformName;
    }

    @Override
    public void pay(double amount) {
        System.out.println("使用第三方支付平台 " + platformName + " 支付 " + amount + " 元。");
    }

    @Override
    public String getDescription() {
        return "第三方支付(" + platformName + ")";
    }
}
  • 实现了 PaymentStrategy 接口, 提供了信用卡( CreditCardPayment )、借记卡( DebitCardPayment )和第三方支付( ThirdPartyPayment )的具体支付逻辑, 且代表了原始的支付方式。
  • pay() 方法执行各自的支付操作, getDescription() 方法返回其基本描述。
装饰器模式部分
抽象装饰器
typescript 复制代码
public class PaymentDecorator implements PaymentStrategy {

    protected PaymentStrategy decoratedPayment;

    public PaymentDecorator(PaymentStrategy decoratedPayment) {
        this.decoratedPayment = decoratedPayment;
    }

    @Override
    public void pay(double amount) {
        decoratedPayment.pay(amount);
    }

    @Override
    public String getDescription() {
        return decoratedPayment.getDescription();
    }
}
  • 实现了 PaymentStrategy 接口, 并持有一个 PaymentStrategy 类型的 decoratedPayment 成员变量
  • 构造函数接收一个 PaymentStrategy 对象, 表示它将要装饰的支付策略
  • 默认转发 pay()getDescription() 方法调用给其内部持有的 decoratedPayment 对象
具体装饰器
java 复制代码
public class RiskAssessmentDecorator extends PaymentDecorator {

    public RiskAssessmentDecorator(PaymentStrategy decoratedPayment) {
        super(decoratedPayment);
    }

    @Override
    public void pay(double amount) {
        System.out.println("执行风险评估...");

        if (amount > 1000) {
            System.out.println("金额较大, 建议进行二次验证。");
        }
        super.pay(amount);

        System.out.println("风险评估完成。");
    }

    @Override
    public String getDescription() {
        return super.getDescription() + " + 风险评估";
    }
}
public class LoyaltyPointDecorator extends PaymentDecorator {

    public LoyaltyPointDecorator(PaymentStrategy decoratedPayment) {
        super(decoratedPayment);
    }

    @Override
    public void pay(double amount) {
        super.pay(amount);
        int points = (int) (amount * 0.1);
        System.out.println("计算积分: 获得 " + points + " 积分");
    }

    @Override
    public String getDescription() {
        return super.getDescription() + " + 积分计算";
    }
}
public class LoggingDecorator extends PaymentDecorator {

    public LoggingDecorator(PaymentStrategy decoratedPayment) {
        super(decoratedPayment);
    }

    @Override
    public void pay(double amount) {
        System.out.println("记录支付日志: 支付金额 " + amount + ", 支付方式: " + decoratedPayment.getDescription() + "。");
        super.pay(amount);
        System.out.println("支付日志记录完成。");
    }

    @Override
    public String getDescription() {
        return super.getDescription() + " + 日志记录";
    }
}
  • 继承了 PaymentDecorator, 分别代表了风险评估( RiskAssessmentDecorator )、积分计算( LoyaltyPointDecorator )和日志记录( LoggingDecorator )三种附加功能。
  • 重写 pay() 方法, 在调用 super.pay(amount) 方法之前或折后添加自己的特定逻辑。
  • 重写 getDescription() 方法, 在原有基础上添加自己的功能描述。
策略模式部分
上下文
csharp 复制代码
public class PaymentProcessor {

    private PaymentStrategy paymentStrategy;

    public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
        this.paymentStrategy = paymentStrategy;
    }

    public void processPayment(double amount) {
        if (paymentStrategy == null) {
            System.out.println("未设置支付策略");
            return;
        }

        System.out.println("\n--- 开始处理支付, 支付方式: " + paymentStrategy.getDescription() + " ---");
        paymentStrategy.pay(amount);
        System.out.println("--- 支付处理完成 ---");
    }
}
  • 持有一个 PaymentStrategy 类型引用, 并提供 setPaymentStrategy() 方法来设置具体的支付策略, 以及 processPayment() 方法来执行支付操作。
  • PaymentProcessor 不关心具体的支付方式或附加功能, 只通过 PaymentStrategy 接口与它们交互, 从而实现了客户端与具体策略的解耦。
测试类
csharp 复制代码
public class PayTest {

    @Test
    public void test_payment() {

        PaymentProcessor processor = new PaymentProcessor();

        System.out.println("\n 场景1: 简单的信用卡支付");
        processor.setPaymentStrategy(new CreditCardPayment("1234-5678-9012-3456"));
        processor.processPayment(150.0);

        System.out.println("\n 场景2: 借记卡支付, 并进行风险评估和积分计算");
        processor.setPaymentStrategy(new RiskAssessmentDecorator(new LoyaltyPointDecorator(new DebitCardPayment("987654321"))));
        processor.processPayment(1200.0);

        System.out.println("\n 场景3: 第三方支付, 并记录日志");
        processor.setPaymentStrategy(new LoggingDecorator(new ThirdPartyPayment("支付宝")));
        processor.processPayment(500.0);

    }

}
运行结果
lua 复制代码
 场景1: 简单的信用卡支付

--- 开始处理支付, 支付方式: 信用卡支付 ---
使用信用卡 1234-5678-9012-3456 支付 150.0 元。
--- 支付处理完成 ---

 场景2: 借记卡支付, 并进行风险评估和积分计算

--- 开始处理支付, 支付方式: 借记卡支付 + 积分计算 + 风险评估 ---
执行风险评估...
金额较大, 建议进行二次验证。
使用借记卡 987654321 支付 1200.0 元。
计算积分: 获得 120 积分
风险评估完成。
--- 支付处理完成 ---

 场景3: 第三方支付, 并记录日志

--- 开始处理支付, 支付方式: 第三方支付(支付宝) + 日志记录 ---
记录支付日志: 支付金额 500.0, 支付方式: 第三方支付(支付宝)。
使用第三方支付平台 支付宝 支付 500.0 元。
支付日志记录完成。
--- 支付处理完成 ---

Process finished with exit code 0

组合优势

  • 装饰器模式专注于 如何增强, 策略模式专注于 做什么, 每个模式都解决了其特定的问题, 使得系统结构清晰, 易于理解和维护。
  • 策略模式使得核心算法可以独立于客户端进行切换, 而装饰器模式则允许在不修改核心算法代码的情况下, 动态地添加各种横切关注点。

装饰器模式+建造者模式

建造者模式(创建型)

将一个复杂对象的构建和它的表示分离, 使得同样的构建过程可以创建不同的表示。核心思想是一步步地构建一个复杂对象, 通过一个建造者对象来封装构建过程, 从而使得客户端无需关心对象的内部构建细节, 只需调用建造者提供的方法即可。

案例

制作一个在线简历生成器, 用户可以根据自己的需求, 定制不同类型的简历, 例如:

  • 标准简历: 包含基本信息、教育背景、工作经验。
  • 技术简历: 在标准简历基础上, 额外包含项目经验、技能列表。
  • 高级简历: 在技术简历的基础上, 额外包含荣誉奖项、发表论文。 此外, 用户还可以选择为简历的某些部分添加额外的修饰或增强, 例如:
  • 突出显示: 将某些关键信息加粗或高亮。
  • 格式化: 为教育背景或工作经验添加特定的排版样式。
  • 图片嵌入: 在简历中嵌入个人照片或作品集图片。

模式职责

  • 装饰器模式: 动态地为简历的各个组成部分添加额外的修改或增强功能
  • 建造者模式: 分布构建负责的简历对象, 提供一个统一的接口来添加简历的各个部分, 并管理这些部分的构建顺序和逻辑。

代码内容

装饰器模式部分
抽象组件
csharp 复制代码
public interface ResumeElement {

    String render();

}
  • 定义了简历元素的基本行为 render(), 用于渲染元素内容。
具体组件
typescript 复制代码
public class TextElement implements ResumeElement {

    private String content;

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

    @Override
    public String render() {
        return content;
    }
}
public class ImageElement implements ResumeElement {

    private String imageUrl;

    public ImageElement(String imageUrl) {
        this.imageUrl = imageUrl;
    }

    @Override
    public String render() {
        return "[图片: " + imageUrl + "]";
    }
}
  • 实现了 ResumeElement 接口, 分别代表简历中的基本文本块( TextElement )和图片元素( ImageElement )。
抽象装饰器
typescript 复制代码
public abstract class ElementDecorator implements ResumeElement {

    protected ResumeElement decoratedElement;

    public ElementDecorator(ResumeElement decoratedElement) {
        this.decoratedElement = decoratedElement;
    }

    @Override
    public String render() {
        return decoratedElement.render();
    }
}
  • 实现了 ResumeElement 接口, 并持有一个 ResumeElement 类型的 decoratedElement 成员变量, 是所有简历元素修饰器的抽象父类。
  • 构造函数接收一个 ResumeElement 对象, 表示将要装饰的元素。
  • render() 方法中, 默认调用持有的 decoratedElement 对象的 render() 方法。
具体装饰器
typescript 复制代码
public class HighlightDecorator extends ElementDecorator {

    public HighlightDecorator(ResumeElement decoratedElement) {
        super(decoratedElement);
    }

    @Override
    public String render() {
        return "<高亮>" + super.render() + "</高亮>";
    }
}
public class FormatterDecorator extends ElementDecorator {

    private String prefix;

    private String suffix;

    public FormatterDecorator(ResumeElement decoratedElement, String prefix, String suffix) {
        super(decoratedElement);
        this.prefix = prefix;
        this.suffix = suffix;
    }

    @Override
    public String render() {
        return prefix + super.render() + suffix;
    }
}
public class BorderDecorator extends ElementDecorator {

    public BorderDecorator(ResumeElement decoratedElement) {
        super(decoratedElement);
    }

    @Override
    public String render() {
        return "[边框]" + super.render() + "[边框]";
    }
}
  • 继承 ElementDecorator, 分别代表了高亮( HighlightDecorator )、格式化( FormatterDecorator )和边框( BorderDecorator )这三种修饰功能。
  • 每个装饰器都重写了 render() 方法, 在调用 super.render() 的基础上, 添加各自特定的修饰逻辑(如: 添加HTML标签、前缀后缀等)。
建造者模式部分
产品
csharp 复制代码
public class Resume {

    private String basicInfo;

    private List<String> education;

    private List<String> workExperience;

    private List<String> projects;

    private List<String> skills;

    private List<String> awards;

    private List<String> publications;

    public Resume() {
        this.education = new ArrayList<>();
        this.workExperience = new ArrayList<>();
        this.projects = new ArrayList<>();
        this.skills = new ArrayList<>();
        this.awards = new ArrayList<>();
        this.publications = new ArrayList<>();
    }

    public void setBasicInfo(String basicInfo) {
        this.basicInfo = basicInfo;
    }

    public void addEducation(String edu) {
        this.education.add(edu);
    }

    public void addWorkExperience(String workExp) {
        this.workExperience.add(workExp);
    }

    public void addProject(String project) {
        this.projects.add(project);
    }

    public void addSkill(String skill) {
        this.skills.add(skill);
    }

    public void addAward(String award) {
        this.awards.add(award);
    }

    public void addPublication(String publication) {
        this.publications.add(publication);
    }

    public void display() {
        System.out.println("\n--- 简历内容 ---");
        if (basicInfo != null) {
            System.out.println("基本信息: " + basicInfo);
        }

        if (!education.isEmpty()) {
            System.out.println("教育背景: ");
            education.forEach(e -> System.out.println("  - " + e));
        }

        if (!workExperience.isEmpty()) {
            System.out.println("工作经验: ");
            workExperience.forEach(w -> System.out.println("  - " + w));
        }

        if (!projects.isEmpty()) {
            System.out.println("项目经验: ");
            projects.forEach(p -> System.out.println("  - " + p));
        }

        if (!skills.isEmpty()) {
            System.out.println("技能列表: ");
            skills.forEach(s -> System.out.println("  - " + s));
        }

        if (!awards.isEmpty()) {
            System.out.println("荣誉奖项: ");
            awards.forEach(a -> System.out.println("  - " + a));
        }

        if (!publications.isEmpty()) {
            System.out.println("发表论文: ");
            publications.forEach(pub -> System.out.println("  - " + pub));
        }

        System.out.println("--------------------");
    }

}
  • 代表最终构建的复杂简历对象, 包含了简历的各个部分, 提供了 setadd 方法来设置和添加简历的各个组成部分, 以及 display() 方法打印简历的内容。
抽象建造者
arduino 复制代码
public interface ResumeBuilder {

    void buildBasicInfo(String info);

    void buildEducation(String edu);

    void buildWorkExperience(String workExp);

    void buildProjects(String project);

    void buildSkills(String skill);

    void buildAwards(String award);

    void buildPublications(String publication);

    Resume build();

}
  • 定义了构建简历各个部分的抽象方法(如: buildBasicInfo()buildEducation() 等 ), 以及获取最终简历对象的方法 build()
具体建造者
java 复制代码
// 标准简历
public class StandardResumeBuilder implements ResumeBuilder {

    private Resume resume;

    public StandardResumeBuilder() {
        this.resume = new Resume();
    }

    @Override
    public void buildBasicInfo(String info) {
        resume.setBasicInfo(info);
    }

    @Override
    public void buildEducation(String edu) {
        resume.addEducation(new TextElement(edu).render());
    }

    @Override
    public void buildWorkExperience(String workExp) {
        resume.addWorkExperience(new TextElement(workExp).render());
    }

    @Override
    public void buildProjects(String project) {
        // 标准简历不包含项目经验
    }

    @Override
    public void buildSkills(String skill) {
        // 标准简历不包含技能列表
    }

    @Override
    public void buildAwards(String award) {
        // 标准简历不包含荣誉奖项
    }

    @Override
    public void buildPublications(String publication) {
        // 标准简历不包含发表论文
    }

    @Override
    public Resume build() {
        return this.resume;
    }
}

// 技术简历
public class TechnicalResumeBuilder implements ResumeBuilder {

    private Resume resume;

    public TechnicalResumeBuilder() {
        this.resume = new Resume();
    }

    @Override
    public void buildBasicInfo(String info) {
        resume.setBasicInfo(info);
    }

    @Override
    public void buildEducation(String edu) {
        ResumeElement eduElement = new FormatterDecorator(new TextElement(edu), "[教育]", "");
        resume.addEducation(eduElement.render());
    }

    @Override
    public void buildWorkExperience(String workExp) {
        ResumeElement workExpElement = new HighlightDecorator(new TextElement(workExp));
        resume.addWorkExperience(workExpElement.render());
    }

    @Override
    public void buildProjects(String project) {
        ResumeElement projectElement = new HighlightDecorator(new TextElement(project));
        resume.addProject(projectElement.render());
    }

    @Override
    public void buildSkills(String skill) {
        ResumeElement skillElement = new FormatterDecorator(new TextElement(skill), "<技能>", "</技能>");
        resume.addSkill(skillElement.render());
    }

    @Override
    public void buildAwards(String award) {
        // 技术简历不包含荣誉奖项
    }

    @Override
    public void buildPublications(String publication) {
        // 技术简历不包含发表论文
    }

    @Override
    public Resume build() {
        return this.resume;
    }
}

// 高级简历
public class AdvancedResumeBuilder implements ResumeBuilder {

    private Resume resume;

    public AdvancedResumeBuilder() {
        this.resume = new Resume();
    }

    @Override
    public void buildBasicInfo(String info) {
        resume.setBasicInfo(info);
    }
    
    @Override
    public void buildEducation(String edu) {
        ResumeElement eduElement = new FormatterDecorator(new TextElement(edu), "[教育]", "");
        resume.addEducation(eduElement.render());
    }
    
    @Override
    public void buildWorkExperience(String workExp) {
        ResumeElement workExpElement = new HighlightDecorator(new TextElement(workExp));
        resume.addWorkExperience(workExpElement.render());
    }
    @Override
    public void buildProjects(String project) {
        ResumeElement projectElement = new HighlightDecorator(new TextElement(project));
        resume.addProject(projectElement.render());
    }
    
    @Override
    public void buildSkills(String skill) {
        ResumeElement skillElement = new FormatterDecorator(new TextElement(skill), "<技能>", "</技能>");
        resume.addSkill(skillElement.render());
    }


    @Override
    public void buildAwards(String award) {
        ResumeElement awardElement = new HighlightDecorator(new TextElement(award));
        resume.addAward(awardElement.render());
    }

    @Override
    public void buildPublications(String publication) {
        ResumeElement pubElement = new FormatterDecorator(new TextElement(publication), "《", "》");
        resume.addPublication(pubElement.render());
    }
    @Override
    public Resume build() {
        return this.resume;
    }
}
  • 实现了 ResumeBuilder 接口, 负责构建特定类型的简历。
  • 在构建方法中, 可以根据简历类型的不同, 选择是否使用装饰器来修饰特定部分。
  • 也可以在参数中将渲染元素类传入, 动态控制渲染的装饰器。
指挥者
typescript 复制代码
public class ResumeDirector {

    public Resume constructStandardResume(ResumeBuilder builder) {
        builder.buildBasicInfo("姓名: 张三, 电话: 13898473281");
        builder.buildEducation("2018-2022 A大学 计算机科学与技术 本科");
        builder.buildWorkExperience("2022-至今 B科技公司 软件工程师");

        return builder.build();
    }

    public Resume constructTechnicalResume(ResumeBuilder builder) {
        builder.buildBasicInfo("姓名: 张三, 电话: 13898473281");
        builder.buildEducation("2018-2022 A大学 计算机科学与技术 本科");
        builder.buildWorkExperience("2022-至今 B科技公司 软件工程师");
        builder.buildProjects("在线商城系统开发");
        builder.buildSkills("Java, Spring Boot, MySQL, Vue");

        return builder.build();
    }

    public Resume constructAdvancedResume(ResumeBuilder builder) {
        builder.buildBasicInfo("姓名: 张三, 电话: 13898473281");
        builder.buildEducation("2018-2022 A大学 计算机科学与技术 本科");
        builder.buildWorkExperience("2022-至今 B科技公司 软件工程师");
        builder.buildProjects("在线商城系统开发");
        builder.buildSkills("Java, Spring Boot, MySQL, Vue");

        ResumeElement awardImage = new BorderDecorator(new ImageElement("award_certificate.png"));
        builder.buildAwards(awardImage.render() + " 优秀员工奖");

        ResumeElement publicationText = new HighlightDecorator(new TextElement("深度学习在自然语言处理中的应用"));
        builder.buildPublications(publicationText.render() + " (SCI收录)");

        return builder.build();
    }

}
  • 负责使用 ResumeBuilder 来构建特定类型的简历, 封装了构建过程的算法。
  • constructResume()constructTechnicalResume()constructAdvancedResume() 演示了简单、技术、高级简历的构建。
测试类
ini 复制代码
public class ResumeTest {

    @Test
    public void test_resume() {

        ResumeDirector director = new ResumeDirector();
        System.out.println("\n 场景1: 构建标准简历");
        ResumeBuilder standardBuilder = new StandardResumeBuilder();
        Resume standardResume = director.constructStandardResume(standardBuilder);
        standardResume.display();

        System.out.println("\n 场景2: 构建技术简历");
        ResumeBuilder technicalBuilder = new TechnicalResumeBuilder();
        Resume technicalResume = director.constructTechnicalResume(technicalBuilder);
        technicalResume.display();


        System.out.println("\n 场景3: 构建高级简历");
        ResumeBuilder advancedBuilder = new AdvancedResumeBuilder();
        Resume advancedResume = director.constructAdvancedResume(advancedBuilder);
        advancedResume.display();

    }
}
运行结果
xml 复制代码
 场景1: 构建标准简历

--- 简历内容 ---
基本信息: 姓名: 张三, 电话: 13898473281
教育背景: 
  - 2018-2022 A大学 计算机科学与技术 本科
工作经验: 
  - 2022-至今 B科技公司 软件工程师
--------------------

 场景2: 构建技术简历

--- 简历内容 ---
基本信息: 姓名: 张三, 电话: 13898473281
教育背景: 
  - [教育]2018-2022 A大学 计算机科学与技术 本科
工作经验: 
  - <高亮>2022-至今 B科技公司 软件工程师</高亮>
项目经验: 
  - <高亮>在线商城系统开发</高亮>
技能列表: 
  - <技能>Java, Spring Boot, MySQL, Vue</技能>
--------------------

 场景3: 构建高级简历

--- 简历内容 ---
基本信息: 姓名: 张三, 电话: 13898473281
教育背景: 
  - [教育]2018-2022 A大学 计算机科学与技术 本科
工作经验: 
  - <高亮>2022-至今 B科技公司 软件工程师</高亮>
项目经验: 
  - <高亮>在线商城系统开发</高亮>
技能列表: 
  - <技能>Java, Spring Boot, MySQL, Vue</技能>
荣誉奖项: 
  - <高亮>[边框][图片: award_certificate.png][边框] 优秀员工奖</高亮>
发表论文: 
  - 《<高亮>深度学习在自然语言处理中的应用</高亮> (SCI收录)》
--------------------

Process finished with exit code 0

组合优势

  • 建造者模式允许客户端通过不同的建造者或指挥者来创建不同配置的简历, 装饰器模式则提供了在构建过程中对简历的局部进行精细化修饰的能力, 实现了高度的定制化。
  • 客户端无需关系简历的复杂构建细节和内部元素的修饰逻辑, 只需通过建造者和指挥者提供的接口, 即可一步步构建所需简历。

不积跬步,无以至千里;不积小流,无以成江海。

相关推荐
BeyondCode程序员5 小时前
苹果内购 V1 与 V2 支付流程对比(附示例java代码)
java·后端
华仔啊5 小时前
Redis 不只是缓存!Java 打工人必知的 10 个真实工作场景,第 5 个太香了
java·后端
程序边界5 小时前
Oracle到金仓数据库信创改造迁移实施规划方案(上篇)
后端
韦德说5 小时前
我的副业之 - 三年磨一剑,让非技术人员也能实现建站自由
后端·程序员·开源
绝无仅有5 小时前
某大厂MySQL面试之SQL注入触点发现与SQLMap测试
后端·面试·github
绝无仅有5 小时前
某互联网大厂的面试go语言从基础到实战的经验和总结
后端·面试·github
澡点睡觉5 小时前
【golang长途旅行第38站】工厂模式
开发语言·后端·golang
小蒜学长5 小时前
基于SpringBoot+Vue的健身房管理系统的设计与实现(代码+数据库+LW)
java·数据库·vue.js·spring boot·后端
这里有鱼汤5 小时前
你以为 FastAPI 足够强?其实 Litestar 能让你的项目更轻量高效
后端·python