.NET Разработчик
6.54K subscribers
442 photos
3 videos
14 files
2.12K links
Дневник сертифицированного .NET разработчика. Заметки, советы, новости из мира .NET и C#.

Для связи: @SBenzenko

Поддержать канал:
- https://boosty.to/netdeveloperdiary
- https://patreon.com/user?u=52551826
- https://pay.cloudtips.ru/p/70df3b3b
Download Telegram
День 1336. #ЗаметкиНаПолях #DesignPrinciples
OCP Против YAGNI. Начало
В этой серии постов рассмотрим противоречия между принципом Open/Closed и принципом «Вам это не понадобится».

OCP
Принцип Open/Closed гласит, что:
«Программные сущности (классы, модули, функции и т. д.) должны быть открыты для расширения, но закрыты для модификации.»

В настоящее время существует две интерпретации этого определения:

1. Дядюшка Боб Мартин говорит о предотвращении волновых эффектов. Когда вы изменяете часть кода, вам не нужно вносить изменения во всю базу кода, чтобы приспособиться к этой модификации. В идеале вы должны иметь возможность добавлять новые функции, ничего не меняя в существующем коде.
Обычно это реализуется с помощью полиморфизма. Например, следующий пример нарушает версию OCP Мартина:
public void Draw(Shape shape)
{
switch (shape.Type)
{
case ShapeType.Circle:
DrawCircle(shape);
break;
case ShapeType.Square:
DrawSquare(shape);
break;
default:
throw new
ArgumentOutOfRangeException();
}
}

Здесь, чтобы ввести новую форму, вам нужно будет изменить метод Draw. Чтобы исправить это, вы можете создать абстрактный класс Shape, а затем перенести логику рисования в подклассы:
public abstract class Shape
{
public abstract void Draw();
}

public class Circle : Shape
{
public override void Draw()
{ … }
}

Теперь, если нужно добавить новую фигуру, вы просто создаете подкласс и переопределяете метод Draw. Так вы закрыли класс Shape и открыли точку расширения в нём.

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

Например, для библиотеки с открытым методом
CreateCustomer(string email)
вы не можете просто добавить новый обязательный параметр:
CreateCustomer(string email, string accountNumber)

Это будет критическим изменением для клиентского кода, который уже привязан к исходной версии метода. Это можно делать во время разработки, но не после публикации. Если нужно внести изменение после публикации, вы создаёте новый модуль (версию модуля). Однако вы по-прежнему можете изменять реализацию, если она не меняет API. Вся тема управления версиями веб-API — это, по сути, принцип OCP Мейера.

Ещё один важный момент: версия OCP Мейера имеет смысл только в контексте нескольких команд разработчиков, когда каждый модуль разрабатывается разными командами. Если вы являетесь и автором, и «клиентом» кода, нет необходимости придерживаться таких сложных схем.

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

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

Источник:
https://enterprisecraftsmanship.com/posts/ocp-vs-yagni/
👍19
День 1337. #ЗаметкиНаПолях #DesignPrinciples
OCP Против YAGNI. Продолжение
Начало

YAGNI
“You ain’t gonna need it” (Вам это не понадобится) означает, что вы не должны тратить время на функциональность, которая сейчас не нужна. Вам не следует разрабатывать эту функциональность, а также изменять существующий код с учетом её появления в будущем. Два основных момента, которые объясняют, почему это хорошая идея:
1. Требования бизнеса постоянно меняются. Если вы тратите время на функцию, которая не нужна бизнес-людям в данный конкретный момент, вы крадёте время у тех функций, которые им нужны прямо сейчас. Более того, когда им наконец-то понадобится разработанный функционал, их взгляд на него, скорее всего, изменится, и вам всё равно придётся вносить в него коррективы. Такая деятельность расточительна и приводит к чистым убыткам, поскольку было бы выгоднее просто реализовать функцию с нуля, когда в ней возникнет реальная потребность.

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

Бывают ситуации, когда YAGNI неприменим.
Например, вы проектируете функциональность, которую трудно изменить в будущем. Это ориентированные на клиента API, сторонние библиотеки, фундаментальные архитектурные решения, пользовательские интерфейсы (их может быть трудно изменить, поскольку пользователи неохотно принимают новый внешний вид). В таких ситуациях стоит потратить некоторое время, чтобы попытаться предсказать, как будущие функции будут сочетаться с решениями, которые вы принимаете сейчас. Например, заранее продумать систему управления версиями веб-API, потому что после публикации API изменить её будет невозможно. Аналогично, публичный метод или класс в общедоступной библиотеке должен оставаться там для обратной совместимости, даже если вы решите, что он больше не нужен. Менять такие вещи сложно.

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

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

Окончание следует…

Источник:
https://enterprisecraftsmanship.com/posts/ocp-vs-yagni/
👍10
День 1338. #ЗаметкиНаПолях #DesignPrinciples
OCP Против YAGNI. Окончание
Начало
Продолжение

Обратите внимание, что YAGNI не только про добавление неиспользуемых функций «на будущее», но и про запрет изменения существующих функций для учёта возможных изменений в будущем. И в этом заключается противоречие. Этот «учёт возможных изменений в будущем» — именно то, что предлагает версия OCP Боба Мартина.

Вернёмся к коду метода Draw, использующего switch, из первой части. С одной стороны, у нас есть YAGNI, который говорит, что с этим оператором switch всё в порядке, если полученный код прост, его легко понять и поддерживать. С другой стороны, у нас есть OCP Боба Мартина, в котором говорится, что нам нужно иметь возможность расширять его без изменения имеющегося кода, то есть без изменения самого оператора switch.

Что выбрать?

Обратите внимание, что мы говорим о противоречии между YAGNI и версии OCP Боба Мартина, а не о версии Бертрана Мейера. Это потому, что YAGNI не противоречит последней, они в принципе говорят о разных вещах.

Что касается версии Боба Мартина, то её можно рассматривать с двух разных точек зрения.

1. Когда вы являетесь и автором, и «клиентом» кода, который пишете, YAGNI имеет приоритет над OCP. Потому что YAGNI, наряду с KISS, является самым важным принципом в разработке ПО. Следование ему должно быть первоочередной задачей любого программного проекта. Зачем преждевременно закладывать точки расширения в свой код, если это приведет к чрезмерному усложнению? Действительно ли рефакторинг switch в иерархию классов стоит усилий и дополнительных затрат на обслуживание? Конечно нет. Гораздо лучше закладывать точки расширения постфактум, когда уже есть полная картина и когда вы видите, что оператор switch стал слишком раздутым. В этом случае вы можете отрефакторить код и извлечь иерархию классов. Но не раньше, чем потребность в этом станет очевидной.

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

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

Источник: https://enterprisecraftsmanship.com/posts/ocp-vs-yagni/
👍13