十二种常见设计模式代码详解

Posted eyes++

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了十二种常见设计模式代码详解相关的知识,希望对你有一定的参考价值。

零:设计模式分类

设计模式有创建型模式、结构型模式与行为型模式

  • 创建型:单例模式、工厂模式(简单工厂,工厂方法,抽象工厂)
  • 结构型:适配器模式、门面模式、装饰器模式、注册树模式、代理模式、管道模式
  • 行为型:策略模式、观察者模式、命令模式、迭代器模式

1. 创建型模式

创建型模式的一个重要思想就是封装,利用封装,把直接获得一个对象改为通过一个接口获得一个对象

  • 单例模式:我们把对象的生成从new改为通过一个静态方法,通过静态方法的控制,使得我们总是返回同一个实例给调用者,确保了系统只有一个实例。
  • 工厂模式:与单例模式相同,生成对象改为接口,还可以通过传参实例化不同的类
    • 简单工厂:为不同的类提供一个生产对象的工厂。要获取类的对象只能通过工厂生产获取
    • 工厂方法模式:为工厂方法提供接口,规范接口方法。每一个工厂针对一个类生产对象
    • 抽象工厂模式:在不指定具体类的情况下创建一系列相关或依赖对象,通常创建的类都实现相同的接口

2. 结构型模式

解析类和对象的内部结构和外部组合,通过优化程序结构解决模块之间的耦合问题

种类:

  • 装饰器模式
  • facade门面模式
  • 注册树模式(IOC/DI)
  • 适配器模式
  • Pipeline模式
  • 代理模式

3. 行为型模式

行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎么相互协作共同完成单个对象都无法单独完成的任务,它设计算法与对象间职责的分配

种类:

  • 策略模式
  • 观察者模式
  • 命令模式
  • 迭代器模式

一:单例模式

单例模式(singleton),即一种类的设计只会最多产生一个对象的设计思想

作用:

  • 主要用在数据库应用中,因为一个应用中会存在大量的数据库操作,使用单例模式则可以避免大量的new操作消耗资源。
  • 如果系统中需要有一个类来全局控制某些配置信息,那么使用单例模式可以很方便的实现
  • 在一次页面请求中,便于进行调试,因为所有的代码(例如数据库操作类)会集中在一个类中,我们可以在类中设置钩子,输出日志,从而避免到处var_dump、echo

应用场景:数据库连接、缓存操作、分布式存储

单例模式的要点:

  • 构造函数需要标记为private(访问控制:防止外部代码使用new操作符创建对象),单例类不能在其他类中实例化,只能被其自身实例化
  • 拥有一个保存类的实例的静态成员变量
  • 拥有一个访问这个实例的公共的静态方法(常用getInstance()方法进行实例化单例类,通过instanceof操作符可以检测到类是否已经被实例化)
    • 简称"三私一公":
    • 私有化构造方法:不让外部产生多个对象
    • 私有化克隆方法:不允许对象被克隆对象产生新对象
    • 私有化静态属性:运行进入类内部产生对象
    • 公有化静态方法:保存已经产生的对象
<?php
/*
 * 单例模式
 */

// 数据库操作类
class Dbsql

    // 私有属性存储对象
    private static $ins = null;

    // 私有化构造方法,避免在类外产生对象
    private function __construct()

    // 公有静态方法,创建实例
    public static function createIns()
    
        // 不存在对象就创建对象
        self::$ins == null ? new self() : self::$ins;
        return self::$ins;
    

    // 私有化克隆方法,避免在类外克隆对象
    private function __clone()

    // 功能方法
    // ...


$m1 = Dbsql::createIns();
$m2 = Dbsql::createIns();

var_dump($m1 === $m2);  // bool(true)

二:工厂模式

工厂模式是我们最常用的实例化对象模式,是用工厂方法代替new操作的一种模式。

