Нужны советы по дизайну

12

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

public static void ValveController
{
    public static void OpenValve(string valveName)
    {
        // Implementation to open the valve
    }

    public static void CloseValve(string valveName)
    {
        // Implementation to close the valve
    }
}

(Реализация будет записывать несколько байтов данных в последовательный порт для управления клапаном - «адрес», полученный из имени клапана, и либо «1», либо «0», чтобы открыть или закрыть клапан).

Другой разработчик спросил, нужно ли нам вместо этого создавать отдельный класс для каждого физического клапана, которых насчитывается десятки. Я согласен, что было бы лучше писать код, PlasmaValve.Open()а не ValveController.OpenValve("plasma"), но это перебор?

Кроме того, мне было интересно, как лучше решить проект с учетом нескольких гипотетических будущих требований:

  1. Нас просят поддержать клапан нового типа, для открытия и закрытия которого требуются разные значения (не 0 и 1).
  2. Нас просят поддержать клапан, который можно установить в любое положение от 0 до 100, а не просто «открыть» или «закрыть».

Обычно я бы использовал наследование для такого рода вещей, но недавно я начал думать о «композиции поверх наследования» и задавался вопросом, есть ли более подходящее решение для использования композиции?

Эндрю Стивенс
источник
2
Я хотел бы создать общий класс клапана, который имеет идентификатор для конкретного клапана (не строка, возможно, перечисление) и любую информацию, необходимую для управления потоком внутри методов OpenValve / CloseValve. В качестве альтернативы вы могли бы сделать класс valv абстрактным и создать отдельные реализации для каждого, где клапан открытия / закрытия просто вызывает логику внутри данного класса клапанов для случая, когда разные клапаны имеют разные механизмы открытия / закрытия. Общий механизм будет определен в базовом классе.
Джимми Хоффа
2
Не беспокойтесь о гипотетических будущих требованиях. YAGNI.
фунтовые
3
@pdr YAGNI - это обоюдоострое лезвие, я согласен с тем, что в целом стоит следовать, но если довести до крайности, можно сказать, что делать что-либо, чтобы помочь будущему удобству обслуживания или читабельности, нарушает YAGNI, поэтому я считаю, что сфера применения YAGNI слишком неоднозначна для много. Тем не менее, многие люди понимают, где использовать YAGNI и где бросить его, потому что учет будущего спасет вас от серьезной боли. Я просто думаю, что нужно быть осторожным, предлагая людям следовать за YAGNI, когда вы не знаете, где они окажутся в этом спектре.
Джимми Хоффа
2
Человек, «состав по наследству» переоценен. Я бы сделал абстрактный класс / интерфейс Valve, а затем разделил их на PlasmaValve. И тогда я убедился бы, что мой ValveController будет работать с Valve (s), не заботясь о том, какой именно подкласс они.
MrFox
2
@suslik: Абсолютно. Я также видел отличный код, называемый спагетти людьми, которые не понимают принципы SOLID. Мы могли бы продолжать это навсегда. Моя точка зрения заключается в том, что я видел больше проблем, вызванных отказом от установленных принципов (рожденных в результате многолетнего опыта), чем из-за чрезмерной приверженности. Но я согласен, что обе крайности опасны.
фунтовые

Ответы:

12

Если каждый экземпляр объекта вентиля будет выполнять тот же код, что и этот ValveController, то кажется, что несколько экземпляров одного класса - правильный путь. В этом случае просто укажите, каким клапаном он управляет (и каким образом) в конструкторе объекта клапана.

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

stonemetal
источник
1
+1 за упоминание операторов переключения на основе типа как запах кода. Я часто вижу подобные заявления о переключателях, когда разработчик утверждает, что он просто следовал за KISS. Прекрасный пример того, как принципы дизайна могут быть извращены хе
Джимми Хоффа
2
Несколько экземпляров также могут упростить связывание клапанов в последовательности, что позволит вам смоделировать фактический трубопровод установки в виде ориентированного графика в вашем коде. Затем вы также можете добавить бизнес-логику в классы, если вам нужно сделать что-то вроде открытия одного клапана, когда другой закрывается, чтобы избежать повышения давления, или закрытия всех клапанов ниже по потоку, чтобы вы не получили эффект «гидравлического удара» когда клапан открывается снова.
TMN
1

Моя главная задача - использовать строки для параметра, идентифицирующего клапан.

По крайней мере, создайте Valveкласс, который имеет getAddressв форме основные потребности реализации, и передайте их классу ValveControllerи убедитесь, что вы не можете создать несуществующие клапаны. Таким образом, вам не придется обрабатывать неправильные строки в каждом из методов открытия и закрытия.

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

Если вам нравится тестировать, вы также должны сделать ValveControllerсинглтон, чтобы вы могли его высмеять (или создать тренажер для операторов).

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