Transcript
Page 1: Yuri Trukhin - Software developement best practices

Лучшие практики разработки ПОПринципы гибкого проектирования

Юрий Трухинsenior developer, CNIPGIS LLCMicrosoft Student Partner Guru

Page 2: Yuri Trukhin - Software developement best practices

Лепшыя практыкі распрацоўкі праграмнага забеспячэння

прынцыпы гнуткага праектаванняЮрий Трухинsenior developer, CNIPGIS LLCMicrosoft Student Partner Guru

беларуская версія

Page 3: Yuri Trukhin - Software developement best practices

Признаки плохого дизайна ПО

Page 4: Yuri Trukhin - Software developement best practices

Признаки плохого дизайна ПО

• Жесткость: дизайн трудно поддается изменению• Хрупкость: дизайн легко разрушается• Косность: дизайн трудно использовать повторно• Вязкость: трудно добиться желаемого• Ненужная сложность: избыточное проектирование• Ненужные повторения: чрезмерное использование

копирования и вставки• Непрозрачность: плохо выраженная цель

Часто эти признаки появляются в результате нарушения одного из принципов проектирования.

Page 5: Yuri Trukhin - Software developement best practices

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

Page 6: Yuri Trukhin - Software developement best practices

public partial class Copier { public static void Copy() { int c; while ((c = Keyboard.Read()) != -1) { Printer.Write(c); } } }

Page 7: Yuri Trukhin - Software developement best practices

public class CopierWithPerfocards { /// <summary> /// не забудьте сбросить этот флаг /// </summary> private static bool ptFlag = false;

public static void Copy() { int c; while ((c = (ptFlag ? PaperTape.Read() : Keyboard.Read())) != -1) Printer.Write(c); } }

Page 8: Yuri Trukhin - Software developement best practices

