Банда четырех паттерны

.

Книга содержит описание 23 паттернов (шаблонов) программирования. Авторами являются четыре специалиста, часто их называют «Бандой четырех».

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

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

Общая терминология

Читатель иногда может поймать себя на мысли, что описываемый паттерн он/она знает, просто использовался без названия. Это один из целей книги — предложить комьюнити общую терминологию. Это намного укоротит диалоги:

— Здесь хорошо подошел бы Facade.

— Согласен!

Структура

Содержимое книги «каталогизировано». Каждый паттерн имеет свое название, альтернативные названия, главную идею, пример кода и т.д. Очень сложно запомнить все описанные паттерны со всеми деталями и нюансами, потому данная книга может стать отличным справочником.

Примеры

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

Критика идеи использования паттернов

Во многих книгах мы можем прочитать мнение о том, что код должен быть настолько простым и понятным, насколько это возможно. «Хитрый» код в большинстве случаев означает плохой код. Пол Грэм — основатель Hacker News — писал:

Паттерны в моей программе — это причина для беспокойства.

Есть ли смысл читать паттерны банды четырех если ты js-разработчик?

Структура программы должна строиться исключительно для решения проблемы. Любую другую причину для изменения структуры я воспринимаю как ошибку по работе с абстракциями.

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

Однажды я услышал одну интересную мысль:

Замечательно, что разработчик использует паттерны, но еще лучше — использовать их только по необходимости.

Заключение

Стив Макконнелл в своей книге «Совершенный код» составил список литературы для разработчика программного обеспечения. Он разделил книги на три уровня: Начинающий, Практикующий, Профессионал. «Совершенный код» был в списке для начинающих, когда «Паттерны проектирования» — для профессионалов. Делайте выводы сами

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

Паттернами проектирования (Design Patterns) называют решения часто встречающихся проблем в области разработки программного обеспечения.

Шаблоны проектирования «банды четырёх (GoF)»

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

Концепцию паттернов впервые описал Кристофер Александер в книге «Язык шаблонов. Города. Здания. Строительство».

Идея показалась привлекательной авторам Эриху Гамму, Ричарду Хелму, Ральфу Джонсону и Джону Влиссидесу, их принято называть «бандой четырёх» (Gang of Four). В 1995 году они написали книгу «Design Patterns: Elements of Reusable Object-Oriented Software», в которой применили концепцию типовых паттернов в программировании. В книгу вошли 23 паттерна, решающие различные проблемы объектно-ориентированного дизайна.

Зачем знать паттерны?

Самое главная причина — паттерны упрощают проектирование и поддержку программ.

  • Проверенные решения.

    Ваш код более предсказуем когда вы используете готовые решения, вместо повторного изобретения велосипеда.

  • Стандартизация кода.

    Вы делаете меньше ошибок, так как используете типовые унифицированные решения, в которых давно найдены все скрытые проблемы.

  • Общий язык.

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

Каталог шаблонов проектирования

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

Порождающие паттерны — это паттерны, которые абстрагируют процесс инстанцирования или, иными словами, процесс порождения классов и объектов. Среди них выделяются следующие:

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

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

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

  4. Прототип (Prototype)

  5. Одиночка (Singleton)

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

Структурные паттерны — рассматривает, как классы и объекты образуют более крупные структуры — более сложные по характеру классы и объекты. К таким шаблонам относятся:

  1. Адаптер (Adapter)

  2. Мост (Bridge)

  3. Компоновщик (Composite)

  4. Декоратор (Decorator)

  5. Фасад (Facade)

  6. Приспособленец (Flyweight)

  7. Заместитель (Proxy)

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

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

  1. Цепочка обязанностей (Chain of responsibility)

  2. Команда (Command)

  3. Интерпретатор (Interpreter)

  4. Итератор (Iterator)

  5. Посредник (Mediator)

  6. Хранитель (Memento)

  7. Наблюдатель (Observer)

  8. Состояние (State)

  9. Стратегия (Strategy)

  10. Шаблонный метод (Template method)

  11. Посетитель (Visitor)

Паттерны проектирования. Банда четырех

.

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

Design Patterns

Однако наследование не просто ко­пирует интерфейс базового класса. Когда вы создаете объект производного класса, внутри него содержится подобъект базового класса. Этот подобъект вы­глядит точно так же, как выглядел бы созданный обычным порядком объект ба­зового класса. Поэтому извне представляется, будто бы в объекте производного класса «упакован» объект базового класса.

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

//: reusing/Cartoon.java

// Вызовы конструкторов при проведении наследования, import static net.mindview.util.Print.*,

class Art {

ArtO { print(«Конструктор Art»); }

}

class Drawing extends Art {

DrawingО { print(«Конструктор Drawing»); }

}

