Основы паттернов проектирования | C# и .NET
Последнее обновление: 31.10.2015
Что представляют собой паттерны проектирования? Паттерн представляет определенный способ построения программного кода для решения часто встречающихся проблем проектирования. В данном случае предполагается, что есть некоторый набор общих формализованных проблем, которые довольно часто встречаются, и паттерны предоставляют ряд принципов для решения этих проблем.
Хотя идея паттернов как способ описания решения распространенных проблем в области проектирования появилась довольно давно, но их популярность стала расти во многом благодаря известной работе четырех авторов Эриха Гаммы, Ричарда Хелма, Ральфа Джонсона, Джона Влиссидеса, которая называлась «Design Patterns: Elements of Reusable Object-Oriented Software» (на русском языке известна как «Приемы объектно-ориентированного проектирования. Паттерны проектирования») и которая вышла в свет в 1994 году. А сам коллектив авторов нередко называют «Банда четырёх» или Gang of Four или сокращенно GoF.
Что же дает нам применение паттернов? При написании программ мы можем формализовать проблему в виде классов и объектов и связей между ними. И применить один из существующих паттернов для ее решения. В итоге нам не надо ничего придумывать. У нас уже есть готовый шаблон, и нам только надо его применить в конкретной программе.
Причем паттерны, как правило, не зависят от языка программирования. Их принципы применения будут аналогичны и в C#, и в Jave, и в других языках. Хотя в рамках данного руководства мы будем говорить о паттернах в контексте языка C#.
Также мышление паттернами упрощает групповую разработку программ. Зная применяемый паттерн проектирования и его основные принципы другому программисту будет проще понять его реализацию и использовать ее.
В то же время не стоит применять паттерны ради самих паттернов. Хорошая программа предполагает использование паттернов. Однако не всегда паттерны упрощают и улучшают программу. Неоправданное их использование может привести к усложнению программного кода, уменьшению его качества. Паттерн должен быть оправданным и эффективным способом решения проблемы.
Существует множество различных паттернов, которые решают разные проблемы и выполняют различные задачи. Но по своему действию их можно объединить в ряд групп. Рассмотрим некоторые группы паттернов. В основу классификации основных паттернов положена цель или задачи, которые определенный паттерн выполняет.
Порождающие паттерны
Порождающие паттерны — это паттерны, которые абстрагируют процесс инстанцирования или, иными словами, процесс порождения классов и объектов. Среди них выделяются следующие:
Абстрактная фабрика (Abstract Factory)
Строитель (Builder)
Фабричный метод (Factory Method)
Прототип (Prototype)
Одиночка (Singleton)
Другая группа паттернов — структурные паттерны — рассматривает, как классы и объекты образуют более крупные структуры — более сложные по характеру классы и объекты. К таким шаблонам относятся:
Адаптер (Adapter)
Мост (Bridge)
Компоновщик (Composite)
Декоратор (Decorator)
Фасад (Facade)
Приспособленец (Flyweight)
Заместитель (Proxy)
Третья группа паттернов называются поведенческими — они определяют алгоритмы и взаимодействие между классами и объектами, то есть их поведение. Среди подобных шаблонов можно выделить следующие:
Цепочка обязанностей (Chain of responsibility)
Команда (Command)
Интерпретатор (Interpreter)
Итератор (Iterator)
Посредник (Mediator)
Хранитель (Memento)
Наблюдатель (Observer)
Состояние (State)
Стратегия (Strategy)
Шаблонный метод (Template method)
Посетитель (Visitor)
Существуют и другие классификации паттернов в зависимости от того, относится паттерн к классам или объектам.
Паттерны классов описывают отношения между классами посредством наследования. Отношения между классами определяются на стадии компиляции. К таким паттернам относятся:
Фабричный метод (Factory Method)
Интерпретатор (Interpreter)
Шаблонный метод (Template Method)
Адаптер (Adapter)
Другая часть паттернов — паттерны объектов описывают отношения между объектами. Эти отношения возникают на этапе выполнения, поэтому обладают большей гибкостью. К паттернам объектов относят следующие:
Абстрактная фабрика (Abstract Factory)
Строитель (Builder)
Прототип (Prototype)
Одиночка (Singleton)
Мост (Bridge)
Компоновщик (Composite)
Декоратор (Decorator)
Фасад (Facade)
Приспособленец (Flyweight)
Заместитель (Proxy)
Цепочка обязанностей (Chain of responsibility)
Команда (Command)
Итератор (Iterator)
Посредник (Mediator)
Хранитель (Memento)
Наблюдатель (Observer)
Состояние (State)
Стратегия (Strategy)
Посетитель (Visitor)
И это только некоторые основные паттерны. А вообще различных шаблонов проектирования гораздо больше. Одни из них только начинают применяться, другие являются популярными на текущий момент, а некоторые уже менее распространены, чем раньше.
И в данном руководстве мы рассмотрим наиболее основные и распространенные паттерны и принципы их использования применительно к языку C#.
Как выбрать нужный паттерн?
Прежде всего при решении какой-нибудь проблемы надо выделить все используемые сущности и связи между ними и абстрагировать их от конкретной ситуации. Затем надо посмотреть, вписывается ли абстрактная форма решения задачи в определенный паттерн. Например, суть решаемой задачи может состоять в создании новых объектов. В этом случае, возможно, стоит посмотреть на порождающие паттерны. Причем лучше не сразу взять какой-то определенный паттерн — первый, который показался нужным, а посмотреть на несколько родственных паттернов из одной группы, которые решают одну и ту же задачу.
При этом важно понимать смысл и назначение паттерна, явно представлять его абстрактную организацию и его возможные конкретные реализации. Один паттерн может иметь различные реализации, и чем чаще вы будете сталкиваться с этими реализациями, тем лучше вы будете понимать смысл паттерна. Но не стоит использовать паттерн, если вы его не понимаете, даже если он на первый взгляд поможет вам в решении задачи.
И в конечном счете надо придерживаться принципа KISS (Keep It Simple, Stupid) — сохранять код программы по возможности простым и ясным. Ведь смысл паттернов не в усложнении кода программы, а наоборот в его упрощении.
СодержаниеВперед
Паттерны проектирования на C#
Весенняя скидкаПорождающие паттерны
Абстрактная фабрика
Abstract Factory
Позволяет создавать семейства связанных объектов, не привязываясь к конкретным классам создаваемых объектов.
Строитель
Builder
Позволяет создавать сложные объекты пошагово. Строитель даёт возможность использовать один и тот же код строительства для получения разных представлений объектов.
Фабричный метод
Factory Method
Определяет общий интерфейс для создания объектов в суперклассе, позволяя подклассам изменять тип создаваемых объектов.
Прототип
Prototype
Позволяет копировать объекты, не вдаваясь в подробности их реализации.
Одиночка
Singleton
Гарантирует, что у класса есть только один экземпляр, и предоставляет к нему глобальную точку доступа.
Структурные паттерны
Адаптер
Adapter
Позволяет объектам с несовместимыми интерфейсами работать вместе.
Мост
Bridge
Разделяет один или несколько классов на две отдельные иерархии — абстракцию и реализацию, позволяя изменять их независимо друг от друга.
Компоновщик
Composite
Позволяет сгруппировать объекты в древовидную структуру, а затем работать с ними так, как будто это единичный объект.
Декоратор
Decorator
Позволяет динамически добавлять объектам новую функциональность, оборачивая их в полезные «обёртки».
Фасад
Facade
Предоставляет простой интерфейс к сложной системе классов, библиотеке или фреймворку.
Легковес
Flyweight
Позволяет вместить бóльшее количество объектов в отведённую оперативную память. Легковес экономит память, разделяя общее состояние объектов между собой, вместо хранения одинаковых данных в каждом объекте.
Заместитель
Proxy
Позволяет подставлять вместо реальных объектов специальные объекты-заменители. Эти объекты перехватывают вызовы к оригинальному объекту, позволяя сделать что-то до или после передачи вызова оригиналу.
Поведенческие паттерны
Цепочка обязанностей
Chain of Responsibility
Позволяет передавать запросы последовательно по цепочке обработчиков. Каждый последующий обработчик решает, может ли он обработать запрос сам и стоит ли передавать запрос дальше по цепи.
Команда
Command
Превращает запросы в объекты, позволяя передавать их как аргументы при вызове методов, ставить запросы в очередь, логировать их, а также поддерживать отмену операций.
Итератор
Iterator
Даёт возможность последовательно обходить элементы составных объектов, не раскрывая их внутреннего представления.
Посредник
Mediator
Позволяет уменьшить связанность множества классов между собой, благодаря перемещению этих связей в один класс-посредник.
Снимок
Memento
Позволяет делать снимки состояния объектов, не раскрывая подробностей их реализации. Затем снимки можно использовать, чтобы восстановить прошлое состояние объектов.
Наблюдатель
Observer
Создаёт механизм подписки, позволяющий одним объектам следить и реагировать на события, происходящие в других объектах.
Состояние
State
Позволяет объектам менять поведение в зависимости от своего состояния. Извне создаётся впечатление, что изменился класс объекта.
Стратегия
Strategy
Определяет семейство схожих алгоритмов и помещает каждый из них в собственный класс, после чего алгоритмы можно взаимозаменять прямо во время исполнения программы.
Шаблонный метод
Template Method
Определяет скелет алгоритма, перекладывая ответственность за некоторые его шаги на подклассы. Паттерн позволяет подклассам переопределять шаги алгоритма, не меняя его общей структуры.
Посетитель
Visitor
Позволяет создавать новые операции, не меняя классы объектов, над которыми эти операции могут выполняться.
Войти Связаться
Ссылка на шаблоны проектирования в ANSI C?
спросил
Изменено 7 лет, 7 месяцев назад
Просмотрено 5к раз
Можете ли вы указать мне ссылку на шаблоны проектирования в стандарте C (C89 или C99)? (Не C#, не C++.
- c
- design-patterns
Взгляните на электронную книгу Акселя-Тобиаса Шрайнера «Объектно-ориентированное программирование с использованием ANSI-C». Вам придется обрабатывать некоторые аспекты некоторых шаблонов, но вы сможете реализовать многие из более простых GoF.
Следуя ответу Ника, я предлагаю вам научиться реализовывать вещи, подобные cplusplus, с использованием C (например, структура C с указателем на таблицу указателей функций, эмулирует класс C++ с виртуальными функциями), что означает понимание того, как C++ реализуется компилятором. Сделав это, вы сможете читать шаблоны проектирования для C++ и реализовывать их с помощью C.
Если вам нужна информация о встроенных шаблонах проектирования C в реальном времени, я могу порекомендовать две книги
- «Шаблоны проектирования в реальном времени: надежная масштабируемая архитектура для систем реального времени» (Дуглас, Брюс Пауэл | Elsevier | 1-е издание | 2002) и
- «Шаблоны проектирования для встроенных систем на C: набор инструментов для разработки встроенного программного обеспечения» (Дуглас, Брюс Пауэл | Elsevier | 1-е издание | 2011 г. )
Некоторые шаблоны более высокого уровня зависят от «эмуляции» объектно-ориентированных функций, как упоминалось ранее. Шаблоны описаны очень хорошо (диаграммы UML, примеры). Мне нравятся обсуждения «сил» (Что нужно учитывать?), которые влияют на контекст после применения паттернов.
Зарегистрируйтесь или войдите в систему
Зарегистрируйтесь с помощью Google Зарегистрироваться через Facebook Зарегистрируйтесь, используя электронную почту и парольОпубликовать как гость
Электронная почтаТребуется, но не отображается
Опубликовать как гость
Электронная почтаТребуется, но не отображается
Нажимая «Опубликовать свой ответ», вы соглашаетесь с нашими условиями обслуживания и подтверждаете, что прочитали и поняли нашу политику конфиденциальности и кодекс поведения.
История— Являются ли шаблоны проектирования слабостью языка?
спросил
Изменено 2 года, 5 месяцев назад
Просмотрено 6к раз
Следует ли считать сегодняшние шаблоны дефектами или недостающими функциями в Java и C++?
- Подпрограмма была шаблоном проектирования для машинного языка в 50-х и 60-х годах.
- Объектно-ориентированный класс был шаблоном проектирования для C в 70-х годах.
Посетители, абстрактные фабрики, декораторы и фасады — это сегодняшние шаблоны проектирования для Java и C++.
Какими будут языки завтрашнего дня? Какие узоры будут у у ?
- шаблоны дизайна
- история
- языковой дизайн
Некоторые канонизированные шаблоны проектирования — Адаптер, Фабрика, Команда, Посетитель и т. д. — являются приблизительными функциями, встроенными в другие языки. В голову не приходит:
Обработчики событий в C# — это встроенные версии шаблона наблюдателя. Подумайте о том, как бы вы связывали события в C#, если бы вам приходилось каждый раз запускать собственный наблюдатель.
Шаблон посетителя — это подробное приближение к мультиметодам, пересылке сообщений или очень слабой форме сопоставления с образцом.
Шаблон команды оборачивает конкретное поведение, поэтому вы можете передавать объект между методами, что более или менее приближается к первоклассным функциям.
Шаблон стратегии позволяет динамически вставлять поведение в объект, чтобы в любой момент можно было изменить объект, заменив одно поведение другим. В мире функционального программирования мы называем это композицией функций.
Шаблон абстрактной фабрики принимает аргумент и в результате возвращает фабрику. В общем, вы можете думать о фабриках как об обертках вокруг функции (точнее, обертках вокруг конструктора). Итак, вы передаете аргументы функции и в результате получаете функцию, что делает этот шаблон очень похожим на каррирование.
Шаблон декоратора позволяет прикреплять или удалять поведение объекта во время выполнения. В JavaScript вы можете добавлять или удалять функции без явной реализации шаблона декоратора благодаря объектно-ориентированной модели «прототип».
Итак, у нас есть набор шаблонов проектирования, которые эмулируют функции, присущие другим языкам. Зависть к функциям не обязательно указывает на слабость языка — это шаблонный код, который вам нужно писать снова и снова и снова , что указывает на слабость языка.
6Я бы не назвал их дефектами.
Языки более высокого порядка имеют дело с понятиями на более высоком уровне, чем языки более низкого порядка. Подумайте об этом с точки зрения строительства.
Вы можете построить здание на уровне нефтеперерабатывающего завода, перерабатывая пиломатериалы, выплавляя сталь и таким образом собирая здание.
Вы можете купить доски и стальные балки и построить из них здание.
Вы можете купить готовые стены и фермы и построить из них здание.
Вы можете купить здание и начать с интерьера.
При строительстве здания из досок и балок отсутствует элемент сборной стены или имеются какие-либо дефекты?
2Каждая вещь, которую вы делаете три или более раз при разработке программного обеспечения, образует шаблон.
Каждое повторение. Каждое повторение. Каждое повторение.
Некоторых канонизируют с крутыми именами. Они становятся шаблонами проектирования. Сознательное повторение.
Некоторые из них являются просто «лучшими практиками» или «это сработало для меня». Это шаблоны проектирования без ясности.
Некоторые из них просто «вещи, которые вы обычно делаете». Это шаблоны проектирования без какого-либо осознанного осознания того, что вы повторяетесь.
Шаблоны проектирования не имеют ничего общего со слабостью или неполнотой языка. У них всего делать с хорошими идеями, сознательно повторно использовать.
Сегодняшние шаблоны проектирования — это не прямая дорога к языкам завтрашнего дня. Языковая парадигма не продвигается вперед через серию «исправлений ошибок в предыдущих языках». Если бы это было так, мы бы никогда не создали Visual Basic.
И шаблоны проектирования — это гораздо более крупный и широкий интеллектуальный инструмент, чем упрощенный набор функций языка программирования.
3Нет, в них не отсутствуют особенности или дефекты языков. Но языки должны предоставлять средства для написания кода полезного шаблона 9.0085 проще, чем сложнее. Вот где функции, которые есть , предоставляемые языком, могут быть как благом, так и помехой.
Я думаю, что Эван поднимает интересную тему. Возьмем его пример с «подпрограммой». В ранних языках программирования идея передачи параметров подпрограмме и возврата результата требовала явного кодирования. В современных языках это встроено. Или, возьмем другой пример: If/then/else встроено в большинство, если не во все современные языки. Но когда я работал на ассемблере, для этого мне приходилось писать код. Не так уж много, конечно, но тем не менее, вам нужно было написать оператор перехода или перехода, чтобы обойти блок else. И тот факт, что вам приходилось писать эти вещи самостоятельно, означал, что разные программисты будут делать это немного по-разному, и был бесконечный соблазн поумничать и сделать это немного по-другому в этой программе, чтобы сделать ее более эффективной или получить какую-то другую выгоду. предполагаемое преимущество.
Последний пример, который приходит на ум, — это итераторы. Вы должны написать их вручную на C++ и ранних версиях Java, но они встроены в Java 5. Возможно, это синтаксический сахар, вы можете просто создавать функции-итераторы. Лично я считаю, что это хорошая функция. Это радикально повышает мою продуктивность? №
Есть ли что-то, что мы делаем все время, что логически должно быть встроено в язык для его стандартизации и упрощения? Увлекательный вопрос. Я не думаю, что кто-то будет всерьез утверждать, что даже их любимый язык совершенен и его усовершенствование абсолютно невозможно. Но каким будет направление следующего языка?
Конечно, некоторые функции, которые были добавлены к языкам, являются бесполезным дополнительным багажом. По моему скромному мнению, перечисления Java делают больше, чем необходимо, они добавляют кучу багажа без уважительной причины. Я уверен, что другие не согласятся и скажут, что они находят их невероятно полезными.
У меня нет заключения. Я просто согласен, что это увлекательный вопрос.
2Я где-то читал что-то вроде «чем больше у вас шаблонов, тем менее мощным является ваш язык», и я нахожу это определение очень хорошим.
Причина понятна для ИМО: если вы видите повторяющийся шаблон (или вам приходится его использовать), это означает, что это своего рода копирование и вставка на логическом уровне. Конечно, это не так плохо, как копирование и вставка утверждений, но все же это избыточность. Я все еще повторяю себя снова и снова.
Чем более выразительным является язык, и тем больше вы должны быть в состоянии уловить и учесть эту избыточность, преобразовав ее при повторном использовании вместо повторной реализации, и повторное использование будет легче читать и понимать в исходном коде.
Каждый раз, когда вы снова и снова делаете одно и то же (включая, например, «ок… давайте реализуем здесь абстрактную фабрику»), это означает, что это должно было быть выражено на более высоком уровне.
Конечно, иногда вы можете попытаться уловить суть чего-либо, но повторное использование может быть более уродливым для чтения, чем повторная реализация (я думаю, например, о некоторых частях C++ «высокоуровневого» материала в <алгоритм>
). Это ИМО явный признак того, что язык недостаточно выразителен.
Я думаю, что некоторые шаблоны проектирования представляют собой слабости языка, а новые языки включают существующие шаблоны в первоклассных гражданах. Примером нового языка, использующего существующие шаблоны проектирования в других языках (внедрение зависимостей, неизменяемость и т. д.) и включающего их в качестве функций уровня языка первого класса, является NOOP от Google.
Мне интересно, сколько всего можно втиснуть в язык, прежде чем он станет слишком «большим».
Мне нравится, когда язык, с которым я работаю, достаточно мал, чтобы я мог удержать его в голове сразу. Шаблоны, такие как DI, связаны со структурными проблемами; должно ли это быть частью языка?
Сколько знаний языка действительно нужно разработчику?
В случае Code Contracts (требует, обеспечивает) хорошо, когда это первоклассная часть языка, но это не обязательно. Он все еще может быть частью библиотеки.
4Платформы, облегчающие использование определенных шаблонов в языках, существуют, поэтому они скорее выбор, чем необходимость. Есть шаблоны, которые для определенного проекта могут вам просто не понадобиться, поэтому включение многих из них в качестве основных языковых функций только наложит ограничения, а не облегчит продуктивную работу.
каждый язык представляет собой произвольный набор шаблонов проектирования с синтаксисом в качестве обозначения для выбранных шаблонов. для невыбранных шаблонов вы должны вмешиваться в ограничения синтаксиса, чтобы выразить их. большинство языков не позволяет сильно изменить свой синтаксис, чтобы ассимилировать шаблоны более высокого уровня.
язык, который может делать это без ограничений, был бы языком без жесткого синтаксиса. (или, по-другому, такой, который позволяет плавно усваивать любой паттерн высокого уровня). см. металингвистическую абстракцию
мне на ум приходит схема/шепелявость.
Шаблоны проектирования не являются слабостью языков. Они предлагают дизайнерские решения повторяющихся проблем.
Поскольку со временем многое изменилось, я думаю, что шаблоны корпоративной интеграции будут пользоваться популярностью.
Если необходимо обмениваться данными между различными корпоративными приложениями, эти шаблоны обеспечивают наилучшее решение.
-
Стили интеграции
документирует различные способы интеграции приложений, предоставляя исторический отчет о технологиях интеграции. Все последующие шаблоны следуют стилю обмена сообщениями. -
Шаблоны каналов
описывают, как сообщения передаются по каналу сообщений. Эти шаблоны реализованы в большинстве коммерческих и открытых систем обмена сообщениями. -
Шаблоны построения сообщений
описывают цель, форму и содержание сообщений, которые проходят через систему обмена сообщениями. Базовым шаблоном для этого раздела является шаблон сообщения. -
Шаблоны маршрутизации
обсуждают, как сообщения направляются от отправителя к правильному получателю. Шаблоны маршрутизации сообщений потребляют сообщение из одного канала и повторно публикуют это сообщение, обычно без изменений, в другой канал на основе набора условий. Шаблоны, представленные в этом разделе, являются специализациями шаблона Message Router. -
Шаблоны преобразования
изменяют содержимое сообщения, например, для согласования различных форматов данных, используемых отправляющей и принимающей системами. Данные, возможно, придется добавить, удалить или изменить существующие данные. Базовым шаблоном для этого раздела является переводчик сообщений. -
Шаблоны конечных точек
описывают, как клиенты системы обмена сообщениями создают или потребляют сообщения. -
Шаблоны управления системой
описывают инструменты для поддержания работоспособности сложной системы, основанной на сообщениях, включая работу с состояниями ошибок, узкими местами в производительности и изменениями в участвующих системах.
Чтобы ответить на вопрос «Какие шаблоны они будут иметь?»:
В Java есть шаблон Singleton, который реализован в Kotlin с ключевым словом object
Я не знаю, являются ли шаблоны слабостью языков, но для меня это один Сильные стороны Kotlins в том, что он упрощает использование синглтонов.