标签 设计模式 下的文章

命令模式解决了行为者与请求者过于紧耦合。即命令模式将一个请求指定一个响应者的模式进行了解耦化。
命令模式:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,请求排队或记录日志已经执行可撤销的操作。
命令模式的优点:第一、比较容易的设计一个队列;第二、比较容易的命令写入日志;第三、允许接收请求的一方决定是否要否决请求;第四、比较容易的实现请求的撤销和重做;第五、增加新命令不影响其他类;第六、把请求者和响应者分离。
场景:淘宝下订单,紧耦合就是点击下单按钮,请求直接发送给响应者。使用设计模式的命令模式进行解耦操作,就是点击下订单,请求发送给一个中央订单处理系统,然后由中央订单处理系统这个中间件分发给淘宝订单中心、天猫订单中心、聚划算订单中心等不同的操作,同时添加日志记录等。以PHP为代码环境来说明命令模式。

<?php
class OrderCenter{
    public $orderList;
    public function setOrder($order){
        //没有库存
        $isStock = true;
        //秒杀活动没有开始
        $isActivityTime = true;
        if(!$isStock && !$isActivityTime){
            exit('活动没开始,即将跳转回之前的页面');
        }else{
            $this->orderList[] = $order;
        }
    }
    public function cancelOrder(){

    }
    //假定是一个队列服务
    public function queue(){
        foreach($this->orderList as $key=>$order){
            echo '订单处理成功<br>';
            $order->setOrder();
            unset($this->orderList[$key]);
        }
    }
}
class Order{
    public function buyClothes(){
        echo '购买衣服成功<br>';
    }
    public function buyShoes(){
        echo '购买鞋子成功<br>';
    }
}
abstract class Command{
    public $request;
    public function __constrcut($requestObj){
        $this->request = $requestObj;
    }
    public function setOrder(){}
}
class TaoBao extends Command{
    public function __construct($requestObj){
        parent::__constrcut($requestObj);
    }
    public function setOrder(){
        echo '淘宝订单:<br>';
        $this->request->buyClothes();
        $this->request->buyShoes();
    }
}
class Tmall extends Command{
    public function __construct($requestObj){
        parent::__constrcut($requestObj);
    }
    public function setOrder(){
        echo '天猫订单:<br>';
        $this->request->buyClothes();
        $this->request->buyShoes();
    }
}
//客户端/接口
$orderObj = new Order();
$taobaoObj = new TaoBao($orderObj);
$tmallObj = new Tmall($orderObj);
$orderCenterObj = new OrderCenter();
$orderCenterObj->setOrder($taobaoObj);
$orderCenterObj->setOrder($tmallObj);
$orderCenterObj->queue();

桥接模式,就是实现系统可能有多角度分类,每一种分类都有可能变化,可能增加或减少。那么,就把这种多角度分离出来让他们独自变化,减少它们之间的耦合。
比如,现在的智能手机,安卓的ipk文件就不能安装在苹果的ios系统上。分类一(按照手机操作系统来分):手机系统分为安卓和IOS,安卓的软件分为游戏、音乐等,IOS的软件也分为游戏、音乐等。分类二(按照软件来分):软件分为游戏和音乐,游戏分为安卓游戏和IOS游戏,音乐也分为安卓音乐和IOS音乐。
思考:增加一个手机系统,如Windos 8。那么,分类就变了。如下:分类一(按照手机操作系统来分):手机系统分为安卓和IOS和Windows 8,安卓的软件分为游戏、音乐等,IOS的软件也分为游戏、音乐等,Windows 8的软件也分为游戏和音乐等。分类二(按照软件来分):软件分为游戏和音乐,游戏分为安卓游戏和IOS游戏和Windows 8游戏,音乐也分为安卓音乐和IOS音乐和Windows 8音乐。
这种分类的弊病非常明显,如果要增加一个手机的操作系统,相应的需要改变原有分类,增加大量的class文件。操作系统和手机软件的分类是高强度耦合。
在面向对象的变成里,耦合度越高的越不利于复用,设计模式的重点就是降低耦合,减少复制-粘贴的编码模式。在这个例子中,引出了一种设计模式,叫做桥接模式。
桥接模式:将抽象的部分与它的实现分离,使他们可以独立变化。
所谓的实现,就是抽象类和派生类用来实现自己的对象。白话文就是:就是实现系统可能有多角度分类,每一种分类都有可能变化,可能增加或减少。那么,就把这种多角度分离出来让他们独自变化,减少它们之间的耦合。
桥接模式的分类:手机操作系统分为安卓和IOS,手机软件分为游戏和音乐。操作系统和软件相互独立,没有强的明显关系,操作系统和手机软件不进行分类关联,它们俩的真实关系请继续往后看。
那么,耦合度降低了,增加windows8只需要在操作系统分类下增加一个windows8,软件则不需要变化。
再引入一个原则,合成-聚合复用原则:尽量使用合成-聚合,尽量不要使用类的继承。
聚合是弱拥有关系,A可以包含B,但是B不是A的一部分。合成是强拥有关系,严格的部分和整体,两者拥有相同的生命周期。
大雁的翅膀和大雁本身是合成关系,生命周期一样,是强拥有关系。大雁和雁群是聚合关系,大雁只属于一个雁群,但是雁群不仅仅只有这一个大雁。(本例摘自《大话设计模式》)
回到上一个话题,在桥接模式的分类下,手机操作系统和手机软件之间的关系,就是聚合关系,弱的关系,才可以降低耦合。
以PHP为代码环境,说明手机的例子。