public class Cartoon extends Drawing {

public CartoonO { print(«Конструктор Cartoon»); } public static void main(String[] args) { Cartoon x = new CartoonO;

}

} /* Output; Конструктор Art Конструктор Drawing Конструктор Cartoon * ///:-

Как видите, конструирование начинается с «самого внутреннего» базового класса, поэтому базовый класс инициализируется еще до того, как он станет доступным для конструктора производного класса. Даже если конструктор класса Cartoon не определен, компилятор сгенерирует конструктор по умолча­нию, в котором также вызывается конструктор базового класса.

Конструкторы с аргументами

В предыдущем примере использовались конструкторы по умолчанию, то есть конструкторы без аргументов. У компилятора не возникает проблем с вызовом таких конструкторов, так как вопросов о передаче аргументов не возникает. Если класс не имеет конструктора по умолчанию или вам понадобится вызвать конструктор базового класса с аргументами, этот вызов придется оформить явно, с указанием ключевого слова super и передачей аргументов:

//: reusing/Chess.java

// Наследование, конструкторы и аргументы.

import static net.mindview.util.Print.*;

class Game {

Game(int i) {

print(«Конструктор Game»),

}

}

class BoardGame extends Game { BoardGame(int i) { super(i);

print(«Конструктор BoardGame»);

}

}

public class Chess extends BoardGame { Chess О {

super(ll);

print(«Конструктор Chess»);

}

public static void main(String[] args) { Chess x = new ChessO:

}

} /* Output- Конструктор Game Конструктор BoardGame Конструктор Chess *///:-

Если не вызвать конструктор базового класса в BoardGame(), то компилятор «пожалуется» на то, что не может обнаружить конструктор в форме Game(). Вдобавок вызов конструктора базового класса должен быть первой командой в конструкторе производного класса. (Если вы вдруг забудете об этом, компи­лятор вам тут же напомнит.)

Делегирование

Третий вид отношений, не поддерживаемый в Java напрямую, называется деле­гированием. Он занимает промежуточное положение между наследованием и композицией: экземпляр существующего класса включается в создаваемый класс (как при композиции), но в то же время все методы встроенного объекта становятся доступными в новом классе (как при наследовании). Например, класс SpaceShipControls имитирует модуль управления космическим кораблем:

//. reusing/SpaceShipControls.java

public class SpaceShipControls { void up(int velocity) {} void down(int velocity) {} void left(int velocity) {} void right(int velocity) {} void forward(int velocity) {} void back(int velocity) {} void turboBoostO {} } ///-

Для построения космического корабля можно воспользоваться наследова­нием:

// reusing/SpaceShip java

public class SpaceShip extends S^ceShipControls { private String name.

public SpaceShip(String name) { this.name = name, }

public String toStringO { return name. }_______

public static void main(String[] args) {

SpaceShip protector = new SpaceShipC’NSEA Protector»), protector forward(lOO).

}

} /// ~

Однако космический корабль не может рассматриваться как частный случай своего управляющего модуля — несмотря на то, что ему, к примеру, можно при­казать двигаться вперед (forward()). Точнее сказать, что SpaceShip содержит SpaceShipControls, и в то же время все методы последнего предоставляются клас­сом SpaceShip. Проблема решается при помощи делегирования:

// reusing/SpaceShipDelegation java

public class SpaceShipDelegation { private String name, private SpaceShipControls controls =

new SpaceShipControlsO: public SpaceShipDelegation(String name) {

this name = name. }

// Делегированные методы: public void back(int velocity) { controls.back(velocity);

}

public void do
wn(int velocity) { controls.down(velocity);

}

public void forward(int velocity) { controls forward(velocity).

}

public void leftCint velocity) { controls left(velocity).

}

public void rightOnt velocity) { controls right(velocity);

}

public void turboBoostO {

controls.turboBoostO.

}

public void up(int velocity) { controls.up(velocity):

}

public static void main(String[] args) { SpaceShipDelegation protector =

new SpaceShipDelegationC’NSEA Protector»);                продолжение &

protector.forwarcK 100);

}

} ///:-

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

Хотя делегирование не поддерживается языком Java, его поддержка присут­ствует во многих средах разработки. Например, приведенный пример был авто­матически сгенерирован в JetBrains Idea IDE.

Сочетание композиции и наследования

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

II: reusing/PlaceSetting.java 11 Совмещение композиции и наследования, import static net.mindview.util.Print.*;

class Plate {

‘PlateCint i) {

print(«Конструктор Plate»);

}

}

class DinnerPlate*extends Plate { DinnerPlate(int i) { super(i),

print(«Конструктор DinnerPlate»);

class Utensil {

Utensil(int i) {

print(«Конструктор Utensil»);

}

}

class Spoon extends Utensil { Spoon(int i) {

super(i);

print’CKoHCTpyKTop Spoon»);

class Fork extends Utensil { Fork(int i) {

super(i);

System.out.println(«Конструктор Fork»);

}

class Knife extends Utensil { Knife(int i) {

super(i):

print(«Конструктор Knife»);

class Custom {

Custom(int i) {

print(«Конструктор Custom»);

public class’PIaceSetting extends Custom { private Spoon sp; private Fork frk; private Knife kn; private DinnerPlate pl; public PIaceSetting(int i) { super(i + 1); sp = new Spoon(i + 2); frk = new Fork(i + 3); kn = new Knifed + 4); pl = new DinnerPlated + 5); pri nt(«Конструктор PlaceSetti ng»):

~ 45 ~

Предыдущая страницаСледующая страница

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *