简介PHP OOP几种设计模式

前言:
  你好,希望你会喜欢我的文章,能从中学到东西坚持看完,或者有共鸣、建议、指点,期待交流!
  OOP及面向对象,封装、继承、多态为此三大特性。
  为了开发中更加快速、优雅、简明、方便,因此在OOP的基础上诞生了各种设计模式,本质上其实是OOP编写代码的行为方式,巧妙运用OOP来开发各种应用程序,我们使用它可以帮助我们更加深入理解OOP。
  也可看做是OOP思想一种历史文化的衍生,但不要被这种思想所限制,你可以在它的基础上开拓或创新(个人观点)。所谓推陈出新随着版本更替,相信今后也会有更多设计模式。
  
  23种设计模式分为三类,创建型、结构型、行为型。在不同语言中,设计模式的实现虽说大同小异但也存在区别,下面主要描述PHP OOP中的一些设计模式。

设计模式:
  单例模式、工厂模式、观察者模式、职责链模式、适配器模式、装饰器模式、桥接模式…等。
   

一、单例模式

    属于创建型,一种创建对象相对完美的方式。
  该模式仅仅提供一个唯一入口,为了解决频繁使用的类多次实例化调用和多次销毁;为了约束及良好利用系统资源;为了控制实例化的次数和数量。

遵循三私一公原则:
  1.私有构造化,让其不能被外部实例化。
  2.私有克隆法,或者在函数方法里自定义返回错误。
  3.私有一个静态成员属性保存本身实例或其它实例对象。当有需求通过唯一入口访问进来,此静态成员属性如果包含需求所要的实例对象就直接返回此对象,反之则通过“路径”(目前框架普遍的自加载PSR-4规则)实例化这个需求类,然后将这个实例化对象保存进静态成员属性,供下次调用时直接获取不必再次实例化。
  4.一个公共静态方法,访问本类的唯一入口。

如下:

<?php
/* 单例模式
 * 静态属性在程序的运行到结束中,只加载一次,常驻内存
 */
class single{
    private static $single = []; //静态成员属性 保存本身或其它实例
    private function __construct(){} //私有构造方法
    private function __clone(){} //私有克隆法
    public static function getObj($class){
        var_dump(self::$single);
        //判断需求对象$class是否存在 存在就返回,反之则实例化这个类
        if(isset(self::$single[$class])){
            return self::$single[$class];
        }else{
            //假如我们需要此类本身($class为single)
            $obj = new $class(); //同new single();
            self::$single[$class] = $obj;//保存这个对象
        }
        return self::$single[$class];
    }
}
single::getObj('single'); //输出 array(0) { }
echo '<hr>';
single::getObj('single'); //输出 array(1) { ["single"]=> object(single)#1 (0) { } }

二、工厂模式

    也是属于创建型,顾名思义也是创建对象的方式。
  当你要生成一个复杂对象,及在不同条件下需要不同实例对象,就可以用工厂模式。它为你省去在需要不同实例对象的时候多次’new class’,只用给其需求类的类名(参数),就能返回所需实例对象。同时具备可扩展性、直面接口、隐蔽性让其调用者不必知道具体代码逻辑。
  此模式的弊点在于每当你有新需求,都需要扩展一个需求实体类,使之项目中类个数增多而且繁杂。

三类:  
  1.抽象类:定义其子类(实体类)的规则。
  2.实体类:继承抽象类必须实现其定义的方法属性,及实现需求的具体逻辑代码。
  3.工厂类:提供实体类的对象。
  
如下:

<?php
/* 工厂模式
 * 所有接口类(interface)都是抽象类(abstract),且都不能被实例化
 * interface只能有方法体不能有成员属性,一个类可以继承多个interface,而abstract只能被单继承
 * 定义interface的时候不加'class',而abstract需要
 * 在interface中的抽象方法只能是public的,默认也是public权限
 * interface不能有方法体({}换成;结束),而abstract可以有方法体
 * 当定义一个抽象方法其类也必须是抽象类
 * abstract中可以有普通函数(有方法体) 抽象函数没有方法体({}换成;结束)
 * 在子类实现abstract的方法时,其子类的可见性必须大于或等于抽象方法的定义
 */