<?php
abstract class Soft{
    public function run(){}
}
class Game extends Soft{
    public function run(){
        echo '手机游戏正在运行...<br>';
    }
}
class Mp3 extends Soft{
    public function run(){
        echo '手机音乐播放器正在运行...<br>';
    }
}
abstract class OS{
    protected $softObj;
    public function setSoftObj($softObj){
        $this->softObj = $softObj;
    }
    public function run(){}
}
class Ios extends OS{
    public function run(){
        $this->softObj->run();
    }
}
class Android extends OS{
    public function run(){
        $this->softObj->run();
    }
}
//客户端/接口
echo '购买了Iphone一台,搭载IOS操作系统<br>';
$iphone = new Ios();
$iphone->setSoftObj(new Game());
$iphone->run();
$iphone->setSoftObj(new Mp3());
$iphone->run();

echo '购买了三星一台,搭载安卓操作系统<br>';
$samsung = new Ios();
$samsung->setSoftObj(new Game());
$samsung->run();
$samsung->setSoftObj(new Mp3());
$samsung->run();

单例模式,顾名思义,单个的实例,就是对某个对象,只new一次。单例模式是设计模式常见的一种,用来创建封装好的类的唯一一个实例,这样一来,可以严格控制客户怎么样访问它以及何时访问它,对唯一实例的受控访问。
单例模式:保证一个类只有一个实例,并提供一个访问它的全局访问点。
单例模式如何防止一个类被多次new呢?首先,每个类都有一个构造函数,即使没有显式的声明,也是以public存在的,将构造函数设为private。其次,让该类保存实例化后的对象,并提供一个对外的接口。
示例场景:mysql中user表。以PHP为代码环境,来模拟设计模式中的单例模式。

<?php
class UserMysqlModel{
    public function get(){
        echo '获取user表的数据<br>';
    }
    public function set(){
        echo '写入user表的数据<br>';
    }
    public function edit(){
        echo '修改user表的数据<br>';
    }
    public function del(){
        echo '删除user表的数据<br>';
    }
}
class UserBusiness{
    private static $userMysqlModelObj = '';
    private function __construct(){

    }
    public static function getInstance(){
        if(empty(self::$userMysqlModelObj)){
            self::$userMysqlModelObj = new UserMysqlModel();
        }
        return self::$userMysqlModelObj;
    }
}
//客户端/接口
$userBusinessObj = UserBusiness::getInstance()->get();
$userBusinessObj = UserBusiness::getInstance()->set();
$userBusinessObj = UserBusiness::getInstance()->edit();
$userBusinessObj = UserBusiness::getInstance()->del();

迭代器模式,将一个列表从头到尾或者从尾到头进行一次遍历。迭代器模式是被提名要求废除的一种设计模式。因为很多的高级语言,如PHP,Python,JAVA等,都已经拥有了foreach。
迭代器模式:提供一种方法顺序,来访问一个聚合中的各个元素,而不暴露该对象的内部表示。
迭代器模式用来访问一个列表的第一个,最后一个,或者某一个的下一个。
以PHP为代码环境模拟一下迭代器模式的思想。抽象一个Iterator类的理由是有可能是正序,有可能倒序查找。

