Основные паттерны программирования. Шаблоны архитектуры системы. Зачем нужны шаблоны проектирования

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

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

Например, следует ли считать алгоритмы и структуры данных паттернами? По этому вопросу существуют противоположные мнения. Согласно одному из них, алгоритмы являются вычислительными паттернами, а хорошо известная фундаментальная монография Дональда Кнута "Искусство программирования" по сути, представляет собой каталог таких паттернов. Согласно другому мнению, алгоритмы не являются паттернами, так как решаемые ими проблемы слишком малы (оперируют такими понятиями как вычислительная сложность и потребление ресурсов), а область решения хорошо очерчена. Паттерны же решают проблемы большего масштаба, при этом паттерн дает не конкретное решение, а некий путь к решению, причем, выбор правильного паттерна - задача нетривиальная, предполагающая от архитектора наличие интуиции, опыта, определенного творчества.

Классификация паттернов

В силу популярности каталога GoF часто под паттернами проектирования подразумевают все виды паттернов программной индустрии, что является не совсем корректным. В области разработки программных систем существует множество паттернов, которые отличаются областью применения, масштабом, содержимым, стилем описания. Например, в зависимости от сферы применения существуют такие паттерны как паттерны анализа, проектирования, тестирования, документирования, организации процесса разработки, планирования проектов и другие. В настоящее время наиболее популярными паттернами являются паттерны проектирования. Одной из распространенных классификаций таких паттернов является классификация по степени детализации и уровню абстракции рассматриваемых систем.

Паттерны делятся на следующие категории:

  • Архитектурные паттерны
  • Паттерны проектирования
  • Идиомы

Архитектурные паттерны, являясь наиболее высокоуровневыми паттернами, описывают структурную схему программной системы в целом. В данной схеме указываются отдельные функциональные составляющие системы, называемые подсистемами, а также взаимоотношения между ними. Примером архитектурного паттерна является хорошо известная программная парадигма "модель-представление-контроллер" (model-view-controller - MVC). В свою очередь, подсистемы могут состоять из архитектурных единиц уровнем ниже.

Паттерны проектирования описывают схемы детализации программных подсистем и отношений между ними, при этом они не влияют на структуру программной системы в целом и сохраняют независимость от реализации языка программирования. Паттерны GoF относятся именно к этой категории. Под паттернами проектирования объектно-ориентированных систем понимается описание взаимодействия объектов и классов, адаптированных для решения общей задачи проектирования в конкретном контексте. В русскоязычной литературе обычно встречаются несколько вариантов перевода оригинального названия design patterns - паттерны проектирования, шаблоны проектирования, образцы. Здесь в основном используется первый вариант, иногда второй.

Идиомы, являясь низкоуровневыми паттернами, имеют дело с вопросами реализации какой-либо проблемы с учетом особенностей данного языка программирования. При этом часто одни и те же идиомы для разных языков программирования выглядят по-разному или не имеют смысла вовсе. Например, в C++ для устранения возможных утечек памяти могут использоваться интеллектуальные указатели. Интеллектуальный указатель содержит указатель на участок динамически выделенной памяти, который будет автоматически освобожден при выходе из зоны видимости. В среде Java такой проблемы просто не существует, так как там используется автоматическая сборка мусора. Обычно, для использования идиом нужно глубоко знать особенности применяемого языка программирования. Следует отметить, что в программной области существуют и другие виды паттернов, не относящиеся к проектированию вообще, например, паттерны анализа, тестирования, документирования и др.

Типы паттернов проектирования

Порождающие паттерны

  • Абстрактная фабрика (Abstract factory) - класс, который представляет собой интерфейс для создания компонентов системы.
  • Фабричный метод (Factory method) - определяет интерфейс для создания объекта, но оставляет подклассам решение о том, какой класс инстанцировать.
  • Прототип (Prototype) - определяет интерфейс создания объекта через клонирование другого объекта вместо создания через конструктор.
  • Строитель (Builder) - класс, который представляет собой интерфейс для создания сложного объекта.
  • Одиночка (Singleton) - класс, который может иметь только один экземпляр.
  • Отложенная инициализация (Lazy initialization) - объект, инициализируемый во время первого обращения к нему.

