пятница, 24 апреля 2009 г.

Singlton and Monostate


Singlton
Позволяет создавать один экземпляр класса, и использовать его на протяжении всей жизни процесса.

Обычная мотивация применения паттерна* :
  1. Необходимость - иметь один объект выполняющий однотипные действия (например Logger, Config)
  2. Требование - должен быть один обьект, обычно контролирующий/обрабатывающий запросы (например Module)

Monostate
Дает такое же поведение что и синглтон, но не требует единичного экземпляра.
"Не один экземпляр, а одно состояние множества экземпляров".


Рассмотрим следующую ситуацию.
На сайт зашел пользователь. Есть два варианта 
  1. он авторизован 
  2. он не авторизован
Нам от него необходимо знать откуда он (регион). В первом случае мы можем посмотреть это в базе (т.к. всю возможную информацию о пользователе мы сохраняем), во втором случае у нас есть только ip.  Далее предположим, что мы можем запросить один из наших проектов опредилить регион пользователя по ip, партнерский проект отдает нам ID региона. У нас есть таблица соответствия ID_региона_партнера => ID_региона_в_нашей_базе. 
Зачем нам все это? У нас имеются объявления, которые необходимо показывать пользователям. Обьявления имеют свой гео-таргетинг. Таким образом, для того что бы показать обьявление, нам необходимо знать из какого региона пользователь.
Можно спросить, "Что стоит сделать небольшой запрос к БД?", что бы получить ID региона в нашей базе, по имеющемуся ID партнера. Да, проблем нет, данныйй запрос выполнится максимально быстро, стоимость этого запроса минимальна, поиск идет по уникальному индексу. Но есть одно "но", на проекте количество запросов к основному бэкенду 10-20 в секунду. И что, получается будем дергать базу 10 раз в секунду? Конечно же нет. Мы все это сдампим один раз и потом будем отдавать из памяти.

Другой пример. При использовании mod_perl, при инициализации сессии, мы получаем ссылку на объект Apache::Request. Мы можем все время пробрасывать ссылку на этот объект в вызываемые методы, а можем сохранить ее и использовать сохраненную ссылку.


Покажем как это будет выглядеть:
  • Singlton

package Region;
use strict;
use warnings;

my $_self = undef;

sub new
{
my $class = shift;

if ( ref $_self ) {
return $_self;
}

$_self = bless {}, $class;
return $_self->_init();
}

#инициализация хеша регионов
sub _init
{
my $self = shift;

$self = new($self) if !ref $self;

#инициируем хеш регионов
#обращаемся к базе за списком (либо как-нибудь еще)
my $st = $dbh->prepare("
begin
:cr := region.getRegions;
end;
");

$st->bind_param_inout(':cr', \$cr, 0, {ora_type => ORA_RSET});
$st->execute;

$st = $cr->fetchall_arrayref({}); $cr->finish; $cr = $st;

for (@$cr) {
$self->{$_->{PID}} = $_->{OID};
}

return $self;
}

#получение региона
sub get
{
my $self = shift;
my $id = shift;

$self = new($self) if !ref $self;

return $self->{$id};
}
1;


  • Monostate
package ApacheRequest;
use strict;
use warnings;

my $AR= undef;

sub new
{
        my $class = shift;

        my $self = bless [], $class;
        return $self;
}

sub set
{
        my $self = shift;
        my $requestHandle = shift;

        $AR = $requestHandle;
}

sub get
{
        my $self = shift;

        return $AR;
}

1;

Производительность обоих объектов соизмеримо равна. Так что, если вы реши использовать у себя в проекте один из этих паттрнов, то можете выбирать что больше нравится.
Я пользуюсь правилом
  • singlton - если необходимо кеширование
  • monostate - если уж очень сильно нужны глобальные объекты

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

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

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

2 комментария:

  1. Я слчшал такое мнение, что Singleton, как и Monostate нарушают принципы ООП. Все эти глобальные переменные и прочее - это обычно в функциональном программировании применяется..

    ОтветитьУдалить
  2. Повышаются взаимосвязи. Особенно явно видна неэффективность при написании тестов.

    Проявляется в том, что предварительная инициализация занимает больше времени и обьема, чем сам тест. Хотя много можно вынести в setUp

    ОтветитьУдалить