Как бы вы написали код стиральной машины? - PullRequest
11 голосов
/ 18 июня 2010

Представьте, что у меня есть класс, представляющий простую стиральную машину.Он может выполнять следующие операции в следующем порядке: включить -> стирка -> центрифуга -> выключить .Я вижу две основные альтернативы:

  1. У меня может быть класс WashingMachine с методами turnOn (), wash (int минут), центрифуга (int revs), turnOff (). Проблема в том, что интерфейс ничего не говорит о правильном порядке операций.В лучшем случае я могу выдать InvalidOprationException, если клиент пытается центрифугировать до того, как машина была включена.Я также могу использовать отдельный класс Program, который передает обороты центрифуги и минуты стирки в WashingMachine и упрощает эти методы.

  2. Я могу позволить самому классу позаботиться о правильных переходах и иметьединственный метод nextOperation () .С другой стороны, проблема в том, что семантика плохая.Клиент не будет знать, что произойдет, когда он вызовет nextOperation ().Представьте, что вы реализуете событие нажатия кнопки центрифуги, чтобы оно вызывало nextOperation ().Пользователь нажимает кнопку центрифуги после того, как машина включена и поднимается!машина начинает мыть.Возможно, мне понадобится несколько свойств в моем классе для параметризации операций, или, возможно, отдельный класс Program с полями washLength и centrifugeRevs, но это не является проблемой.

Какая альтернатива лучше?Или, может быть, есть другие, лучшие альтернативы, которые я пропустил, чтобы описать?

Ответы [ 10 ]

15 голосов
/ 18 июня 2010

У меня был бы класс «высокого уровня» State Machine, который контролирует вход / запуск / выход каждого состояния (где состояниями могут быть такие вещи, как «заполнение», «стирка», «полоскание», «опорожнение», «всухую» и т. д.)

Составьте Диаграмму перехода состояний всех необходимых вам состояний, включая (для каждого состояния)

  1. какие требования существуют перед вводом состояния (условия входа)
  2. что должно произойти при вводе состояния (действия при вводе)
  3. что происходит во время состояния (сама задача)
  4. какие требования существуют, прежде чем вы сможете покинуть штат (условия выхода)
  5. что происходит при выходе из состояния (выход из действий)

Вам могут или не могут понадобиться условия входа / выхода (например, в некоторых случаях вы можете форсировать условия с помощью действия входа / выхода). Однако по соображениям безопасности некоторые условия могут быть хорошими (например, выход из состояния «ожидания» или переход в «опасное» состояние, такое как отжигание)

Вы также создаете Transitions, который определяет связи между состояниями. Переход имеет

  1. a 'из' состояния
  2. а 'в' состояние
  3. условия перехода (возможен ли переход?
  4. переходные действия (что должно произойти при переходе)

Опять же, вам может не понадобиться все это, и во многих переходах будут указаны только состояния «из» и «в».

В обоих случаях старайтесь, чтобы каждый State и Transition был настолько простым, насколько это необходимо нуждается в (попробуйте поставить условия / действия там, где они имеют наибольшее значение, очевидно, есть потенциальная двойная их количество должно быть определено в State и a Transition из-за общего дизайна)

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

Если вы сделаете его особенно универсальным, тогда States и Transitions можно зарегистрировать с помощью State Machine во время строительства, или вы можете просто набрать State Machine, чтобы содержать их все.

Затем вы можете запросить переходы либо из в пределах состояний (т. Е. Определенное состояние может знать, когда оно закончилось, и знать, в какое состояние перейти в следующий), либо у вас может быть внешний класс, который контролирует переходы состояний (тогда каждое состояние очень простое, просто заботится о своих собственных действиях, а внешний класс определяет порядок и время переходов).

По моему опыту с этими вещами, хорошая идея отделить логику высокого уровня преднамеренного порядка / времени каждого действия и логику низкого уровня реакции на какое-либо аппаратное событие (например, переход из состояния «заполнения», когда уровень воды достигнут).

Это действительно общий дизайн, и вы можете достичь абсолютно одинаковой функциональности множеством разных способов - редко есть одиночный правильный способ делать вещи ...

4 голосов
/ 18 июня 2010

Я думаю, что turnOn () , wash () , centerfuge () и т. Д. Должны быть закрытыми / защищенными методами. Публичный интерфейс должен быть doTheWash (режим WashMode) . Сама стиральная машина знает, какие режимы она поддерживает, и как заставить их работать, пользователю стиральной машины не нужно вмешиваться в порядок или продолжительность операций. Разумно ожидать, что автор класса стиральных машин вызовет закрытые методы в разумном порядке.

1 голос
/ 18 июня 2010

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

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

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

Если вы посмотрите на систему стирки с этой точки зрения, вы можете использовать шаблон Command внутри контроллера для моделирования программы стирки и шаблона Observer для уведомления подсистем (например: Дверной выключатель прослушивает контроллер для получения сигнала для блокировки двери ).

1 голос
/ 18 июня 2010

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

Так что внутренне у вас могут быть закрытые функции Wash, Rinse, Spin, фактическим интерфейсом будут SetCycle () и Start () и Stop().Вы также можете иметь некоторые дополнительные свойства, такие как уровень воды, скорость перемешивания, температура воды и т. Д.полный.

0 голосов
/ 18 июня 2010

Опция A похожа на ручную шайбу, а опция B - на автоматическую шайбу. Оба варианта полезны для таргетинга на пользователей:

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

Пользователь варианта B может наслаждаться простотой и писать фиктивные сериалы - научите себя мыться за 21 секунду: -)

