Почему Государственный Шаблон Проекта по If-else - PullRequest
1 голос
/ 22 апреля 2019

Повсюду я нахожу людей, говорящих, что лучше использовать шаблон проектирования состояний, если это не так.Я хочу знать, почему:

  1. Это усовершенствование if-else в реальных условиях использования?
  2. Делает ли это код более тестируемым?

Может ли кто-нибудь привести пример, когда шаблон состояний действительно улучшает ситуацию, трудно понять, почему я возьму на себя издержки следования шаблону состояний, когда я могу использовать if-else.

Примеры поверхИнтернета недостаточно, чтобы показать, как этот шаблон решает реальную проблему или делает код более понятным.

Ответы [ 4 ]

3 голосов
/ 22 апреля 2019

Нам нужно будет написать код, чтобы объяснить факты, но сначала давайте рассмотрим некоторые.Шаблон состояний позволяет вам создавать состояния потока и использовать, чтобы эти состояния решали, куда идти и что возвращать.Итак, давайте предположим, что вы не хотите контролировать текущее состояние объекта каждый раз, когда используете его и изменяете его внутренние значения.В этом случае ваш штат может контролировать себя для вас и отправлять вас в нужное место всякий раз, когда вы звоните.Шаблон State очень полезен при работе с графиками, но я покажу вам еще один пример, который я видел в Android-приложении GoF Design Patterns.

Рассмотрим UML: enter image description here

Мы собираемся реализовать это.

public interface AtmState {
    void withdraw(int amount);
    void refill(int amount);
}

public class Working implements AtmState {

    Atm atm;

    Working(Atm atm) {
        this.atm = atm;
    }

    public void withdraw(int amount) {

        int cashStock = atm.getCashStock();
        if(amount > cashStock) {
            /* Insufficient fund.
            * Dispense the available cash */
            amount = cashStock;
            System.out.print("Partial amount ");
        }

        System.out.println(amount + "$ is dispensed");
        int newCashStock = cashStock - amount;
        atm.setCashStock(newCashStock);
        if(newCashStock == 0) {
            atm.setState(new NoCash(atm));
        }
    }

    public void refill(int amount) {
        System.out.println(amount + "$ is loaded");
        atm.setCashStock(atm.getCashStock()+amount);
    }
}

public class NoCash implements AtmState {

    Atm atm;

    NoCash(Atm atm) {
        this.atm = atm;
    }

    public void withdraw(int amount) {
        System.out.println("Out of cash");
    }

    public void refill(int amount) {
        System.out.println(amount + "$ is loaded");
        atm.setState(new Working(atm));
        atm.setCashStock(atm.getCashStock()+amount);
    }
}

На данный момент мы определили два состояния, которые взаимодействуют друг с другом, они «знают», когда перейти от себя к другомусостояние, поэтому вам не нужно создавать контроллер для обработки, когда нужно изменить состояние объекта, они уже знают, когда менять.Теперь давайте получим нашу реализацию Atm:

public class Atm implements AtmState {
    int cashStock;
    AtmState currentState;

    public Atm() {
        currentState = new NoCash(this);
    }

    public int getCashStock() {
        return cashStock;
    }

    public void setCashStock(int CashStock) {
        this.cashStock = CashStock;
    }

    public void setState(AtmState state) {
        currentState = state;
    }

    public AtmState getState() {
        return currentState;
    }

    public void withdraw(int amount) {
        currentState.withdraw(amount);
    }

    public void refill(int amount) {
        currentState.refill(amount);
    }
}

Хорошо, теперь у нас есть два оператора объекта и одна реализация Atm.Теперь мы можем тестировать его отдельно, поэтому мы можем писать тесты только для состояния NoCash, как мы можем делать это для состояния Working.Это более гранулированный, как вы можете видеть.И здесь у нас есть код нашего клиента:

public class StateClient {

    public static void main(String [] args) {
        Atm atm = new Atm();
        atm.refill(100);
        atm.withdraw(50);
        atm.withdraw(30);
        atm.withdraw(30); // overdraft
        atm.withdraw(20); // overdraft
        atm.refill(50);
        atm.withdraw(50);
    }
}

Вывод:

100$ is loaded
50$ is dispensed
30$ is dispensed
Partial amount 20$ is dispensed
Out of cash
50$ is loaded
50$ is dispensed

Обратите внимание, что нам не нужно обрабатывать состояние нашего банкомата, и мы даже можем легко его проверить.Кроме того, вы не вписывали операторы if-else в свой клиентский код, вы уже записали его в самих состояниях , где оно должно быть , потому что вашему состоянию нужно знать, когда проверять себя, а не себе, но не вашеклиент.Вашему клиенту просто нужно получить правильный ответ на любой вызов.

Как я уже говорил, тестировать это намного проще, потому что теперь вы можете иметь отдельные и небольшие тесты для любого состояния.Ваша логика распространяется в местах, где это имеет смысл, и ваш код становится действительно легче понять.

Надеюсь, я смогу вам помочь.

0 голосов
/ 22 апреля 2019

Одна проблема с предложениями if-else заключается в том, что они могут быть очень длинными и что очень трудно добавить другое состояние, потому что вы должны изменить код многих различных классов. Представьте, что вы хотите разработать игру с главным меню, с игровым циклом и с готовым экраном. Без State-pattern вам пришлось бы проверять текущее состояние программы в самых разных местах кода, как в методе update или в методе draw. Если вы хотите добавить четвертое состояние, например, экран настроек, вам придется изменить код множества различных классов, что не является идеальным.

Но с помощью State-pattern вы можете решить эту проблему очень элегантно. enter image description here

0 голосов
/ 22 апреля 2019

Это усовершенствование if-else в случае использования в реальном мире?

Если ваше if-else использует простую бизнес-логику, то нет смысла использовать шаблон разработки состояний. Но если у вас сложная бизнес-логика, то, вероятно, лучше использовать шаблон проектирования состояний, так как это поможет вам отделить код и, следовательно, поможет в обслуживании. Также, если вы хотите добавить новое состояние, нет необходимости переходить в класс, просто создайте новый класс, и во время выполнения вы можете внедрить его.

Это делает код более тестируемым?

Поскольку состояние соответствует различным классам, они не жестко запрограммированы, и, следовательно, вы можете внедрить их во время выполнения, что означает, что они могут быть смоделированы в соответствии с вашим тестовым примером. Таким образом легче юнит-тестирование.

0 голосов
/ 22 апреля 2019

1) Как всегда, любой общий "это лучше?"Тип вопроса может быть вопросом мнения или иным образом с ответом «Это зависит».Одним примером реального варианта использования может быть наличие объекта с одним экземпляром, который является элементом управления для нескольких других процессов или потоков.Процессы могут опрашивать объект на предмет его состояния и обрабатывать свою специфическую логику, основанную на этом.

Рассмотрим гараж.У вас может быть конечный автомат, который при опросе сообщает процессу опроса, что гараж заполнен или не заполнен, и, возможно, время суток, которое он может изменить с полного на не полный, или с не полного на полный на основенекоторые факторы (оговорки, например).Затем у вас есть другие процессы, которые контролируют входящий трафик, исходящий трафик, монитор на входе, сообщающий потенциальным клиентам, есть ли место для парковки, система бронирования парковок, сезонные / семестровые / годовые пропуска и т. Д. Каждое из этих вещей необходимо знатьНезависимо от всех остальных, является ли гараж полон.

2) Любой объект, в котором вы можете явно установить состояние, легко тестируется.Сравните это с издевательской логикой, чтобы перейти в состояние, которое вы хотите протестировать, последнее склонно к большей (человеческой) подверженности ошибкам.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...