interface role{
    //假设做一个游戏有多种人物角色,规定其必须实现的方法
    function roleName(); //不能有方法体 {}换成;结束
    function weapon();
    function genius();
}

//实体类
class warrior implements role{  //接口类必须使用implements继承 多继承以,隔开
    public function roleName(){
        return '战豪';
    }
    public function weapon(){
        return '剑、戟、枪';
    }
    public function genius(){
        return '每次攻击回复100气血';
    }
}

class mage implements role{
    public function roleName(){
        return 'N维空间';
    }
    public function weapon(){
        return '法杖、水晶球、毛笔';
    }
    public function genius(){
        return '每释放一个法术便增加5%移速持续10s,最多叠加6层';
    }
}

//工厂
class factory{
     public static function getObj($class){
         switch ($class){
             case 'warrior':
                 $obj = new warrior();
                 break;
             case 'mage':
                 $obj = new mage();
                 break;
             default:
                 $obj = null;
         }
         return $obj;
     }
}
$obj1 = factory::getObj('warrior');
$obj2 = factory::getObj('mage');
echo '战士:'.$obj1->roleName().'使用武器 '.$obj1->weapon().',天赋是'.$obj1->genius().'<hr>';
//输出 战士:战豪使用武器 剑、戟、枪,天赋是每次攻击回复100气血
echo '法师:'.$obj2->roleName().'使用武器 '.$obj2->weapon().',天赋是'.$obj2->genius();
//输出 法师:N维空间使用武器 法杖、水晶球、毛笔,天赋是每释放一个法术便增加5%移速持续10s,最多叠加6层

三、观察者模式

    属于行为型模式,一个对象状态发生改变时就会通知所依赖的对象。
  通俗点说就是当一个事件发生,就会触发跟这个事件牵连的一系列操作。
  例如:
  当某用户登录后,需要给其推送邮件,又要按喜好给其推荐文章,还要通知其粉丝上线消息…等等一系列依赖操作;
  当某商城有用户下单,需要给用户发优惠券,又要给用户增添积分,还要通知商家订单信息…等等又是一系列依赖操作。
  这时候我们可以用观察者模式很方便、优雅、便于管理的解决这些问题。例子中用户登录、下单这些事件就是被观察者,而依赖这个事件的发邮件、推送文章、发优惠券…都是观察者,我们联想到OOP中来,被观察者这个对象发生改变时,就可以通知所有依赖它的观察者对象。
  当一个对象改变,依赖其对象很多的时候(及一对多关系),就可以使用观察者模式。

    1.观察者:做依赖操作的实体类。
  2.被观察者:发生事件的对象,得依项目场景来看,可以是一个类或一句代码。
  3.通知:给观察者做维护的类,注册保存对象、删除对象、执行对象。

如下:

<?php
/*观察者模式*/
abstract class observer  //定义观察者规则 及必须实现的方法
{   //假如用户下单,需要给商户推送短信、微信推送、邮箱推送
    abstract protected function style($merchant); //推送信息的模板
    abstract protected function push($obj);  //推送逻辑代码
}

/*观察者 微信推送和短信推送*/
//微信推送
class wechat extends observer{
    public function style($merchant){
        $this->push($merchant);
    }
    protected function push($obj){
        echo '给'.$obj.'微信推送成功 ';
    }
}

//短信推送
class short extends observer{
    public function style($merchant){
        $this->push($merchant);
    }
    protected function push($obj){
        echo '给'.$obj.'短信推送成功 ';
    }
}

//邮箱推送
class email extends observer{
    public function style($merchant){
        $this->push($merchant);
    }
    protected function push($obj){
        echo '给'.$obj.'邮件推送成功';
    }
}