<?php
abstract class Iterator{
    public function first(){

    }
    public function last(){

    }
}
//正序
class PositiveOrder extends Iterator{
    private $list;
    private $listTmp;
    public function __construct($list){
        $this->list = $list;
        $this->listTmp = array_values($list);
    }
    public function first(){
        return $this->listTmp[0];
    }
    public function last(){
        return $this->listTmp[(count($this->listTmp)-1)];
    }
}
//倒序
class ReverseOrder extends Iterator{
    private $list;
    private $listTmp;
    public function __construct($list){
        $this->list = $list;
        $this->listTmp = array_values($list);
    }
    public function first(){
        return $this->listTmp[(count($this->listTmp)-1)];
    }
    public function last(){
        return $this->listTmp[0];
    }
}

我们还可以去模拟获取某个元素的下一个元素等。
在PHP中,活跃社区的各个开发者都在为PHP的明天做贡献,为我们提供了已经封装好的函数,用来操作PHP数组的内部指针。

next();    定位指针到当前位置的后一个
prev();       定位指针到当前位置的前一个
reset();   重置指针到数组的开始
end();     定位指针到数组的最后
current(); 取得当前指针位置的值
key();     取得当前指针位置的键

使用示例:

<?php
$arr=array("php"=>"脚本","python"=>"脚本","mysql"=>"数据库");
while(list($key,$value)=each($arr)){
    echo $key.'============>'.$value.'<br>';
}
/**
 * 输出结果
 * php============>脚本
 * python============>脚本
 * mysql============>数据库
 */
reset($arr);
while(list($key,$value)=each($arr)){
    echo $key.'============>'.$value.'<br>';
}
/**
 * 输出结果
 * php============>脚本
 * python============>脚本
 * mysql============>数据库
 */
reset($arr);
next($arr);
while(list($key,$value)=each($arr)){
    echo $key.'============>'.$value.'<br>';
}
/**
 * 输出结果
 * python============>脚本
 * mysql============>数据库
 */
end($arr);
echo current($arr).'=============>'.key($arr).'<br>';
/**
 * 输出结果
 * mysql============>数据库
 */
prev($arr);
echo current($arr).'=========>'.key($arr).'<br>';
/**
 * 输出结果
 * python============>脚本
 */

组合模式告诉我们,对待部分和对待整体是一样的。整体和部分就是总部和分部的关系。使用设计模式中的组合模式,客户端不需要知道它调用的到底是整体的接口还是部分的接口。北京总公司为整体,下属有上海分公司,北京总公司财务,北京总公司人事。上海分公司下属有上海分公司财务,上海分公司财务。这就是整体与部分的关系,是组合模式的使用前提。需求中是体现部分和整体的结构时,用户不需要关心是在使用整体的对象还是单个对象而是使用统一的接口对象时,就可以考虑设计模式中的组合模式了。
组合模式:可以用整体-部分的结构来表示一个对象的结构层次。使用组合模式使得用户对组合对象和单个对象没有不同的感受。
场景:北京总公司,上海分公司,北京财务部,上海财务部的公司结构。以PHP为代码环境。代码仅仅为了说明组合模式,并不推荐实际开发中的使用。

<?php
//具体的公司
class ConcreteCompany{
    public $name;
    private $companyList = array();
    public function __construct($name){
        $this->name = $name;
    }
    public function add($obj){
        $this->companyList[] = $obj;
    }
    public function display(){
        foreach($this->companyList as $company){
            echo '--';
            echo $company->name;
            echo '<br>';
            if(!empty($company->companyList)){
                echo '--';
                $company->display();
            }
        }
    }
}
class Department{
    public $name;
    public function __construct($name){
        $this->name = $name;
    }
    public function add(){
        echo '已经是最小分类了,不能再细分了<br>';
    }
    public function display(){
        echo '--';
        echo $this->name;
        echo '<br>';
    }
}
//客户端/接口
$headCompany = new ConcreteCompany('北京总公司');
$headCompany->add(new Department('北京总公司财务部'));
$headCompany->add(new Department('北京总公司人事部'));

$concreteCompany1 = new ConcreteCompany('上海分公司');
$concreteCompany1->add(new Department('上海分公司财务部'));
$concreteCompany1->add(new Department('上海分公司人事部'));
$headCompany->add($concreteCompany1);

$concreteCompany2 = new ConcreteCompany('青岛分公司');
$concreteCompany2->add(new Department('青岛分公司财务部'));
$concreteCompany2->add(new Department('青岛分公司人事部'));
$headCompany->add($concreteCompany2);

echo '全公司组织架构:<br>';
$headCompany->display();
echo '<br><br>上海分公司组织架构:<br>';
$concreteCompany1->display();
echo '<br><br>上海分公司组织架构:<br>';
$concreteCompany2->display();