Sergey Teplyakov, .NET Expert, “SOLID Principles in the real world”:
• Why design principles matters?
• SOLID principles in the real world
S – Single Responsibility Principle
O – Open-Closed Principle
L – Liskov Substitution Principle
I – Interface Segregation Principle
D – Dependency Inversion Principle
9. SOLID принципы
• S – Single Responsibility Principle
• O – Open-Closed Principle
• L – Liskov Substitution Principle
• I – Interface Segregation Principle
• D – Dependency Inversion Principle
10. Опасности принципов
• Что «сильнее» композиция или
наследование?
• Остерегайтесь культа карго
11. В чем проблема с текущим описанием?
• Принципы слишком неформальны
• Берут свое начало в 90-х (С++)
• Не ясно, когда им следовать, а когда -
нет
12. Как следовать этим принципам?
Принципы существуют для того, чтобы помогать в
устранении дурных запахов в дизайне. Это не духи,
которыми надо обильно поливать всю систему.
Чрезмерная приверженность принципам ведет к пороку
ненужной сложности.
13. Single Responsibility Principle
• Исходное определение:
У класса должна быть только одна причина для изменения
• Смысл принципа SRP:
Борьба со сложностью
• SRP violation means low cohesion!
http://bit.ly/SingleResponsibilityPrinciple
16. Смешивание логики и инфраструктуры
• Windows сервис ходит в базу
• WCF-сервис сам содержит логику обработки
запросов
• Azure Role содержит логику по
перекладыванию данных из одного места в
другое.
• Смешивание логики и «сохраняемости»
(persistence)
19. Я хочу иметь возможность сосредоточиться на
сложных аспектах системы по отдельности,
поэтому когда мне становится сложно это
делать, я начинаю разбивать классы и выделять
новые.
23. Интерфейс ContextActionBase
public abstract class ContextActionBase
{
public abstract void Execute();
public abstract bool IsAvailable();
public abstract string Text { get; }
}
26. SRP – это способ поиска скрытых абстракций,
достаточно сложных, чтобы им отвели
отдельную именованную сущность и спрятали в
их недрах все детали.
27. Open-Closed Principle
Классы, модули, функции и т.п.) должны быть
открытыми для расширения, но закрытыми для
модификации
http://bit.ly/OpenClosedPrinciple
29. Объяснение Боба Мартина
• Они открыты для расширения. Это означает, что поведение модуля можно
расширить. Когда требования к приложению изменяются, мы добавляем в
модуль новое поведение, отвечающее изменившимся требованиям. Иными
словами, мы можем изменить состав функций модуля.
• Они закрыты для модификации. Расширение поведения модуля не
сопряжено с изменениями в исходном или двоичном коде модуля. Двоичное
исполняемое представление модуля, будь то компонуемая библиотека, DLL
или EXE-файл, остается неизменным (выделено мною).
30. Нарушает ли фабрика OCP?
abstract class Importer
{
public abstract void ImportData();
}
static class ImporterFactory
{
public static Importer Create(string fileName)
{
var extension = Path.GetExtension(fileName);
switch (extension)
{
case "json":
return new JsonImporter();
case "xls":
case "xlsx":
return new XlsImporter();
default:
throw new InvalidOperationException("Extension is not supported");
}
}
}
31. Принцип единственного выбора
Всякий раз, когда система программного обеспечения
должна поддерживать множество альтернатив, их
полный список должен быть известен только одному
модулю системы.
Бербран Мейер
33. Определение от Бертрана Мейера
• Модуль называют открытым, если он еще доступен для расширения.
Например, имеется возможность расширить множество операций в нем или
добавить поля к его структурам данных.
• Модуль называют закрытым, если он доступен для использования другими
модулями. Это означает, что модуль (его интерфейс – с точки зрения скрытия
информации) уже имеет строго определенное окончательное описание. На
уровне реализации закрытое состояние модуля означает, что модуль можно
компилировать, сохранять в библиотеке и делать его доступным для
использования другими модулями (его клиентами).
34. Open-Closed Principle
• Что такое OCP? Это фиксация интерфейса класса/модуля, и
возможность изменения или подмены реализации/поведения.
• Цели OCP: борьба со сложностью и ограничение изменений
минимальным числом модулей.
• Как мы реализуем OCP? С помощью инкапсуляции, которая позволяет
изменять реализацию без изменения интерфейса и с помощью
наследования, что позволяет заменить реализацию, которая не
затронет существующих клиентов базового класса.
35. Типичный пример нарушения
расширяемости (OCP)
public static int Count<TSource>(this IEnumerable<TSource> source)
{
var collectionoft = source as ICollection<TSource>;
if (collectionoft != null) return collectionoft.Count;
var collection = source as ICollection;
if (collection != null) return collection.Count;
int count = 0;
using (IEnumerator<TSource> e = source.GetEnumerator())
{
while (e.MoveNext()) count++;
}
return count;
}
36. Расширяемость: OOP vs. FP
• В ООП: легко добавлять новый тип, сложно – новую операцию
• В ФП: легко добавлять новую операцию, сложно – новый тип
Expression Problem!
41. class IsIssueFixableVisitor : IValidationResultVisitor
{
public bool IsIssueFixable { get; private set; }
public void Visit(NoErrorValidationResult vr)
{
IsIssueFixable = false;
}
public void Visit(ContractErrorValidationResult vr)
{
IsIssueFixable =
vr.Error == MalformedContractError.VoidReturnMethodCall;
}
public void Visit(ContractWarningValidationResult vr)
{
IsIssueFixable =
vr.Warning == MalformedContractWarning.NonVoidReturnMethodCall;
}
public void Visit(CustomWarningValidationResult vr)
{
IsIssueFixable = false;
}
}
42. ФП подход: сопоставление с образцом
public T Match<T>(
Func<CodeContractErrorValidationResult, T> errorMatch,
Func<CodeContractWarningValidationResult, T> warningMatch,
Func<ValidationResult, T> defaultMatch)
{
var errorResult = this as CodeContractErrorValidationResult;
if (errorResult != null)
return errorMatch(errorResult);
var warningResult = this as CodeContractWarningValidationResult;
if (warningResult != null)
return warningMatch(warningResult);
return defaultMatch(this);
}
44. Как пользуюсь принципами?
• Принципы – не самоцель!
• Дизайн постоянно эволюционирует. Валидируйте его согласно этим
принципам на каждой итерации.
45. Цикл постов о SOLID-принципах
• http://bit.ly/SingleResponsibilityPrinciple
• http://bit.ly/OpenClosedPrinciple
• http://bit.ly/LiskovSubstitutionPrinciple
• http://bit.ly/InterfaceSegregationPrinciple
• http://bit.ly/DependencyInversionPrinciple
46. Чего почитать по теме
• О культе карго в программировании
sergeyteplyakov.blogspot.com/2013/09/blog-post_24.html
• Критика книги Боба Мартина «Принципы, паттерны и методики гибкой
разработки на языке C#»
sergeyteplyakov.blogspot.com/2013/12/about-agile-principles-patterns-and.html
• Цикл постов об управлении зависимостями
http://sergeyteplyakov.blogspot.com/2013/10/articles.html#dependency_manage
ment
• Лучшая книга по ООП эвар! – Бертран Мейер «Объектно-ориентированное
конструирование программных систем»
http://sergeyteplyakov.blogspot.com/2012/03/blog-post_19.html
Notes de l'éditeur
Молодые разработчики и менеджеры стараются найти ответ на вопрос жизни