/*通知 维护观察者及存放 删除 执行*/
class subject{
    private $observerList = []; //保存观察者对象
    public function register($class){
        //注册观察者 本质就是把观察者对象存到$observerList中
        $this->observerList[] = $class;
    }
    public function runPush($merchant){
        //一次性统一执行(通知)所有依赖(观察者)
        $info = $this->observerList;
        foreach($info as $v){
            $v->style($merchant);
        }
    }
}

/*被观察者 用户下订单*/
//订单
class order{
    static public function place($user){
        echo $user.'下了一个订单';
    }
}

$obj = new subject();
//注册(存放)观察者
$obj->register(new wechat());
$obj->register(new short());
$obj->register(new email());
order::place('用户');  // 输出 用户下了一个订单
$obj->runPush('商户'); // 输出 给商户微信推送成功 给商户短信推送成功 给商户邮件推送成功

四、职责链模式

    还可以称为责任链模式,也是属于行为型模式,接收者以链式处理请求。
  该模式将请求对象和接收对象分离。当一个请求过来,接收对象如果处理不了该请求就会传给下一个接收对象(也可以说是传给上级)。
  其实很像我们经常使用的if sele/switch语句,当请求过来条件不满足就会往下传,“明明一个判断语句能解决的问题,为什么我们要用该模式了?”。
  该模式远比普通判断语句灵活,判断语句是固定的从上往下传,但是职责链模式可以将请求传给任意一个接收者,而且不用担心此请求处理不了使系统奔溃;并且对于业务变动,普通判断语句会牵涉到很多地方需要修改代码。职责链模式却不会,它将请求和接收分离,两者之间不存在耦合

    1.抽象类:抽象定义接收者的上级,抽象规定接收者需实现属性,一个将请求传递到上级的普通方法。该抽象类解决耦合性。
  2.实体类:也就是接收者对象,具体处理请求代码。
  
如下:

<?php
/*职责链模式
 *假设去鞋店买一款鞋子,鞋店没有找代理商 代理商也没有 最终找厂家
 *(鞋店->代理商->厂家)我们把鞋子分为三种类型(就以1,2,3代替)
 *鞋店只有‘1’类型,代理商有‘1’‘2’,厂家有全部类型
 */
abstract class duty  //抽象接收者
{
    protected $superior; //申明下个接收对象
    public function pass($obj){
        $this->superior = $obj; //设置下个接收对象
    }
    abstract public function request($request); //处理请求函数
}

//鞋店
class shop extends duty{
    public function request($request){
        if($request == '1'){  //鞋店只有1号鞋子
            echo '鞋店有('.$request.')号鞋子,成功出货'; return;
        }else if(empty($this->superior)){ //如果下个接收者不存在 则设置
            $this->superior = new agent(); //设置下个接收者(代理商)
        }
        $this->superior->request($request); //传个下个接收者(代理商)
    }
}

//代理商
class agent extends duty{
    public function request($request){
        if($request == '1'||$request == '2'){
            echo '代理商有('.$request.')号鞋子,成功出货'; return;
        }else if(empty($this->superior)){
            $this->superior = new factoay(); //设置下个接收者(厂家)
        }
        $this->superior->request($request); //传给下个接收者(厂家)
    }
}

//厂家
class factoay extends duty{
    public function request($request){
        if($request == '1'||$request == '2'||$request == '3'){
            echo '厂家有('.$request.')号鞋子,成功出货';
        }else{
            echo '抱歉没有这号鞋子';
        }
    }
}

//买1号鞋子
$obj = new shop();
$obj->request('1'); //鞋店有(1)号鞋子,成功出货
//买2号鞋子
$obj->request('2'); //代理商有(2)号鞋子,成功出货
//买3号鞋子
$obj->request('3'); //厂家有(3)号鞋子,成功出货

$obj = new agent();
$obj->request('3'); //厂家有(3)号鞋子,成功出货

