Расширить абстрактный класс синглтона - PullRequest
20 голосов
/ 30 ноября 2009

Если у вас был фабричный класс, который создает какие-то новые объекты, и этот класс factroy является одноэлементным, например:

class Database_Factory extends Base_Factory {
    private static $factory;
    private $objects = array();

    public function __get($profile) {
        // check for object and return it if it's created before
    }

    public static function getInstance(){
        if (!self::$factory)
            self::$factory = new self();
        return self::$factory;
    }
}

Один и тот же код повторяется в любое время, когда некоторый объект нуждается в собственной фабрике. Поэтому я решил сделать этот фабричный класс абстрактным и реализовать только конкретные подпрограммы для каждой фабрики. Но PHP не позволяет создавать экземпляры абстрактного класса.

abstract class Base_Factory {
    public static function getInstance(){
        if (!self::$factory)
            self::$factory = new self();
        return self::$factory;
    }
}

Неустранимая ошибка: невозможно создать экземпляр абстрактного класса Base_Factory

Что бы вы сделали?

Ответы [ 6 ]

36 голосов
/ 30 ноября 2009

В методах PHP self всегда относится к классу, в котором определен метод. Начиная с версии 5.3.0, PHP поддерживает «позднюю статическую привязку», где вы можете использовать ключевое слово static для доступа к переопределенным статическим методам, а также функцию get_called_class() для получения имени производного класса в статическом контексте.

Однако в вашем дизайне есть существенный недостаток: статическое свойство $factory, определенное в Base_Factory, является общим для всех производных классов. Поэтому при первом создании и сохранении синглтона в этом свойстве все другие вызовы getInstance() будут возвращать один и тот же объект, независимо от того, какой производный класс используется.

Вы можете использовать статический словарь, отображающий имена классов в одноэлементные объекты:

abstract class Base_Factory {
    private static $_instances = array();
    public static function getInstance() {
        $class = get_called_class();
        if (!isset(self::$_instances[$class])) {
            self::$_instances[$class] = new $class();
        }
        return self::$_instances[$class];
    }
}

О, еще одна вещь: тот факт, что вы ищете возможность повторного использования кода для одноэлементных объектов, может свидетельствовать о том, что вы чрезмерно используете шаблон одноэлементного проектирования! Спросите себя, действительно ли классы, которые вы планируете реализовать как синглтоны, являются синглетонами, и не будет ли случая, когда вы захотите иметь несколько экземпляров определенного класса.

Часто гораздо лучше использовать только один синглтон, представляющий текущий «контекст приложения», который предоставляет средства доступа для объектов, которые являются одиночными по отношению к этому контексту.

8 голосов
/ 20 января 2010

PHP 5.3 +

abstract class Singleton
{
    /**
     * Instance
     *
     * @var Singleton
     */
    protected static $_instance;

    /**
     * Constructor
     *
     * @return void
     */
    protected function __construct() {}

    /**
     * Get instance
     *
     * @return Singleton
     */
    public final static function getInstance() {
        if (null === static::$_instance) {
            static::$_instance = new static();
        }

        return static::$_instance;
    }
}
4 голосов
/ 30 ноября 2009

PHP> = только 5.3 *

abstract class Base_Factory {
    protected static $factory;
    public static function getInstance(){
        if (!self::$factory) {
            $class = get_called_class();
            self::$factory = new $class();
        }
        return self::$factory;
    }
}
1 голос
/ 30 ноября 2009

Зарегистрируйте свои синглтоны в таком простом классе, как этот

class Singletons {
    static private $singleton = array();
    public function getSingleton($class) {
    if (!isset(self::$singleton[$class])) {
        self::$singleton[$class] = new $class;
    }
    return self::$singleton[$class];
    }
}

Тогда сделай это

class aSingleton {
    public $i;
    public function test() {
    ++$this->i;
    echo get_class() . " called {$this->i} times\n";
    }
}

Singletons::getSingleton('aSingleton')->test();
Singletons::getSingleton('aSingleton')->test();

выход

aSingleton called 1 times
aSingleton called 2 times
0 голосов
/ 30 ноября 2009

По определению абстрактный класс не может быть создан в PHP, как любые другие объектно-ориентированные языки. Таким образом, ваш Base_Factory должен быть интерфейсом, а не абстрактным классом.

Из руководства по PHP: «Не разрешается создавать экземпляр класса, который был определен как абстрактный.»

0 голосов
/ 30 ноября 2009

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

if(__CLASS__!='Base_Factory')

Затем используйте $ this вместо self для ссылки на текущий объект вместо текущего класса.

if (!$this->factory)
        $this->factory = new self();
    return $this->factory;
...