Паттерн Фабрика реализацияя через __callStatic

Professor

Новичок
Добрый день.
Реализовал этот паттерн так:
PHP:
    public function __callStatic($name, $arguments) {

        try {
            $className = "Core_Auth_Adapter" . $name;
            return new $className($arguments);
        } catch (Exception $e) {
            return FALSE;
        }
    }
Обращение:
PHP:
Adapter::Login(array(/*тут какие то данные*/));
Adapter::Vkontakte(array(/*тут какие то данные*/));
Adapter::Facebook(array(/*тут какие то данные*/));
Таким образом реализовал адаптеры разных методов подключения.

Вопросы:
Это нормальный подход?
Это вообще Фабрика, или я какую то ересь несу?
Если этот код плох, то как сделать лучше?
 

Professor

Новичок
Наверно в моей реализации смысл паттерна вообще теряется.
В общем жду ваших мыслей. =)
 

Adelf

Administrator
Команда форума
Вообще, я поддерживаю такие вещи в своем коде. Когда вместо магических строк типа Adapter::instance("Facebook") используют методы, которые к тому же можно задокументить и получить для них автокомплит в IDE.
Но в данном, конкретном случае, коду вредно знать и вызывать напрямую нужные адаптеры. Это должно быть стандартно.. Просто подозреваю что код у тебя будет такой:
PHP:
switch($adapter):
case 'login' ... Adapter::Login(...);
case 'facebook' ... Adapter::Facebook(...);
Что вредно и некрасиво.

З.Ы. Ты назвал свой проект - Муть? :)
 

ksnk

прохожий
Professor Фабрика - это то, что выпекает новые объекты по заданным именам. А вот фабрика ли тут нужна? Нужно ли , чтобы существовало в приложении много копий адаптеров Фейсбука, или разумнее хранить единственный адаптер и выдавать ссылку на него тому, кто нуждается?

К тому же, удобнее иметь конфигурируеvый "словарь" адаптеров -
PHP:
...
'adapter.classes'=>array(
  'Facebook'=>'myNewFacebookAdapterVariant',
// 'Facebook'=>'Core_Auth_Adapter_facebook',
..
),
просто для удобства смены классов.
 

Professor

Новичок
З.Ы. Ты назвал свой проект - Муть? :)
Ага, так получилось :)
Было My Table Booking, домен был свободен, вот и взял. Когда понял как выглядит, уже решил оставить. Пусть будет фишка =)

Professor Фабрика - это то, что выпекает новые объекты по заданным именам.
Спасибо. Осознал что перемудрил.
Тут нужна именно фабрика. Должен быть один адаптер авторизации, и в зависимости от типа авторизации должен возвращать тот объект который отвечает за авторизацию по данному типу.
Я почему то это определение типа авторизации вынес наружу из фабрики. А как раз она должна .то все разруливать.

Если я сделаю так
PHP:
Adapter::Auth(array(/*тут какие то данные*/));
PHP:
static function Auth($params) {
/* тут куча ифоф, которые будут отвечать за то, какой объект вернуть */
 }
Вроде так правильно.
 

Тугай

Новичок
ksnk - то что ты расписал - это Service Locator.

Фабрикой будет класс AdapterFactory c методами getAdapterLogin(), getAdapterFacebook() и getAdapterVkontakte().
Фабрика может тоже печь только один раз. Через кеширование в приватном свойстве.

function __callStatic - не класс, фабрикой быть не может по определению.

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

Professor

Новичок
function __callStatic - не класс, фабрикой быть не может по определению
Какая разница? он Отлавливает вызов несуществующего метода и возвращает объект. Чем отличается от перечисления функций getAdapterLogin(), getAdapterFacebook() и getAdapterVkontakte()?

Описание Паттерна Фабрика: http://cpp-reference.ru/patterns/creational-patterns/factory-method/

Паттерн Factory может быть полезным в решении следующих задач:
Система должна оставаться расширяемой путем добавления объектов новых типов. Непосредственное использование выражения new является нежелательным, так как в этом случае код создания объектов с указанием конкретных типов может получиться разбросанным по всему приложению. Тогда такие операции как добавление в систему объектов новых типов или замена объектов одного типа на другой будут затруднительными (подробнее в разделе Порождающие паттерны). Паттерн Factory Method позволяет системе оставаться независимой как от самого процесса порождения объектов, так и от их типов.
Заранее известно, когда нужно создавать объект, но неизвестен его тип.
У меня как раз такая задача. Типы авторизаций неизвестны, и они могут добавляться и меняться. Не хочется потом по всему коду лазить.


Или я чего то не догоняю?
 

Тугай

Новичок
Adelf
Родственные объекты, которые печет фабрика, могут создаваться как сингилтоны.
 

ksnk

прохожий
Может быть не нужно спорить о религии, а вернуться к коду? ;) Сначала был код. Обрабатывался комплект исходных кодов и из них выковыривались общие кусочки идей. Какая разница как называется программная сущьность, если она делает то, что надо? Тем более, код на cpp вряд ли может однозначно свидетельствовать о конфессиальной принадлежности кода на php.
 

Тугай

