Чи підходить модель спостерігача, коли спостерігачі не залежать один від одного?


9

У мене є class Carякий має 2 властивості: int priceі boolean inStock. Він також тримає Listз abstract class State(порожнього класу). Є два стану, які можна застосувати на автомобілі, і кожне представлене власним класом: class Upgrade extends Stateі class Shipping extends State.

А Carможе містити будь-яку кількість кожного з двох станів. Держави мають такі правила:

  • 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);
    }
}

Очевидно, це не працює. Коли a Shippingвидаляється, щось потрібно перевірити, чи є інша установка стану inStockfalse, тому видалення Shippingне може просто inStock = true. Upgradeзбільшується priceпри кожному дзвінку. Потім я додав константи для значень за замовчуванням і спробував перерахунок на основі цих.

Я ні в якому разі не намагаюся нав’язати будь-яку закономірність, я просто намагаюся знайти рішення для вищезазначених вимог. Зауважте, що на практиці Carміститься багато властивостей і існує багато станів, які можна застосувати таким чином. Я подумав про кілька способів зробити це:

  1. Оскільки кожен спостерігач отримує Car, він може переглядати всіх інших зареєстрованих на даний момент спостерігачів і на основі цього зробити зміни. Я не знаю, чи розумно заплутувати таких спостерігачів.
  2. Коли спостерігач буде доданий або видалений Car, відбудеться перерахунок. Однак цей перерахунок доведеться робити всім спостерігачам незалежно від того, що було додано / видалено.
  3. Майте зовнішній клас "менеджер", який буде викликати методи додавання та видалення та робити перерахунок.

Яка хороша модель дизайну для втілення описаної поведінки та як вона буде працювати?


1
Сенс абстрагування держави у власному класі полягає у відокремленні логіки, яка не взаємодіє між собою, спрощуючи код. Однак ваша бізнес-логіка диктує, що їх логіка пов'язана, а значить, вам доведеться зануритися в посилання, що призведе до цього Богові жахливим безладом. Тут не проблема спостерігача, а схема ролі, яку ви застосовуєте у штаті.
ArTs

Як би ви вирішили проблему, якби це робили вручну?
Джеймс Янгмен

@JamesYoungman Я в кінцевому підсумку використовував свій 3-й варіант - зовнішній менеджер. Правила, які ви пишете на папері для цього випадку, прості, але варіанти, якими надається мова, щоб реалізувати їх, у цьому випадку обмежені . Звідси потреба в дизайнерській схемі. Думка про те, як би це зробити вручну, працює більше для алгоритмів, ніж для застосування чіткого набору правил.
user1803551

@ user1803551 Ви вибрали добре.
Тулан Кордова

Майте один обробник подій для всіх подій. Цей обробник є просто вхідною точкою для перерахунку повного стану об'єкта. Це архітипічна проблема. Ви бачите, що це проявляється під час роботи форми "зверху вниз, зліва направо" - все в порядку, але потім щось змінити посередині, не перерахуйте належним чином. Якщо ви коли-небудь запитуєте "як я можу гарантувати порядок обробки подій?", Тепер ви знаєте, що вам потрібно зробити.
radarbob

Відповіді:


1

Спостерігачі будуть чудово працювати, якщо ви по-різному оцінюєте систему. Замість того, щоб держави ставали спостерігачами, ви можете зробити два нові класи, які будуть "спостерігачами за зміною штату": один спостерігач оновив би "ціну", інший - оновив би "inStock". Таким чином, вони були б незалежними, якщо у вас немає правил щодо ціни залежно від inStock або навпаки, тобто, якщо все можна було б обчислити, поглянувши на зміни стану. Ця методика називається "пошук подій" (наприклад, див. Https://ookami86.github.io/event-sourcing-in-practice/ ). Це схема в програмуванні, яка має деякі помітні програми.

Відповідаючи на більш загальне питання, іноді у вас дійсно є залежності між спостерігачами. Наприклад, ви можете хотіти, щоб один спостерігач реагував перед іншим. У такому випадку, як правило, можливо зробити власну реалізацію класу Observable для вирішення порядку і залежності.


0

Нарешті, я перейшов до варіанту 3 - використовуючи зовнішній менеджер. Менеджер несе відповідальність за додавання та видалення States із Cars та за повідомлення спостережувачів, коли ці зміни відбудуться.

Ось як я змінив код. Я видалив Observable/ ObserverJDK, тому що я роблю власну реалізацію.

Кожен 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якщо вони зацікавлені лише в одній із цих змін. Тоді addStateі removeStateметоди будуть відповідно оновлені. Поряд з попереднім пунктом, такий підхід може стати надійним, розширюваним і гнучким механізмом.
  • Я не вказував, що дає інструкцію додавати і видаляти States, і коли це станеться, оскільки це не важливо для питання. Однак у випадку цієї відповіді слід враховувати наступний момент. Оскільки Stateтепер слід створювати його Car(не відкритий порожній конструктор) перед викликом методу менеджера, addStateі removeStateметоди не потрібно брати, Carа можна просто прочитати з нього state.car.
  • Спостерігачі сповіщаються в порядку реєстрації на спостережуваному за замовчуванням. Можна вказати інший порядок.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.