PHP中常用的设计模式

/ 0评 / 0

注册器(树)模式:用来将对象注册到全局的树上面,在哪里都能访问

namespace DuiCode;

class Register{

    protected $objects;

    //将类映射注册到全局树上
    /**
     * @param $alias    别名
     * @param $object   对象
     */
    static function set($alias, $object){
            self::$objects[$alias] = $object;
    }

    //获取注册的对象
    static function get($name){
        return self::$objects[$name];
    }

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

工厂模式

namespace  DuiCode;

use DuiCode\Database\MySQLi;

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

    static $proxy = null;
    /**
     * @param $id
     * @return User
     */
    static function getUser($id)
    {
        $key = 'user_'.$id;
        $user = Register::get($key);
        if (!$user) {
            $user = new User($id);
            Register::set($key, $user);
        }
        return $user;
    }

    /**
     * @param $name
     * @return bool
     */
    static function getModel($name)
    {
        $key = 'app_model_'.$name;
        $model = Register::get($key);
        if (!$model) {
            $class = '\\App\\Model\\'.ucwords($name);
            $model = new $class;
            Register::set($key, $model);
        }
        return $model;
    }

    static function getDatabase($id = 'proxy')
    {
        if ($id == 'proxy')
        {
            if (!self::$proxy)
            {
                self::$proxy = new \DuiCode\Database\Proxy;
            }
            return self::$proxy;
        }

        $key = 'database_'.$id;
        if ($id == 'slave')
        {
            $slaves = Application::getInstance()->config['database']['slave'];
            $db_conf = $slaves[array_rand($slaves)];
        }
        else
        {
            $db_conf = Application::getInstance()->config['database'][$id];
        }
        $db = Register::get($key);
        if (!$db) {
            $db = new Database\MySQLi();
            $db->connect($db_conf['host'], $db_conf['user'], $db_conf['password'], $db_conf['dbname']);
            Register::set($key, $db);
        }
        return $db;
    }
}

适配器模式:将截然不同的函数接口封装成统一的API
举例:mysql,mysqli,pdo三种数据库操作可以用适配器模式统一成一致,还有将 memcache、redis、file、apc等不同的缓存函数,统一成一致

interface IDatabase{
    function connect($host, $user, $passwd, $dbname);
    function query($sql);
    function close();
}

//PHP中链式操作的实现
class Database{

    protected $db;

    /**!!!!!单例模式!!!!!!!**/
    //构造方法私有化
    private function __construct(){

    }

    //获取实例
    static function getInstance(){
       if (self::$db){
           return self::$db;
       }else {
           self::$db = new self();
           return self::$db;
       }
    }

    function where($where){
        //这一句就是为了实现链式操作
        return $this;
    }

    function order($order){

        return $this;
    }

    function limit($limit){

        return $this;
    }
}

class MySQL implements IDatabase{

    protected $conn;
    function connect($host, $user, $passwd, $dbname){
        // TODO: Implement connect() method.
        $conn = mysql_connect($host, $user, $passwd);
        mysql_select_db($dbname,$conn);
        $this->conn = $conn;
    }

    function query($sql){
        // TODO: Implement query() method.
        $res = mysql_query($sql,$this->conn);
        return $res;
    }

    function close(){
        // TODO: Implement close() method.
        mysql_close($this->conn);
    }
}

策略模式:将一组特定的行为和算法封装成类,以适应某些特定的上下文环境,这种模式就是策略模式,
比如一个电商网站,针对男女用户要各自跳转到不同的商品类目,并且所有广告位展示不同广告。

namespace DuiCode;

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

namespace DuiCode;


class FemaleUserStrategy implements UserStrategy{
    function showAd(){
        // TODO: Implement showAd() method.
        echo "新款女装";

    }

    function showCategory(){
        // TODO: Implement showCategory() method.
        echo "女装";

    }
}


