PHP的单例模式

接上一次的《PHP实现树形结构》。由于菜单中的各种品类基本不变,跑同一次程序时候不会变化,所以只需在数据库查询一次,第一次初始化的时候查询好结果,以后每次取用即可直接返回。这里用php实现单例模式就能实现。

所谓单例模式是指整段程序中,类或者对象只会有一个实例的的设计模式。这种模式特别适合和数据库打交道的程序,比如上面所说的简历菜单树,因为只有一个实例,节省内资源,不用重复查询重复建立连接。

要实现单例模式,以下两点是重点:

1.他的构造函数必须声明为私有,否则其他地方可以随意new,失去了单例的意义。

2.由于外界不能new,所以内部要增加一个公共静态方法给外部取用这个实例。

另外,还可以注意一下以后的扩展。比如另外的树形结构也可以添加进来。

下面这段代码实现了菜单树的单例工厂模式,并且有一定的扩展性。文章最后会说明PHP单例模式的一些特点和不足。

/*
 * 树的单例工厂模式
 * @author zhengguorui
 */

class TreeFactory{

    // 工厂类初始化标记
    static public $instance;

    //声明私有构造方法为了防止外部代码使用new来创建对象。
    private function __construct(){}

    // menu私有唯一实例
    private $TreeOfMenu;

    // menu的初始化方法
    static private function initMenu(){
        self::$instance->TreeOfMenu = new TreeMenu ( '目录导航' );
        $tb = new IModel('category');
        $list = $tb->query("visibility=1", "*", "parent_id, sort, id");
        // 设置树形结构 
        for ($i = 0; $i < count($list); $i++) {
            self::$instance->TreeOfMenu->setNode ( $list[$i]["id"], $list[$i]["parent_id"], $list[$i]["name"] );
        }
        //设置每个节点的品牌列表
        $tb = new IModel('category_extend, brand, goods');
        $list = $tb->query("shop_goods.id = shop_category_extend.goods_id and shop_brand.id = shop_goods.brand_id and shop_goods.is_del=0", "DISTINCT category_id, shop_goods.brand_id, shop_brand.name", "category_id, brand_id"); 
        for ($i = 0; $i < count($list); $i++) {
            self::$instance->TreeOfMenu->setBrand ( $list[$i]["category_id"], $list[$i]["brand_id"], $list[$i]["name"] );
        }
    }

    // 工厂类和各种实例的初始化总入口
    static public function getinstance($instType){
        if(!self::$instance){
            // 第一次则新建并初始化对象
            self::$instance = new self();
        }

        // menu的初始化
        if($instType=="menu"){
            if(!self::$instance->TreeOfMenu){
                self::initMenu();
            }
            // 返回menu实例
            return self::$instance->TreeOfMenu;
        }

        return self::$instance;
    }

}

虽然PHP单例模式可以优化一部分问题,但由于PHP是一种解释型的脚本语言,在吐回页面之后,程序就会消亡。这一点上,php和c以及java这类常驻型程序是不同的。所以PHP的单例模式仅仅是在一次程序的执行中单例,而不能跨用户跨页面实现单例。这也使得PHP的单例模式显得那么“山寨”,只适合用在单页面中触发大量查询请求的页面上。若需要长连接或者处理大量用户查询的网关,还是得由c来接手,这也不得不说是PHP语言上的一种遗憾吧!