Построение одноэлементного класса


Здесь возникает этический вопрос.

Я планирую использовать в своем новом проекте несколько классов менеджеров, которые будут выполнять различные задачи по всему проекту. Эти классы являются одиночными, но требуют построения на основе параметров.

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

Вариант А

Легко просто передать эти параметры методу getInstance, имея значение по умолчанию null. При самом первом вызове будут использоваться параметры, и любые дополнительные вызовы полностью игнорируют их.

Хотя это работает, делать это кажется довольно нелогичным по следующим причинам:

  • Это делает документацию неясной. getInstance' первый параметр должен иметь тип Collection, но может быть null... что здесь происходит? Вы можете утверждать, что написание строки об этом в описании прояснит это, но я бы предпочел, чтобы разъяснение было ненужный.

  • Кажется неправильным передавать getInstance любые параметры построения. Это связано с тем, что имя метода не содержит явного намека на построение, что делает неясным, что это произойдет.

Вариант В

Я думаю о методе setup. Этот метод принимает все параметры, вызывает конструктор класса и изменяет внутреннее состояние класса на initialized.

При вызове метода getInstance до setup, он выбросит NotInitializedException. После вызова программы установки любые дополнительные вызовы setup приведут к PreviouslyInitializedException.

После вызова setup становится доступным getInstance.

Лично мне этот вариант больше нравится. Но это кажется чрезмерным.

Какой вариант вы предпочитаете? И почему?

Author: jrs, 2011-04-30

5 answers

Я бы, вероятно, попытался отказаться от одноэлементного подхода и передать классы менеджеров всему, что в них нуждается.

$manager = new Manager( $collection, $var, $var2 );

$other_class = New OtherClass( $manager );
//or
$other_class = New OtherClass;
$other_class->manager = $manager;
//or
$other_class = New OtherClass;
$other_class->setManager( $manager );
 2
Author: Galen, 2011-04-29 20:20:33

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

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

class NeedsManager
{
    protected $manager;

    public function __construct(Manager $manager)
    {
        $this->manager = $manager;
    }
}

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

 1
Author: rickchristie, 2017-05-23 11:58:22

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

Если это не подходит, возможно, вы неправильно используете одноэлементный шаблон.

 0
Author: Joe Zitzelberger, 2011-04-29 20:15:21

Вы рассматриваете возможность использования шаблона заводского проектирования. Фабрики - это объекты, которые действуют как причудливые конструкторы для других объектов. В вашем случае вы перенесете настройку и получите установку на завод. Статья в вики довольно хороша - http://en.wikipedia.org/wiki/Factory_method_pattern

class SingletonFoo {
  //properties, etc
    static $singleton = NULL;
    private function __constructor(){}
    static function getInstance(){
        if(NULL === self::$singleton) {
            self::$singleton = new SingletonFoo();
        }
        return self::$singleton;
    }
}

class FooFactory {
    static $SingletonFoo = null;
    static function setup($args){
        if( !(NULL === self::$SingletonFoo)){
            throw new AlreadyInstantiatedException();
        }
        self::$SingletonFoo = SingletonFoo::getInstance();
        //Do stuff with $args to build SingletonFoo
        return self::$SingletonFoo;
    }

    static function getInstance(){
        if(NULL === self::$SingletonFoo) {
            throw new NotInstantiatedException();
        }
        return self::$SingletonFoo;
    }
}
 -2
Author: David Souther, 2011-04-29 20:43:08

Не используйте Синглтон, используйте Диспетчер ресурсов (или Контейнер службы, или контейнер DI):

class ResourceManager
{
    protected static $resource;

    public static function setResource($resource)
    {
        if (!empty(self::$resource)) //resource should not be overwritten
        {
            if ($resource!=self::$resource) return false;
            else return true;
        }

        self::$resource = $resource;
        return true;
    }

    public static function getResource()
    {
        return self::$resource;
    }
}

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

Готовый к использованию вариант: http://symfony.com/doc/current/book/service_container.html (Мне не нравится перемещать логику из код для конфигураций, но в автономном модуле это выглядит приемлемо).

 -2
Author: OZ_, 2011-05-10 09:37:48