Новичок
Professor
Суть шаблона - "Определить интерфейс для создания объекта, но предоставить возможность классам которые реализуют этот интерфейс решать какой класс инстанциировать."
Для фабрики классов Core_Auth_Adapter, пишите вызовы Adapter::<Имя>.
Это скорей соглашение, а не интрефейс и для другой фабрики, что писать и как ее реализовать через __callstaic ?
 

Тугай

Новичок
ksnk
Просто чтоб понимать друг друга. Сам разбираюсь, поэтому могу ошибаться.

Registry - просто хранит, отдает объекты по ключу, созданием не занимается.
Factory - создает объекты обычно с общим базовым классом, то что можно создать зафиксировано в интерфейсе. Эта фабрика производит печеньки, пирожки и булочки.
(печеньки, пирожки и булочки) - это интерфейс, автомобиля ждать от нее не надо.
Abstrac Factory - надстройка над несколькими фабриками с общим интерфейсом. Есть канадская и французская фабрики, обе производят печеньки, пирожки и булочки, выбираем фабрику и сотрудничаем.
Service Locator - умный Registry, который не только хранит, но и создает объекты по ключу. Может быть очень умным и ключ вплоть до полноценного языка запросов.
 

ksnk

прохожий
Тугай В моем практическом опыте, ни разу не встречались чистые registry и Factory в таких определениях. Они всегда обрастали лишним умом и становились Service Locator. Так что 3 термина, вроде как и заняты, а использовать их не получается :)
Professor
Можно пойти проектировать "сверху". Обычно, юзер указывает явно, каким образом он желал бы идентифицироваться на сайте. То есть в скрипт прилетает какая-то текстовая строка, означающая тип авторизации. Почему бы вместо Adapter::Auth не использовать что-то вроде $adapter=call_user_function(array('Adapter',$login_method)) ?
Тогда "интерфейсная" часть адаптера будет примерно такого же вида как сейчас.
 

Professor

Новичок
Я сделал вот так:
PHP:
static public function auth($data)
    {

        if (isset($data['login'])) {
            $type = "login";
        } elseif (isset($data['vk_id'])) { 
            $type = "vkontakte";
        } elseif (isset($data['fb_id'])) {
            $type = "facebook";
        }else{
           $type = "login";
        }

        if (isset(self::$listAdapter[$type])) {//в $listAdapter хранятся ссылки на все возможные типы авторизации
            try {
                $className = self::$listAdapter[$type];
                return new $className($data);
            } catch (Exception $e) {
                return FALSE;
            }
        }
        
        return FALSE;
    }
То есть, от наличия в массиве $data определенных данных, выбираем метод авторизации.
 

hell0w0rd

Продвинутый новичок
Professor
жесть какая:)
На вход принимайте массив вида
PHP:
['type' => Auth::FACEBOOK, 'id' => 100500]
И будет вам счастье
 

Professor

Новичок
hell0w0rd, а как мне определить что мне нужно передать Auth::FACEBOOK, или Auth::VKONTAKTE ?
Точно так, же как и я сделал, только логику определения вы предлагаете вынести.
 

hell0w0rd

Продвинутый новичок
hell0w0rd, а как мне определить что мне нужно передать Auth::FACEBOOK, или Auth::VKONTAKTE ?
Точно так, же как и я сделал, только логику определения вы предлагаете вынести.
я предлагаю не придумывать мудреные параметры, в вашем случае кстати можно вообще убрать массив
 

Тугай

Новичок
Professor
Все супер, кроме }else{$type = "login";}, должно быть скорее }else{$type = "unknown";}

Все же убеждаюсь, что фреймворки нужно изучать, и не изобретать велосипеды :) На ZF2, код мог бы выглядеть примерно так:
PHP:
public function auth($data)
{
    $sm = $this->getServiceLocator();
    if (isset($data['login'])) {
        return $sm->get('loginLogin')->auth($data['login'], $data['passwd']);
    } elseif (isset($data['vk_id'])) { 
        return $sm->get('loginVKontakte')->auth($data['vk_id']);
    } elseif(isset($data['fb_id'])) {
        return $sm->get('loginFacebook')->auth($data['fb_id']);
    } else {
        return false;
    }
}
 

keltanas

marty cats
Фабрикой может служить любый метод/функция. Только надо сразу задаться вопросом: "нахрена оно надо?"
А если почитать книжки, где про фабрики написано, то увидим, что это надо для ухода от статических зависимостей. Чтобы программировать на основе интерфейсов а не реализаций.
А используя фабрику через статический метод, ты подменяешь одну статическую зависимость на другую. При чем в добавок явно уходишь от интерфейсов неизвестно куда.
Тогда снова возникает вопрос: "зачем ты делал эту фабрику?" Чем это отличается от:
PHP:
new Core_Auth_AdapterLogin();
new Core_Auth_AdapterVkontakte();
new Core_Auth_AdapterFacebook();
Если этот код плох, то как сделать лучше?
А что ты хочешь сделать лучше? Сделать фабрику - это и есть твоя цель? Или это средство, чтобы сделать лучше что-то другое?
 

Professor

Новичок
keltanas, спасибо.
Но мы уже выяснили что я перемудрил.

Задача такая сделать одну точку входа для авторизации(1 класс), которая разные способы авторизации будет обрабатывать по разному. Для каждого способа свой класс.

И чуть выше, я написал как это реализовал. Задача решена, но вот на сколько правильно мне не совсем понятно.
 
Сверху