Советы по проектированию программ

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

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

Проектирование частного дома, с чего начинать? Основы. Видео №1

Подробнее о требованиях…

# 7 Определите важные функциональные требования

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

Как архитекторы, наша миссия — найти системный дизайн, который

  1. позволяет и поддерживает реализацию заданных функциональных требований
  2. соответствует указанным нефункциональным требованиям

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

В совете №5 я уже упоминал, что нефункциональные требования оказывают значительное влияние на архитектурный дизайн системы. А как насчет функциональных требований?

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

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

С чего начать разработку проекта? — Вопросы и Ответы #10

В качестве тривиального примера приложение, управляемое формами данных и отчетами, может предложить использование (реляционной) базы данных. Конечно, мы могли бы использовать другой подход и вместо этого использовать простой файловый ввод-вывод. Но как только для отчетов потребуются какие-то агрегированные данные или сложная проекция, мы начнем реализовывать части функций управления данными, которые база данных будет предоставлять из коробки. Это простой пример, демонстрирующий случайную сложность — термин, введенный Фредом Бруксом в его знаменитой статье «Никакой серебряной пули». Это техническая сложность, которая не имеет отношения к проблеме, часто возникающая из-за неправильного выбора языка и инструментов.

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

Пример
Рассмотрим приложение CRM, которое имеет следующие функциональные требования:

1) Формы данных, такие как создание нового клиента, могут быть визуально адаптированы к корпоративному дизайну организации путем указания пользовательских цветов, пользовательского шрифта, а также пользовательского изображения бренда.
2) Обязательные поля в формах данных окрашены в мягкий цвет, чтобы подчеркнуть их важность.

Как только мы нашли проект системы, который позволяет и поддерживает реализацию требования 1, маловероятно, что требование 2 окажет значительное влияние на наши соображения по проектированию системы. Оба требования требуют богатого пользовательского интерфейса и возможности его визуальной настройки на основе правил или какой-либо конфигурации.

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

Читайте также:
Запуск программы невозможен так как отсутствует gfsdk shadowlib

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

Начните с рассмотрения индивидуальных требований и пометьте их ключевыми словами, указывающими на возможные идеи решения или конструктивные особенности, от которых может выиграть требование. Как указано в совете №1, я рекомендую начинать с абстрактных концепций и не вводить конкретные варианты технологий слишком рано. Вначале вы можете работать с концептуальными ключевыми словами, такими как база данных графиков, push-коммуникация в ion, механизм отчетов, многофункциональный интерфейс, ОС реального времени, gp u accelerat io n и т. д.

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

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

# 8 Определите сценарии атрибутов качества

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

  • Технические ограничения
  • Атрибуты качества, такие как доступность, расширяемость или переносимость

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

Гораздо более интересным (и сложным) является анализ атрибутов качества.

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

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

Хотя значения атрибутов качества, таких как расширяемость или надежность, стандартизированы2, они, безусловно, оставляют много места для различных интерпретаций. В совете № 6 я уже упоминал, что неплохо обсудить объем атрибутов качества. Это может быть первым полезным шагом к лучшему пониманию идей и мотивов, стоящих за ними.

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

Сценарий атрибута качества в основном описывает измеримую реакцию системы на определенное взаимодействие (стимул). Он состоит из шести частей:

  • Источник стимула: объект (человек или система), который генерирует стимул.
  • Стимул: происходящее взаимодействие.
  • Окружающая среда: состояние, при котором возникает раздражитель. Например, система может быть в состоянии перегрузки.
  • Артефакт: получатель стимула. Это либо система, либо ее часть.
  • Ответ: действие, которое происходит после появления стимула.
  • Мера отклика: определяет измеримый результат сценария.

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

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

О конструкции компонентов…

# 10 Дизайн с разделением мыслей

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

Читайте также:
Как защитить свою программу на delphi

Однако до тех пор, пока эти строительные блоки не спроектированы как отдельные процессы, они никогда не будут по-настоящему независимыми друг от друга. Как же так?

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

  • правильная конфигурация всех частей системы
  • выполнение зависимостей всех частей системы, будь то библиотеки или сторонние системы, такие как системы баз данных или брокеры сообщений
  • и фаза инициализации всех частей системы во время запуска

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

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

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

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

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

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

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

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

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

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

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

Читайте также:
Авторская программа дошкольного образования это

Заключительные слова

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

Если вам понадобится дополнительная консультация или личная помощь по вашему текущему проекту, не стесняйтесь связаться со мной. Вместе со своей командой в axio concept GmbH я помогаю компаниям разрабатывать и создавать программные архитектуры и программные продукты.

❤️ Подпишитесь на меня в Medium, Twitter или LinkedIn, чтобы получать уведомления о новых статьях.

Источник: evogeek.ru

Принципы проектирования ПО

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

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

  • S — Single Responsibility (единственная ответственность),
  • O — Open Closed (открытость/закрытость),
  • L — Liskov substitution (принцип подстановки Барбары Лисков),
  • I — Interface Segregation (принцип разделения интерфейсов),
  • D — Dependency Inversion Principles (принцип инверсии зависимостей).

1_e_H81_bCIgypraK0M8eiTQ_1-1801-9e9b1f.png

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

Single Responsibility

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

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

Open Closed

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

Таким образом, программные сущности должны быть открыты для расширения, однако закрыты для модификации.

Liskov substitution

Согласно данному принципу, программисту следует соблюдать наследственность так, чтобы логика программного приложения нигде не нарушалась. К примеру, если новый класс «X-Class» является дочерним для класса «A-Class», то новый класс должен повторять функции родителя таким образом, чтобы эти функции не меняли поведение родительского класса. В таком случае вы без проблем сможете применять объект X-Class вместо объекта A-Class, не нарушая при этом логики программного приложения.

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

Interface Segregation

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

Или же: много интерфейсов, которые специально предназначены для клиентов, — это лучше, чем 1 интерфейс общего назначения.

Dependency Inversion Principles

Любой, кто когда-нибудь применял для создания приложения TDD, знает, насколько важно расщеплять код перед тестированием и моделированием. Иными словами, когда определенный класс «ex:Purchase» имеет зависимость от класса «Users», то установка пользовательских объектов должна инициировать снаружи класс «Purchase».

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

Источник: otus.ru

Методы проектирования программы

Методы проектирования программы

Существует несколько методов проектирования программ:

  • последовательное уточнение или проектирование «сверху вниз»;
  • проектирование «снизу вверх».

Современные программы не может написать один специалист. Как правило, над программой работает целая команда. Для этого задача разбивается на части — на подзадачи.

Такой прием называется последовательным уточнением или проектированием «сверху вниз»: от основной задачи к мелким подзадачам и подпрограммам, которые их решают.

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

Источник: kozhevnikovamn.ru

Рейтинг
( Пока оценок нет )
Загрузка ...
EFT-Soft.ru