Русский
Русский
English
Статистика
Реклама

Роберт мартин

React. Не в глубь, а в ширь. Композиция против реальности

16.06.2021 14:10:53 | Автор: admin

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

Задача: в проект нужны тултипы. Сказано сделано.

interface OwnProps {  hint: string}export const Tooltip: FC<OwnProps> = ({ hint, children }) => {  // допустим, в зависимости от кол-ва символов и пространства на экране  // производится позиционирование  const [config, setConfig] = useState(null)  const ref = useRef(null)    useLayoutEffect(() => {    // реализация алгоритма позиционирования    // ...    setConfig(someConfig)  }, [hint])    return (    <div ref={ref}>      {children}      <TooltipComponent config={config} hint={hint} />    </div>  )

Спустя какое-то время в проекте должен появиться ещё один тултип, он красивее и принимает обработчик клика. Самое простое и кратчайшее решение изменить имеющийся компонент Tooltip.

interface TooltipProps {  hint: string  onClick?: () => void}export const Tooltip: FC<TooltipProps> = ({ hint, children, onClick }) => {  // допустим, в зависимости от кол-ва символов и пространства на экране  // производится позиционирование  const [config, setConfig] = useState(null)  const ref = useRef(null)  useLayoutEffect(() => {    // реализация алгоритма позиционирования    // ...    setConfig(someConfig)  }, [hint])    // А ВОТ И НОВЙ ВАРИАНТ ИСПОЛЬЗОВАНИЯ!!!  // в этом компоненте уже обязательно нужен onClick  if (onClick) {    return (      <div ref={ref}>      {children}      <AnotherTooltipComponent config={config} hint={hint} onClick={onClick} />    </div>    )  }  return (    <div ref={ref}>      {children}      <TooltipComponent config={config} hint={hint} />    </div>  )}

Мы модифицировали старый компонент, добавили инструкцию if и всё заработало. Единственное, что несколько смущает на данном этапе, это то, что из интерфейса TooltipProps совсем не очевидно, что обработчик onClick на самом деле не просто опциональное свойство, а ещё и определитель: какой вариант тултипа нужно вернуть. В общем, может и не очевидно, а может и очевидно, ясно одно: Done is better than perfect.

И вот нас снова просят добавить новый тултип DiscountTooltipComponent, который тоже обязательным свойством принимает обработчик onClick. Чтобы отличать два компонента DiscountTooltipComponent от AnotherTooltipComponent мы используем дополнительное свойство type.

interface TooltipProps {  hint: string  type?: 'another' | 'discount'  onClick?: () => void}export const Tooltip: FC<TooltipProps> = ({ type, hint, children, onClick }) => {  // допустим, в зависимости от кол-ва символов и пространства на экране  // производится позиционирование  const [config, setConfig] = useState(null)  const ref = useRef(null)  useLayoutEffect(() => {    // реализация алгоритма позиционирования    // ...    setConfig(someConfig)  }, [hint])    // А ВОТ И НОВЙ ВАРИАНТ ИСПОЛЬЗОВАНИЯ!!!  // в этом компоненте уже обязательно нужен onClick  if (type && onClick) {    return (      <div ref={ref}>      {children}      {type === 'another' ? (        <AnotherTooltipComponent config={config} hint={hint} onClick={onClick} />      ) : (        <DiscountTooltipComponent config={config} hint={hint} onClick={onClick} />      }    </div>    )  }  return (    <div ref={ref}>      {children}      <TooltipComponent config={config} hint={hint} />    </div>  )}

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

Начнём сверху, с интерфейса TooltipProps. Глядя на него, совсем не очевидно, что поля type и onClick связаны между собой. Следовательно, не очевидны и варианты использования компонента Tooltip. Мы можем указать type = "another", но не передать onClick, и тогда typescript не выдаст ошибки.

Самое время обратиться к принципу разделения интерфейсов (Interface Segregation Principle), который на уровне компонентов называется принципом совместного повторного использования. Он гласит:

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

Чтобы проблема стала видна отчётливее, представим, что прошло ещё немного времени.

Аналитики просят залогировать нажатие на DiscountTooltipComponent.

interface TooltipProps {  hint: string  type?: 'another' | 'discount'  onClick?: () => void}export const Tooltip: FC<TooltipProps> = ({ type, hint, children, onClick }) => {  // допустим, в зависимости от кол-ва символов и пространства на экране  // производится позиционирование  const [config, setConfig] = useState(null)  const ref = useRef(null)    useLayoutEffect(() => {    // реализация алгоритма позиционирования    // ...    setConfig(someConfig)  }, [hint])    // ЗДЕСЬ М БУДЕМ ЛОГИРОВАТЬ  const handleClick = () => {    if (type === 'discount') {      // произвести логирование    }    if (onClick) {  onClick()    }}  // А ВОТ И НОВЙ ВАРИАНТ ИСПОЛЬЗОВАНИЯ!!!  // в этом компоненте уже обязательно нужен onClick  if (type) {    return (    <div ref={ref}>      {children}      {type === 'another' ? (        <AnotherTooltipComponent config={config} hint={hint} onClick={handleClick} />      ) : (        <DiscountTooltipComponent config={config} hint={hint} onClick={handleClick} />      }    </div>    )  }    return (    <div ref={ref}>      {children}      <TooltipComponent config={config} hint={hint} />    </div>  )}

Теперь все, кто использовал Tooltip в его первозданном виде, получили в нагрузку handleClick, который ими никак не используется, но ресурсы на него расходуются. А те, кто использовал компонент с type='another', получили не нужную обертку handleClick. Что, если мы разделим интерфейсы, например:

interface Tooltip {  hint: string}interface TooltipInteractive extends Tooltip {  onClick: () => void}

Теперь выделим общую логику в компонент TooltipSettings:

interface TooltipSettingsProps {  hint: string  render: (config: any, hint: string) => JSX.Element}export const TooltipSettings: FC<TooltipSettingsProps> = ({ render }) => {  // допустим в зависимости от кол-ва символов и пространства на экране  // производится позиционирование  const [config, setConfig] = useState(null)  const ref = useRef(null)  useLayoutEffect(() => {    // реализация алгоритма позиционирования    // ...    setConfig(someConfig)  }, [hint])  return (    <div ref={ref}>      {children}      {render(config, hint)}    </div>  )}

Реализуем интерфейс Tooltip:

export const Tooltip: FC<Tooltip> = ({ hint }) => (  <TooltipSettings hint={hint} render={(config, hint) => <TooltipComponent config={config} hint={hint} />} />)

Реализуем интерфейс TooltipInteractive:

export const AnotherTooltip: FC<TooltipInteractive> = ({ hint, onClick }) => (  <TooltipSettings    hint={hint}    render={(config, hint) => <AnotherTooltipComponent onClick={onClick} config={config} hint={hint} />}  />)

В частности DiscountTooltipComponent:

export const DiscountTooltip: FC<TooltipInteractive> = ({ hint, onClick }) => {  const handleClick = () => {    // произвести логирование    // вызвать обработчик    onClick()  }  return (    <TooltipSettings      hint={hint}      render={(config, hint) => <DiscountTooltipComponent onClick={handleClick} config={config} hint={hint} />}    />  )}

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

Подробнее..

Чистая архитектура. Часть II Парадигмы программирования

26.04.2021 16:20:03 | Автор: admin

Эта серия статей вольный и очень краткий пересказ книги Роберта Мартина (Дяди Боба) Чистая Архитектура, выпущенной в 2018 году. Начало тут.

Парадигмы программирования

Дисциплину, которая в дальнейшем стала называться программированием, зародил Алан Тьюринг в 1938 году. В 1945 он уже писал полноценные программы, которые работали на реальном железе.

Первый компилятор был придуман в 1951 году Грейс Хоппер (бабушка с татуировкой Кобола). Потом начали создаваться языки программирования.

Обзор парадигм

Существует три основных парадигмы: структурное, объектно-ориентированное и функциональное. Интересно, что сначала было открыто функциональное, потом объектно-ориентированное, и только потом структурное программирование, но применяться повсеместно на практике они стали в обратном порядке.

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

Объектно-ориентированное программирование было открыто в 1966 году.

Функциональное программирование открыто в 1936 году, когда Чёрч придумал лямбда-исчисление. Первый функциональный язык LISP был создан в 1958 году Джоном МакКарти.

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

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

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

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

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

С тех пор оператора goto не стало практически ни в одном языке программирования.

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

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

Объектно-ориентированное программирование

ООП это парадигма, которая характеризуется наличием инкапсуляции, наследования и полиморфизма.

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

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

Наследование позволяет делать производные структуры на основе базовых, тем самым давая возможность осуществлять повторное использование этих структур. Наследование было реально сделать в языках до ООП, но в объектно-ориентированных языках оно стало значительно удобнее.

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

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

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

Любая зависимость всегда может быть инвертирована. В этом и есть мощь ООП.

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

Функциональное программирование

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

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

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

Интересным подходом для уменьшения изменяемых данных является Event Sourcing. В нём мы храним не сами данные, а историю событий, которые привели к изменениям этих данных. Так как в лог событий можно только дописывать, это означает, что все старые события уже нельзя изменить. Чтобы получить текущее состояние данных, нужно просто воспроизвести весь лог. Для оптимизации можно использовать снапшоты, которые делаются, допустим, раз в день.

Заключение

Таким образом, каждая из трёх парадигм ограничивает нас в чём-то:

  • Структурное отнимает у нас возможность вставить goto где угодно.

  • ООП не позволяет нам доступаться до скрытых членов классов и навязывает нам инверсию зависимостей.

  • ФП запрещает изменять переменные.

Продолжение следует...

Подробнее..

Чистая архитектура. Часть III Принципы дизайна

27.04.2021 14:21:47 | Автор: admin

Эта серия статей вольный и очень краткий пересказ книги Роберта Мартина (Дяди Боба) Чистая Архитектура, выпущенной в 2018 году.Предыдущая часть здесь.

Принципы дизайна

Принципы SOLID говорят нам, как нам соединять наши функции и данные в классы, и как эти классы должны быть взаимосвязаны. То есть эти принципы относятся к уровню модулей (mid-level). Однако они также применимы и к уровню компонентов (high-level архитектура).

Принципы SOLID зародились в конце 80-х и стабилизировались в начале 2000-х.

SRP: Принцип единственной ответственности

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

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

Модуль это некоторое связанное множество функций и структур данных.

Пример нарушения принципа: класс Employee с тремя методами calculatePay(), reportHours(), save(). Все эти три метода относятся к разным актёрам. calculatePay() это бухгалтерия, reportHours() это отдел кадров, save() это администратор базы данных. Если один из актёров просит внести изменения в свой метод, то это может сломать логику других методов. Также это вносит трудности при слиянии изменений от разных актёров.

Решение проблемы вынести функции в отдельные классы. В каждом классе будут только та логика, которая относится к соответствующему актёру.

OCP: Принцип открытости/закрытости

Программный артефакт должен быть открытым для расширения, но закрытым для изменения.

Простыми словами: артефакт должен быть расширяемым без необходимости изменения его кода.

Этот принцип очень важен для архитектурных компонент.

Пример: финансовый отчёт умеет отображаться на веб-странице. Теперь клиент просит печатать его на чёрно-белом принтере. Если OCP выполняется, то доля кода, которого придётся изменить, будет близка к нулю. Чтобы этого достичь, компонент представления отчётов должен быть защищённым от изменения, а для этого он должен зависеть не от конкретной реализации это представления, а от интерфейса. Далее конкретные компоненты будут добавляться путём расширения, т.е. имплементации интерфейса.

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

Проявлением нарушения OCP является также зависимость от вещей, которые не используются.

LSP: Принцип подстановки Лисков

Принцип замещения: если S является подтипом T, тогда объекты типа T в программе могут быть замещены объектами типа S без каких-либо изменений желательных свойств этой программы.

Другими словами, все реализации интерфейса должны строго соответствовать контракту, описанном в этом интерфейсе.

Пример нарушения LSP: классическая проблема квадрата/прямоугольника.

Пример более высокоуровневого нарушения LSP: агрегатор сервисов такси, где в одном из сервисов название одного из параметров REST-запроса отличается от остальных. Тогда добавление этого сервиса приведёт к нарушению работы всего агрегатора. Эту проблему можно решить, например, путём вынесения маппинга параметров во внешнюю БД.

ISP: Принцип разделения интерфейса

Программные сущности не должны зависеть от методов, которые они не используют.

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

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

DIP: Принцип инверсии зависимостей

Программные сущности должны зависеть от абстракций, не от деталей.

В Java это значит, что модули должны зависеть от интерфейсов или абстрактных классов.

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

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

Не рекомендуется наследоваться от часто изменяемых классов.

Как создавать инстансы конкретных классов, если мы не можем от них зависеть? Для этого есть несколько решений:

  • Шаблон Абстрактная Фабрика

  • Контейнеры, которые сами внедряют зависимости (XML, аннотации)

  • Сервисы

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

Продолжение следует...

Подробнее..

Перевод Актуальность принципов SOLID

05.06.2021 22:17:23 | Автор: admin

Впервые принципы SOLID были представлены в 2000 году в статье Design Principles and Design Patterns Роберта Мартина, также известного как Дядюшка Боб.

С тех пор прошло два десятилетия. Возникает вопрос - релевантны ли эти принципы до сих пор?

Перед вами перевод статьи Дядюшки Боба, опубликованной в октябре 2020 года, в которой он рассуждает об актуальности принципов SOLID для современной разработки.

Недавно я получил письмо с примерно следующими соображениями:

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

Принцип подстановки Лисков давно устарел, потому что мы уже не уделяем столько внимания наследованию, сколько уделяли 20 лет назад. Думаю, нам стоит рассмотреть позицию Дена Норса о SOLID - Пишите простой код

В ответ я написал следующее письмо.

Принципы SOLID сегодня остаются такими же актуальными, как они были 20 лет назад (и до этого). Потому что программное обеспечение не особо изменилось за все эти годы, а это в свою очередь следствие того, что программное обеспечение не особо изменилось с 1945 года, когда Тьюринг написал первые строки кода для электронного компьютера. Программное обеспечение - это все еще операторы if, циклы while и операции присваивания - Последовательность, Выбор, Итерация.

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

Итак, пройдемся по принципам по порядку.

SRP - Single Responsibility Principle Принцип единственной ответственности.

Объединяйте вещи, изменяющиеся по одним причинам. Разделяйте вещи, изменяющиеся по разным причинам.

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

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

Слайды Дена Норса полностью упускают этот момент и убеждают меня в том, что он вообще не понимает сам принцип (либо иронизирует, что более вероятно предположить, зная Дена). Его ответ на SRP - Пишите простой код. Я согласен. SRP - один из способов сохранять код простым.

OSP - Open-Closed Principle Принцип открытости-закрытости

Модуль должен быть открытым для расширения, но закрытым для изменения.

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

Или... хотим ли мы отделить абстрактные понятия от деталей реализации? Хотим ли мы изолировать бизнес-правила от надоедливых мелких деталей графического интерфейса, протоколов связи микросервисов и тонкостей поведения различных баз данных? Конечно хотим!

И снова слайд Дэна преподносит это совершенно неправильно.

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

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

LSP - Liskov Substitution Principle Принцип подстановки Лисков

Программа, использующая интерфейс, не должна путаться в реализациях этого интерфейса.

Люди (включая меня) допустили ошибку, полагая что речь идет о наследовании. Это не так. Речь о подтипах. Все реализации интерфейсов являются подтипами интерфейса, в том числе при утиной типизации. Каждый пользователь базового интерфейса, объявлен этот интерфейс или подразумевается, должен согласиться с его смыслом. Если реализация сбивает с толку пользователя базового типа, то будут множиться операторы if/switch.

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

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

ISP - Interface Segregation Principle Принцип разделения интерфейса

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

Мы по-прежнему работаем с компилируемыми языками. Мы все еще зависим от дат изменений, чтобы определить, какие модули должны быть перекомпилированы и перевыпущены. Покуда это справедливо, нам придется столкнуться с проблемой - когда модуль A зависит от модуля B во время компиляции, но не во время выполнения, изменения в модуле B ведут к перекомпиляции и перевыпуску модуля A.

Проблема особенно остро стоит в статически типизированных языках, таких как Java, C#, C++, GO, Swift и т.д. Динамически типизированные языки страдают гораздо меньше, но тоже не застрахованы от этого - существование Maven и Leiningen тому доказательство.

Слайд Дэна на эту тему ошибочен.

(Примечание. На слайде Ден обесценивает утверждение Клиенты не должны зависеть от методов, которые они не используют фразой Это же и так правда!!)

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

(Примечание. Речь о фразе Если классу нужно много интерфейсов - упрощайте класс!)

Да, если вы можете разбить класс с двумя интерфейсами на два отдельных класса, то это хорошая идея (SRP). Но такое разделение часто недостижимо и даже нежелательно.

DIP - Dependency Inversion Principle Принцип инверсии зависимостей

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

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

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

Лучший способ создать путаницу - сказать всем будьте проще и не дать никаких дальнейших инструкций.

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru