Java装饰器模式实现披萨配料:如何处理文件订单动态添加
嘿,这个动态给披萨应用装饰器的需求我之前也碰到过,刚好可以结合装饰器模式的特性来优雅解决,给你分享个靠谱的实现思路!
核心思路
装饰器模式的关键是用装饰器类嵌套包装基础对象,要实现动态添加,我们只需要:
- 解析输入文件,提取订单ID和需要添加的配料列表
- 建立配料名称到对应装饰器类的映射
- 遍历配料列表,依次用对应的装饰器包装当前披萨实例
具体实现步骤
1. 输入解析
首先要把输入行拆成有用的部分:
- 输入示例:
AddTopping 7 HotPepper Soudjouk Salami - 拆分后:命令
AddTopping、订单ID7、配料列表[HotPepper, Soudjouk, Salami]
可以用字符串分割来实现,注意处理空格和无效行的情况。
2. 配料映射:用工厂模式替代硬编码
直接根据字符串创建类实例,最安全易维护的方式是用工厂类,把配料名称和对应的装饰器构造逻辑绑定起来,避免硬编码和反射的潜在问题。
3. 动态装饰披萨
先创建基础披萨实例,然后遍历配料列表,每次用工厂生成的装饰器包装当前披萨——这和你手动写new Salami(new Pepper(new Onion(pizza)))的效果完全一致!
完整代码示例
基础披萨与装饰器类(先确保你的类结构是这样)
public abstract class Pizza { public abstract String getDescription(); public abstract double getCost(); } // 基础披萨类 public class PizzaBakery extends Pizza { @Override public String getDescription() { return "Basic Pizza"; } @Override public double getCost() { return 5.0; } } // 示例配料装饰器:HotPepper public class HotPepper extends Pizza { private final Pizza pizza; public HotPepper(Pizza pizza) { this.pizza = pizza; } @Override public String getDescription() { return pizza.getDescription() + ", Hot Pepper"; } @Override public double getCost() { return pizza.getCost() + 1.5; } } // 同理实现Soudjouk、Salami等其他配料类...
配料工厂类
import java.util.HashMap; import java.util.Map; import java.util.function.Function; public class ToppingFactory { // 注册配料名称到装饰器构造逻辑的映射 private static final Map<String, Function<Pizza, Pizza>> TOPPING_MAP = new HashMap<>(); static { // 把每个配料的构造函数注册进来,键要和输入文件里的名称完全匹配 TOPPING_MAP.put("HotPepper", HotPepper::new); TOPPING_MAP.put("Soudjouk", Soudjouk::new); TOPPING_MAP.put("Salami", Salami::new); TOPPING_MAP.put("Onion", Onion::new); TOPPING_MAP.put("Pepper", Pepper::new); // 新增配料只需要在这里加一行就行 } public static Pizza applyTopping(String toppingName, Pizza pizza) { Function<Pizza, Pizza> toppingCreator = TOPPING_MAP.get(toppingName); if (toppingCreator == null) { throw new IllegalArgumentException("Unknown topping: " + toppingName); } return toppingCreator.apply(pizza); } }
订单处理类(读取文件+动态装饰)
import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; public class PizzaOrderProcessor { public static void main(String[] args) { String orderFilePath = "your_orders.txt"; // 替换成你的输入文件路径 try (BufferedReader reader = new BufferedReader(new FileReader(orderFilePath))) { String line; while ((line = reader.readLine()) != null) { processSingleOrder(line); } } catch (IOException e) { System.err.println("Failed to read order file: " + e.getMessage()); } } private static void processSingleOrder(String line) { String[] parts = line.trim().split("\\s+"); // 校验输入格式是否合法 if (parts.length < 3 || !"AddTopping".equals(parts[0])) { System.err.println("Skipping invalid order line: " + line); return; } // 提取订单ID和配料列表 String orderId = parts[1]; String[] toppings = new String[parts.length - 2]; System.arraycopy(parts, 2, toppings, 0, toppings.length); // 初始化基础披萨 Pizza pizza = new PizzaBakery(); // 动态应用所有配料装饰器 for (String topping : toppings) { pizza = ToppingFactory.applyTopping(topping, pizza); } // 输出订单结果 System.out.printf("Order #%s%nFinal Pizza: %s%nTotal Cost: $%.2f%n%n", orderId, pizza.getDescription(), pizza.getCost()); } }
备选方案:用反射实现(不推荐)
如果觉得工厂模式需要手动注册太麻烦,也可以用反射动态创建装饰器实例,但这种方式类型安全性差,而且如果类名和输入名称不一致还要额外处理映射,示例代码如下:
import java.lang.reflect.Constructor; public class ReflectiveToppingFactory { // 假设所有配料类都在同一个包下,比如com.yourproject.toppings private static final String TOPPING_PACKAGE = "com.yourproject.toppings"; public static Pizza applyTopping(String toppingName, Pizza pizza) { try { Class<?> toppingClass = Class.forName(TOPPING_PACKAGE + "." + toppingName); Constructor<?> constructor = toppingClass.getConstructor(Pizza.class); return (Pizza) constructor.newInstance(pizza); } catch (Exception e) { throw new IllegalArgumentException("Failed to create topping: " + toppingName, e); } } }
还是更推荐工厂模式,代码更清晰,排查问题也更方便,新增配料只需要在工厂里加一行注册代码就行。
内容的提问来源于stack exchange,提问作者Eren
相关产品推荐
相关产品推荐

