PHP结构型设计模式(上)

结构型设计模式: GOF 23个设计模式中,属于结构型设计模式7个。分别为适配器模式装饰器模式代理模式外观模式桥接模式组合模式享元模式php


PHP设计模式(五)—适配器模式(Adapter Pattern)

适配器模式(Adapter Pattern):将某个对象的接口适配为另外一个对象所指望的接口。属于结构型设计模式。mysql

(一)为何须要适配器模式sql

1,某个操做数据库的有两套不一样的数据库操做方法,咱们经过适配器统一成一个接口。例如,咱们待会把mysql和mysqli统一成一个接口。数据库

2,咱们有多套数据库对应了多种数据库操做,例如MySQL,SqlServer,Oralce,Redis都有对应的操做函数,或操做类。PDO把这些都统一成一个接口。设计模式

3,系统的增长一些新功能,建立了一个新的接口,可是老的接口并不想废弃。可使用适配器模式,对用户隐藏这两个接口,提供用户所但愿的接口。安全

(二)适配器UML图bash

Adapter Pattern

(三)设计实例微信

把MySQL和mysqli统一成一个接口,用户能够调用一样的方法使用MySQL和mysqli操做数据库。函数

<?php
//MySQL待操做适配类
class MySQLAdaptee implements Target
{
    protected $conn;    //用于存放数据库链接句柄
    //实现链接方法
    public function connect($host, $user, $passwd, $dbname)
    {
        $conn = mysql_connect($host, $user, $passwd);
        mysql_select_db($dbname, $conn);
        $this->conn = $conn;
    }
    //查询方法
    public function query($sql)
    {
        $res = mysql_query($sql, $this->conn);
        return $res;
    }
    //关闭方法
    public function close()
    {
        mysql_close($this->conn);
    }
}
//MySQLi操做待适配类
class MySQLiAdaptee 
{
    protected $conn;
    public function connect($host, $user, $passwd, $dbname)
    {
        $conn = mysqli_connect($host, $user, $passwd, $dbname);
        $this->conn = $conn;
    }
    public function query($sql)
    {
        return mysqli_query($this->conn, $sql);
    }
    public function close()
    {
        mysqli_close($this->conn);
    }
}
//用户所期待的接口
Interface Target{
    public function connect($host, $user, $passwd, $dbname);
    public function query($sql);
    public function close();
}
//用户期待适配类
Class DataBase implements Target {
    protected $db ;     //存放MySQLiAdapter对象或MySQLAdapter对象
    public function  __construct($type){
        $type = $type."Adapter" ;
        $this->db = new $type ;
    }
    public function connect($host, $user, $passwd, $dbname){
        $this->db->connect($host, $user, $passwd, $dbname);
    }
    public function query($sql){
        return $this->db->query($sql);
    }
    public function close(){
        $this->db->close();
    }
}
//用户调用同一个接口,使用MySQL和mysqli这两套不一样示例。
$db1 = new DataBase('MySQL');
$db1->connect('127.0.0.1','root','1234','myDB');die;
$db1->query('select * from test');
$db1->close();

$db2 = new DataBase('MySQLi');
$db2->connect('127.0.0.1','root','1234','myDB');
$db2->query('select * from test');
$db2->close();
复制代码

上面的代码只是一个示例,若是你运行以上的代码报了mysql函数不存在或是被废弃的错误。这是正常的,由于MySQL这套函数在PHP5.5以上的版本已经被废弃了。感兴趣的还能够去了解一下PDO的实现。 经过上面的代码,咱们能够看到,使用适配器能够把不一样的操做接口封装起来,对外显示成用户所指望的接口。post

这就比如你家墙上有一个电源三相插孔,可是插孔的孔距之间过小。你的电器三相插头插脚距太大的插不进去,或许你还有个两相的插头,或许你还有条USB线和type-C线,这些都无法插到三相接口里。因而你买了个插脚适合插到你墙上的排插,而后这个排插是这些年新出的,USB也能插。因而你把你的三相插头,两相插头,USB线,type-c线都插到排插上。实际上就是间接地连在了你墙壁上的三相插孔上。

没错,适配器要作的就是这么回事。

有些书也把适配器模式分为:类的适配器模式,对象的适配器模式,接口的适配器模式


PHP设计模式(六)—装饰器模式(Decorator Pattern)

装饰器模式(Decorator Pattern): 容许向一个已有的对象添加新的功能或部份内容,同时又不改变其结构。属于结构型模式,它是做为现有的类的一个包装。

(一)为何须要装饰器模式:

1,咱们要对一个已有的对象添加新功能,又不想修改它原来的结构。

2,使用子类继承的方法去实现添加新功能,会不可避免地出现子类过多,继承链很长的状况。并且很多书籍都规劝咱们竭力保持一个对象的父与子关系不超过3个。

3,装饰器模式,能够提供对对象内容快速非侵入式地修改。

(二)装饰器模式UML图

Decorator Pattern

(三)简单实例