Структурные паттерны

  • Адаптер (Adapter) - объект, обеспечивающий взаимодействие двух других объектов, один из которых использует, а другой предоставляет несовместимый с первым интерфейс.
  • Компоновщик (Composite) - объект, который объединяет в себе объекты, подобные ему самому.
  • Декоратор или Обёртка (Decorator) - класс, расширяющий функциональность другого класса без использования наследования.
  • Фасад (Facade) - объект, который абстрагирует работу с несколькими классами, объединяя их в единое целое.
  • Единая точка входа (Front Controller) - обеспечивает унифицированный интерфейс для интерфейсов в подсистеме. Front Controller определяет высокоуровневый интерфейс, упрощающий использование подсистемы.
  • Заместитель (Proxy) - объект, который является посредником между двумя другими объектами, и который реализует/ограничивает доступ к объекту, к которому обращаются через него.

Поведенческие паттерны

  • Команда, Экшен, Транзакция (Command) - представляет действие. Объект команды заключает в себе само действие и его параметры.
  • Стратегия (Strategy) - предназначен для определения семейства алгоритмов, инкапсуляции каждого из них и обеспечения их взаимозаменяемости.
  • Шаблонный метод (Template method) - определяет основу алгоритма и позволяет наследникам переопределять некоторые шаги алгоритма, не изменяя его структуру в целом.
  • Наблюдатель, Слушатель (Observer) - определяет зависимость типа «один ко многим» между объектами таким образом, что при изменении состояния одного объекта все зависящие от него оповещаются об этом событии.
  • Цепочка обязанностей (Chain of responsibility) - предназначен для организации в системе уровней ответственности.

Шаблоны проектирования - это руководства по решению повторяющихся проблем. Это не классы, пакеты или библиотеки, которые можно было бы подключить к вашему приложению и сидеть в ожидании чуда. Они скорее являются методиками, как решать определенные проблемы в определенных ситуациях.

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

Существуют следующие порождающие шаблоны:

Простая фабрика (Simple Factory)

В объектно-ориентированном программировании (ООП), фабрика - это объект для создания других объектов. Формально фабрика - это функция или метод, который возвращает объекты изменяющегося прототипа или класса из некоторого вызова метода, который считается «новым».

Пример из жизни: Представьте, что вам надо построить дом, и вам нужны двери. Было бы глупо каждый раз, когда вам нужны двери, надевать вашу столярную форму и начинать делать дверь. Вместо этого вы делаете её на фабрике.

Простыми словами: Простая фабрика генерирует экземпляр для клиента, не раскрывая никакой логики.

Перейдем к коду. У нас есть интерфейс Door и его реализация:

Interface Door { public function getWidth(): float; public function getHeight(): float; } class WoodenDoor implements Door { protected $width; protected $height; public function __construct(float $width, float $height) { $this->width = $width; $this->height = $height; } public function getWidth(): float { return $this->width; } public function getHeight(): float { return $this->height; } }

Затем у нас есть наша DoorFactory , которая делает дверь и возвращает её:

Class DoorFactory { public static function makeDoor($width, $height): Door { return new WoodenDoor($width, $height); } }

И затем мы можем использовать всё это:

$door = DoorFactory::makeDoor(100, 200); echo "Width: " . $door->getWidth(); echo "Height: " . $door->getHeight();

Когда использовать: Когда создание объекта - это не просто несколько присвоений, а какая-то логика, тогда имеет смысл создать отдельную фабрику вместо повторения одного и того же кода повсюду.

Фабричный метод (Fabric Method)