优点:如果你想更改所实例化的类名等,则只需更改该工厂方法内容,不需要注意寻找代码中具体实例化的地方(new处)修改。为系统结构提供零花的动态扩展机制,减少了耦合。

分类:

  • 简单工厂模式
  • 工厂方法模式
  • 抽象工厂模式

1. 简单工厂模式

简单工厂模式又称静态工厂模式,之所以这么说,是因为简单工厂模式是通过一个静态方法来创建对象的。

<?php
/*
 * 简单工厂
 */
class Dbmysql
    public function conn()
    
        echo "连接Mysql";
    


class DbSqlit

    public function conn()
    
        echo "连接sqlite";
    


class DbFactory

    public static function createIns($type)
    
        // 这么写的话,如果我要连接Oracle,就需要修改代码,不符合开闭原则
        switch ($type)
        
            case "mysql":
                return new DbMysql();
            case "sqlit":
                return new DbSqlit();
            default:
                throw new ErrorException("error type!");
        
    


// 客户端
$mysql = DbFactory::createIns("mysql");
$mysql->conn();

$mysql = DbFactory::createIns("sqlit");
$mysql->conn();

2. 工厂方法模式

简单工厂模式实现了产品类的代码跟客户端代码分离,但会有一个问题,优秀的代码是符合开闭原则,如果你要加一个C类产品,就要修改工厂类里面的代码,也就是要增加语句如:switch—case,对于这个问题,工厂方法模式可以解决

工厂方法模式就是为配一个产品提供一个独立的工厂类,通过不同的工厂实例来创建不同的产品实例。

优点:

  • 拥有良好封装性,代码结构清晰。对于每一个对象的创建都是有条件约束的。如:调用一个具体的产品对象,只需要知道这个产品的类名和约束参数就可以了,不用知道创建对象自身的复杂程度,降低模块之间的耦合度。
  • 拥有良好的拓展性,新增一个产品类,只需要适当的增加工厂类或者扩展一个工厂类,如下面这个例子,当需要增加一个数据库Oracle的操作,只需要增加一个Oracle类,厂类不用修改任务就可以完成系统扩展。
  • 屏蔽产品类。这一特点非常重要,产品类的实现如何变化,调用者都不需要关心,他只关心产品的接口,只要接口保持不变,系统中的上层模块就不要发生变化。

使用场景:

  • 支付宝、微信、银联的连接方式(connectMode),支付方式(payMode)。使用工厂模式,客户就不需要知道具体的连接方式和支付方式了,只需要调用connectMode和payMode即可
  • MySQL、SQL Server、Oracle等数据库的连接方式(connectMode)、查询方式(selectMode)等操作可以使用工厂模式进行封装
<?php
/*
 * 工厂方法
 */
interface Db

    public function conn();


class DbMysql implements Db

    public function conn()
    
        echo "连接mysql";
    


class DbSqlit implements Db

    public function conn()
    
        echo "连接Sqlit";
    


interface Factory

    public static function createIns();


class MysqlFactory implements Factory

    public static function createIns()
    
        return new DbMysql();
    


$mysql = MysqlFactory::createIns();

3. 抽象工厂模式

抽象工厂模式的用意为:给客户提供一个接口,可以创建多个产品族中的产品对象,而且使用抽象工厂模式还要满足条件:

  • 系统中有多个产品族,而系统一次只可能消费其中一族产品
  • 同属于一个产品族的产品可以使用

产品族:位于不同产品等级结构中,功能相关联的产品组成的家族。下面例子中的汽车和空调就是两个产品树,奔驰C200使用了格力某型号空调,那他俩就是一个产品族,同理,奥迪A4和海尔某型号空调也是一个产品族

<?php
/*
 * 抽象工厂
 */
interface AutoProduct

    public function dirve();


// 奥迪A4
class AudiA4Product implements AutoProduct

    public function dirve()
    
        echo "奥迪A4";
    