0 голосов
/ 18 июня 2010

В последнее время я использую функционал и использую неизменяемые объекты с Мартином Фаулером / JQuery плавный стиль сцепления .

  • Я использую систему типов стольконасколько это возможно, поскольку это делает рефакторинг.
  • С моим методом компилятор заставит вас сделать это правильно.
  • Поскольку объекты несколько неизменны, легче доказать, что математически этот конечный автомат является детерминированным.

код:

public class Washer {
    public void exampleRun() {
        new On()
            .wash(30)
            .spin(100)
            .turnOff();
    }
    public abstract static class State {}
    public static class On extends State {
        public Wash wash(int minutes) {
            return new Wash(minutes);
        }
    }
    public static class Wash extends State {
        private int minutes;
        public Wash(int minutes) {
            super();
            this.minutes = minutes;
        }

        public Spin spin(int revs) {
            return new Spin(revs);
        }

    }
    public static class Spin extends State {
        private int revs;
        public Spin(int revs) {
            super();
            this.revs = revs;
        }
        public Off turnOff() {
            return new Off();
        }
    }
    public static class Off extends State{}
}
0 голосов
/ 18 июня 2010

На моей стиральной машине у вас есть доступ к двум ручкам. Я немного смущен тем, что ограничения в вашем случае.

  1. Размер груза (маленький, средний, большой)
  2. Ручка цикла (Пермская пресса, трикотажные изделия, деликатесы, обычные)
    • Каждый цикл содержит различные «Фазы», ​​на которых он может быть активирован на

Когда вы «активируете» цикл (вращая ручку), машина начнет работать, если повернуть ее к середине или началу цикла.

Вот действительно очень простой пример того, как я мог бы перевести это в класс:

class WashingMachine
{
    public void Activate(Cycle c, LoadSize s);
    ...
}
0 голосов
/ 18 июня 2010

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

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

0 голосов
/ 18 июня 2010

Хм, немного странно, но здесь идет;

Я бы посмотрел даже на делегатов. Так что для определенного типа цикла стирки, тяжелого, легкого, приятного, экономичного и т. Д. Я бы сказал интерфейс для каждого.

Затем, используя дженерики, я бы создал соответствующий тип стирки, который мне нужен.

В этом случае тип стирки будет иметь событие стирки, которое по порядку делегирует циклы для выполнения.

0 голосов
/ 18 июня 2010

Во-первых, именно это я и сделаю. wash и centrifuge будут проверять флаг, который отслеживает, если машина включена, и генерировать исключение, если это не так, но в противном случае нет обязательного порядка операций; Вы можете позвонить centrifuge до wash, если вы действительно почувствовали необходимость

...