若是有一个游戏角色,他原来就是默认穿一件长衫。如今游戏改进了,以为这个角色,除了穿一件长衫前,还能够在里面穿一件袍子,或是一件球衣。在外面穿一套盔甲或是宇航服。

<?php
/*游戏原来的角色类
class Person{
    public function clothes(){
        echo "长衫".PHP_EOL;
    }
}
*/

//装饰器接口
interface Decorator
{
   public function beforeDraw();
   public function afterDraw();
}
//具体装饰器1-宇航员装饰
class AstronautDecorator implements Decorator
{
    public function beforeDraw()
    {
        echo "穿上T恤".PHP_EOL;
    }
    function afterDraw()
    {
        echo "穿上宇航服".PHP_EOL;
        echo "穿戴完毕".PHP_EOL;
    }
}
//具体装饰器2-警察装饰
class PoliceDecorator implements Decorator{
    public function beforeDraw()
    {
        echo "穿上警服".PHP_EOL;
    }
    function afterDraw()
    {
        echo "穿上防弹衣".PHP_EOL;
        echo "穿戴完毕".PHP_EOL;
    }
}
//被装饰者
class Person{
    protected $decorators = array(); //存放装饰器
    //添加装饰器
    public function addDecorator(Decorator $decorator)
    {
        $this->decorators[] = $decorator;
    }
    //全部装饰器的穿长衫前方法调用
    public function beforeDraw()
    {
        foreach($this->decorators as $decorator)
        {
            $decorator->beforeDraw();
        }
    }
    //全部装饰器的穿长衫后方法调用
    public function afterDraw()
    {
        $decorators = array_reverse($this->decorators);
        foreach($decorators as $decorator)
        {
            $decorator->afterDraw();
        }
    }
    //装饰方法
    public function clothes(){
        $this->beforeDraw();
        echo "穿上长衫".PHP_EOL;
        $this->afterDraw();
    }
}
//警察装饰
$police = new Person;
$police->addDecorator(new PoliceDecorator);
$police->clothes();
//宇航员装饰
$astronaut = new Person;
$astronaut->addDecorator(new AstronautDecorator);
$astronaut->clothes();
//混搭风
$madman = new Person;
$madman->addDecorator(new PoliceDecorator);
$madman->addDecorator(new AstronautDecorator);
$madman->clothes();
复制代码

固然,上面的代码没有严格地按照UML图,这是由于当被装饰者只有一个时,那 Component也就是ConcreteComponent。同理,若是,只有一个装饰器,那也不必实现一个implment接口。

若是咱们有两个不一样的被装饰者,那固然就应该抽象出一个Component,让这两个被装饰者去继承它。也许,那继承不是又来了吗。既然外面都用到继承,直接把装饰器的方法放到继承里面不就好了。

但是你想,若是,直接经过继承的话,那装饰过的被装饰者就应该继承自被装饰者,并且被装饰者由于装饰的不一样还要有不少不一样类型的子类。而使用装饰者模式的话,继承链缩短了,并且不一样的装饰类型还能够动态增长。


PHP设计模式(七)—代理模式(Proxy Pattern)

代理模式(Proxy Pattern):构建了透明置于两个不一样对象以内的一个对象,从而可以截取或代理这两个对象间的通讯或访问。

(一)为何须要代理模式

1,远程代理,也就是为了一个对象在不一样地址空间提供局部表明。隐藏一个对象存在于不一样地址空间的事实。

2,虚拟代理,根据须要来建立开销很大的对象,经过它来存放实例化须要很长时间的真实对象。

3,安全代理,用来控制真实对象的访问对象。

4,智能指引,当调用真实对象的时候,代理处理一些事情。

(二)代理模式UML图

Proxy Pattern

(三)简单实例

案例一:你想买一张学友哥的新唱片,之前你都是在县城CD店里买的。如今CD行业不景气,没得卖了。你只能找人去香港帮你代购一张。

<?php
//代理抽象接口
interface shop{
    public function buy($title);
}
//原来的CD商店,被代理对象
class CDshop implements shop{
    public function buy($title){
        echo "购买成功,这是你的《{$title}》唱片".PHP_EOL;
    }
}
//CD代理
class Proxy implements shop{
    public function buy($title){
        $this->go();
        $CDshop = new CDshop;
        $CDshop->buy($title);
    }
    public function go(){
        echo "跑去香港代购".PHP_EOL;
    }
}

//你93年买了张 吻别
$CDshop = new CDshop;
$CDshop->buy("吻别");
//14年你想买张 醒着作梦 找不到CD商店了,和作梦似的,不得不找了个代理去香港帮你代购。
$proxy = new Proxy;
$proxy->buy("醒着作梦");
复制代码

案例二:经过代理实现MySQL的读写分离,若是是读操做,就链接127.0.0.1的数据库,写操做就读取127.0.0.2的数据库

