Форум на Kuban.ru (http://forums.kuban.ru/)
-   Веб-дизайн и программирование (http://forums.kuban.ru/f1030/)
-   -   Паттерны проектирования (http://forums.kuban.ru/f1030/patterny_proektirovaniya-3052260.html)

КоРоЛь 12.09.2012 09:21

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

КоРоЛь 12.09.2012 10:19

Паттерн Абстрактная фабрика

[url]http://ru.wikipedia.org/wiki/%D0%90%D0%B1%D1%81%D1%82%D1%80%D0%B0%D0%BA%D1%82%D0%BD%D0%B0%D1%8F_%D1%84%D0%B0%D0%B1%D1%80%D0%B8%D0%BA%D0%B0_(%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F[/url])

Начнем обсуждение с рассмотрения простого примера.
Допустим есть некий веб-система документооборота и в нем мы реализуем 2 разных интерфейса для управления различными типами пользователей (физ и юр лица, коротко юрик и физик).
У каждого типа пользователя есть общие и уникальные атрибуты (сначала идут общие).
Физик: ID, телефон, email,ФИО, номер паспорта, ОГРНИП
Юрик: ID, телефон, email, название организации, ОГРН

Допустим этот набор полей хранится в базе и он всегда нам доступен через операцию
$db::get_fields($type)
где $type = 0 юрик, 1 физик.
В каждом веб-интерфейсе должен быть фильтр и таблица. Операции фильтрации, сортировки, создания, редактирования, удаления полностью идентичны для обоих типов.
Вот здесь и применяется шаблон абстрактная фабрика. Схематично я изобразил его так:

[img]http://web112.biz/files/abstract_factory.jpg[/img]

А теперь код:

<?php

interface UserFactory
**

public function get_filter_data();

public function get_table_data();
**

class LegalFactory implements UserFactory
**

public function get_filter_data()
**
return (new LegalFilter());
**

public function get_table_data()
**
return (new LegalTable());
**

**

class NaturalFactory implements UserFactory
**

public function get_filter_data()
**
return (new NaturalFilter());
**

public function get_table_data()
**
return (new NaturalTable());
**

**

abstract class UserFilter
**

// возвращает данные, которые уже дльше передаем в представление filter.tpl
public function get_data();
**

class LegalFilter extends UserFilter
**

**

class NaturalFilter extends UserFilter
**

**

abstract class UserTable
**

// возвращает данные, которые уже дльше передаем в представление table.tpl
public function get_data();
**

class LegalTable extends UserTable
**

**

class NaturalTable extends UserTable
**

**

class Client
**

private $factory;

public function legal_builder()
**
$this->factory = new LegalFactory();
**

public function natural_builder()
**
$this->factory = new NaturalFactory();
**

public function out()
**
echo $this->factory->get_filter_data();

echo $this->factory->get_table_data();
**

**

$client = new Client();
$client->legal_builder();
$client->out();

КоРоЛь 12.09.2012 10:21

В коде забыл дописать использование $db::get_fields($type)

но я думаю, вы и сами без проблем допишите

КоРоЛь 12.09.2012 10:27

Чего мы добились? Мы инкапсулировали наиболее часто изменяемую логику: изменение типов лиц (например, добавление 3-го ИП), изменение полей для каждого тип.
При этом код клиента остается неизменным очень долгое время.

У этого паттерна есть родственный - фабричный метод.
Он отличается тем, что за создание объекта отвечает абстрактный родительский метод, который переопределяется в подклассах.

КоРоЛь 12.09.2012 10:46

Код плохо вставился. Вместо ** должно быть **

КоРоЛь 12.09.2012 10:47

т.е. открывающая фигурная скобка

Гламурный Креведко 12.09.2012 13:18

Ничего, мы привыкли к людям, которые кроме классов и MVC ничего не знают.

CPU 12.09.2012 14:40

В далеком 2005-м году видел проект, нашпигованный паттернами по самое нехочу. Работал крайне медленно.
Поддерживать его было вообще мрак.

Правда, это был еще .NET 1.1, но с тех пор отношение к паттернам у меня осторожное.

Искусственный Интеллект1 12.09.2012 22:35

Это что такое было? :)

КоРоЛь 14.09.2012 10:28

7-CPU >
Чтобы паттерны не превратились в плохое решение, не нужно слепо следовать им. Еще есть рефакторинг и код ревью.

XentaAbsenta 14.09.2012 10:53

0-КоРоЛь > Весьма активно использую.
Наиболее полезен "Шаблонный метод", на втором месте "Фасад", тройку лидеров замыкает Синглтон.

КоРоЛь 14.09.2012 11:11

Паттерн Стратегия.
[url]http://ru.wikipedia.org/wiki/%D0%A1%D1%82%D1%80%D0%B0%D1%82%D0%B5%D0%B3%D0%B8%D1%8F_(%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD_%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F[/url])

Разрабатываем веб-систему, состоящую из набора интерфейсов. Каждый интерфейс состоит из фильтра и таблицы.
Поля в фильтре задаются программно. Т.е. какие поля будут выводиться в конкретном фильтре — мы заранее не знаем.
У нас есть следующие типы полей:
текст (имеется ввиду текстовое поле ввода)
селект
чекбокс (имеется ввиду один квадратик)
радиокнопки (имеется ввиду несколько кружочков)

Чтобы вывести поле, нам нужно знать данные:
name — имя латинскими буквами, используется для идентификации поля в форме (атрибут name у инпута)
caption — русскоязычное название
value — текущее значение
value_list — список предопределенных значений поля. Эти данные нужны только для селекта и радиокнопки

Простой метод решения — сделать общего предка Field и все операции запихнуть в него - изображен на следующей схеме:
[img]http://web112.biz/files/patterns/bad_primer_for_strategy.jpg[/img]

Получается, что у текста и чекбокса тоже может быть список значений — но это неправильно.

Правильное решение. Выделить типы поведения полей. В данном случае у нас может быть 2 поведения: TextBehavior и SelectBehavior.
При этом класс field будет делегировать процедуру получения значений другому объекту.
Как это будет выглядеть:

[img]http://web112.biz/files/patterns/strategy.jpg[/img]

Получается, что при создании поля клиент должен установить поведение этому полю.
В классе SelectBehavior мы расписываем процедуру получения списка значений.
В классе TextBehavior мы оставляем пустую реализацию метода get_value_list

Теперь сам код:

<?php

// общий класс
class Field
**

protected $name;
protected $caption;
protected $value;
protected $behavior;

public function __construct(Behavior $behavior)
**
$this->set_behavior($behavior);
**

public function set_behavior($behavior)
**
$this->behavior = $behavior;
**

public function get_value_list()
**
return $this->behavior->get_value_list();
**

**

class Text extends Field
**
// тут пишем специфичный код для поля
**

class Select extends Field
**
// тут пишем специфичный код для поля
**

class Checkbox extends Field
**
// тут пишем специфичный код для поля
**

class Radio extends Field
**
// тут пишем специфичный код для поля
**

interface Behavior
**

public function get_value_list();
**

class SelectBehavior
**

public function get_value_list()
**
// берем список значений из базы
$value_list = array('a', 'b', 'c');
return $value_list;
**

**

class TextBehavior
**

public function get_value_list()
**

**

**

/**
* Код клиента теперь можно писать таким образом
*/
$text_behavior = new TextBehavior();
$select_behavior = new SelectBehavior();


$text = new Text($text_behavior);
$checkbox = new Checkbox($text_behavior);

$select = new Select($select_behavior);
$radio = new Radio($select_behavior);

/**
* дальше идет вывод полей например так
echo $text->display();
* или передача шаблонизатору
*/

КоРоЛь 14.09.2012 11:17

Стратегия - уникальный паттерн, который можно использовать где угодно. Задача этого паттерна - инкапсулировать сложные алгоритмы.

Еще стратегия является частью реализации паттерна MVC.

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

Так же, этот паттерн показывает наглядные преимущества композиции перед наследованием.

КоРоЛь 14.09.2012 11:20

10-Философ >
Синглтон в вебе должен быть на первом месте )

В каких контекстах (предметных областях) обычно используете фасад?

КоРоЛь 14.09.2012 11:25

Еще стратегия незаменимый паттерн в программировании игрушек.
Когда разных персонажей могут быть разные виды оружия.

XentaAbsenta 14.09.2012 11:30

11-КоРоЛь > прекращай такие простыни постить.

13-КоРоЛь > фасад - по прямому назначению (например, веб-сервис объединяет тучу мелких компонентов в одну подсистему)

>Синглтон в вебе должен быть на первом месте )
до самого веба я так и не добрался (пока не добрался)

