观察者模式中观察者需不同更新参数的解决方案探讨
观察者模式实现方案分析与优化建议
咱们先逐个拆解你提到的两种实现思路,再聊聊更贴合设计规范的优化方案:
一、现有两种思路的问题分析
1. 按观察者类型判断传参的思路
这种做法直接违背了观察者模式的核心设计初衷——发布者只需要依赖观察者接口,完全不需要知道具体的观察者实现类。
试想一下,以后要是新增一种观察者(比如飞船生产日志记录器),你就必须修改notifyObservers方法,加新的else if分支,这直接违反了开闭原则(对扩展开放、对修改关闭)。而且发布者和具体观察者类耦合死了,后续维护成本会越来越高。
2. 全参数统一update的思路
这种思路虽然满足了“统一update方法”的要求,但缺点也很突出:
- 参数列表过于臃肿,观察者要接收一堆自己根本用不上的参数,代码可读性极差;
- 以后要是新增生产相关的参数(比如新增一种资源或飞船类型),所有观察者的
update方法都得跟着修改,同样违反开闭原则,也不符合接口隔离原则(观察者不该依赖自己不需要的参数)。
二、更合理的优化方案:用事件对象封装更新数据
核心思路是:把所有更新需要的参数封装成一个事件对象(DTO),让观察者的update方法只接收这个对象,观察者按需从对象里提取自己需要的信息。这样既符合观察者模式规范,又兼顾了扩展性和可读性。
具体实现示例
1. 定义事件类,封装所有生产相关数据
public class ShipProductionEvent { // 资源消耗字段 private int metalRequired; private int energyRequired; private int carbonRequired; private int deuteriumRequired; private int vibraniumRequired; // 飞船生产数量字段 private int numberOfLightFighters; private int numberOfHeavyFighters; private int numberOfStarDestroyers; // 可根据需求扩展其他字段 // 构造器:初始化所有字段 public ShipProductionEvent(int metalRequired, int energyRequired, int carbonRequired, int deuteriumRequired, int vibraniumRequired, int numberOfLightFighters, int numberOfHeavyFighters, int numberOfStarDestroyers) { this.metalRequired = metalRequired; this.energyRequired = energyRequired; this.carbonRequired = carbonRequired; this.deuteriumRequired = deuteriumRequired; this.vibraniumRequired = vibraniumRequired; this.numberOfLightFighters = numberOfLightFighters; this.numberOfHeavyFighters = numberOfHeavyFighters; this.numberOfStarDestroyers = numberOfStarDestroyers; } // 提供getter方法,让观察者获取需要的数据 public int getMetalRequired() { return metalRequired; } public int getEnergyRequired() { return energyRequired; } public int getNumberOfLightFighters() { return numberOfLightFighters; } // 其他字段的getter... }
2. 调整观察者接口,让update接收事件对象
public interface Observer { void update(ShipProductionEvent event); }
3. 修改发布者的notifyObservers方法
public class SpaceShipFactory implements Publisher { private List<Observer> observers = new ArrayList<>(); // 工厂自身的状态字段:资源需求、生产的飞船数量等 private int metalRequired; private int energyRequired; // ... 其他状态字段 @Override public void notifyObservers() { // 创建事件对象,把当前生产的所有数据封装进去 ShipProductionEvent event = new ShipProductionEvent( this.metalRequired, this.energyRequired, this.carbonRequired, this.deuteriumRequired, this.vibraniumRequired, this.numberOfLightFighters, this.numberOfHeavyFighters, this.numberOfStarDestroyers ); // 遍历所有观察者,统一传入事件对象 for (Observer obs : observers) { obs.update(event); } } // 其他方法:添加/移除观察者、生产飞船逻辑等... }
4. 观察者按需提取数据
- PlanetResources的update实现:只关注资源消耗相关数据
public class PlanetResources implements Observer { private int metallicMicrolattice; private int energy; private int carbyne; private int deuterium; private int vibranium; @Override public void update(ShipProductionEvent event) { // 只提取自己需要的资源字段,更新资源存储 this.metallicMicrolattice -= event.getMetalRequired(); this.energy -= event.getEnergyRequired(); this.carbyne -= event.getCarbonRequired(); this.deuterium -= event.getDeuteriumRequired(); this.vibranium -= event.getVibraniumRequired(); } }
- StationedShip的update实现:只关注飞船生产数量数据
public class StationedShip implements Observer { private ArrayList<SpaceShip> lightFighters; private ArrayList<SpaceShip> heavyFighters; private ArrayList<SpaceShip> starDestroyers; // ... 其他飞船列表 @Override public void update(ShipProductionEvent event) { // 提取飞船生产数量,更新飞船存储 addLightFighters(event.getNumberOfLightFighters()); addHeavyFighters(event.getNumberOfHeavyFighters()); addStarDestroyers(event.getNumberOfStarDestroyers()); } // 辅助方法:批量添加飞船到对应列表 private void addLightFighters(int count) { for (int i = 0; i < count; i++) { lightFighters.add(new LightFighter()); } } // 其他添加飞船的辅助方法... }
三、这个方案的核心优势
- 符合观察者模式规范:发布者只依赖
Observer接口,完全不知道具体的观察者实现,彻底解耦; - 扩展性极强:以后新增资源类型、飞船类型,只需要修改
ShipProductionEvent类加字段;新增观察者类型,只需要实现Observer接口,完全不需要修改发布者和已有观察者的代码; - 可读性高:观察者的
update方法逻辑清晰,只处理自己关心的数据,不会被无用参数干扰; - 维护成本低:所有生产相关的数据都集中在事件类里,修改或新增数据只需要在一个地方操作。
内容的提问来源于stack exchange,提问作者user42493
相关产品推荐
相关产品推荐