public class CopierWithPerfocardsReadAndWrite { //не забудьте сбросить этот флаг public static bool ptFlag = false; public static bool punchFlag = false; public static void Copy() { int c; while ((c = (ptFlag ? PaperTape.Read() : Keyboard.Read())) != -1) punchFlag ? PaperTape.punchFlag(c) : Printer.Write(c); } }

Page 9: Yuri Trukhin - Software developement best practices

public interface Reader { int Read(); } public class KeyboardReader : Reader { public int Read() { return Keyboard.Read(); }; }

public partial class Copier { public static Reader reader = new KeyboardReader(); public static void Copy() { var c; while ((c = (reader.Read())) != -1 ) { Printer.Write(c); } } }

Page 10: Yuri Trukhin - Software developement best practices

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

Page 11: Yuri Trukhin - Software developement best practices

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

• Принцип единственной обязанности• Принцип открытости/закрытости• Принцип подстановки Лисков• Принцип инверсии зависимости• Принцип разделения интерфейсов• Принцип эквивалентности повторного

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

Page 12: Yuri Trukhin - Software developement best practices

Принцип единственной обязанности (SRP)

«Никто кроме Будды не должен брать на себя ответственность за сокровенные знания»

Э.Кобхэм Брюэр, 1897

Page 13: Yuri Trukhin - Software developement best practices

Принцип единственной обязанности (SRP)

У класса должна быть только одна причина для изменения.• Любое изменение требований проявляется в изменении

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

• Если класс отвечает за несколько действий, его обязанности оказываются связанными.

• Это является причиной хрупкого дизайна приложений.

Page 14: Yuri Trukhin - Software developement best practices

Принцип единственной обязанности (SRP)

Приложение «Вычислительная

геометрия»

Rectangle

+draw()+area() : double

Графическое приложение

GUI

Более одной обязанности

Page 15: Yuri Trukhin - Software developement best practices

Принцип единственной обязанности (SRP)

Проблемы:

Page 16: Yuri Trukhin - Software developement best practices

Принцип единственной обязанности (SRP)

Проблемы:• В приложение «Вычислительная геометрия» попадает не нужная

логика вместе с классом Rectangle.• Если изменение графического приложения потребует изменить класс

Rectangle, придется заново собирать, тестировать и развертывать приложение «Вычислительная геометрия».

… или приложение «Вычислительная геометрия» неожиданно может перестать работать

Page 17: Yuri Trukhin - Software developement best practices

Принцип единственной обязанности (SRP)

Приложение «Вычислительная

геометрия»

Rectangle

+draw()

Графическое приложение

GUI

Обязанности разделены

GeometricRectangle

+area() : double

Page 18: Yuri Trukhin - Software developement best practices

Принцип единственной обязанности (SRP)

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

Page 19: Yuri Trukhin - Software developement best practices

Принцип единственной обязанности (SRP)

Пример:Public interface Modem{

public void Dial(string pno);public void Hangup();public void Send (char c);public char Recv();

}Сколько обязанностей у интерфейса Modem?

Page 20: Yuri Trukhin - Software developement best practices

Принцип единственной обязанности (SRP)

Сколько обязанностей?:Public interface Modem{

public void Dial(string pno); //управление соединениемpublic void Hangup(); //управление соединениемpublic void Send (char c); //передача данныхpublic char Recv(); //передача данных

}

Нужно ли разделить интерфейс?

Page 21: Yuri Trukhin - Software developement best practices

Принцип единственной обязанности (SRP)

Нужно ли разделять интерфейс?:ДА!Если может потребоваться изменение сигнатуры методов управления соединением, класс, вызывающий send и receive придется повторно компилировать и развертывать чаще, чем хотелось бы.

Это приводит к жесткости дизайна. Следует разделить интерфейс на DataChannel и Connection

Page 22: Yuri Trukhin - Software developement best practices

Принцип единственной обязанности (SRP)

Нужно ли разделять интерфейс?:НЕТ!Если приложение не модифицируют таким образом, что обязанности изменяются порознь, то разделять нет необходимости.

Это приводит к излишней сложности. Изменения интерфейса не требуются.

Page 23: Yuri Trukhin - Software developement best practices

Принцип единственной обязанности (SRP)

Нужно ли разделять интерфейс?:НЕТ!Если приложение не модифицируют таким образом, что обязанности изменяются порознь, то разделять нет необходимости.

Это приводит к излишней сложности. Изменения интерфейса не требуются.

Вывод: ось изменения становится таковой, если изменение имеет место. Если на то нет причин, неразумно применять ЛЮБОЙ принцип проектирования.

Page 24: Yuri Trukhin - Software developement best practices

Принцип единственной обязанности (SRP)

Разделение связанных обязанностей. Обеспечение сохранности.

Employee

+CalculatePay+StorePersistense System

Связь бизнес-правил и подсистемы сохраниния -> неприятности!Обычно тесты заставляют их разделять, если нет – в случае если появляется жесткость и хрупкость – нужен рефакторинг!(Для данного случая применимы паттерны Фасад, Объект доступа к данным, Прокси для разделения обязанностей).

Page 25: Yuri Trukhin - Software developement best practices

Принцип открытости-закрытости

«Голланская дверь: существительное. Дверь, разделенная на две части по горизонтали так, что каждая створка может открываться и закрываться независимо»

The American Heritage Dictionary of English language, 2000

Page 26: Yuri Trukhin - Software developement best practices

Принцип открытости-закрытости

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

Page 27: Yuri Trukhin - Software developement best practices

Принцип открытости-закрытости

Основные храктеристики модулей, разработанных в соответствии с принципом открытости-закрытости:• Они открыты для расширения (поведение модуля можно расширить,

можно менять состав функций модуля).• Они закрыты для модификации (расширение поведение модуля не

сопряжено с изменением в исходном или двоичном коде модуля).

Как это сделать?

Page 28: Yuri Trukhin - Software developement best practices

Принцип открытости-закрытости

Как это сделать?С ПОМОЩЬЮ АБСТРАКЦИИ!

Абстракция – абстрактный базовый класс, поведение – производный класс. Модуль, манипулирующий абстракцией можно сделать закрытым для модификации – он зависит от ФИКСИРОВАННОЙ абстракции.Модуль при этом остается расширяемым.

Page 29: Yuri Trukhin - Software developement best practices

Принцип открытости-закрытости

Пример:

Client Server

Класс Client не является открытым и закрытым.

Page 30: Yuri Trukhin - Software developement best practices

Принцип открытости-закрытости

Применив паттерн «Стратегия» получим:

Client“interface”

Client Interface

Класс Client одновременно является открытым и закрытым.

Server

Page 31: Yuri Trukhin - Software developement best practices

Принцип открытости-закрытости

Альтернатива: Паттерн «Шаблонный метод»

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

Policy

+PolicyFunction()#ServiceFunction()

Implementation

#ServiceFunctiom

Page 32: Yuri Trukhin - Software developement best practices

Принцип открытости-закрытости

Пример принципа OCP

Page 33: Yuri Trukhin - Software developement best practices

--shape.h--------------------------------------------------------------------enum ShareType (circle, square);struct Shape{ ShapeType itsType;}--circle.h-------------------------------------------------------------------struct Circle{ ShapeType itsType; double itsRadius; Point itsCenter;};void DrawCircle(struct Circle*);

--square.h-------------------------------------------------------------------struct Square{ ShapeType itsType; double itsSide; Point itsTopLeft;};

void DrawSquare(struct Square*);

Принцип открытости-закрытости

Page 34: Yuri Trukhin - Software developement best practices

--drawAllShapes.cc-----------------------------------------------------------typedef struct Shape *ShapePointer;

//функция не может быть закрытой для добавления других фигур, //не удовлетворяет принципам OCPvoid DrawAllShapes(ShapePointer list[], int n){ int i; for (i=0; i<n; i++) { struct Shape* s = list[i]; switch (s->itsType) { case square: DrawSquare((struct Square*)s); break; case circle: DrawCircle((struct Circle*)s); break; } }}

Принцип открытости-закрытости

///Резюме:///это решение жесткое,///тк после добавление фигуры Triange(треугольник)///необходимо заново откомпилировать и развернуть файлы , содержащие опредлеление///Shape, Square,Circle и DrawAllShapes.//////это решение хрупкое,///т.к. есть много других switch/case и if/else, которые сложно отыскать и понять.//////это решение костное, потому что всякий, кто попытается использовать ///функцию DrawAllShapes в другой программе, вынужден будет тащить за собой ///определения Square и Circle, даже если в этой программе они не используются.

Page 35: Yuri Trukhin - Software developement best practices

Принцип открытости-закрытостиpublic interface Shape { void Draw(); }

public class Square : Shape { public void Draw() { //нарисовать квадрат } }public class Circle : Shape { public void Draw() { //нарисовать круг } }

public void DrawAllShape(IList shapes) { foreach (Shape shape in shapes) shape.Draw(); }

Page 36: Yuri Trukhin - Software developement best practices

Принцип открытости-закрытости

Внимание, изменились требования!

Page 37: Yuri Trukhin - Software developement best practices

Принцип открытости-закрытости

Изменившиеся требования

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

Page 38: Yuri Trukhin - Software developement best practices

Принцип открытости-закрытости

Вывод 1

Модель Shape не является естественной в системе, где упорядоченность связана с типом фигуры.

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

Page 39: Yuri Trukhin - Software developement best practices

Принцип открытости-закрытости

Расстановка «точек подключения»

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

«Стимулирование изменений» • Сначала пишем тесты.• Разработку ведем очень короткими циклами, измеряемыми в днях,

а не в неделях• Разрабатываем содержательные функции до инфраструктуры и

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

Page 40: Yuri Trukhin - Software developement best practices

Принцип открытости-закрытости

Заключение

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

Page 41: Yuri Trukhin - Software developement best practices

Принцип подстановки лисков

«Должна быть возможность вместо базового типа подставить любой его подтип»

Page 42: Yuri Trukhin - Software developement best practices

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

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

2. Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.

Page 43: Yuri Trukhin - Software developement best practices

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

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

Page 44: Yuri Trukhin - Software developement best practices

Принцип эквивалентности повторного использования и выпуска

Единица повторного использования равна единице выпуска.(либо все классы, включенные в компонент можно повторно использовать, либо ни один!)

Page 45: Yuri Trukhin - Software developement best practices

Принцип общей закрытости

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

Page 46: Yuri Trukhin - Software developement best practices

Принцип совместного повторного использования

Все классы внутри компонента используются совместно. Если вы можете повторно использовать один класс, то можете использовать и остальные.

Page 47: Yuri Trukhin - Software developement best practices

Принцип ацикличности зависимостей

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

Page 48: Yuri Trukhin - Software developement best practices

Принцип устойчивых зависимостей

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

Page 49: Yuri Trukhin - Software developement best practices

Принцип устойчивых абстракций

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

Page 50: Yuri Trukhin - Software developement best practices

Что дальше?

• DI/IOC• Prism• Вред реинжиниринга• Паттерны• Постоянное развитие• Смотрите исходный код (codeplex и др)• Помогайте другим (форумы msdn и др)

Page 51: Yuri Trukhin - Software developement best practices

Спасибо за внимание!

Юрий ТрухинSenior developer (CNIPGIS LLC)Microsoft Student Partner GURU

[email protected]

twitter.com/trukhinyuri

Удачных проектов!

Page 52: Yuri Trukhin - Software developement best practices

© 2008 Microsoft Corporation. All rights reserved. Microsoft, Windows, Windows Vista and other product names are or may be registered trademarks and/or trademarks in the U.S. and/or other countries.

The information herein is for informational purposes only and represents the current view of Microsoft Corporation as of the date of this presentation. Because Microsoft must respond to changing market conditions, it should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information provided after

the date of this presentation. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, AS TO THE INFORMATION IN THIS PRESENTATION.


Top Related