Design patterns part 1
-
Upload
victor-matyushevskyy -
Category
Documents
-
view
1.636 -
download
2
Transcript of Design patterns part 1
Шаблони проектування
Частина 1
Coupling and cohesion
Coupling (зв’язність)• Afferent– Скільки компонентів залежать від
модуля– Показує наскільки модуль
відповідальний
• Efferent– Від скількох компонентів залежить
модуль– Показує наскільки модуль
незалежний– Включає: наслідування, interface impl,
типи параметрів, типи змінних, exceptions,...
• Ca and Ce є метриками стабільності
Instability
I = Ce / (Ce + Ca)
• показує нестійкість до змін• 0 – стабільний модуль• 1 – нестабільний
Ми залежимо від
Залежать від нас
Cohesion (пов’язаність, зчеплення)
• Показує наскільки сильні взаємозв’язки між частинами одного модулю
• Чи всі методи класу використовують всі його поля
• Низька пов’язаність:– Клас з багатьма статичними
методами– “Несфокусований” клас
Програмне забезпечення
• Успішне– Legacy (унаслідуване, застаріле)–Maintainable (легко підтримуване)
• Неуспішне
Legacy software
• Довго експлуатується• Продовжує експлуатуватись• Задовільняє потреби замовника• Але разом з тим–Містить дефекти, не містить нових
функцій
• Важко покращується
Maintainable software
• Довго експлуатується• Продовжує експлуатуватись• Задовільняє потреби замовника• Але разом з тим–Містить дефекти, не містить нових
функцій
• Легко покращується
Бажані властивості
• Хочемо змінювати програму– Проясняються старі/з’являються нові
вимоги
• Не хочемо змінювати готові класи– Зміна – внесення нестабільності– Потрібно перетестувати– Залежності транзитивні
Low coupling + high cohesion сприяють легкому внесенню змін у
ПЗ
Більшість шаблонів проектування є рецептами для зниження coupling
та підвищення cohesion
Шаблони проектування
link link
Створення об’єктів, creational patterns
Dependencies
public class Survey { public SurveyState State { get; set; } public DateTime StartDate { get; set; } public DateTime FinishDate { get; set; }
public void Start() { State = SurveyState.InProgress; try { var email = new SmtpEmailSender(); email.Send("Survey has started!", Constants.AdminEmail); } catch (SmtpException e) { ... } }}
Survey
SmtpEmailSender
High level
module
Low level
module
Depends on
public class Survey { private readonly IEmailSender _email; public SurveyState State { get; set; } public DateTime StartDate { get; set; } public DateTime FinishDate { get; set; }
public Survey(IEmailSender email) { _email = email; }
public void Start() { State = SurveyState.InProgress; try { _email.Send("Survey has started!", Constants.AdminEmail); } catch (SmtpException e) { ... } }}
Survey
SmtpEmailSender
High level
module
IEmailSender
Implements
Survey
SmtpEmailSender
IEmailSender
Application
Survey
SmtpEmailSender
High level
module
IEmailSender
Dependency is inverted
Dependency inversion
• High level modules should not depend on low-level modules
• Both should depend on abstractions• Abstractions should not depend on
details• Details should depend on
abstractions
Що отримуємо• Залежності вказуються явно, немає
прихованих залежностей• Класи самодокументовані• Класи більше не відповідають за створення
об’єктів• Після створення клас гарантовано
знаходиться у робочому стані• Легко знаходити класи з багатьма
параметрами в конструкторі (і зменшувати їх відповідальності)
• Легше знаходити методи, яким потрібні не всі залежності класу
Приклад
public void Import(){ var importContext = _contextCreator.Create(_userName); // ... _fileImporter.Import(); // ...}
Прикладpublic void Import(){ var importContext = _contextCreator.Create(_userName); using (IFileImporter fileImporter = new FileImporter(importContext)) { fileImporter.Import(); }}
Abstract Factory
Потреба
• Залежність залежить від параметра, який не відомий клієнту на час його створення
• if/else та switch блоки для вирішення, яку залежність створити
• Залежність потрібно до-ініціалізувати
• Об’єкт повинен керувати часом життя залежності
SurveyImporter
FileImporter
SurveyImporter
FileImporter
IFileImporterFactory
IFileImporter
FileImporterFactory
Реалізація
• Виклик конструктора об’єкта+ передача параметра+ передача створених/отриманих залежностей
• switch, Dictionary<TParameter, Func<TResult>>
Часто фабрика повертає Null Object в default-вітці switch’а
public sealed class FileImporterFactory : IFileImporterFactory{ public IFileImporter Create(Context importContext) { switch (GetFileStorageType(importContext)) { case "csv": return new FileImporter(importContext, csvReader); case "xls": return new FileImporter(importContext, xlsReader); default: return new NullFileImporter(importContext); } }
Null Object
public SurveysImporter( IFileImporter headersFileImporter, IFileImporter dataFileImporter, IFileImporter additionalDataImporter){ if (headersFileImporter == null) { throw new ArgumentNullException("headersFileImporter"); } if (dataFileImporter == null) { throw new ArgumentNullException("dataFileImporter"); }
_headersFileImporter = headersFileImporter; _dataFileImporter = dataFileImporter; _additionalDataImporter = additionalDataImporter;}
Тут треба пояснюючий
коментар
public void Import(){ _headersFileImporter.Import(); // ... _dataFileImporter.Import(); // ... if (_additionalDataImporter != null) { _additionalDataImporter.Import(); }}
Буде розмножуват
ись по вашому коду
Потреба
• Код для NULL-випадку повинен бути ідентичним до решти випадків
Реалізаціяpublic sealed class NullFileImporter : IFileImporter{ public void Import() { }}
public sealed class SurveysImporter{ public void Import() { _headersFileImporter.Import(); // ... _dataFileImporter.Import(); // ... _additionalDataImporter.Import();}
Переваги
• Чистіший та стисліший код• Відсутність перевірок на null • Простіший код• Зменшення кількості
відповідальностей класів-клієнтів
Builder
Fluent Buildervar team = new Team( "Manchester United", "The Red Devils", Color.Red, "Manchester", "Old Trafford");
var builder = new TeamBuilder();var team = builder .CreateTeam("Real Madrid") .WithNickName("Los Merengues") .WithShirtColor(Color.White) .FromTown("Madrid") .PlayingAt("Santiago Bernabeu") .Build();
Призначення
• Конструювання складного об’єкта• Відділення логіки конструювання
від представлення даних об’єкта
Fluent Builder: Mocksvar workerPlanMock = new Mock<IWorkerPlan>();workerPlanMock .Setup(workerPlan => workerPlan.GetCountOfItemsToProcess()) .Returns(2);var worker = new Worker(workerPlanMock.Object);...
StringBuilderStringBuilder builder = new StringBuilder();builder.Append("...");builder.AppendFormat("...{0}", "value") .AppendLine() .Append("...");string result = builder.ToString();
SqlConnectionStringBuildervar builder = new SqlConnectionStringBuilder { DataSource = "localhost", InitialCatalog = "MyDB", IntegratedSecurity = true, ConnectTimeout = 10 // seconds };string connectionString = builder.ConnectionString;
Lazy Instantiation
Потреба public void Import(){ var importContext = _contextCreator.Create(_userName); if (ShouldReadFromFileSystem(importContext)) { FileImporter.Import(); }}
Реалізація private IFileImporter _fileImporter;private readonly Func<IFileImporter> _fileImporterFactory;
private IFileImporter FileImporter{ get { if (_fileImporter == null) { _fileImporter = _fileImporterFactory(); } return _fileImporter; }}
Призначення
• Відкладення створення об’єкта до моменту, коли об’єкт стає необхідний
• Оптимізація • Thread safety!
Lazy<T>private readonly Lazy<IFileImporter> _fileImporter;
private IFileImporter FileImporter{ get { return _fileImporter.Value; }}
Створення об’єктів• Coupling & Cohesion• Dependencies, Dependency
Inversion• Abstract Factory• Null Object• Fluent Builder• Lazy Instantiation