Почему Singelton - плохая практика?

  • Автор темы Духовность™
  • Дата начала

флоппик

promotor fidei
Команда форума
Партнер клуба
Угу, хранятся объекты классов Database_Connection_$drivername.
 

AmdY

Пью пиво
Команда форума
вот-вот. для работой с дб singleton не нужен, нужен реестр или сервис локатор. думаю триумвират сам ответил на свой вопрос и привёл нужный пример.
 

Духовность™

Guest
мне сложно у меня в приложении 1 синлетон - главный аппликейшен.
Все модели наследуют абстрактный класс модели в котором при инитиализации делается
PHP:
$this->setAdapter( Core_Application::getInstance()->getAdapter() );
А если надо объект БД использовать в нескольких разных классах, не связанных абстрактным классом реализующим setAdapter()?

А у тебя если захочешь датабасе сменить - надо переписывать класс или во всем скрипте менять название класса.
а это такая большая проблема? Я рефакторингом порой часами занимаюсь и сменить имя класса в двух-трех местах - самое безобидное с точки зрения моих рефакторингов задача.

займу позицию защитников синглтонов
я не защитник,я просто пытаюсь понять.
 

HraKK

Мудак
Команда форума
grigori написал(а):
Я действительно не понимаю, что такое "нужное", что подменять и когда
Нужен мне адаптер PDO, MySQLi, Oracle или Postgre я его определю в бутстрапе апликейшена.
То есть чтоб сменить базу мне надо сделать иньекцию этого объекта. Тебе - переопределить DB_DEFAULT в сеттинге, то есть изменить параметр фабрики(гадаю).
Чем хорош мой подход? Да чем угодно, я могу в бутстрапе подкинуть не просто адаптер MySQLi а расширенный адаптер реализующий главное интерфейс Core_Db_Adapter_Interface. Ты такого сделать не можешь, не изменяя код.
Дальше я могу налету заменить адаптер, ты нет. Константы, не меняются.

grigori написал(а):
Я предложил DB_DEFAULT потому что не знаю, как ты инициализируешь 2ю базу. Из твоего примера совсем непонятно, как получить 2 объекта для работы с 2мя базами.
Если мне надо 2 базы я сделаю еще фабрику и буду оттуда ее тягать, но в 99% не нужно, лично мне. Когда надо, я делал так - переопределял в моделе констракт на:
PHP:
public function __construct()
	{
		$this->setAdapter( Core_Db::factory( 'MySQLi', 'localhost', 'root', '' )->setDatabase('requests') );
	}
Соответсвенно он начинал работаь с другой базой, но это для единичного случая, если мне надо на протяжении всего использовать 2 базы то просто в код
$this->getAdapter() я бы добавил фабрику по получении нужного мне объекта.

Духовность™ написал(а):
А если надо объект БД использовать в нескольких разных классах, не связанных абстрактным классом реализующим setAdapter()?
Сет адаптер вызывается в бутстрапе апликейшена. Апликейшен ты как раз можешь получить через синглетон(вот тут он очень уместен и как одиночка и как доступ) и запросить у него адаптер.

Духовность™ написал(а):
а это такая большая проблема? Я рефакторингом порой часами занимаюсь и сменить имя класса в двух-трех местах - самое безобидное с точки зрения моих рефакторингов задача.
Да, потому что я делаю фреймоврк, а не готовое решение на 1 раз. Если ты в нем что-то поправишь, как ты будешь обновлять версии? Каждый раз что-то правя?)
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
HraKK, спасибо! Давай обсудим :)

IMHO взаимоисключающие параграфы:
чтоб сменить базу мне надо сделать иньекцию этого объекта. Тебе - переопределить DB_DEFAULT в сеттинге, то есть изменить параметр фабрики(гадаю).
Если мне надо 2 базы я сделаю еще фабрику и буду оттуда ее тягать,
В контексте обсуждения синглтона самое интересное - работа с несколькими базами. У меня такое бывает не так чтоб уж совсем редко.
Тягать из фабрики ты будешь так же передавая параметр.

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

