diff lib/IMPL/Object/Factory.pm @ 407:c6e90e02dd17 ref20150831

renamed Lib->lib
author cin
date Fri, 04 Sep 2015 19:40:23 +0300
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/IMPL/Object/Factory.pm	Fri Sep 04 19:40:23 2015 +0300
@@ -0,0 +1,209 @@
+package IMPL::Object::Factory;
+use strict;
+
+use IMPL::Const qw(:prop);
+
+use IMPL::declare {
+    base => [
+        'IMPL::Object' => undef,
+        'IMPL::Object::Serializable' => undef
+    ],
+    props => [
+        factory => PROP_RO,
+        parameters => PROP_RO,
+        method => PROP_RO
+    ]
+};
+
+# custom factory, overrides default
+sub new {
+    my $self = shift;
+    
+    return ref $self ? $self->CreateObject(@_) : $self->IMPL::Object::new(@_);
+}
+
+sub CTOR {
+    my ($this,$factory,$parameters,$method) = @_;
+    
+    $this->factory($factory) or die new IMPL::InvalidArgumentException("The argument 'factory' is mandatory");
+    $this->parameters($parameters) if $parameters;
+    $this->method($method) if $method;
+}
+
+# override default restore method
+sub restore {
+    my ($class,$data,$surrogate) = @_;
+    
+    my %args = @$data;
+    
+    if ($surrogate) {
+        $surrogate->self::CTOR($args{factory},$args{parameters},$args{method});
+        return $surrogate;
+    } else {
+        return $class->new($args{factory},$args{parameters},$args{method});
+    }
+}
+
+sub CreateObject {
+    my $this = shift;
+    
+    if (my $method = $this->method) {
+        $this->factory->$method($this->MergeParameters(@_));    
+    } else {
+        $this->factory->new($this->MergeParameters(@_));        
+    }
+}
+
+sub MergeParameters {
+    my $this = shift;
+    
+    $this->parameters ? (_as_list($this->parameters),@_) : @_;
+}
+
+
+sub _as_list {
+    ref $_[0] ?
+        (ref $_[0] eq 'HASH' ?
+            %{$_[0]}
+            :
+            (ref $_[0] eq 'ARRAY'?
+                @{$_[0]}
+                :
+                $_[0]
+            )
+        )
+        :
+        ($_[0]);
+}
+
+
+1;
+
+__END__
+
+=pod
+
+=head1 SYNOPSIS
+
+=begin code
+
+my $factory = new IMPL::Object::Factory(
+    'MyApp::User',
+    {
+        isAdmin => 1
+    }
+);
+
+my $class = 'MyApp::User';
+
+my $user;
+
+$user = $class->new(name => 'nobody'); # will create object MyApp::User
+                                       # and pass parameters (name=>'nobody')
+                                            
+$user = $factory->new(name => 'root'); # will create object MyApp::User
+                                       # and pass paremeters (isAdmin => 1, name => 'root')
+
+=end code
+
+Или сериализованная форма в XML.
+
+=begin code xml
+
+<factory type="IMPL::Object::Factory">
+    <factory>MyApp::User</factory>,
+    <parameters type="HASH">
+        <isAdmin>1</isAdmin>
+    </parameters>
+</factory>
+
+=end code xml
+
+=head1 DESCRIPTION
+
+C<[Serializable]>
+
+Класс, реализующий фабрику классов.
+
+Фабрика классов это любой объект, который имеет метод C< new > вызов которого приводит к созданию нового
+объекта. Например каждый класс сам явялется фабрикой, поскольку, если у него вызвать метод
+C< new >, то будет создан объект. Полученные объекты, в силу механизмов языка Perl, также
+являются фабриками, притом такимиже, что и класс.
+
+Данный класс меняет поведение метода C< new > в зависимости от контекста вызова: статического
+метода или метода объекта. При вызове метода C< new > у класса происходит создание объекта
+фабрики с определенными параметрами. Далее объект-фабрика может быть использована для создания
+объектов уже на основе параметров фабрики.
+
+=head1 MEMBERS
+
+=over
+
+=item C< CTOR($factory,$parameters,$method) >
+
+Создает новый экземпляр фабрики.
+
+=over
+
+=item C<$factory>
+
+Либо имя класса, либо другая фабрика.
+
+=item C<$parameters>
+
+Ссылка на параметры для создания объектов, может быть ссылкой на хеш, массив и т.д.
+
+Если является ссылкой на хеш, то при создании объектов данной фабрикой этот хеш
+будет развернут в список и передан параметрами методу C<new>.
+
+Если является ссылкой на массив, то при создании объектов данной фабрикой этот массив
+будет передан в списк и передан параметрами методу C<new>.
+
+Если является любым другим объектом или скаляром, то будет передан параметром методу
+C<new> как есть.
+
+=item C<$method>
+
+Имя метода (или ссылка на процедуру), который будет вызван у C<$factory> при создании
+текущей фабрикой нового объекта.
+
+=back
+
+=item C< [get] factory >
+
+Свойство, содержащее фабрику для создание новых объектов текущей фабрикой. Чаще всего оно содержит
+имя класса.
+
+=item C< [get] parameters >
+
+Свойство, содержит ссылку на параметры для создания объектов, при создании объекта эти параметры будут
+развернуты в список и переданы оператору C< new > фабрике из свойства C< factory >, за ними будут
+следовать параметры непосредственно текущей фабрики.
+
+=item C<MergeParameters(@params)>
+
+Метод смешивающий фиксированные параметры с параметрами переданными методу C<new(@params)>. По умолчанию
+добавляет пареметры фабрики в конец к фиксированным параметрам. Для изменения этого поведения требуется
+переопределить данный метод. Также этот метод можно переопределить для передачи параметров, значения
+которых вычисляются.
+
+=item C<new(@params)>
+
+Создает новый объект, используя свйство C<factory> как фабрику и передавая туда параметры
+из свойства C<parameters> и списка C<@params>. Ниже приведен упрощенный пример, как это происходит.
+
+=begin code
+
+sub new {
+    my ($this,@params) = @_;
+    
+    my $method = $this->method || 'new';
+    
+    return $this->factory->$method(_as_list($this->parameters), @params);
+}
+
+=end code
+
+=back
+
+=cut