Как я могу уменьшить сложность в библиотеке, не увеличивая сложность в другом месте? - PullRequest
2 голосов
/ 24 февраля 2010

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

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

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

Мои цели:

Уменьшить количество повторений кода

Поддерживать подобный уровень сложности на прикладном уровне

Упростить добавление новых команд

Моя идея:

Имеют одну функцию для отправки команды и одну для получения (функция получения вызывается автоматически при обнаружении ответа от устройства). Иметь структуру, содержащую все данные команды / ответа, которые будут переданы отправляющей функции и возвращены получающей функцией. Поскольку каждая команда имеет соответствующее значение перечисления, есть оператор switch, который устанавливает данные для каждой команды для отправки.

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

Заранее спасибо! (Пожалуйста, дайте мне знать, если уточнение необходимо)

Ответы [ 3 ]

2 голосов
/ 24 февраля 2010

Это напоминает мне дебаты REST против SOA, хотя и в меньшем физическом масштабе.

Если я вас правильно понимаю, сейчас у вас есть звонки типа

device->DoThing();
device->DoOtherThing();

и иногда я получаю ответный звонок типа

callback->DoneThing(ThingResult&);
callback->DoneOtherTHing(OtherThingResult&)

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

Вы, кажется, хотите предложить

device->Do(ThingAndOtherThingParameters&)
callback->Done(ThingAndOtherThingResult&)

чтобы иметь единую точку входа с более сложными данными.

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

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

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

Удачи.

1 голос
/ 24 февраля 2010

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

Теперь в тексте вы говорите об архитектуре обработки команд , где с каждой командой связан свой набор данных. В прежние времена это, как правило, реализовывалось с большим сигналом case в цикле, где каждый случай вызывал отдельную подпрограмму с разными параметрами и, возможно, некоторый код установки. Гризли. Анализаторы сложности McCabe ненавидят это.

В наши дни с помощью языка OO вы можете использовать динамическую диспетчеризацию . Создайте базовый абстрактный класс «command» со стандартным методом «handle ()» и сделайте так, чтобы каждая отдельная команда наследовала его, чтобы добавить своих собственных членов (для представления различных «аргументов» в разные команды). Затем вы создаете большой массив из них при запуске, обычно индексируемый идентификатором команды. Для таких языков, как C ++ или Ada, это должен быть массив указателей на «командные» объекты, чтобы динамическая диспетчеризация работала. Затем вы можете просто вызвать соответствующий объект команды для идентификатора команды, который вы прочитали с клиента. Большой гудящий оператор case теперь неявно обрабатывается динамической диспетчеризацией.

Где вы можете получить большую экономию в этом сценарии, в подклассах. У вас есть несколько команд, которые используют одни и те же параметры? Создайте для них подкласс, а затем выведите все эти команды из этого подкласса. У вас есть несколько команд, которые должны выполнить одну и ту же операцию с одним из параметров? Создайте для них подкласс с помощью этого единственного метода, реализованного для этой операции, а затем выведите все эти команды из этого подкласса.

1 голос
/ 24 февраля 2010

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

Мой план будет:

  • Определите объекты, которые необходимы более высоким слоям данных для выполнения работы. Моделируйте объекты в классах C ++ с их точки зрения, а не с точки зрения аппаратного обеспечения
  • Определить интерфейс библиотеки, используя вышеуказанные объекты
  • Запустите реализацию библиотеки. Возможно, необходим промежуточный уровень, который отображает программные объекты на аппаратные объекты
  • Есть много вещей, которые вы можете сделать, чтобы уменьшить количество повторений кода. Вы можете использовать полиморфизм. Определите класс с базовой функциональностью и расширьте его. Вы также можете использовать служебные классы, которые реализуют функции, необходимые для многих команд.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...