КоРоЛь 14.09.2012 11:35

Из разговора двух программистов, использующих паттерны:

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

КоРоЛь 14.09.2012 11:40

[quote=Философ;26833995] 11-КоРоЛь &gt; прекращай такие простыни постить. [/quote]
Что не так и почему на ты?

XentaAbsenta 14.09.2012 11:43

16-КоРоЛь > Если коту делать нечего, он яйца лижет.
Программисту делать нечего - ищет куда-бы ещё прилепить паттерн. Если ничего хорошего придумать не удалось, по умолчанию используется Визитор - поубивал бы!

КоРоЛь 14.09.2012 12:11

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

Если грамотно использовать визитор, это очень мощный паттерн.
Через него можно создавать/удалять/сортировать/перемещать/находить ветки/листья дерева.

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

Про визитор есть любопытная статья на хабре (про upcast visitor можно не читать)
[url]http://habrahabr.ru/post/133183/[/url]

XentaAbsenta 14.09.2012 18:07

19-КоРоЛь > отвратительная статья!

Аффтар с самого начала демонстрирует незнание методик OOD: без выделения семантики объектов сразу начинает с диаграммы классов.
Есстественно, что с самых первых строк нарушение SRP...
в мусор!

КоРоЛь 19.09.2012 08:39

слабовато как-то..


Текущее время: 20:38. Часовой пояс GMT +3.