PHP结构型设计模式(上)

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


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

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

(一)为什么需要适配器模式

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

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

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

(二)适配器UML图

(三)设计实例

把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的实现。 通过上面的代码,我们可以看到,使用适配器可以把不同的操作接口封装起来,对外显示成用户所期望的接口。

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

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

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


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

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

(一)为什么需要装饰器模式:

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

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

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

(二)装饰器模式UML图

(三)简单实例

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

<?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图

(三)简单实例

案例一:你想买一张学友哥的新唱片,以前你都是在县城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图

(三)简单实例

比如说我们去医院就诊,医院有医生员工系统,有药品系统,有患者资料系统。但是我们只是在前台挂个号,就能在其他系统里都看到我们。外观系统就差不多这样。

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

<?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设计模式-结构型设计模式(下) **介绍,欢迎大家评论指正


我最近的学习总结:


转载于:https://juejin.im/post/592043a4a22b9d00586f9adc

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值