Лучше создать библиотеку с несколькими функциями или создать классы? - PullRequest
3 голосов
/ 27 января 2011

Я разрабатываю программное обеспечение для связи с устройством.

Программное обеспечение отправит команды для устройства. Устройство должно ответить, используя протокол ниже:

<STX><STX><COMMAND>[<DATA_1><DATA_2>...<DATA_N>]<CHKSUM><ETX>

где:

<STX> is the Start of TeXt (0x55);
<COMMAND> can be 0x01 for read, 0x02 for write, etc;
<DATA> is any value;
<CHKSUM> is the checksum;
<ETX> is the End of TeXt (0x04).

Итак, я должен проверить полученные данные.

Затем полученные данные:

  • не может быть пустым;
  • должен содержать 3 или более символов;
  • должен иметь заголовок в первых двух символах строковых данных;
  • должен иметь нижний колонтитул в последнем символе строковых данных;
  • должен иметь действительную контрольную сумму.

Если ответ верный, тогда я могу обработать данные. Но прежде чем мне придется извлечь эти данные из полученного ответа.

Хорошо, это относительно простая задача. Раньше я делал это по процедурному принципу, используя только одну функцию и помещая множество if.

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

Чтобы проверить ответ устройства, лучше, например, создать класс «ValidateReceivedData» и передать полученные данные в конструктор этого класса? А затем создайте открытый метод с именем «IsReceivedDataValid», который проверяет все шаги, указанные выше?

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

Я бы тоже хотел использовать модульный тест.

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

Ответы [ 3 ]

2 голосов
/ 27 января 2011

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

ProtocolParser класс:

  • Принимает SerialPort объект или его эквивалент в конструкторе и прослушивает его для входящих байтов
  • Передает полученные байты на OnByteReceived, который реализует конечный автомат для протокола (с такими состояниями, как Unknown, Stx1Received, Stx2Received, ..., CkSumReceived).
  • После получения всего хорошего сообщения создается объект типа Packet, который принимает список байтов в своем конструкторе. Затем он вызывает событие PacketReceived, передавая Packet в качестве аргумента.
  • Если получен неверный байт, он вызывает событие BadDataReceived и передает неверные данные (возможно, для целей ведения журнала / отладки).

Packet класс:

  • Берет список / массив байтов и сохраняет их как свойства Command и Data.
  • Не нужно сохранять контрольную сумму, поскольку этот класс предназначен только для представления действительного пакета.

Указанные выше классы достаточны для реализации протокола приема. Вы должны быть в состоянии проверить это, посмеиваясь над классом SerialPort (то есть ProtocolParser может фактически взять IDataSource вместо SerialPort).

Затем можно добавить класс более высокого уровня для реализации функций, специфичных для вашего устройства, которые будут прослушивать событие PacketReceived ProtocolParser.

1 голос
/ 27 января 2011

Я бы пошел на шаг дальше, чем ответ Йохая, и создал бы следующие классы:

  1. Команда: На самом деле это не класс, а значение Enum, поэтому вы можете проверить его с помощью Command.Read и т. Д.вместо того, чтобы просто «знать», что означают 0x01 и 0x02.
  2. Сообщение: просто обычный объект (POJO / POCO / что угодно), который предназначен для хранения представления данных сообщения.Он будет содержать следующие поля:
    1. Команда (тип перечисления, упомянутый ранее)
    2. Данные: список данных.В зависимости от того, как представлены данные, вы можете создать для этого класс или просто представить каждый элемент данных в виде строки.
  3. MessageParser: это будет иметь функцию, которая будет анализироватьстрока или текстовый поток и создать объект сообщения.Если текст недействителен, я бы сгенерировал настраиваемое исключение (другой класс), которое может быть перехвачено вызывающей стороной.
  4. MessageExecutor: Это будет принимать объект Message и выполнять действие, которое он представляет.

Создав объект промежуточного представления (Сообщение), вы позволяете разделить различные выполняемые вами действия.Например, если Powers That Be решат, что текст сообщения может быть отправлен в формате XML или JSON, вы можете создавать различные классы MessageParser, не связываясь с логикой, которая решает, что делать с сообщением.

Thisтакже значительно упрощает модульное тестирование, поскольку вы можете тестировать анализатор сообщений независимо от исполнителя.Сначала протестируйте анализатор сообщений, вызвав функцию синтаксического анализа и изучив получившийся объект сообщения.Затем протестируйте исполнителя, создав объект Message и убедившись, что предпринято соответствующее действие.

1 голос
/ 27 января 2011

Конечно, будет лучше использовать дизайн ООП.
По тому, что вы объяснили, я бы сделал как минимум 2 класса:

  1. Сообщение

  2. Исполнитель

Сообщение получит команду от устройства, а Исполнитель обработает сообщение.

Объект сообщения будет инициирован с устройстваответ.Он проанализирует его и сохранит поля, как вы описали:

STX
КОМАНДА
ДАННЫЕ
CHKSUM
ETX

Затем объект Executer получит объект Messageи выполните фактическое выполнение сообщения и удерживайте логический код.

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