Core_Application::getAdapter() может быть реестром, который возвращает эти ваши расширенные адаптеры.

я могу налету заменить адаптер, ты нет.
Что значит "налету"?

я делал так - переопределял в модели констракт
IMHO определение параметров подключения к базе в классе модели - хуже, чем использование константы в случае
Core_Application::getAdapter(DB_ANOTHER)
Разве нет?


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

HraKK

Мудак
Команда форума
В контексте обсуждения синглтона самое интересное - работа с несколькими базами. У меня такое бывает не так чтоб уж совсем редко.
Тягать из фабрики ты будешь так же передавая параметр.
Вот именно, ты сам себе противоречишь. Одиночка, но в тоже время 2 объекта соединения.
Да я буду тягать с параметром, но не из синглетона который на это не расчитан, хотя и делаются getInstanse( DB_SOME );
Как и все, я работаю не напрямую с драйвером, а через обертку/фасад, который реализует интерфейс.
Смысл не в том через что ты работаешь, а как ты его подменить можешь. Если можешь, отлично.
Что значит "налету"?
Значит что я на протяжении запуска апликейшена могу изменить адаптер через какой я работаю по всему приложению, просто назначив другой драйвер, а ты?
IMHO определение параметров подключения к базе в классе модели - хуже, чем использование константы в случае
Core_Application::getAdapter(DB_ANOTHER)
Разве нет?
Параметры можно тягать из бутстрапа или сеттинга, то я сделал костыль для 1 места, вот и все, не заморачиваясь. Как сделать правильно я уже говорил.
Смысл существования этого единого ядра я пока пытаюсь понять
ПХП не многопоточный(да-да-да), а значит есть только 1 апликейшен работающий в данный момент. Он всегда один(уже напрашивается одиночка), получая его - ты гарантированно попадешь на нужное тебе окружение используя его интерфейс и не плодя лишних не нужных одиночек, регистров и т. д.
 

Активист

Активист
Команда форума
> когда синглтон - не для базы, а для доступа к ядру фреймворка
У меня именно так. Есть API, и есть одиночка, который "склеивает" в едино разные компоненты ядра. Хотя сейчас я могу использовать обсолютно разные подходы, и фабрики, и реестр и пошло пошло пошло, везде плюсы и везде минусы. Одну типичную задачу каждый ИНДВИДУМ решает по своему, это отличает нас от роботов, главное, что бы было понятно другим.

В общем ООП это еще один язык программирования, язык в языке )
 

HraKK

Мудак
Команда форума
Да нет, на самом деле мы все просто не знаем ООП в идеале. Поэтому, мы как 2 мексиканца выучившие английский язык по словарю гадаем чье произношение лучше. Лично мое мнение, тот вариант который предоставляет больше гибкости при этом ограничивая все не нужные(не добавляет сахара) и есть правильный в ООП.

А еще, Grigori у тебя код покрыт тестами? Если нет, попробуй покрой и тогда поймешь многие свои бока :)

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

grigori

( ͡° ͜ʖ ͡°)
Команда форума
Вот именно, ты сам себе противоречишь. Одиночка, но в тоже время 2 объекта соединения.
Я не использую синглтоны вообще. У меня ощущение, что ты не понял мой вопрос.
Да я буду тягать с параметром, но не из синглетона который на это не расчитан, хотя и делаются getInstanse( DB_SOME );
Вопрос был о том, почему ты вызываешь статический метод для получения объекта для того, чтобы получить другой объект, вместо того, чтобы получать нужный объект прямо.

Значит что я на протяжении запуска апликейшена могу изменить адаптер через какой я работаю по всему приложению, просто назначив другой драйвер, а ты?
Ничего не мешает мне заменить текущий объект подключения на другой.
PHP:
class Operator{
    /**
     * Object of the database connection
     *
     * @var gksPDO
     */
    public $DB;
    /**
     * Default database connection object
     *
     * @var gDB
     */
    static private $defaultDB;
//...
}

class DB extends Operator{

