论WordPress的单例模式,和更好的初始化方法

WordPress中,插件使用单例模式,性能更好,免得资源浪费。

平时,以面向对象的写法是这样的:

class Plugin
{
    /**
     * Constructor.
     */
    public function __construct()
    {
        // Actions
        add_action('wp_enqueue_scripts', array($this, 'enqueue_script'));

        // Filters
        add_filter('the_content', array($this, 'the_content'));
    }

    /**
     * 一些方法
     */
    public function enqueue_script()
    {
        // ...
    }
}
$plugin = new Plugin();

或者,其他流行的方法是实例化一个对象类。在外部,使用该实例注册你的动作和过滤器。

class Plugin
{

    public function enqueue_script()
    {
        // ...
    }

    public function the_content($content)
    {
        // ...

        return $content;
    }
}
$plugin = new WP_Kickass_Plugin();

// Actions
add_action('wp_enqueue_scripts', array($plugin, 'enqueue_script'));

// Filters
add_filter('the_content', array($plugin, 'the_content'));

这两种方法都有一个问题,那就是没有一种简单的方法可以在之后将该对象取回,容易被覆盖。当然了,名字可以尽量起得不一样。

实际上,WordPress同样可以使用单例模式,实现单例模式的类有三个元素。你必须:

  1. 有一个私有静态变量来存储实例化的对象。
  2. 将构造函数设为私有。
  3. 创建一个公共静态方法(通常称为“get_instance”)来获取实例化的对象。

看一下前面的两个例子。可以按照上述步骤将它们转换为单例。生成的代码与原始代码非常接近。第一个示例使用构造函数注册所有操作和过滤器,可以在下面看到转换后的代码。

class Plugin
{
   
    private static $instance;

    public static function get_instance()
    {
        if (null === self::$instance) {
            self::$instance = new self();
        }

        return self::$instance;
    }

    /**
     * Constructor.
     */
    private function __construct()
    {
        // Actions
        add_action('wp_enqueue_scripts', array($this, 'enqueue_script'));

        // Filters
        add_filter('the_content', array($this, 'the_content'));
    }

    /**
     *加载其他功能
     */
    public function enqueue_script()
    {
        // ...
    }

   
    public function the_content($content)
    {
        // ...

        return $content;
    }
}
$plugin = Plugin::get_instance();

将其转换为单例需要进行一些更改。将构造函数设为私有,并添加了静态变量和“get_instance”方法,结果与前面的示例几乎相同。

同样,可以放到外面来调用:

class Plugin
{

    private static $instance;


    public static function get_instance()
    {
        if (null === self::$instance) {
            self::$instance = new self();
        }

        return self::$instance;
    }

    /**
     * Constructor.
     */
    private function __construct()
    {
        // So ronery...
    }


    public function the_content($content)
    {
        // ...

        return $content;
    }
}

// Actions
add_action('wp_enqueue_scripts', array(Plugin::get_instance(), 'enqueue_script'));

// Filters
add_filter('the_content', array(Plugin::get_instance(), 'the_content'));

我们需要创建一个空的私有构造函数。这是因为默认情况下构造函数是公共的。私有构造函数的另一个副作用是我们不能使用new关键字创建对象。我们需要通过调用“get_instance”将其传递给寄存器函数。值得注意的是,单例使第二种方法的吸引力大大降低。如果在构造函数中注册钩子,那么单例模式更有趣。

从构造函数中初始化插件

构造函数的作用是准备对象以供使用,用插件API注册操作和过滤器根本不适合这个任务。使用静态方法创建对象并在那里注册所有内容显然更好。

class Plugin
{
    /**
     * 注册插件
     */
    public static function register()
    {
        $plugin = new self();

        // Actions
        add_action('wp_enqueue_scripts', array($plugin, 'enqueue_script'));

        // Filters
        add_filter('the_content', array($plugin, 'the_content'));
    }

    public function __construct()
    {
        // Setup your plugin object here
    }

    public function enqueue_script()
    {
        // ...
    }
}
Plugin::register();

这使WordPress代码更加解耦。唯一的问题是,一旦对象注册,就不能更改它。
构造函数现在也是公共的,没有必要私有了。这意味着实例化一个新对象对WordPress不再有任何影响。
这些变化还有另一个积极的好处。可以进行类单元可测试。

将插件API拆分为自己的类,这是克服使用单例模式的更高级的方法,其思想是将插件API的使用与所有类分离。创建一个类,该类的唯一责任是与之交互。
这就是我在编写使用接口的示例时想到的,创建该类就不需要在其他类中使用“add_action”和“add_filter”。我们在这里使用插件API,但是对于插件中非常依赖的任何API,都应该这样做。
关于单例模式值得了解,也就是说,使用它是一种选择,你不必这么做。对于你来说,有各种各样的方法来回避单例模式解决的问题。

THE END