Как я могу планировать свое программное обеспечение, чтобы избежать чрезмерного переписывания и взаимозависимостей
Я пишу контроллер мотора, который имеет пару интерфейсов (кнопки, Bluetooth, тактильные ручки), и эта задача неуклонно растет и становится больше, чем я предполагал. Я попытался просто пойти на это, начав с низкоуровневых модулей (например, написать код для разговора по шине I2C), а затем выше (код для общения с конкретным устройством на шине I2C...), но слишком часто мне приходится возвращаться к своим нижним модулям, чтобы справиться с причудами, которые я не учел. Либо это занимает много времени, либо я получаю действительно взломанный код.
Моя цель - 8-битный MCU, так что снизу вверх кажется, что я могу лучше использовать аппаратное обеспечение. Если я иду сверху вниз, у меня нет какой-либо структуры для сборки или тестирования / отладки.
Я попытался составить несколько общих диаграмм и диаграмм для конкретных уровней / драйверов, но я не уверен, как их структурировать, поэтому я могу быть очень систематическим и избегать нечетного сигнала, который должен пройти через 2-3 слои.
Я предполагаю, что это является причиной для получения степени CS? Я инженер-электрик:P
7 ответов
Похоже, вы на правильном пути. Иногда никакое планирование не может помешать вам перепроектировать или реорганизовать части системы позднее. Попробуйте некоторые из следующих советов:
- Храните ваш код в модулях, разделенных логическими функциями.
- Не дублируйте код, а разрабатывайте повторно используемые методы для совместного использования.
- Старайтесь избегать соблазна добавлять хаки для особых случаев. В конце концов это станет неосуществимым. Вместо этого отрегулируйте и реорганизуйте небольшие секции как можно скорее. Попытка сделать большой редизайн в конце будет сложнее.
- Не пытайтесь чрезмерно проектировать систему с самого начала, так как вы можете просто тратить время, когда переходите к реальной реализации.
- Сохраняйте нижние уровни как можно более простыми, а затем создавайте более продвинутые возможности.
- Документируйте свои функции и пишите несколько юнит-тестов, особенно после добавления сложных условных операторов.
- Старайтесь отлавливать ошибки как можно выше в стеке. Например, проверка входных данных и проверка возвращаемых значений. Это облегчит отладку.
При работе в многоуровневом коде заманчиво погрузиться в более низкий уровень, когда API не позволяет вам делать именно то, что вы хотите. Это особенно трудно при одновременной записи нескольких уровней.
Вот несколько предложений:
- Относитесь ко всем другим уровням, кроме того, над которым вы работаете, как к запечатанным. Создано другой компанией, разработчиком и т. Д. Не поддавайтесь желанию изменить другой уровень, чтобы решить проблему на текущем уровне.
- Создайте "родственный уровень" для того, над кем вы работаете. Это трудно описать в абстрактном смысле, но допустим, что ваш нижний уровень - это бизнес-уровень, а более высокий уровень - это пользовательский интерфейс, создайте ДРУГОЙ уровень пользовательского интерфейса для другого приложения. Теперь наличие двух потребителей одного и того же API может помочь указать, что должно быть на каждом уровне.
- Попробуйте чередовать порядок, в котором вы работаете на уровнях. Например, в некоторых приложениях я нахожу более полезным сначала спроектировать пользовательский интерфейс, а затем перейти к бизнес-уровню / базе данных, чтобы заставить этот пользовательский интерфейс работать так, как задумано. В других случаях имеет смысл использовать статистику для модели данных и работать с пользовательским интерфейсом. Но дело в том, что вы "думаете" об API по-разному в этих двух сценариях. И, посмотрев на многоуровневый код с обеих сторон, помогает.
- Опыт имеет значение. Иногда просто допустить ошибку из-за чрезмерно связанного кода - единственный способ научиться избегать этого. Вместо того, чтобы планировать, что ваше приложение будет совершенным, планируйте, что оно будет несовершенным. Под этим я подразумеваю сначала настроить быстрый цикл разработки / тестирования / рефакторинга, чтобы вы могли быстро адаптироваться к ошибкам, которые вы не увидите до тех пор, пока не сделаете их. Это также область, где "одноразовые прототипы" пригодятся. Сделайте простой черновой набросок, поучитесь на нем и выбросьте его. Выброшенная часть важна. Даже если это удивительно, начните строить еще один с нуля. Вы неизбежно сделаете его лучше (и, по моему опыту, более организованным) на основе того, что вы узнали из прототипа.
Это действительно больше вопрос опыта, чем вашей степени. Если вы все еще изучаете, как управлять оборудованием, тогда, конечно, ваш код изменится. Я не буду мучиться из-за этого. Однако, поскольку вы действительно делаете прототипы, вы должны быть готовы к рефакторингу кода, как только он заработает. Удалите избыточности, разделите данные и функциональные возможности, и организуйте свои интерфейсы так, чтобы это имело смысл.
По моему опыту, код драйвера устройства нуждается в нисходящем и восходящем дизайне / реализации, что я называю снаружи. Вы знаете, что пользователь захочет сделать, и вы можете написать эти интерфейсы, и вы знаете, что должен делать драйвер нижнего уровня, и вы пишете это. Если они не соответствуют друг другу в середине, переосмыслите компоновку вашего модуля.
Для улучшения подвергните свой дизайн и код проверке людьми с большим опытом. Оставьте свое эго без внимания и просто возьмите их на себя. Вы также можете прочитать книгу об объектно-ориентированном анализе и дизайне (мне нравились книги Питера Коада. Я не знаю, кто их сейчас пишет). Хороший пример покажет, как разделить проблему на объекты с четкими ролями и обязанностями.
Другой момент, когда вы закончите создавать прототипы драйверов и узнаете, как управлять оборудованием, - это убедиться, что у вас есть подробные требования. Ничто не перекручивает код больше, чем обнаружение требований во время написания. Вы также можете попробовать изучить UML и проектировать с помощью диаграмм, прежде чем писать код. Это не работает для всех. Также обратите внимание, что вам не нужно кодировать на языке, который поддерживает объектно-ориентированные конструкции, чтобы использовать OOD.
Если ваша проблема заключается в том, как создать правильные абстракции, что, по-видимому, имеет место, я думаю, что самое важное, что вы можете сделать, чтобы научиться (кроме того, чтобы просить обзоры кода дизайна / прочитать книги / прочитать код), это подумать, прежде чем вы НАЧАТЬ ПИСЬМЕННЫЙ КОД.
Обычно бывает, что вы начинаете с приблизительного представления о том, что вы хотите и как это сделать, а затем приступаете к написанию кода. Позже вы обнаружите, что вы не продумали вещи и у вас есть несколько зияющих дыр, которые сейчас, потому что вы потратили время на написание кода, трудно исправить, что приводит либо к потере времени, либо к хакерскому коду.
Тщательно продумайте, как создать дизайн, который легко справится с изменениями. Например, инкапсулируйте данные, которые должны перемещаться между слоями, поэтому, если позже вы обнаружите, что пропустили важный параметр, вы можете легко добавить его в структуру без необходимости везде изменять код.
Попробуйте "запустить дизайн в своей голове" несколько раз, пока вы не будете уверены, что рассмотрели самые важные детали и можете сказать (это самая важная часть, потому что вы всегда будете пропускать вещи, или требования будут меняться) что если вы что-то пропустили, вы можете настроить модули относительно легко.
UML может помочь вам структурировать мышление о дизайне. Это, конечно, не панацея, но она показывает различные критерии, которые следует учитывать при создании дизайна программного обеспечения.
Это немного похоже на совет учителя классических шахмат: "сядь на руки":-)
Драйверы поддаются слоистому подходу.
У ваших водителей может быть несколько "классов":
- Только вход
- Только вывод
- и я, и О.
Они должны иметь стандартизированный интерфейс, например:
GetKnobLevel()
GetNextKeyboardButton
Или другой подход - иметь что-то вроде
syscall(DRIVERNUMBER, command)
внесение параметров / результатов в указанные регистры.
Это простой, полезный подход. Более сложный вариант может заключаться в том, чтобы иметь циклические очереди между кодом аппаратного обеспечения и кодом программного обеспечения вместо регистров.
Вот ментальная модель, которую я использую:
---
Application
---
OS
---
Driver communicators
---
drivers
---
hardware
Между каждым слоем существует четко определенный интерфейс без дисперсии (я продолжаю воображать слоеный пирог с толстой глазурью между слоями...). Конечно, ОС может не существовать для вас.
Если ваш MCU поддерживает программные и аппаратные прерывания, такие как процессор x86, вы можете использовать их для изоляции драйверов от коммуникаторов драйверов.
Честно говоря, это немного "слишком силовое" решение. Но в ситуации, когда ваша сложность становится существенной, легче иметь жесткий инжиниринг, чем свободный проект.
Если вы обмениваетесь данными между уровнями, вы можете использовать глобальную переменную для каждого коммуникационного "канала" и обращаться к ней дисциплинированным образом, используя только функции для доступа к ней.
Как правило, вы захотите сделать бумажный дизайн на каком-то уровне, некоторую исследовательскую работу и перепроектировать, прежде чем вы действительно начнете кодировать свой проект. Блок-схемы и схемы шинного перехода здесь хорошо работают.
Это подход, который я одобрил в своей работе со встроенными системами, и он хорошо работает для меня.
Кроме того - эта проблемная область недостаточно хорошо изучена в традиционных программах по информатике. Это гораздо менее простительно, чем Интернет или современные операционные системы.
На мой взгляд, наиболее важными аспектами хорошо структурированного кода являются низкая степень связи и отделение побочных эффектов от состояния.
Также - код это ремесло. Не думайте, что вы можете сделать идеальное решение с самого начала. Будьте готовы изменить свой код по мере изучения новых вещей. На самом деле - убедитесь, что вы принимаете изменить себя как часть вашей работы.
Не для того, чтобы быть слишком бойким, но на ум приходит цитата из "Мифического человеко-месяца": "Планируй выбросить одну, ты все равно будешь".
Следствием чего является "Сделай так, чтобы это работало. Сделай это правильно. Сделай это быстро".
Я полагаю, что это заставляет меня выступать за предварительный дизайн, но не быть парализованным этим. Это не должно быть идеально с первого раза. План рефакторинга. Надеюсь, вы будете писать вещи таким образом, что на самом деле вы не выбрасываете много кода, а перестраиваете вещи более приятным образом.