Фабричный метод - порождающий шаблон проектирования, предоставляющий подклассам интерфейс для создания экземпляров некоторого класса. В момент создания наследники могут определить, какой класс создавать. Иными словами, данный шаблон делегирует создание объектов наследникам родительского класса. Это позволяет использовать в коде программы не специфические классы, а манипулировать абстрактными объектами на более высоком уровне.

Пример из жизни: Рассмотрим пример с менеджером по найму. Невозможно одному человеку провести собеседования со всеми кандидатами на все вакансии. В зависимости от вакансии он должен распределить этапы собеседования между разными людьми.

Простыми словами: Менеджер предоставляет способ делегирования логики создания экземпляра дочерним классам.

Перейдём к коду. Рассмотрим приведенный выше пример про HR-менеджера. Изначально у нас есть интерфейс Interviewer и несколько реализаций для него:

Interface Interviewer { public function askQuestions(); } class Developer implements Interviewer { public function askQuestions() { echo "Спрашивает про шаблоны проектирования!"; } } class CommunityExecutive implements Interviewer { public function askQuestions() { echo "Спрашивает о работе с сообществом"; } }

Теперь создадим нашего HiringManager:

Abstract class HiringManager { // Фабричный метод abstract public function makeInterviewer(): Interviewer; public function takeInterview() { $interviewer = $this->makeInterviewer(); $interviewer->askQuestions(); } }

И теперь любой дочерний класс может расширять его и предоставлять необходимого интервьюера:

Class DevelopmentManager extends HiringManager { public function makeInterviewer(): Interviewer { return new Developer(); } } class MarketingManager extends HiringManager { public function makeInterviewer(): Interviewer { return new CommunityExecutive(); } }

Пример использования:

$devManager = new DevelopmentManager(); $devManager->takeInterview(); // Вывод: Спрашивает о шаблонах проектирования! $marketingManager = new MarketingManager(); $marketingManager->takeInterview(); // Вывод: Спрашивает о работе с сообществом

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

Абстрактная фабрика (Abstract Factory)

Абстрактная фабрика - порождающий шаблон проектирования, предоставляет интерфейс для создания семейств взаимосвязанных или взаимозависимых объектов, не специфицируя их конкретных классов. Шаблон реализуется созданием абстрактного класса Factory, который представляет собой интерфейс для создания компонентов системы (например, для оконного интерфейса он может создавать окна и кнопки). Затем пишутся классы, реализующие этот интерфейс.

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

Простыми словами: Фабрика фабрик. Фабрика, которая группирует индивидуальные, но связанные/зависимые фабрики без указания их конкретных классов.

Обратимся к коду. Используем пример про двери. Сначала у нас есть интерфейс Door и несколько его реализаций:

Interface Door { public function getDescription(); } class WoodenDoor implements Door { public function getDescription() { echo "Я деревянная дверь"; } } class IronDoor implements Door { public function getDescription() { echo "Я железная дверь"; } }

Затем у нас есть несколько DoorFittingExpert для каждого типа дверей:

Interface DoorFittingExpert { public function getDescription(); } class Welder implements DoorFittingExpert { public function getDescription() { echo "Я работаю только с железными дверьми"; } } class Carpenter implements DoorFittingExpert { public function getDescription() { echo "Я работаю только с деревянными дверьми"; } }

Теперь у нас есть DoorFactory , которая позволит нам создать семейство связанных объектов. То есть фабрика деревянных дверей предоставит нам деревянную дверь и эксперта по деревянным дверям. Аналогично для железных дверей:

Interface DoorFactory { public function makeDoor(): Door; public function makeFittingExpert(): DoorFittingExpert; } // Деревянная фабрика вернет деревянную дверь и столяра class WoodenDoorFactory implements DoorFactory { public function makeDoor(): Door { return new WoodenDoor(); } public function makeFittingExpert(): DoorFittingExpert { return new Carpenter(); } } // Железная фабрика вернет железную дверь и сварщика class IronDoorFactory implements DoorFactory { public function makeDoor(): Door { return new IronDoor(); } public function makeFittingExpert(): DoorFittingExpert { return new Welder(); } }

Пример использования:

$woodenFactory = new WoodenDoorFactory(); $door = $woodenFactory->makeDoor(); $expert = $woodenFactory->makeFittingExpert(); $door->getDescription(); // Вывод: Я деревянная дверь $expert->getDescription(); // Вывод: Я работаю только с деревянными дверями // Аналогично для железной двери $ironFactory = new IronDoorFactory(); $door = $ironFactory->makeDoor(); $expert = $ironFactory->makeFittingExpert(); $door->getDescription(); // Вывод: Я железная дверь $expert->getDescription(); // Вывод: Я работаю только с железными дверями

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

Когда использовать: Когда есть взаимосвязанные зависимости с не очень простой логикой создания.

Строитель (Builder)

Строитель - порождающий шаблон проектирования, который предоставляет способ создания составного объекта. Предназначен для решения проблемы антипаттерна «Телескопический конструктор».

Пример из жизни: Представьте, что вы пришли в McDonalds и заказали конкретный продукт, например, БигМак, и вам готовят его без лишних вопросов. Это пример простой фабрики. Но есть случаи, когда логика создания может включать в себя больше шагов. Например, вы хотите индивидуальный сэндвич в Subway: у вас есть несколько вариантов того, как он будет сделан. Какой хлеб вы хотите? Какие соусы использовать? Какой сыр? В таких случаях на помощь приходит шаблон «Строитель».

Простыми словами: Шаблон позволяет вам создавать различные виды объекта, избегая засорения конструктора. Он полезен, когда может быть несколько видов объекта или когда необходимо множество шагов, связанных с его созданием.

Давайте я покажу на примере, что такое «Телескопический конструктор». Когда-то мы все видели конструктор вроде такого:

Public function __construct($size, $cheese = true, $pepperoni = true, $tomato = false, $lettuce = true) { }

Как вы можете заметить, количество параметров конструктора может резко увеличиться, и станет сложно понимать расположение параметров. Кроме того, этот список параметров будет продолжать расти, если вы захотите добавить новые варианты. Это и есть «Телескопический конструктор».

Перейдем к примеру в коде. Адекватной альтернативой будет использование шаблона «Строитель». Сначала у нас есть Burger , который мы хотим создать:

Class Burger { protected $size; protected $cheese = false; protected $pepperoni = false; protected $lettuce = false; protected $tomato = false; public function __construct(BurgerBuilder $builder) { $this->size = $builder->size; $this->cheese = $builder->cheese; $this->pepperoni = $builder->pepperoni; $this->lettuce = $builder->lettuce; $this->tomato = $builder->tomato; } }

Затем мы берём «Строителя»:

Class BurgerBuilder { public $size; public $cheese = false; public $pepperoni = false; public $lettuce = false; public $tomato = false; public function __construct(int $size) { $this->size = $size; } public function addPepperoni() { $this->pepperoni = true; return $this; } public function addLettuce() { $this->lettuce = true; return $this; } public function addCheese() { $this->cheese = true; return $this; } public function addTomato() { $this->tomato = true; return $this; } public function build(): Burger { return new Burger($this); } }

Пример использования:

$burger = (new BurgerBuilder(14)) ->addPepperoni() ->addLettuce() ->addTomato() ->build();

Когда использовать: Когда может быть несколько видов объекта и надо избежать «телескопического конструктора». Главное отличие от «фабрики» - это то, что она используется, когда создание занимает один шаг, а «строитель» применяется при множестве шагов.

Прототип (Prototype)

Задаёт виды создаваемых объектов с помощью экземпляра-прототипа и создаёт новые объекты путём копирования этого прототипа. Он позволяет уйти от реализации и позволяет следовать принципу «программирование через интерфейсы». В качестве возвращающего типа указывается интерфейс / абстрактный класс на вершине иерархии, а классы-наследники могут подставить туда наследника, реализующего этот тип.

Пример из жизни: Помните Долли? Овечка, которая была клонирована. Не будем углубляться, главное - это то, что здесь все вращается вокруг клонирования.

Простыми словами: Прототип создает объект, основанный на существующем объекте при помощи клонирования.

То есть он позволяет вам создавать копию существующего объекта и модернизировать его согласно вашим нуждам, вместо того, чтобы создавать объект заново.

Обратимся к коду. В PHP это может быть легко реализовано с использованием clone:

Class Sheep { protected $name; protected $category; public function __construct(string $name, string $category = "Горная овечка") { $this->name = $name; $this->category = $category; } public function setName(string $name) { $this->name = $name; } public function getName() { return $this->name; } public function setCategory(string $category) { $this->category = $category; } public function getCategory() { return $this->category; } }

Затем он может быть клонирован следующим образом:

$original = new Sheep("Джолли"); echo $original->getName(); // Джолли echo $original->getCategory(); // Горная овечка // Клонируем и модифицируем то что нужно $cloned = clone $original; $cloned->setName("Долли"); echo $cloned->getName(); // Долли echo $cloned->getCategory(); // Горная овечка

Также вы можете использовать волшебный метод __clone для изменения клонирующего поведения.

Когда использовать: Когда необходим объект, похожий на существующий объект, либо когда создание будет дороже клонирования.

Одиночка (Singleton)

Одиночка - порождающий шаблон проектирования, гарантирующий, что в однопроцессном приложении будет единственный экземпляр некоторого класса, и предоставляющий глобальную точку доступа к этому экземпляру.

Пример из жизни: В стране одновременно может быть только один президент. Один и тот же президент должен действовать, когда того требуют обстоятельства. Президент здесь является одиночкой.

Простыми словами: Обеспечивает тот факт, что создаваемый объект является единственным объектом своего класса.

Вообще шаблон одиночка признан антипаттерном, необходимо избегать его чрезмерного использования. Он необязательно плох и может иметь полезные применения, но использовать его надо с осторожностью, потому что он вводит глобальное состояние в ваше приложение и его изменение в одном месте может повлиять на другие части приложения, что вызовет трудности при отладке. Другой минус - это то, что он делает ваш код связанным.

Прим. перев. Подробнее о подводных камнях шаблона одиночка читайте в .

Перейдем к коду. Чтобы создать одиночку, сделайте конструктор приватным, отключите клонирование и расширение и создайте статическую переменную для хранения экземпляра:

Final class President { private static $instance; private function __construct() { // Прячем конструктор } public static function getInstance(): President { if (!self::$instance) { self::$instance = new self(); } return self::$instance; } private function __clone() { // Отключаем клонирование } private function __wakeup() { // Отключаем десериализацию } }

Пример использования:

$president1 = President::getInstance(); $president2 = President::getInstance(); var_dump($president1 === $president2); // true

Данной статьей мы начинаем серию статей, посвященных паттернам проектирования.

Статьи рассчитаны на тех, кто уже хорошо знает ООП.

Что такое паттерны в программировании

Ну, что ж, давайте сначала разберемся что такое паттерн. А затем плавно перейдем к такому понятию как "паттерны в программировании".

Паттерн - это повторяющийся элемент в различных сферах жизни.

Пример 1: окрас тигра - это паттерн.

Пример 2: Коробка передач - это паттерн.

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

Теперь, вместо того чтобы выдумывать велосипед заново, можно воспользоваться знаниями людей, которые уже решали эти проблемы до нас. Таким образом:

Шаблон проектирования / шаблон программирования / паттерн - это типичные способы решения часто возникающих задач в сфере разработки ПО.

ВАЖНО:

Паттерн - это не готовое решение, которое можно откуда-то скопировать и вставить в Вашу программу. Это только общие принципы, которые надо уметь правильно применить.

Мне надо знать паттерны?

Ответ: "Да"

Потому что:

  • Паттерны очень часто применяются на практик е. Конечно, для начинающих программистов понимание паттернов не всегда заходит легко. Так что наберитесь терпения и учим, учим, учим...
  • Паттерны часто спрашивают на собеседованиях.
  • И самое главное - паттерны предлагают Вам готовые решения . Они помогут Вам сохранить время и усилия, а качество программы повысится.

Откуда они взялись

Хотя сама идея паттернов далеко не новая, популярной она стала после выхода книги "Приёмы объектно-ориентированного проектирования. Паттерны проектирования ". Это произошло в 1994 году. С тех пор мир захватила "шаблономания" 🙂

Какие они бывают

  • Порождающие (Creational Design Patterns)

Эти шаблоны что-то создают. Например, "как создать объект, который нельзя изменить"? "Как создать класс, который будет создавать новые объекты других классов?"?

  • Структурные (Structural Design Patterns)

Отвечают за иерархию классов и интерфейсов. Например, "как заставить объекты с несовместимыми интерфейсами работа вместе"?

  • Поведенческие (Behavioral Design Patterns)

Помогает добиться нужного поведения от объектов. Например, "как сделать так, чтобы объекты одного класса следили за изменениями в других классах и реагировали на них"?

Из чего состоит паттерн?

  • Задача, которую решает паттерн
  • Решение:
    • Структуры классов, составляющих решение;
    • Примера на одном из языков программирования;
  • Связь с другими паттернами

А конкретнее?

Существует 23 классических шаблона проектирования, с которых все и началось. В настоящий момент паттернов намного больше - минимум в 2-3 раза больше.

Здесь о каждом из них мы, конечно, говорить не будем - это много 🙂 Но мы расскажем об основных паттернах в будущих статьях.

Самыми-самыми "базовыми" шаблонами проектирования можно назвать следующие:

  • Singleton ("Одиночка")
  • Builder ("Строитель")
  • Factory ("Фабрика")
  • Wrapper ("Обертка")
  • Proxy ("Прокси")

С них можно начинать изучение паттернов. Ниже в этой статье Вы найдете ссылочки на статьи по этим паттернам.


К сожалению, не успел к началу вопроса, многое уже посоветовали, но эту статейку вроде не успели еще кинуть. Недавно нашел ее и просто поразился как просто и доступно это изложено + с примерами кода на php. Просто шикарный перевод великолепной статьи!

От себя же хочу сказать, что единственный способ понять паттерны - это столкнуться с проблемами которые они решают, ибо паттерны ни что иное как шаблоны решения каких то проблем (и предотвращения). Так что делаем вывод - нет проблем, не может быть и решений (конечно, вы просто не осознаете, что они есть, так как проект растет довольно медленно и чаще это какие то правки или добавление нового функционала, который не зависит от старого). Я очень долго пытался с ними разобраться, пробовал читать все перечисленные книги, но вроде читаешь такой и типа понимаешь, но с другой стороны какбы и нет. Вроде понятно, но где это применять хрен знает. Вообщем, как уже сказали, нужны реальные проблемы и тогда открываешь книгу с решениями этих проблем и думаешь какое решение выбрать. Это как с рецептами.. Хочешь что то приготовить, можешь как бы и сам, но не факт, что вкусно получится, тогда открываешь книгу проверенных рецептов и начинаешь применять все по шагам, опираясь при том на ингридиенты, которые у тебя имеются.

Так что посоветую 2 варианта изучения.
1) Тупо работаешь над сложные проектами, только действительно сложными, а не сайтиками на cms. И со временем ты начинаешь встречаться с проблемами. Тогда открываешь паттерны и тебе не придется даже как то их особо понимать, потому что это будет естевственно для тебя. Я думаю ты используешь ide вместо редактора кода. Но к примеру я помню тот момент, когда я пользовался саблаймом и знал, что есть ide, но я писал на тот момент простые вещи и когда мне говорили, почему я не юзаю ide, ведь в ней столько всего, я не понимал их потому что мне и саблайма за глаза хватало. Но пришло время, когда надо было то и се и саблайма стало мало. И тут открываю ide, а там уже есть все необходимое и думаешь в такие моменты, как я раньше этим не пользовался. А дело в том, что раньше и не надо было. Может неудачный пример, но вы поняли) Конечно, этот вариант изучения не совсем реален, по скольку сложный проект еще найти надо, да еще попасть в команду, которая не говнокодит, так как и крупные проекты бывают достаточно плохо написаны. Но можно как вариант к примеру делать свою cms и применять в ней как можно больше паттернов.

2) Тупо садитесь и изучаете паттерны (как и делал я). Но не просто изучаете, а к какждому паттерну придумываете как можно больше проблем, которые может у вас были или могут быть, так как паттерны в большинстве случаях любят описывать в метафорах, но чаще это получается слишком абстрактно, поэтому нужно чтобы вы придумали свои конкретные задачи, где бы вы попробовали применить этот паттерн. И второй этап - садитесь и пишите эти задачи. Тупо открываете свой яп и реализовываете паттерн. При чем несколько раз с разными проблемами.