$obj = new shop();
$obj->pass(new factoay()); //如果没货直接找厂家
$obj->request('3'); //厂家有(3)号鞋子,成功出货

$obj = new factoay();
$obj->request('3'); //厂家有(3)号鞋子,成功出货

五、适配器模式

    属于结构型模式,给两个不兼容的接口提供桥梁。
  当我们系统已经做好某个功能的API对接,此时出现了新需求而加入新的API对接,其中有可能会牵涉到很多代码修改,那么如何保证不需修改大量代码,又可以在两个或多个接口间随意调用。
  该模式会提供一个公共接口(桥梁 / 适配器),实则就是提供一个单一类,通过该类来调用任意一个独立接口,它负责在接口间的切换。
  适合用于一个功能涉及到多种相似接口的时候,如支付(微信、支付宝、银联)、登录(QQ、新浪)…等。
  
  1.接口/实体类:调起接口/功能类的具体逻辑代码。
  2.适配器:单一类,接口间/类间切换。
如下:

<?php
/* 适配器模式
 * 假如业务上需满足QQ、新浪登录
 * 主要简述在软件应用系统中,适配器的实现过程
 */
interface loginType{    //定义适配器接口规范
    function login();   //调起登录
    function callback(); //获取回调信息
}

//适配器
class loignAdaptor implements loginType
{
    public $adaptor;
    public function __construct($obj){
        $this->adaptor = $obj; //将对象(接口) 放入$adaptor中
    }

    public function login(){
        $this->adaptor->login(); //使用$adaptor中已放入对象的login()方法
    }

    public function callback(){
        $this->adaptor->callback(); //同上
    }
}

//QQ登录
class qqLogin{
    public function login(){
        echo '调起QQ登录';
    }

    public function callback(){
        echo 'QQ登录成功,获取回调信息';
    }
}

//新浪登录
class sinaLogin{
    public function login(){
        echo '调起新浪登录';
    }

    public function callback(){
        echo '新浪登录成功,获取回调信息';
    }
}

//qq登录
$obj = new loignAdaptor(new qqLogin());
$obj->login();    //输出 调起QQ登录
$obj->callback(); //输出 QQ登录成功,获取回调信息

//新浪登录
$obj = new loignAdaptor(new sinaLogin());
$obj->login();    //输出 调起新浪登录
$obj->callback(); //输出 新浪登录成功,获取回调信息

六、装饰器模式

    属于结构型模式,对一个类进行装饰。
  该模式在一个现有类的基础上扩展新功能,听起来类似子类继承。
  不同之处在于此模式中装饰类和被装饰类都是独立个体,互不影响、不耦合,另外装饰器模式相对较灵活,但也复杂。
  当你想扩展一个类,又不想影响其现有类,就可以使用它。
 
  1.实体类:当前现有类。
  2.装饰基类:装饰类父类,可以是一个抽象类
  3.装饰类:实现装饰类的具体逻辑代码。

如下

<?php
/* 装饰器模式
 * 假如我们描述一个果盘,现有类里有苹果,然后再分别定义两个装饰类 梨和橘
 */
abstract class compote{  //首先定义一个抽象类 果盘
    abstract function fruits();
}

//现有实体类
class apple extends compote {
    public function fruits(){
        echo '果盘里有苹果';
    }
}

//定义装饰抽象类(基类)
//因为是同样是抽象类 所以直接继承父类抽象属性
abstract class decorator extends compote {  //继承果盘抽象类  和定义装饰类规则
    protected $classObj;
    public function getObj($obj){
        $this->classObj = $obj;
    }
    public function objCompote(){
        if(!empty($this->classObj)) {
            $this->classObj->fruits();   //执行classObj中的 fruits();
        }
    }
}

//装饰类 梨
class pear extends decorator{
    public function fruits(){
        $this->objCompote();
        echo ' 梨子';
    }
}

//装饰类 橘
class orange extends decorator{
    public function fruits(){
        $this->objCompote();
        echo ' 橘子';
    }
}