<?php
class Proxy
{   
    protected $reader;
    protected $wirter;
    public function __construct(){
        $this->reader = new PDO('mysql:host=127.0.0.1;port=3306;dbname=CD;','root','password');
        $this->writer = new PDO('mysql:host=127.0.0.2;port=3306;dbname=CD;','root','password');
    }
    public function query($sql)
    {
        if (substr($sql, 0, 6) == 'select')
        {
            echo "读操做: ".PHP_EOL;
            return $this->reader->query($sql);
        }
        else
        {
            echo "写操做:".PHP_EOL;
            return  $this->writer->query($sql);
        }
    }
}
//数据库代理
$proxy = new Proxy;
//读操做
$proxy->query("select * from table");
//写操做
$proxy->query("INSERT INTO table SET title = 'hello' where id = 1");


//固然对于数据库来讲,这里应该使用单例模式的方法来存放$reader$writer,但我只是举个例子,不想把单例加进来把代码搞复杂。
//可是若是你要实现这样的一个数据库代理,我以为仍是有必要用上单例模式的知识
复制代码

一句话来讲,代理模式,就是在访问对象时经过一个代理对象去访问你想访问的对象。而在代理对象中,咱们能够实现对访问对象的截断或权限控制等操做。


PHP设计模式(八)—外观模式(Facade Pattern)

外观模式 (Facade Pattern): 为子系统中的一组接口提供一个一致的界面,定义一个高层接口,这个接口使得这一子系统更加容易使用。

(一)为何须要外观模式

1,开发阶段,子系统愈来愈复杂,增长外观模式提供一个简单的调用接口。

2,维护一个大型遗留系统的时候,可能这个系统已经很是难以维护和扩展,但又包含很是重要的功能,为其开发一个外观类,以便新系统与其交互。

3,外观模式能够隐藏来自调用对象的复杂性。

(二)外观模式UML图

Facade Pattern

(三)简单实例

好比说咱们去医院就诊,医院有医生员工系统,有药品系统,有患者资料系统。可是咱们只是在前台挂个号,就能在其余系统里都看到咱们。外观系统就差很少这样。

若是没有挂号系统的话,咱们就先要去医生系统通知一下医生, 而后去患者系统取一下患者资料交给医生,再去药品系统登记一下,最后到药房领药。

<?php
//医院医生员工系统
class DoctorSystem{

    //通知就诊医生
    static public function getDoctor($name){
        echo __CLASS__.":".$name."医生,挂你号".PHP_EOL;
        return new Doctor($name);
    }
}
//医生类
class Doctor{
    public $name;
    public function __construct($name){
        $this->name = $name;
    }
    public function prescribe($data){
        echo __CLASS__.":"."开个处方给你".PHP_EOL;
        return "祖传秘方,药到必死";
    }
}
//患者系统
class SufferSystem {
    static function getData($suffer){
        $data = $suffer."资料";
        echo  __CLASS__.":".$suffer."的资料是这些".PHP_EOL ;
        return  $data;
    }
}
//医药系统
class MedicineSystem {
    static function register($prescribe){
        echo __CLASS__.":"."拿处处方:".$prescribe."------------通知药房发药了".PHP_EOL;
        Shop::setMedicine("砒霜5千克");
    }
}
//药房
class shop{
    static public $medicine;
    static function setMedicine($medicine){
        self::$medicine = $medicine;
    }
    static function getMedicine(){
        echo __CLASS__.":".self::$medicine.PHP_EOL;
    }
}

//若是没有挂号系统,咱们就诊的第一步
//通知就诊医生
$doct = DoctorSystem::getDoctor("顾夕衣");
//患者系统拿病历资料
$data = SufferSystem::getData("何在");
//医生看病历资料,开处方
$prscirbe = $doct->prescribe($data);
//医药系统登记处方
MedicineSystem::register($prscirbe);
//药房拿药
Shop::getMedicine();

echo PHP_EOL.PHP_EOL."--------有了挂号系统之后--------".PHP_EOL.PHP_EOL;

//挂号系统
class Facade{
    static public function regist($suffer,$doct){
        $doct = DoctorSystem::getDoctor($doct);
        //患者系统拿病历资料
        $data = SufferSystem::getData($suffer);
        //医生看病历资料,开处方
        $prscirbe = $doct->prescribe($data);
        //医药系统登记处方
        MedicineSystem::register($prscirbe);
        //药房拿药
        Shop::getMedicine();
    }
}
//患者只须要挂一个号,其余的就让挂号系统去作吧。
Facade::regist("叶好龙","贾中一");
复制代码

外观模式,也叫门面模式。它多用于在多个子系统之间,做为中间层。用户经过Facade对象,直接请求工做,省去了用户调用多个子系统的复杂动做。

外观模式常举的一个例子,就是咱们买了好多支股票,可是时间有限。盯盘很复杂,咱们搞得一团糟。因此,咱们干脆买了股票基金。股票基金就比如于外观模式的Facade对象,而子系统就是股票基金投的各支股票。


上一篇PHP建立型设计模式

感谢阅读,因为笔者也是初学设计模式,能力有限,文章不可避免地有失偏颇 后续更新** PHP设计模式-结构型设计模式(下) **介绍,欢迎你们评论指正


我最近的学习总结:


欢迎你们关注个人微信公众号 火风鼎
相关文章
相关标签/搜索