// 奔驰C200
class BenzC200Product implements AutoProduct

    public function dirve()
    
        echo "奔驰C200";
    


interface AirCondition

    public function blow();


// 格力
class GreeAirCondition implements AirCondition

    public function blow()
    
        echo "格力空调";
    


// 海尔
class HeierAirCondition implements AirCondition

    public function blow()
    
        echo "海尔空调";
    


# 工厂接口
interface Factory

    public function getAuto();
    public function getAirCondition();


# 工厂A  奔驰C200 + 格力
class AFactory implements Factory

    public function getAuto()
    
        return new BenzC200Product();
    
    public function getAirCondition()
    
        return new GreeAirCondition();
    

    public static function createIns()
    


# 工厂B  奥迪4 + 海尔
class BFactory implements Factory

    public function getAuto()
    
        return new AudiA4Product();
    
    public function getAirCondition()
    
        return new HeierAirCondition();
    

    public static function createIns()
    


// 工厂运作
$auto_carA = AFactory::getAuto();
$auto_airA = AFactory::getAirCondition();

$auto_carB = BFactory::getAuto();
$auto_airB = BFactory::getAirCondition();

三:适配器模式

概念:将某个类的接口转换成与另一个接口兼容。适配器通过将原始接口进行转换,给用户提供一个兼容接口,使得原来因为接口不同而无法一起使用的类可以得到兼容

应用场景:老代码接口不适应新的接口需求,或者代码很多很乱不便于继续修改,或者使用第三方类库

<?php
/*
 * 适配器模式
 */
interface Weather

    public function show();


class PHP_Weather implements Weather

    public function show()
    
        // 得到的天气信息
        $weather = [
            "weather"   => "小雨",
            "tep"       => 6,
            "win"       => 3
        ];
        return serialize($weather);
    


$weather = new PHP_Weather();
$info = unserialize($weather->show());
echo "天气:$info['weather']<br>";
echo "温度:$info['tep']<br>";
echo "风力:$info['win']<br>";

// 把上面的类变成兼容,使得py、java能直接使用
interface WeatherAdapter

    public function getWeather();

// java接入的接口
class Java_Weather implements WeatherAdapter

    protected $weather;
    public function __construct(Weather $weather) 
        $this->weather = $weather;
    
    public function getWeather()
    
        $info = unserialize($this->weather->show());
        // 即转为json格式
        return json_encode($info);
    

四:装饰器模式

装饰器模式又叫装饰者模式,装饰模式是在不必改变原类文件和使用继承的情况下,动态的拓展一个对象的功能。他是通过创建一个包装对象,也就是装饰来包裹真实的对象。

实例:

  • 组件对象的接口:可以给这些对象动态的添加职责
  • 所有装饰器的父类:需要定义一个与组件接口一致的接口,并持有一个Component对象,该对象其实就是被装饰的对象
  • 具体的装饰器类:实现具体要向被装饰对象添加的功能。用来装饰具体的组件对象或者另外一个具体的装饰器对象。

使用场景:

  • 需要动态的给一个对象添加功能,这些功能可以再动态的撤销
  • 需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变得不现实
  • 当不能采用生成子类的方法进行扩充时.一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸式增长。另一种情况可能是因为类定于被隐藏,或类定义不能用于生成子类
<?php
/*
 * 装饰器模式
 */

// 组件对象接口
interface IComponent

    // 动态的给对象添加功能
    public function display();


// 待装饰的对象
class Person implements IComponent

    protected $name;
    public function __construct($name)
    
        $this->name = $name;
    
    public function display()
    
        echo "装饰的" . $this->name . "<br>";
    


// 所有装饰器的父类
class Clothes implements IComponent

    protected $component;
    public function Decorate(IComponent $component)
    
        $this->component = $component;
    
    public function display()
    
        if(!empty($this->component)) 
            $this->component->display();
        
    


// 具体的装饰器
class Shoes extends Clothes

    public function display()
    
        echo "鞋子";
        parent::display();
    

