четверг, 25 сентября 2014 г.

The Dependency Inversion Principle

Цикл статей о SOLID принципах

--------------------------------------------------

clip_image001

Принцип инверсии зависимости (Dependency Inversion Principle – DIP):

  • Модули верхнего уровня не должны зависеть от модулей нижнего уровня. И те и другие должны зависеть от абстракций.
  • Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.

Роберт Мартин «Принципы, паттерны и методики гибкой разработки» ([Martin2006]).

Принцип инверсии зависимостей – один из самых известных сегодня принципов проектирования, который лежит в основе популярных техник внедрения зависимостей (Dependency Injection). Однако, если посмотреть лишь на его название и описание, то будет довольно сложно понять, что же он означает. Поэтому, если спросить простых обывателей о том, что означает этот принцип, то они начнут что-то говорить о пользе интерфейсов и абстракций, и, вообще, будут путаться в показаниях.

четверг, 18 сентября 2014 г.

LSP Часть 2. О сложностях наследования

Цикл статей о SOLID принципах

--------------------------------------------------

Бытует мнение, что генерация исключений InvalidOperationException или NotSupportedException методами наследника означает нарушение этим классом принципа замещения Лисков. И хотя в некоторых случаях это действительно так, судить так однозначно нельзя.

Является ли нарушением LSP, что ImmutableList<T> и ReadOnlyCollection<T> реализует IList<T>, поскольку попытка добавления элемента в такую коллекцию приводит к генерации NotSupportedException? Может показаться, что нарушают, однако в «контракте» интерфейса IList<T> четко сказано, что метод Add будет добавлять элемент, только в случае выполнения «предусловия» – коллекция должна быть изменяемой! Аналогично дела обстоят с потоками ввода вывода, методы Read/Write которых могут генерировать исключения, если свойствами CanRead/CanWrite возвращают false. Во всех этих случаях мы можем говорить о неудачном дизайне, но не можем говорить о нарушении принципа подстановки Лисков!

вторник, 9 сентября 2014 г.

Liskov Substitution Principle

Цикл статей о SOLID принципах

--------------------------------------------------

Принцип подстановки Лисков (Liskov Substitution Principle, LSP):

Должна быть возможность вместо базового типа подставить любой его подтип.
Роберт К. Мартин "Принципы, паттерны и практики гибкой разработки", 2006

...если для каждого объекта o1 типа S существует объект o2 типа T такой, что для всех программ P, определенных в терминах T, поведение P не изменяется при замене o2 на o1, то S является подтипом (subtype) для T.
Барбара Лисков "Абстракция данных и иерархия", 1988

Наследование и полиморфизм является ключевым инструментом ОО разработчика при борьбе со сложностью, для получения простого и расширяемого решения. Наследование используется в большинстве паттернов проектирования и лежит в основе таких принципов, как Open-Closed Principle и Dependency Inversion Principle.

вторник, 2 сентября 2014 г.

Open/Closed Principle. ФП vs. ООП

Цикл статей о SOLID принципах

--------------------------------------------------

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

Давайте рассмотрим вопрос расширяемости в рамках семейства типов более подробно.

Большинство примеров, показывающих пользу «открытости» модулей обычно сводятся к демонстрации мощи полиморфизма над старым структурным подходом: «Смотрите, как здорово, когда мы избавляемся от конструкции switch в функции Draw и переносим всю логику в класс Shape и его наследники! Теперь наше решение является расширяемым и соответствует принципу Открыт/Закрыт, поскольку мы легко можем к квадрату и треугольнику добавить еще ромб с кругом!».

Да, действительно, добавить новый класс в существующую иерархию довольно легко, но что если мы хотим добавить в существующую иерархию новую операцию, например, метод GetArea в иерархию Shapes?

вторник, 26 августа 2014 г.

Open/Closed Principle

Цикл статей о SOLID принципах

--------------------------------------------------

Принцип открыт/закрыт (Open-Closed Principle, OCP): Программные сущности (классы, модули, функции и т.п.) должны быть открытыми для расширения, но закрытыми для модификации.
Роберт Мартин. Принципы, паттерны и практики гибкой разработки, 2010

Из всех «цельных» (SOLID) принципов, принцип Открыт/Закрыт является самым неоднозначным. Его неоднозначность кроется в противоречивости его определения (как что-то может быть одновременно «открытым» и «закрытым»?), а подкрепляется неоднозначными и разнообразными формулировками этого принципа в разных источниках. Не удивительно, что даже такие монстры, типа Эрика Липперта или Джона Скита относятся к этому принципу неоднозначно и признаются в его непонимании.

среда, 13 августа 2014 г.

Single Responsibility Principle

Цикл статей о SOLID принципах

--------------------------------------------------

Принцип единственной обязанности (Single-Responsibility Principle, SRP): У класса должна быть только одна причина для изменения.
Роберт Мартин. Принципы, паттерны и практики гибкой разработки

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

При этом нарушение SRP бывают как на уровне классов/модулей, так и на уровне подсистем и приложений. Классическим примером из моей практики являются два вопиющих нарушения SRP на уровне приложений: использование Windows Forms приложения в качестве WCF сервиса, и необходимость взаимодействия виндового сервиса с пользователем через Message Box-ы.

вторник, 5 августа 2014 г.

Interface Segregation Principle

Цикл статей о SOLID принципах

--------------------------------------------------

Вы никогда не ловили себя на мысли, что Принцип разделения интерфейса (ISP, Interface Segregation Principle) вам не вполне понятен или, что он является лишь разновидностью принципа единой ответственности (SRP, Single Responsibility Principle)? До недавнего времени у меня было подобное отношение, но оно несколько изменилось, после того, как я посмотрел на него с другой точки зрения.

Причина некоторого недопонимания принципа ISP, как мне кажется, кроется вот в чем. Во-первых, ISP отличается от других принципов SOLID тем, с какой стороны он применяется (об этом позднее), а во-вторых, у него далеко не самое лучшее определение.

Давайте начнем с определения. Вот формулировка принципа ISP из любимой мною книги Боба Мартина «Принципы, паттерны и методики гибкой разработки»:

"Этот принцип относится к недостаткам "жирных" интерфейсов. Говорят, что класс имеет жирный интерфейс, если функции этого интерфейса недостаточно сцепленные (not cohesive). Иными словами, интерфейс класса можно разбить на группу методов. Каждая группа предназначена для обслуживания разных клиентов. Одним клиентам нужна одна группа методов, другим – другая."