class Page{
    protected $strategy;
    function index(){
        echo "AD:";
        $this->strategy->showAd();
        echo "
"; echo "Category:"; $this->strategy->showCategory(); echo "
"; } function setStrategy(\DuiCode\UserStrategy $strategy){ $this->strategy = $strategy; } } $page = new Page; if (isset($_GET['female'])){ $strategy = new \DuiCode\FemaleUserStrategy(); }else{ $strategy = new \DuiCode\MaleUserStrategy(); } $page->setStrategy($strategy);

数据对象映射模式,是将对象和数据存储映射起来,对一个对象的操作会映射为对数据存储的操作,比如ORM类,将复杂的SQL语句映射成对象属性的操作。

namespace DuiCode;

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

    protected  $db;

    function  __construct($id){
            $this->db = new \DuiCode\Database\MySQLi();
            $this->db->connect('127.0.01','root','root','test');
            $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(){
        // TODO: Implement __destruct() method.
            $this->db->query("UPDATE user SET name = '{$this->name}',mobile='{$this->mobile}',regtime='{$this->regtime}' WHERE id = {$this->id} LIMIT 1;");
    }
}

观察者模式:当一个对象状态发生改变时,依赖它的对象全部会受到通知,并自动更新。观察者模式实现了低耦合,非侵入式的通知与更新机制。

namespace DuiCode;

interface Observer{
    function update($event_info = null);
}

namespace DuiCode;
abstract class EventGenerator{

    private  $observers = array();

    function addObserver(Observer $observer){
        $this->observers[] = $observer;
    }

    function notify(){
        foreach ($this->observers as $observer){
            $observer->update();
        }
    }

}


class Event extends \DuiCode\EventGenerator{
    function trigger(){
        echo "Event
\n"; /**传统模式update echo "逻辑1
\n"; echo "逻辑2
\n"; echo "逻辑3
\n"; **/ //发送通知 $this->notify(); } }

原型模式:与工厂模式类似,都是用来创建对象,与工厂模式的实现不同,原型模式是先创建好一个原型对象,然后通过clone原型对象来创建新的对象。这样就免去了类创建时重复的初始化操作。原型模式适用于大对象的创建,创建一个大对象需要很大的开销,如果每次new就会消耗很大,原型模式仅需内存拷贝即可。

//原型模式
$prototype = new \DuiCode\Canvas();
$prototype->init();

$canvas3 = clone $prototype;
$canvas3->init();
$canvas3->rect(3, 6, 4, 12);
$canvas3->draw();

echo '

\n'; $canvas4 = clone $prototype; $canvas4->init(); $canvas4->rect(1, 3, 2, 6); $canvas4->draw();

装饰器模式:可以动态地添加修改类的功能。一个类提供了一项功能,如果在修改并添加额外的功能,传统的编程模式,需要写一个子类继承它,并重新实现类的方法。使用装饰器模式,仅需在运行时添加一个装饰器对象,即可实现,可以实现最大的灵活性。

namespace DuiCode;
interface DrawDecorator{
    function beforeDraw();
    function afterDraw();
}


namespace DuiCode;

class ColorDrawDecorator implements DrawDecorator{
    protected $color;

    function __construct($color = 'red'){
        $this->color = $color;
    }

    function beforeDraw(){
        // TODO: Implement beforeDraw() method.
        echo "
"; } function afterDraw(){ // TODO: Implement afterDraw() method. echo "
"; } } namespace DuiCode; class SizeDrawDecorator implements DrawDecorator{ protected $size; function __construct($size = '14px'){ $this->size = $size; } function beforeDraw(){ // TODO: Implement beforeDraw() method. echo "
"; } function afterDraw(){ // TODO: Implement afterDraw() method. echo "
"; } } namespace DuiCode; class Canvas{ public $data; protected $decorators = array(); function init($width = 20, $height = 10){ $data = array(); for ($i = 0; $i < $height; $i++){ for ($j = 0; $j < $width; $j ++){ $data[$i][$j] = '*'; } } $this->data = $data; } //添加装饰器 function addDecorator(DrawDecorator $decorator){ $this->decorators[] = $decorator; } //画之前 function beforeDraw(){ foreach ($this->decorators as $decorator) { $decorator->beforeDraw(); } } //画之后 function afterDraw(){ //数组反转 $decorators = array_reverse($this->decorators); foreach ($decorators as $decorator) { $decorator->afterDraw(); } } function draw(){ $this->beforeDraw(); foreach ($this->data as $line){ foreach ($line as $char){ echo $char; } echo "
\n"; } $this->afterDraw(); } function rect($a1,$a2,$b1,$b2){ foreach ($this->data as $k1=>$line){ if ( $k1 < $a1 or $k1 > $a2) continue; foreach ( $line as $k2 => $char ){ if ( $k2 < $b1 or $k2 > $b2 ) continue; $this->data[$k1][$k2] = ' '; } } } }

迭代器模式:在不需要了解内部实现的前提下,遍历一个聚合对象的内部元素。相对于传统的编程模式,迭代器模式可以隐藏遍历元素所需的操作。

class AllUser implements \Iterator{

    protected $ids;
    protected $data = array();
    protected $index;

    function __construct(){
        $db = Factory::getDatabase();
        $result = $db->query("SELECT id FROM user");
        $this->ids = $result->fetch_all(MYSQLI_ASSOC);

    }

    //获取当前的元素(第三个调用)
    function current(){
        // TODO: Implement current() method.
        $id = $this->ids[$this->index]['id'];
        return Factory::getUser($id);
    }

    //下一个元素(第四步)
    function next(){
        // TODO: Implement next() method.
        $this->index ++;
    }

    //验证当前是否还有下一个元素(第二个调用)
    function valid(){
        // TODO: Implement valid() method.
        return $this->index < count($this->ids);
    }

    //重置整个迭代器
    function rewind(){
        // TODO: Implement rewind() method.
        return $this->index;
    }

    //在迭代器中的位置(第一个调用)
    function key(){
        // TODO: Implement key() method.
        $this->index = 0;
    }
}

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

namespace DuiCode;

interface IUserProxy{
    function getUserName($id);
    function setUserName($id, $name);
}


namespace DuiCode;
class Proxy implements IUserProxy{
    function getUserName($id){
        // TODO: Implement getUserName() method.
        //这里写数据库操作
    }

    function setUserName($id, $name){
        // TODO: Implement setUserName() method.
        //这里写数据库操作
    }
}

下面介绍一下PHP中封装好的一些数据结构

//SPL常用数据结构的使用
//栈  先进后出
$stack = new SplStack();
$stack->push("打他");  //入栈
$stack->pop();   //出栈


//队列 先进先出
$queue = new SplQueue();
$queue->enqueue("揍他");  //入队列
$queue->dequeue();   //出队列


//堆

//最小堆
$heap = new SplMinHeap();
$heap->insert("夯他");  //插入
$heap->extract();    //提取


//固定长度的数组
$array = new SplFixedArray(10);

$array[0] = 123;
$array[9] = 1234;

PHP魔术方法大致分成四类:
1、__set/__get,修改对象不存在的属性和获取对象不存在的属性;
2、__toString();将一个对象当成一个字符串打印的时候调用;
3、__call/__callStatic:调用一个不存在的方法或者一个不存在的静态方法的时候调用;
4、__invoke,将对象当成函数调用的时候该魔术方法被调

面向对象的基本原则:
1.单一职责:一个类,只需要做好一件事情。
2.开放封闭:一个类应该是可扩展的,而不可修改的。
3.依赖倒置:一个类,不应该强依赖另外一个类。每个类对于另外一个类都是可替代的。
4.配置化:尽可能地使用配置,而不是硬编码。
5.面向接口编程:只需要关心接口,不需要关心实现。

配置与设计模式
1.PHP中使用ArrayAccess实现配置文件的加载
2.在工厂方法中读取配置,生成可配置化得对象
3.使用装饰器模式实现权限验证,模板渲染,JSON串化
4.使用观察者模式实现数据更新事件的一系列更新操作
5.使用代理模式实现数据库的主从自动切换

更多源码请查LearningPHP

评论已关闭。