【问题标题】:PHP unfold array singleton constructorPHP展开数组单例构造函数
【发布时间】:2011-02-04 17:03:14
【问题描述】:

我尝试实现一个能够将参数传递给讲师的单例模式。对于单例模式,我想创建一个基类。这样做的主要问题是并非所有派生类都具有相同数量的参数。对于单例基类,到目前为止我有这个:

abstract class Singleton
{
    /**
     * Function: __construct
     * Description: the function which creates the object.
     */
    protected function __construct()
    {

    }
    private final function __clone()
    {
    }
    public static function getInstance()
    {
        $args = func_get_args();
        static $instance = null;

        return $instance = $instance ?  : new static();
    }
}    

到目前为止,这是可行的,但我不能用这个将参数传递给构造函数。当然我可以直接将$args数组传递给构造函数,但是我不想在派生类的每个构造函数中都展开数组,我希望能够将参数作为普通参数传递。

我已经尝试了几件事:我尝试过使用 call_user_func_array,但我不知道如何使用这个函数构造一个对象(如果可能的话),我也尝试过使用 ReflectionClass。 ReflectionClass 的问题是这个类不能访问构造函数,因为构造函数是受保护的。

那么有人对我有什么想法可以解决这个问题吗?

ps。对不起,如果我表现得很困难,但我只是试图找到最好的解决方案(并理解那个解决方案)。

【问题讨论】:

    标签: php singleton


    【解决方案1】:

    首先,您最好使用静态成员变量。而且由于您无法动态调用new,因此您无法使用init 函数。

    我会怎么做:

    abstract class Singleton
    {
        protected static $instances = array();
        /**
         * Function: __construct
         * Description: the function which creates the object.
         */
        private function __construct()
        {
        }
        private final function __clone()
        {
        }
    
        protected abstract function _init();
    
        public static function getInstance()
        {
            $args = func_get_args();
            $class = strtolower(get_called_class());
            if (!isset(self::$instances[$class])) {
                self::$instances[$class] = new $class();
                call_user_func_array(
                    array(
                        self::$instances[$class],
                        '_init'
                    ),
                    $args
                );
            }
            return self::$instances[$class];
        }
    }
    

    因此,您的扩展类将使用_init 而不是__construct

    class foo extends Singleton {
        protected function _init($foo) {
            $this->foo = $foo;
        }
    }
    

    请注意,Singleton::__construct 现在是私有的,以防止被滥用。

    尽管singletonsevilshouldavoided,但所有这一切都无法承受。所以如果你必须使用它们,你可以。但是找到更好的方法,因为它们真的那么糟糕......

    【讨论】:

    • 注意:这是一个“multiton”而不是标准的“singleton”,looks remarkably similar to the code posted on wikipedia
    • 难道没有更清洁的方法(php 5.3?)来实现这一点吗?我非常喜欢 php 5.3 中的“新静态”语法,但据我所知,使用 call_user_func_array 调用构造函数是不可能的。顺便说一句,我认为如果使用得当,单例并不总是邪恶的:我更喜欢单例而不是全局变量/纯静态类,例如数据库访问我不希望每个需要访问数据库的类都创建自己的连接。
    • @Tiddo:无法调用call_user_func_array 来创建新对象。您应该阅读最后一句中的这些链接。你不应该用单例(一个花哨的全局变量)来换取全局变量或静态类(另一个花哨的全局变量)。您应该将它们换成Dependency Injection。给课程他们需要的东西,不要从课程中获取你需要的东西......
    • @ircmaxell 我喜欢依赖注入的风格,我还没有想到。但是,链接中显示的依赖注入模式仍然使用静态类(容器类),如果您不使用容器类,则必须使用全局变量,例如数据库连接。所以我不明白为什么它更好。
    • @Tiddo:您可以使用管理器(容器类)来完成,也可以根据需要传递依赖项。我建议看看:this related answer
    【解决方案2】:

    如果您的意思是要在您的实例中使用一些默认设置,您可以在子实例中设置一个私有设置数组:

    class Simpleton extends Singleton
    {
      private $settings;
    
      protected function __construct()
      {
        $this->settings = array(
          //default settings here
        );
      }
    }
    

    不要尝试将参数合并到单例中。如果您需要一个参数化的构造函数,请不要使用单例。

    单例应该有默认值,然后是访问器/修改器来调整应该可以调整的设置。

    【讨论】:

    • @zzzzBov 这并不是真正的默认设置。简化我的部分情况:我正在创建一个具有不同起点(不同网页)的应用程序,但不同的起点需要不同的数据库。所以我想创建 1 个数据库连接类,但使用的数据库取决于请求的页面。我知道在这种情况下我可以使用单独的 setDabase 方法,但在我的情况下这样做并不实际,我更喜欢在初始化单例时这样做。
    • @Tiddo,不要使用单例进行数据库连接。从结构上讲,处理与多个数据库的多个连接是有意义的。对于这种情况,单例不是正确的设计模式。
    • @zzzzBov,我有多个连接到多个数据库,但很少发生我必须同时访问 2 个数据库。但是我确实需要在一次运行中多次访问同一个数据库,并且每次我需要访问同一个数据库时创建新连接并不是一个好主意。每个数据库最好有一个连接,所以我需要某种类型的全局访问连接。
    • @Tiddo,然后在全局范围内保留对已连接数据库的引用。除非您完全从闭包中工作(这将是荒谬的),否则不可能 将新变量/函数引入全局范围。我不提倡用大量变量污染全局范围,但重要变量应该可以全局访问。
    • @zzzzBov 我知道我可以在全局范围内做到这一点,但如果可能的话,我会尽量避免这种情况;)ps。对不起,我很痛苦,我只是想找到一种很好的方法来解决这个问题,这对我的应用程序很有用
    猜你喜欢
    • 2011-11-20
    • 2013-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-24
    • 2015-08-06
    • 1970-01-01
    • 2023-03-11
    相关资源
    最近更新 更多