Также советую четко понять uml диаграммы. Таким образом, чтобы освежить паттерн вы не будете читать примеры, а просто посмотрите диаграмму и сразу вспомните, зачем он нужен и как его можно реализовать.
Вот пожалуй и все

Современное программирование практически невозможно без применения специальных шаблонов проектирования (паттернов). Паттерны представляют собой специальные способы построения и организации программного продукта, которое позволяет решить наиболее «популярные» сложности и ошибки в программировании. Обобщая данный термин, можно сказать, что паттерны представляют собой своеобразный свод правил, который позволяет избавить программиста от общих, формализованных проблем.
Все известные на сегодняшний день паттерны проектирования можно разделить на несколько подвидов, это: порождающие шаблоны, паттерны поведения, структурные и, так называемые, принципы SOLID.
К порождающим паттернам относятся следующие: Абстрактная фабрика, Builder, Factory Method, Singleton, Prototype. Они называются порождающими в связи с тем, что подобные паттерны абстрагируют процесс создания экземпляров классов и объектов.
К поведенческим паттернам относятся: Interpreter, Iterator, Mediator, Template Method, State, Strategy, Visitor. Они называются поведенческими потому что определяют процессы взаимодействия между классами и объектами, соответственно, определяют поведение.
Структурные паттерны включают в себя: Proxy, Facade, Composite, Bridge, Adapter. Такие паттерны в разработке программного обеспечения, в коммерческих целях, встречаются довольно редко, поэтому и их описание найти немного проблематично. По сравнению с перечисленными выше паттернами, эти используются не часто, только в определенных, исключительных случаях. Подобные шаблоны рассматривают классы и объекты, используемые в программе, как структуры, соответственно, более сложные.
Применение подобных паттернов позволяет улучшить понимание кода, например, если вы работаете не один, а в команде, а также упростить процедуру внедрения новых участков кода или изменения старых, без необходимости в написании избыточного кода.
Принципы Solid, по своей сути представляют один большой шаблон проектирования, который включает в себя несколько маленьких паттернов. SOLID, как можно догадаться, является акронимом и означает следующее: S – принцип единственной обязанности, O - открытости и закрытости, L - подстановки Лисков, I - разделения интерфейсов в программу, и D -инверсии зависимостей. В отличии от представленных выше паттернов, подобные принципы нельзя назвать какими-то шаблонами решения формализованных проблем. Эти принципы позволяют создать грамотное, структурированное программное решение, которое упрощает изменение определенных участков кода и даже его изменение.
Таким образом, подытоживая все описанное выше, можно сказать, что программирование без применения принципов SOLID или паттернов, буквально невозможно. Скорее всего, работать без них можно только если вы создаете какое-то небольшое приложения, над которым в дальнейшем будете также работать только вы один и точно знаете – какой участок кода за что отвечает. Если вы работаете в команде, то без применения шаблонов и принципов – вам далеко не уйти. Так будет проще разбираться в коде вашим товарищам и сама работа станет легче.