PHP 设计模式
in PHPer技术 with 0 comment

PHP 设计模式

in PHPer技术 with 0 comment

设计模式

1.工厂模式

工厂模式使用类方法创建对象,而不是new,方便对象类的修改

比如 在项目 大量 使用Database类,代码如下:

$obj = new  Core\Database();

此时 Database类名发生改变,或需要继承统计父类,则需要修改大量的文件。

而在工厂模式中 我们可以 创建一个类,来封装实例化的操作,代码如下:

class Factory
{
    static function createDatabase()
    {
        $db = new Database();
        return $db;
    }
}

在其他地方需要用到 Database类,只需要静态调用下即可:

$db = Core\Factory::createDatabase();

2.单例模式

常用场景:数据库连接。 假设我们有一个类,是连接到数据库,如果不使用一个单例模式,如果在很多类中都 创建了数据库的连接,这时候实际上是对资源的一种浪费,我们只要创建一个数据库的连接即可。

实现思路如下:
1.声明 一个 private 方法的构造函数。

    private function __construct()
    {
        //TODO 数据库连接操作
    }

避免外部 直接用new关键字实例化 Database 类。
比如:

$obj = new  Core\Database();
  1. 创建私有方法后,对外提供接口,创建一个静态方法,代码如下。
    protected  $db; //保存数据连接成功的资源

    static function getInstance()
    {
        if(self::$db){
            return self::$db; //如果已经连接过数据库,直接返回连接资源即可,无需再次实例化
        } else{
            self::$db = new self(); //内部实例化Database,连接数据库,保存$db属性
            return self::$db; //在返回连接资源
        }

    }

  1. 外部调用 Database开放的 getInstance方法即可
$db = Core\Database::getInstance();

3.注册树模式

什么是注册树模式?

  注册树模式当然也叫注册模式,注册器模式。之所以我在这里矫情一下它的名称,是因为我感觉注册树这个名称更容易让人理解。像前两篇一样,我们这篇依旧是从名字入手。注册树模式通过将对象实例注册到一棵全局的对象树上,需要的时候从对象树上采摘的模式设计方法。 这让我想起了小时候买糖葫芦,卖糖葫芦的将糖葫芦插在一个大的杆子上,人们买的时候就取下来。不同的是,注册树模式摘下来还会有,能摘很多次,糖葫芦摘一次就没了。。。

  为什么要采用注册树模式?

  单例模式解决的是如何在整个项目中创建唯一对象实例的问题,工厂模式解决的是如何不通过new建立实例对象的方法。 那么注册树模式想解决什么问题呢? 在考虑这个问题前,我们还是有必要考虑下前两种模式目前面临的局限。 首先,单例模式创建唯一对象的过程本身还有一种判断,即判断对象是否存在。存在则返回对象,不存在则创建对象并返回。 每次创建实例对象都要存在这么一层判断。 工厂模式更多考虑的是扩展维护的问题。 总的来说,单例模式和工厂模式可以产生更加合理的对象。怎么方便调用这些对象呢?而且在项目内如此建立的对象好像散兵游勇一样,不便统筹管理安排啊。因而,注册树模式应运而生。不管你是通过单例模式还是工厂模式还是二者结合生成的对象,都统统给我“插到”注册树上。我用某个对象的时候,直接从注册树上取一下就好。这和我们使用全局变量一样的方便实用。 而且注册树模式还为其他模式提供了一种非常好的想法。

  如何实现注册树?

  通过上述的描述,我们似乎很容易就找到了解决方法。首先我们需要一个作为注册树的类,这毋庸置疑。所有的对象“插入”到注册树上。这个注册树应该由一个静态变量来充当。而且这个注册树应该是一个二维数组。这个类应该有一个插入对象实例的方法(set()),当让相对应的就应该有一个撤销对象实例的方法(_unset())。当然最重要的是还需要有一个读取对象的方法(get())。拥有这些,我们就可以愉快地完成注册树模式啦~~~

  下面让三种模式做个小小的结合。单纯创建一个实例对象远远没有这么复杂,但运用于大型项目的话,便利性便不言而喻了。

<?php
//创建单例
class Database
{
    static  protected  $db;

    private function __construct()
    {
        self::$db=rand(1,9999);
    }

    /**
     * @return Database
     */
    static function getInstance()
    {
        if(self::$db){
            return self::$db; //如果已经连接过数据库,直接返回即可,无需再次实例化
        } else{
            self::$db = new self(); //内部实例化Database
            return self::$db;
        }

    }
 
}
//注册树
class Register
{
    protected static $objects;

    // 将一个对象注册到一个全局树上
    static  function set($alias,$object)
    {
        self::$objects[$alias] = $object;
    }

    static function get($name)
    {
        return self::$objects[$name];
    }

    function _unset($alias)
    {
        unset(self::$objects[$alias]);
    }
}


//工厂模式
class Factory
{
    /**
     * @return Database
     */
    static function createDatabase()
    {
        $db = Database::getInstance();
        Register::set('db1',$db);
        return $db;
    }
}

//入口文件
$db = Core\Factory::createDatabase();
$db = Core\Register::get('db1');
var_dump($db);

4.适配器模式

适配器就是利用接口,将不同的类串联起来,根据需要的功能重写一组方法。

适配器的概念,举个简单的例子,session的缓存 可以由 file mysql redis memcache 多种实现,但是为了session的操作 无非 set get ,适配器是定制一套统一的操作方法,由底层去实现 即为适配器。

namespace Core {

interface IDatabase
{
    function  connect($host,$user,$passwd,$dbname);
    function  query($sql);
    function  close();
}
    class Database
    {
        static  protected  $db;