class Overcoat extends Clothes

    public function display()
    
        echo "外套";
        parent::display();
    


$qd = new Person("乔丹");

$shoes = new Shoes();
$shoes->Decorate($qd);
$shoes->display();

$coat = new Overcoat();
$coat->Decorate($qd);
$coat->display();

五:注册树模式

注册树模式也叫注册模式或注册器模式,顾名思义,注册树就是把对象实例注册到一颗全局的对象树上,需要对象的时候就从书上取下来,就好比书上长的果子,需要的时候就摘一个下来,只是这个对象树的果子是摘不完的

不管前面用单例还是工厂建立的对象,都是一个个游兵散将,没有得到很好的管理,那如果用了注册树模式,就可以把创建出来的对象注册到全局树上,需要的时候取下来用,可以很好的管理创建的对象

<?php
/*
 * 注册树模式
 */
class DbMysql

    public function conn()
    
        echo "连接Mysql";
    

class DbSqlite

    public function conn()
    
        echo "连接sqlite";
    

class MysqlFactory

    public static function getIns()
    
        return new DbMysql();
    

class SqliteFactory

    public static function getIns()
    
        return new DbSqlite();
    


// 注册数实现存储对象(IOC控制反转思想)
class RegisterTree

    protected static $objects;
    // 添加对象到注册树中
    public static function set($alias, $object)
    
        self::$objects[$alias] = $object;
    
    // 从注册树中获取对象
    public static function get($alias)
    
        return self::$objects[$alias];
    
    // 销毁注册树上的对象
    public function __unset($alias)
    
        unset(self::$objects[$alias]);
    


// 注册
RegisterTree::set("mysql", MysqlFactory::getIns());
RegisterTree::set("sqlite", SqliteFactory::getIns());

// 客户端
$mysql = RegisterTree::get("mysql");
$mysql->conn();

$sqlite = RegisterTree::get("sqlite");
$sqlite->conn();

六:门面模式

门面模式(Facade)又称外观模式,用于为子系统中的一组接口提供一致的界面。门面模式定义了一个高级接口,这个接口使得子系统更加容易使用:引入门面角色之后,用户只需要直接与门面角色交互,用户与子系统之间的复杂关系由门面角色来实现,从而降低了系统的耦合度


作用:

  • 为一些复杂的子系统提供一组接口
  • 提高子系统的独立性
  • 在层次化结构中,可以使用门面模式定义系统的每一层接口

优点:

  • 他对客户屏蔽了子系统组件,因而减少了客户处理的对象的数目并使得子系统使用起来更加方便
  • 实现了子系统与客户之间的松耦合关系
  • 如果应用需要,他不限制他们使用子系统类。因此可以在系统易用性与能用性之间加以选择
<?php
/*
 * 门面模式
 */

class Camera

    public function turnOn()
    
        echo "打开摄像机";
    
    public function turnOff()
    
        echo "关闭摄像机";
    


class Light

    public function turnOn()
    
        echo "打开灯";
    
    public function turnOff()
    
        echo "关闭灯";
    


// 门面类
class Facade

    private $_camera;
    private $_light;
    public function __construct()
    
        $this->_camera = new Camera();
        $this->_light = new Light();
    
    // 启动接口
    public function activate()
    
        $this->_camera->turnOn();
        $this->_light->turnOn();
    
    // 关闭接口
    public function deactivate()
    
        以上是关于十二种常见设计模式代码详解的主要内容,如果未能解决你的问题,请参考以下文章

详解SSTI模板注入

MongoDB十二种最有效的模式设计

Linux负载均衡集群LVS十二种调度算法

设计模式-简单工厂工厂方法模式抽象工厂模式详解

设计模式抽象工厂模式 ( 简介 | 适用场景 | 优缺点 | 产品等级结构和产品族 | 代码示例 )

EXT的layout十二种布局