$obj = new apple();
$obj->fruits();      //输出 果盘里有苹果

//添加装饰  梨子
$obj1 = new pear();
$obj1->getObj($obj); //当前$obj1对象中$classObj属性包含$obj对象
$obj1->fruits();     //输出 果盘里有苹果 梨子

//添加装饰  橘子
$obj2 = new orange();
$obj2->getObj($obj1); //当前$obj2中$classObj属性包含$obj1对象,且$obj1中$classObj又包含了$obj
$obj2->fruits();      //输出 果盘里有苹果 梨子 橘子

/* 综上模式中 利用基类方法getObj()包含存入对象,且呈现了一种叠加方式 
 * 当我们最终使用fruits()时,其中调用基类方法objCompote() 将存储对象的$classObj属性一层层剥开。
 *
 * 记录一个知识点:
 *    “当两个类继承同一个基类(父类)时,其两者各自的父类也是相对独立存在”,
 *    就以上例子可以看出 成员属性$classObj赋值没有被重定义,而是实现了叠加。
 */

七、桥接模式

    属于结构型模式,通过桥接结构解耦抽象化和实体,使两者独立。
  其中抽象化并不是指抽象类,这里说的是一种相对的抽象方,此模式中抽象方通过桥接引用实体方。
  当处理两者或两者以上的种类,每个种类又有多个类型,这种多维多级多变化的复杂结构问题时,该模式通过桥接手段使每种类型可以独立变化,减少互相之间的影响和耦合,且易于扩展。
  
  1.抽象方基类:定义一个成员属性保存实体方对象,实现桥接引用。
  2.抽象方:继承基类并实现抽象方具体逻辑代码的实体类。
  3.实体方:实现实体方具体逻辑代码的实体类。
  
如下:

<?php
/* 桥接模式
 * 假如一件产品有塑料、合金、压缩复合纸三种材质类型,
 * 又对应有圆形、方形、三角形,还分别具有红、蓝两种颜色
 * 把三材质看成抽象方,形状和颜色为实体方
 */

//定义材质(抽象方) 抽象类
abstract class texture{
    public $shape;  //定义一个保存形状对象的属性 做为桥接引用
    public $color;  //定义一个保存颜色对象的属性 做为桥接引用
    public function __construct($shapeObj,$colorObj){
        $this->shape = $shapeObj;
        $this->color = $colorObj;
    }
    abstract public function texType();
}
//塑料
class plastic extends texture{
    public function texType(){
        echo '塑料材质、'.$this->shape->shaType().'、'.$this->color->colType();
    }
}
//合金
class metal extends texture{
    public function texType(){
        echo '合金材质、'.$this->shape->shaType().'、'.$this->color->colType();
    }
}
//压缩复合纸
class paper extends texture{
    public function texType(){
        echo '压缩复合纸材质、'.$this->shape->shaType().'、'.$this->color->colType();
    }
}

//实体方
//定义形状抽象类
abstract class shape{
    abstract public function shaType();
}
//圆形
class round extends shape{
    public function shaType(){
        return '圆形';
    }
}
//方形
class square extends shape{
    public function shaType(){
        return '方形';
    }
}
//三角形
class triangle extends shape{
    public function shaType(){
        return '三角形';
    }
}

//定义颜色抽象类
abstract class color{
    abstract public function colType();
}
//红色
class red extends color{
    public function colType(){
        return '红色';
    }
}
//蓝色
class blue extends color{
    public function colType(){
        return '蓝色';
    }
}

//塑料材质
$obj = new plastic(new round(),new blue());
$obj->texType(); //输出 塑料材质、圆形、蓝色
//合金材质
$obj = new metal(new square(),new red());
$obj->texType(); //输出 合金材质、方形、红色
//压缩复合纸材质
$obj = new paper(new triangle(),new blue());
$obj->texType(); //输出 压缩复合纸材质、三角形、蓝色


/*******************本文持续更新*******************/

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值