        private function __construct()
        {
            self::$db=rand(1,9999);
        }

        /**
         * @return Database
         */
        static function getInstance()
        {
            if(self::$db){
                return self::$db; //如果已经连接过数据库,直接返回即可,无需再次实例化
            } else{
                self::$db = new self(); //内部实例化Database
                return self::$db;
            }

        }

        function  where($where)
        {
            return $this;
        }

        function order($order)
        {
            return $this;
        }

        function limit($limit)
        {
            return $this;
        }

    }
}

5.策略

不是只将if else移动到外面,这是一种思想,处理某一个策略使用一个类来处理,而不是将一堆逻辑写到一个action中
简单来说就是:
将一组特定的行为和算法封装成类,以适应特定的上下文环境,这种模式就是策略模式。
比如 在 项目在 多处 地方 需要 显示广告,而不同的性别,不同的用户,需要跳转到不同类目,看到的广告是不同的。在传统编码中,逻辑如下:

if($_GET['sex']== '男'){

}else if($_GET['sex']== '女'){

}else if($_GET['vip']=='土豪级别'){

}
....

上面这种写法 会在项目中产生大量的else if,代码冗余。

此时我们就可以 通过策略模式解决。

第一: 建议 定义一个 行为 接口

<?php


namespace Core;


interface UserStrategy
{
    function showAd();
    function showCategory();

}

第二步,定义女性广告类,基于上面的接口,重写接口内的方法,

<?php


namespace Core;


use Core\UserStrategy;

class FemaleUserStrategy implements UserStrategy
{
    function showAd()
    {
        echo "2014新款女装";
    }
    function showCategory()
    {
        echo "女装";
    }
}

第三步,定义男性广告类,基于上面的接口,重写接口内的方法,

<?php


namespace Core;


class MaleUserStrategy implements UserStrategy{

    function showAd()
    {
       echo "IPhone6";
    }

    function showCategory()
    {
        echo "电子产品";
    }
}

index.php 调用演示如下:

class Page
{
    protected $strategy;

    function index()
    {
        $this->strategy->showAd();
    }

    /**
     * @param \Core\UserStrategy $strategy
     */
    function setStrategy(\Core\UserStrategy $strategy)
    {
        $this->strategy = $strategy;
    }
}
$page = new Page();
$female = 1;
if($female==1){
    $strategy = new \Core\FemaleUserStrategy();
} else {
    $strategy = new \Core\MaleUserStrategy();
}
$page->setStrategy($strategy);
$page->index();

6.数据对象映射模式

数据对象映射模式:将数据的操作封装成一个类,将类的属性作为数据库字段的映射。对数据库表的操作映射为对一个类的操作,而不是直接在业务逻辑中书写sql语句,执行sql语句来操作数据库,这种是不符合面向对象设计思路的。不符合数据对象映射模式。

<?php


namespace Core;


class User
{
    public $id;
    public $name;
    public $mobile;
    public $regtime;

    protected $db;

    function __construct($id)
    {
        $this->db = new \Core\Database\MySQLi();
        $this->db->connect('127.0.0.1','root','dzgphp123','a');
        $res = $this->db->query("select * from user limit 1");

        $data = $res->fetch_assoc();

        $this->id = $data['id'];
        $this->name = $data['name'];
        $this->mobile = $data['mobile'];
        $this->regtime = $data['regtime'];

    }

    function __destruct()
    {

       $res = $this->db->query("update user set name = '{$this->name}',mobile='{$this->mobile}' where id={$this->id}");
        var_dump($res);
    }
}

7.观察者模式

主要场景:当每一事件发生时,发解发多个相同的增,删,改动时,传统方法耦合太高,使用观察者模式,可以降低耦合。实现方式:创建一抽象类为事件生产者,其中包含添加观察者--addObserver(observer $observer)、通知---notify()2个方法及一个私有的变量observers数组.addObserver中保存观察者,notify以遍历的方式通知

EventGenerator.php

<?php


namespace Core;


abstract class EventGenerator
{
    private $observers = array();
    function addObserver(Observer $observer){
        $this->observers[] = $observer;
    }
    function notify()
    {
        foreach ($this->observers as $observer)
        {
            $observer->update();
        }
    }
}

Observer.php

<?php


namespace Core;


interface Observer
{
    /**
     * @param null $event_info
     */
    function update($event_info = null);

}
class Event  extends  \Core\EventGenerator
{
    function trigger()
    {
        echo "Event<br/>\n";

        //update
        $this->notify();
    }
}
class Observer1 implements \Core\Observer
{
    function update($event_info = null)
    {
        echo "逻辑1";
    }
}
$event = new Event;
$event->addObserver(new Observer1);

$event->trigger();


8.原型模式

原型模型使用的场景是,创建新对象成本较大时可以利用已有的对象进行复制来获得,从而降低创建对象的代价,原型模式隐藏了创建对象的复杂性。只需要知道要创建对象的类型,然后通过请求就可以获得和该对象一模一样的新对象,无须知道具体的创建过程。使用的目的是创建新对象,而不是获取同一个对象实例,而且可以进行浅拷贝和深拷贝,单例模式创建的对象都是同一个,该模式只创建了一个对象,而不能创建新的对象。

9.装饰器模式

待研究

10.迭代器模式

待研究

11.代理模式

1. 在客户端与实体之间建立一个代理对象(proxy),客户端对实体进行的操作全部委派给代理对象,隐藏实体的具体实现细节。 2. Proxy还可以与业务代码分离,部署到另外的服务器,业务代码中通过RPC来委派任务。
Responses