관찰자가 서로 독립적이지 않은 경우 관찰자 패턴이 적합합니까?


9

나는 class Car2 개의 속성을 가지고 있습니다 : int priceboolean inStock. 그것은 또한 보유하고 Listabstract class State(빈 클래스). 이 차에 적용 할 수 있으며, 각각 자신의 클래스에 의해 표현되는이 개 상태는 다음과 같습니다 class Upgrade extends Stateclass Shipping extends State.

A Car는 2 개 상태 각각에 제한이 없습니다. 주에는 다음과 같은 규칙이 있습니다.

  • Upgrade: 1자동차 자체에 적용된 각 상태의 가격을 추가 합니다.
  • Shipping: Shipping목록에 상태가 하나 이상 있으면 inStock로 설정됩니다 false.

예를 들어,로 시작 price = 1inStock = 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);
    }
}

분명히, 이것은 작동하지 않습니다. A는 때 Shipping제거, 뭔가 다른 상태 설정이 있는지 확인해야 inStockfalse로는,의 제거가 그래서 Shipping그냥 할 수 없습니다 inStock = true. 전화 Upgradeprice때마다 증가 합니다. 그런 다음 기본값에 상수를 추가하고 해당 값을 기반으로 재 계산을 시도했습니다.

나는 결코 어떤 패턴을 강요하려고 시도하지 않으며 위의 요구 사항에 대한 해결책을 찾으려고 노력하고 있습니다. 실제로 Car는 많은 속성이 포함되며 이러한 방식으로 적용 할 수있는 상태가 많이 있습니다. 나는 이것을하는 몇 가지 방법에 대해 생각했다.

  1. 각 관찰자는을 수신하므로 Car현재 등록 된 다른 모든 관찰자를보고이를 기반으로 변경할 수 있습니다. 이런 관찰자를 얽매는 것이 똑똑한 지 모르겠습니다.
  2. 에 관찰자가 추가되거나 제거되면 Car재 계산이 수행됩니다. 그러나이 재계 산은 방금 추가 / 제거 된 관찰자와 관계없이 모든 관찰자에서 수행해야합니다 .
  3. add 및 remove 메소드를 호출하고 재 계산을 수행하는 외부 "관리자"클래스가 있습니다.

설명 된 동작을 구현하기위한 좋은 디자인 패턴은 무엇이며 어떻게 작동합니까?


1
State를 자체 클래스로 추상화하는 요점은 서로 상호 작용하지 않는 Logic을 분리하여 코드를 단순화하는 것입니다. 그러나 비즈니스 논리에 따라 해당 논리가 연결되어 있으므로 링크를 다시 연결해야하므로이 신이 엉망입니다. 여기서 문제가되는 것은 관찰자 패턴이 아니라 State로 적용하는 역할 패턴입니다.
ArTs

직접 손으로한다면 문제를 어떻게 해결하겠습니까?
James Youngman

@JamesYoungman 나는 외부 관리자 인 제 3 옵션을 사용했습니다. 이 경우에 종이에 쓰는 규칙은 간단하지만이 경우 언어가 제공하는 옵션은 제한 되어 있습니다. 따라서 디자인 패턴이 필요합니다. "어떻게 손으로 하시겠습니까?"에 대한 생각은 명확한 규칙을 적용하는 것보다 알고리즘에 더 효과적입니다.
user1803551

@ user1803551 잘 선택하셨습니다.
Tulains Córdova

모든 이벤트에 대해 하나의 이벤트 핸들러가 있습니다. 이 핸들러는 단순히 객체의 완전한 상태를 다시 계산하기위한 진입 점입니다. 전형적인 문제입니다. "위에서 아래로, 왼쪽에서 오른쪽으로"양식을 작업 할 때 표시됩니다. 모든 것이 A-OK이지만 중간에서 무언가를 변경하면 제대로 다시 계산되지 않습니다. "이벤트 처리 순서를 어떻게 보장 할 수 있습니까?"
radarbob

답변:


1

시스템을 다르게 고려하면 관찰자가 훌륭하게 작동합니다. 상태 자체를 옵저버로 만드는 대신, 두 개의 새로운 클래스를 "스테이트 변경 옵저버"로 만들 수 있습니다. 한 명의 옵저버는 "가격"을 업데이트하고 다른 옵저버는 "재고"를 업데이트합니다. 이 방법으로 재고에 따라 가격에 대한 규칙이 없거나 그 반대의 경우, 즉 상태 변화 만보고 모든 것이 계산 될 수있는 경우 독립적입니다. 이 기술을 "이벤트 소싱"이라고합니다 (예 : 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에 방법을 updateOnAdd그리고 updateOnRemove그들은 단지 이러한 변화 중 하나에 관심이 있다면. 그러면 addStateand removeState메소드가 그에 따라 업데이트됩니다. 이전 시점과 함께이 접근 방식은 강력하고 확장 가능하며 유연한 메커니즘으로 끝날 수 있습니다.
  • 나는을 추가하고 제거하라는 지시 사항을 알려주지 State않았으며 질문에 중요하지 않기 때문에 언제 발생 하는지 지정하지 않았습니다 . 그러나이 답변의 경우 다음 사항을 고려해야합니다. 때문에 State이제 만들어야합니다 Car전에 매니저의 메소드를 호출하는 (노출없이 빈 생성자)에 addStateremoveState방법은를 취할 필요가 없습니다 Car그냥에서 읽을 수 있습니다 state.car.
  • 관찰자는 기본적으로 관찰 가능 항목에 등록 순서대로 통지됩니다. 다른 순서를 지정할 수 있습니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.