    function getUrlList(ModelPaginator $Paginator){
        $sql = 'select SQL_CALC_FOUND_ROWS id,added,use_frame from short_links'.
            $Paginator->generateLimitClause();
        $Redirects = $this->DB->getResultsCollection($sql,'RedirectRecord');
        return $Redirects;
    }
}
Когда создается объект для работы с базой, он выставляет себе дефолтный объект-"адаптер" $this->DB = self::$defaultDB.
Когда мне нужна 2я база, я создаю 2й объект с запросами и передаю в него нужный объект-подключение (или он в конструкторе сам себе его назначает).
Или же, я могу выставить другую дефолтную базу в поле Operator::$defaultDB перед созданием объекта new DB().
Без синглтонов, только 1 статический класс.

ПХП не многопоточный(да-да-да), а значит есть только 1 апликейшен работающий в данный момент. Он всегда один(уже напрашивается одиночка)
Как из определения "всегда один апликейшен" следует вывод "уже напрашивается одиночка"? По мне - совсем не напрашивается.
Ну, один объект, а зачем ему быть синглтоном?

получая его - ты гарантированно попадешь на нужное тебе окружение используя его интерфейс и не плодя лишних не нужных одиночек, регистров и т. д.
Если у тебя всегда "только 1 апликейшен", работающий в один поток, как у него могут быть непредсказуемое или неправильное окружение? Окружение - это ведь тоже объекты, которые ты сам и формируешь, и оно тоже "одно", как и приложение.
Это защита от самого себя что-ли?

у тебя код покрыт тестами?
Писал, покрывал, нормально. Нет проблем заменить self::$defaultDB на мок или еще как-то.
В моем коде есть один маленький статический класс Operator, других зависимостей от имен классов нет.
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
сформулирую правило: если синглтон используется для доступа к объекту, без которого можно обойтись - такой синглтон мешать не будет
 

Активист

Активист
Команда форума
У меня есть класс одиночка, называется core (имея ввиду API), в нем есть инициализированные объекты "общего пользования" , так называемые интерфейсы, которые на всем протяжении работы не создадут ни одного экземпляра объекта по архитектуре, например - класс логер/дебагер (на все приложение один), есть класс phpmailer, "основной объект smarty", он имеет общие свойства - типа "текущий язык, текущая локаль".

Мне нравится, мне удобно.
 

fixxxer

К.О.
Партнер клуба
Станет неудобно рано или поздно :)

Например, я хочу для cli другой дебаггер.
 

AmdY

Пью пиво
Команда форума
fixxxer
ну не начинай.... тем более дебагер сам может решить какой вывод у него должен быть в режиме cli или ajax
 

Активист

Активист
Команда форума
Разница в задачах. Кто пишет фреймворки (все же понимают что такое фреймворк), те не будут использовать синглтон, ибо это фреймворк, а те, кто пишит приложение, особенно модульное - тем одиночка очень помогает.
Одиночка - это некоторое ядро системы, сравним с огромным "бутстартом" (или как его там), например, при наличии ЧПУ (например, в одиночке лежит текущий распарсенный урл, не парсить же его каждый раз), те приложения, которые имеют фрнонэнд (наличие ЧПУ априори говорит о наличии фронтэнда).

Я из любой точки приложения хочу получить информацию по принципу "Делай и не спрашивая", например core::getInstance()->rewriteEnenginear->requestFileName;

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

Да, можно убрать api как архитектуру, воткнуть синглтон на класс дебагера, поскольку по архитектуре ПО дебагер должен быть один (как, напирмер ksyslogd), и передать эту говолную боль на все другие классы, вместо одного синглтона - ядра.

Или например - одиночка - это master proccess - для апаче.

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

cDLEON

Онанист РНРСlub
Или например - одиночка - это master proccess - для апаче.
Вот задача одиночки - быть уверенным , что он один, на всем протяжении работы приложения, и не боятся, что появится некто еще, у которого такие же как у него задачи, и не начнеттворить анархию в системе и не объявит войну.
Опять же - если этот объект будет "настраиваемым", то война всё равно будет....
 
Сверху