当观察者彼此不独立时,观察者模式是否合适?


9

我有一个class Car具有2个属性的属性:int priceboolean inStock。它还持有Listabstract class State(空类)。可以在汽车上应用两种状态,每种状态由其自己的类表示:class Upgrade extends Stateclass Shipping extends State

A Car可以容纳2个状态中的任何一个。各州有以下规则:

  • Upgrade:加到1汽车本身适用的每个州的价格上。
  • Shipping:如果Shipping列表中至少有1个状态,则将inStock其设置为false

例如,以price = 1和开头inStock = true

add Shipping s1    --> price: 1, inStock: false
add Upgrade g1     --> price: 1, inStock: false
add Shipping s2    --> price: 2, inStock: false
add Shipping s3    --> price: 3, inStock: false
remove Shipping s2 --> price: 2, inStock: false
remove Upgrade g1  --> price: 1, inStock: false
remove Shipping s1 --> price: 1, inStock: false
remove Shipping s3 --> price: 1, inStock: true

我在考虑观察者模式,其中每个添加和删除操作都会通知观察者。我有这样的想法,但它不遵守我提出的规则:

abstract class State implements Observer {

    public abstract void update();
}

class Car extends Observable {

    List<State> states = new ArrayList<>();
    int price = 100;
    boolean inStock = true;

    void addState(State state) {

        if (states.add(state)) {
            addObserver(state);
            setChanged();
            notifyObservers();
        }
    }

    void removeState(State state) {

        if (states.remove(state)) {
            deleteObserver(state);
            setChanged();
            notifyObservers();
        }
    }
}

class Upgrade extends State {

    @Override
    public void update(Observable o, Object arg) {

        Car c = (Car) o;
        int bonus = c.states.size() - c.states.indexOf(this) - 1;
        c.price += bonus;
        System.out.println(c.inStock + " " + c.price);
    }
}

class Shipping extends State {

    @Override
    public void update(Observable o, Object arg) {

        Car c = (Car) o;
        c.inStock = false;
        System.out.println(c.inStock + " " + c.price);
    }
}

显然,这是行不通的。当一个Shipping被删除,一些必须检查是否有另一种状态设置inStock为false,所以去除Shipping不能随便inStock = true。每次通话Upgrade增加price。然后,我为默认值添加了常量,并尝试基于这些常量进行重新计算。

我绝不是要强加任何模式,我只是想为上述要求找到解决方案。请注意,实际上Car包含许多属性,并且可以通过这种方式应用许多状态。我考虑了几种方法:

  1. 由于每个观察者都会收到Car,因此它可以查看当前注册的所有其他观察者,并根据此进行更改。我不知道像这样纠缠观察者是否明智。
  2. 在中添加或删除观察者时Car,将进行重新计算。但是,无论是否添加或删除了一个观察者,都必须对所有观察者进行重新计算。
  3. 有一个外部“ manager”类,它将调用add和remove方法并进行重新计算。

什么是实施所描述行为的良好设计模式,它将如何工作?


1
将State抽象到其自己的类中的目的是分离出彼此不交互的Logic,从而简化了代码。但是,您的业务逻辑指示其逻辑已链接,因此,您必须重新探索该链接,从而导致上帝糟糕透顶。问题不在这里的是观察者模式,而是您要向State应用的Role模式。
ArT

如果您手动处理该问题,您将如何解决?
James Youngman

@JamesYoungman我最后使用了第三个选项-外部经理。规则你写在纸上,这种情况很简单,但选择的语言为您提供了实现它们被限制在这种情况下。因此,需要一种设计模式。考虑“如何手工”对于算法比对应用一套清晰的规则更有帮助。
user1803551 2016年

@ user1803551您选择的很好。
图兰斯·科尔多瓦

所有事件都有一个事件处理程序。该处理程序只是重新计算对象完整状态的入口点。这是一个典型的问题。当您处理“从上到下,从左到右”表单时,您会看到它明显-一切正常,但是更改中间的内容将无法正确地重新计算。如果您最终问到“如何保证事件处理顺序?”,现在您知道该做什么了。
–radarbob

Answers:


1

如果您以不同的方式考虑系统,那么观察者将表现出色。您可以使2个新类成为“状态更改观察者”,而不是使状态本身成为观察者:一个观察者将更新“价格”,另一个将更新“ inStock”。这样,如果您没有基于inStock的价格规则,反之亦然,即如果可以仅通过查看状态变化来计算所有价格,它们将是独立的。此技术称为“事件源”(例如,请参见-https://ookami86.github.io/event-sourcing-in-practice/)。这是编程中的一种模式,具有一些值得注意的应用程序。

在回答一个更笼统的问题时,有时您确实在观察者之间存在依赖性。例如,您可能希望一个观察者先于另一个观察者做出反应。在这种情况下,通常可以使Observable类的自定义实现处理顺序或依赖项。


0

我最终选择了选项3-使用外部经理。管理者负责State从中添加和删​​除,Car并在发生这些更改时通知观察者。

这是我修改代码的方式。我删除了JDK 的Observable/ Observer,因为我正在执行自己的实现。

每个都有对其State引用的引用Car

abstract class State {

    Car car;

    State(Card car) { this.car = car; }

    public abstract void update();
}

class Upgrade extends State {

    @Override
    public void update() {

        int bonus = car.states.size() - car.states.indexOf(this) - 1;
        car.price += bonus;
        System.out.println(car.inStock + " " + car.price);
    }
}

class Shipping extends State {

    @Override
    public void update() {

        car.inStock = false;
        System.out.println(car.inStock + " " + car.price);
    }
}

Car仅保留其状态(为避免混淆:属性),不处理添加和删除States:

class Car extends Observable {

    List<State> states = new ArrayList<>();
    int price = 100;
    boolean inStock = true;
}

这是经理。它已经取代Car了管理其State(观察者)的(可观察的)工作。

class StatesManager {

    public void addState(Card car, State state) {

        car.states.add(state);
        for (State state : car. states)
            state.update;
    }

    public void removeState(Card car, State state) {

        car.states.remove(state);
        for (State state : car. states)
            state.update;
    }
}

请记住以下几点:

  • 所有更改都会通知所有观察者。更加聪明的事件分发方案可以消除不必要的对观察者update方法的调用。
  • 观察者可能希望针对不同场合公开更多类似“更新”的方法。只是作为一个例子,它们可以在目前的拆分update方法updateOnAddupdateOnRemove他们是否有兴趣只在这些变化中的一个。然后addStateremoveState方法将相应更新。与前面的观点一起,这种方法最终可以成为一种健壮,可扩展和灵活的机制。
  • 我没有指定给出添加和删除States 的指令的原因,以及何时发生该指令,因为对于该问题而言并不重要。但是,在此答案的情况下,需要考虑以下几点。由于State现在必须Car在调用管理器的方法之前创建其(没有暴露空的构造函数),所以addStateand removeState方法不需要采用,Car而只需从读取即可state.car
  • 默认情况下,将按照观察者的注册顺序通知观察者。可以指定其他顺序。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.