目录

设计模式

本文是PHP常用的设计模式的梳理。

接口 与 类

interface Db_adpter{
    public function connect($config);
    public function query($query, $handle);
}

class Mysql implements Db_adpter{
    private $dblink;
    public function connect($config){}
    public function query($query, $handle){}
}

class SQLite implements Db_adpter{
    private $dblink;
    public function connect($config){}
    public function query($query, $handle){}
}

工厂模式

使用函数或者类方法生成对象,而不是在代码中直接new,使用工厂模式的一个考虑就是,假如在系统中多次使用了这个类new对象,如果该类发生改变,那么所有new代码都需要改正,但是如果把new这一操作封装到工厂方法里,就只需要改工厂里new的代码就行了,所有使用到该对象的地方都不用改。

class  Factory {
    static function createDb(){
        $db = new Database ;
        return $db;
    }
}
Factory::createDb();

单例模式

保证程序运行时,只能存在某个类的一个实例对象!比如链接MySQL的对象,链接Memcache的对象。

<?php
class Mem extends Memcache{
    private static $mem_instance = null;
    private function __construct(){      // 在外部就不能new对象了
        parent::__construct();
    }
    private function __clone(){}         // 私有
    public static function get_mem_ins(){
        if(null === self::$mem_instance){
            self::$mem_instance = new Memcache;
        }
        return self::$mem_instance;
    }
}
$mem = Memcache::get_mem_ins();  //调用

注册树模式

用来将一些全局需要使用的 对象/变量 挂载到注册树上,程序任何地方都可以通过注册器树取到想要的 对象/变量

class Register{
    protected static $objects;
    static function set($alias,$object){
        self::$objects[$alias] = $object;
    }
    static function _unset(){
        unset(self::$objects[$alias]);
    }
    public function get($alias){
        return slef::$objects[$alias];
    }
}

适配器模式

将不同的 函数接口/对象 封装成统一的 API,比如 PHP 数据库操作有 mysql mysqli pdo 3 种,可以使用适配器模式统一成一致。类似的有 cache适配器,将Memcache redis/file/apc等不同缓存函数统一成一致

实现思路:定义一个接口约束 Interface DB ,里面定义了要实现的方法,MySQL,MySQLi,PDO 分别是这个接口的三种实现。

<?php
// 适配器接口
interface Database{
    function connect($host,$user,$passwd,$dbname);
    function query($sql);
    function close();
}

class MySQL implements Database{
    function connect($host,$user,$passwd,$dbname){
    }
    function query($sql){
    }
    function close(){
    }
}

class MySQLi implements Database{
    // ... 类似的,实现接口的3个函数
}

class PDO implements Database{
    // ... 类似的,实现接口的3个函数
}

$db = new MySQL(); // 这边根据需要可以随意将 MySQL 替换成 MySQLi 或者 PDO,三种实现任意切换,
$db->connect('127.0.0.1','root','root','test');
$db->query('show databases');
$db->close();

策略模式

将一组特定的行为和算法封装成类,以适应某些特定的上下文环境,比如一个电商系统,针对男/女生用户分别要展示不同的商品类目,并且所有广告位展示不同的广告

如果在代码中使用if else区分用户,分别实现,那么 当新增加一类用户时,比如儿童,所有的if else都要作相应的调整

// 不使用策略模式
if (男生){
    // 展示男生目录
}else if(女生){
    // 展示女生目录
}else{
    // 展示儿童目录
}
if (男生){
    // 展示男生广告
}else if(女生){
    // 展示女生广告
}else{
    // 展示儿童广告
}
if (男生){
    // 展示男生广告
}else if(女生){
    // 展示女生广告
}else{
    // 展示儿童广告
}

约定一个接口interface Show{...} ,根据上下文环境,分别实现 n 个符合接口的策略类

interface UserStrategy{
    function showAd();
    function showCategory();
}
class ManUserStrategy implements UserStrategy{
    function showAd(){}
    function showCategory(){}
}
class WomenUserStrategy implements UserStrategy{
    function showAd(){}
    function showCategory(){}
}
class ChildUserStrategy implements UserStrategy{ }

if(男性){
    $strategy = 'ManUserStrategy';
}else if(女性){
    $strategy = 'WomenUserStrategy';
} else{
    // 假如新增了一类用户,只需要新增一个策略继承UserStrategy
    // 然后这里的选择策略多一个判断而已,下面调用策略实现功能的代码都是一样的
    $strategy = 'ChildUserStrategy';
}

// 下面的代码都是统一的
$user_strategy = new $strategy();
$user_strategy -> showCategory();
$user_strategy -> showAd();

使用策略模式 实现 IOC 解耦合 控制反转 依赖倒置

  • 依赖: 类 A 调用了 类 B , 称 类 A 依赖于类 B

  • 耦合: 类 A 调用了 类 B 或者 类 B 调用了 类 A ,称类 A 与类 B 之间有耦合关系

  • 耦合度: 类之间依赖程度的一种量化描述

  • 控制: 类 A 调用了 类 B 和 类 C ,称 类 A 控制着 类 B 类 C

  • 假如 A 类里面,需要使用到 B 类的对象帮助其实现功能,那么 A 是依赖 B 的,这是一种紧耦合

class Page{
    // 展示页面
    function index(){
        if(男性){
            $strategy = new ManUserStrategy();
        }else{
            $strategy = new WomenUserStrategy();
        }
        $strategy -> showAd();
        $strategy -> showCategory();
    }
}
// Page 需要使用到 ManUserStrategy/WomenUserStrategy 帮助其实现功能
// 所以 Page 依赖于ManUserStrategy/WomenUserStrategy
$page = new Page();
$page -> index();

但是我们通过约定接口,传递符合接口的策略对象进入 Page 对象,那么 Page 类对于每个具体的策略没有依赖关系,只对约定的接口有依赖关系,这就解除 Page 对于 ManUserStrategy/WomenUserStrategy 的耦合关系了

class Page{
    function index(UserStrategy $strategy){
        $strategy -> showAd();
        $strategy -> showCategory();
    }
}

// 判断使用哪一种策略
if(男性){
    $strategy = 'ManUserStrategy';
}else{
    $strategy = 'WomenUserStrategy';
}

// 在执行的过程中,判断出使用哪一种策略,将该策略传入需要使用该策略的对象,从而实现了该策略与对象的绑定
$page = new Page();
$page -> index(new $strategy());

数据对象 映射模式

对象数据存储映射起来,对一个对象的操作 会 映射为对数据存储的操作,最典型的实现是 ORM,对象的属性映射到数据库的字段,对属性的值的修改,会映射成数据库记录的修改,这样可以使 PHP 对数据库的操作的代码显得更加优雅

class User{
    protected $db;
    public $id;
    public $name;
    public $mobile;
    public $regtime;
    function __construct($id){
        $this -> db = new Mysqli('127.0.0.1','root','root','test'); // 链接到数据库
        $res = $this -> db -> query("select * from test where id = $id"); // 获取 该id 的用户记录
        // 将查询出来的数据库 记录 映射为 对象的属性
        $this -> id = $res['id'];
        $this -> name = $res['name'];
        $this -> mobile = $res['mobile'];
        $this -> regtime = $res['regtime'];
    }
    function __destruct(){
        $sql = "update test set name = '{$this -> name}',mobile = '{$this -> mobile}',";
        $sql .= "regtime = '{$this -> regtime}' where id = '{$this -> id}'";
    }
}

$user = new User(25); // 初始化类的对象,获取了 id 为 25的用户的记录,并且映射成了对象
$user -> name = "codekissyoung"; // 修改对象属性的值
// 代码运行结束,__destruct 会自动运行,将对属性的修改 写回到数据库

依赖倒置原则(Dependence Inversion Principle, DIP)

DIP  的核心思想是上层定义接口,下层实现这个接口, 从而使得下层依赖于上层,降低耦合度,提高整个系统的弹性。这是一种经实践证明的有效策略。

控制反转(Inversion of Control, IoC)

而 IoC 是一种实现 DIP 的方法。IoC 的核心是将类(上层)所依赖的单元(下层)的实例化过程交由第三方来实现。一个简单的特征,就是类中不对所依赖的单元有诸如 $component = new yiicomponentSomeClass() 的实例化语句。

依赖注入(Dependence Injection, DI)

DI 是 IoC 的一种设计模式,是一种套路,按照 DI 的套路,就可以实现 IoC,就能符合 DIP 原则。 DI 的核心是把类所依赖的单元的实例化过程,放到类的外面去实现。

 控制反转容器(IoC Container)

当项目比较大时,依赖关系可能会很复杂。 而 IoC Container 提供了动态地创建、注入依赖单元,映射依赖关系等功能,减少了许多代码量。 Yii 设计了一个 yiidiContainer 来实现了 DI Container。

服务定位器(Service Locator)Service Locator

是 IoC 的另一种实现方式, 其核心是把所有可能用到的依赖单元交由 Service Locator 进行实例化和创建、配置, 把类对依赖单元的依赖,转换成类对 Service Locator 的依赖。 DI 与 Service Locator 并不冲突,两者可以结合使用。 目前,Yii2.0 把这 DI 和 Service Locator 这两个东西结合起来使用,或者说通过 DI 容器,实现了 Service Locator。

组合模式 Composite

  • 组合比继承提供更多的灵活性

  • 将一组对象组合为单个对象

  • 对于客户端代码来说,组合内部是透明的

class UnitException extends Exception {

}

abstract class Unit {
    function getComposite(){
        return null;
    }
    function addUnit ( Unit $unit ) {
        throw new UnitException( get_class( $this )." is a leaf ");
    }
    function removeUnit( Unit $unit ){
        throw new UnitException( get_class( $this )." is a leaf " );
    }
    abstract function bombardStrength();
}

abstract class CompositeUnit extends Unit {
    private $units = [];

    function getComposite(){
        return $this;
    }

    protected function units(){
        return $this -> units;
    }

    function removeUnit ( Unit $unit ) {
        $this -> units = array_udiff( $this -> units , array($unit) , function ($a ,$b){ return ($a === $b)?0:1;});
    }

    function addUnit( Unit $unit ){
        if( in_array($unit ,$this -> units,true) ){
            return;
        }
        $this -> units[] = $unit;
    }
}

// 射手
class Archer extends Unit {
    function bombardStrength() {
        return 4;
    }
}

// 激光炮
class LaserCannonUnit extends Unit {
    function bombardStrength(){
        return 44;
    }
}

// 军队 : 由军队 / 射手 / 激光炮组成
class Army extends Unit {
    private $units = [];
    private $armies = [];

    function addUnit( Unit $unit ){
        if( in_array( $unit , $this -> units , true ) ){
            return;
        }
        $this -> units[] = $unit;
    }

    function removeUnit( Unit $unit ){
        $this -> units = array_udiff( $this -> units , [$unit] , function($a,$b){ return ($a == $b) ? 0 : 1; } );
    }

    function addArmy (Army $army){
        array_push( $this -> armies , $army );
    }

    function bombardStrength(){
        $ret = 0;
        foreach( $this -> units as $unit ){
            $ret += $unit -> bombardStrength();
        }
        foreach( $this -> armies as $army){
            $ret += $army -> bombardStrength();
        }
        return $ret;
    }
}


// army 对象
$main_army = new Army();
$main_army -> addUnit( new Archer() );
$main_army -> addUnit( new LaserCannonUnit() );

// 另一个 army对象
$sub_army = new Army();
$sub_army -> addUnit( new Archer() );
$sub_army -> addUnit( new Archer() );
$sub_army -> addUnit( new Archer() );

// 将一部分军队加入到另一部分军队中
$main_army -> addArmy( $sub_army );
print "攻击强度: {$main_army -> bombardStrength() } \n";

装饰模式 Decorator

  • 通过在运行时,合并对象来拓展功能的一种灵活机制

<?php
abstract class Tile {
    abstract function getWealthFactor();
}

class Plains extends Tile {

    private $wealthfactor = 2;

    function getWealthFactor(){
        return $this -> wealthfactor;
    }
}

abstract class TileDecorator extends Tile {
    protected $tile;
    function __construct( Tile $tile ){
        $this -> tile = $tile;
    }
}

class DiamondDecorator extends TileDecorator{
    function getWealthFactor() {
        return $this -> tile -> getWealthFactor() + 2;
    }
}

class PollutionDecorator extends TileDecorator{
    function getWealthFactor(){
        return $this -> tile -> getWealthFactor() - 4;
    }
}

$tile = new Plains();
print $tile -> getWealthFactor(); // 2

$tile = new DiamondDecorator( new Plains() );
print $tile -> getWealthFactor(); // 4

$tile = new PollutionDecorator( new DiamondDecorator(new Plains()) );
print $tile -> getWealthFactor(); // 0
  • 多个装饰器,犹如管道被串联起来

<?php
class RequestHelper{}

abstract class ProcessRequest {
    abstract function process( RequestHelper $req );
}

class MainProcess extends ProcessRequest {
    function process( RequestHelper $req ){
        print __CLASS__." : doing something userful with request ! \n";
    }
}

abstract class DecorateProcess extends ProcessRequest{
    protected $processrequest;
    function __construct( ProcessRequest $pr ){
        $this -> processrequest = $pr;
    }
}

class LogRequest extends DecorateProcess{
    function process ( RequestHelper $req ) {
        print __CLASS__." : logging data \n";
        $this -> processrequest -> process( $req );
    }
}

class AuthRequest extends DecorateProcess{

    function process ( RequestHelper $req ) {
        print __CLASS__." : authenticating data \n";
        $this -> processrequest -> process( $req );
    }
}

class StrucRequest extends DecorateProcess{
    function process ( RequestHelper $req ) {
        print __CLASS__." : struc request data \n";
        $this -> processrequest -> process( $req );
    }
}

$process = new AuthRequest( new StrucRequest( new LogRequest( new MainProcess() )) );
$process -> process( new RequestHelper() );
// AuthRequest : authenticating data
// StrucRequest : struc request data
// LogRequest : logging data
// MainProcess : doing something userful with request !

外观模式 Facade

  • 为复杂多变的系统创建一个简单的接口

  • 只是为一个分层或者一个子系统创建一个单一的入口,这样会带来许多好处,比如 有助于分离项目中的不同部分,其次对于客户端开发者来说,访问代码变得非常简洁

  • 另外只在一个地方调用子系统,减少了出错的可能性

<?php
// 测试文件 test_facade
// 234-ladies_jumper 55
// 532-gents_hat 44

function getProductFileLines($file){
    return file($file);
}

function getProductObjectFromId($id,$productname){
    return new Product($id,$productname);
}

function getNameFromLine($line){
    if(preg_match("/.*-(.*)\s\d+/",$line,$array)){
        return str_replace('_',' ',$array[1]);
    }
    return '';
}

function getIDFromLine($line){
    if(preg_match("/^(\d{1,3})-/",$line,$array)){
        return $array[1];
    }
    return -1;
}

class Product{
    public $id;
    public $name;
    function __construct($id,$name){
        $this -> id = $id;
        $this -> name = $name;
    }
}

// 外观模式封装
class ProductFacade{
    private $products = [];
    private $file;

    function __construct($file){
        $this -> file = $file;
        $this -> compile();
    }

    private function compile(){
        $lines = getProductFileLines($this -> file);
        foreach($lines as $line){
            $id = getIDFromLine($line);
            $name = getNameFromLine($line);
            $this -> products[$id] = getProductObjectFromID($id,$name);
        }
    }

    function getProducts(){
        return $this -> products;
    }

    function getProduct($id){
        return $this -> products[$id];
    }
}

$lines = getProductFileLines('test_facade');
$objects = [];
foreach($lines as $line){
    $id = getIDFromLine($line);
    $name = getNameFromLine($line);
    $objects[$id] = getProductObjectFromID($id,$name);
}
var_dump($objects);

// 使用外观模式
$facade = new ProductFacade("test_facade");
$product = $facade -> getProduct('234');
var_dump($product);

Single 单例模式

  • 该对象可以被系统中任何对象使用

  • 该对象不会存储在全局变量中

  • 系统中有且只有一个该对象

<?php
class Preferences {
    private $props = [];
    private static $instance;

    private function __construct(){}

    public function setProperty ( $key , $val ) {
        $this -> props[$key] = $val;
    }

    public function getProperty( $key ){
        return $this -> props[$key];
    }

    public static function getInstance(){
        if( empty( self::$instance ) ){
            self::$instance = new self();
        }
        return self::$instance;
    }
}

$pref = Preferences :: getInstance();
$pref -> setProperty("name","codekissyoung");

unset($pref);
$pref2 = Preferences :: getInstance();
print $pref2 -> getProperty("name")."\n";

Factory Method 工厂模式

  • 抽象类高于实现

  • 工厂模式就是为了解决,在抽象时,如何创建对象实例,答案就是用特定的类来处理实例化

<?php
abstract class AppEncoder {/*{{{*/
    abstract function encode();
}/*}}}*/

class BloggsApptEncoder extends AppEncoder {/*{{{*/
    function encode(){
        return "Appointment data encoded in BloggsCal format \n";
    }
}/*}}}*/

class MegaApptEncoder extends AppEncoder {/*{{{*/
    function encode(){
        return "Appointment data encoded in MegaCal format \n";
    }
}/*}}}*/

// 对象工厂 用于按需求生产对象
class CommsManager {

    const BLOGGS = 1;
    const MEGA   = 2;
    private $mode = 1;

    function __construct( $mode ){
        $this -> mode = $mode;
    }

    function getHeaderText() {
        switch( $this -> mode ){
            case ( self::MEGA ):
                return "MegaCal header \n";
                break;
            default:
                return "BloggsCal header \n";
                break;
        }
    }

    function getApptEncoder(){
        switch( $this -> mode ){
            case (self::MEGA):
                return new MegaApptEncoder();
                break;
            default:
                return new BloggsApptEncoder();
                break;
        }
    }
}

$comms = new CommsManager( CommsManager::MEGA );
$appEncoder = $comms -> getApptEncoder();
print $comms -> getHeaderText();
print $appEncoder -> encode();
  • 缺点 : 工厂里面有很多条件语句,如果我们加入新协议,就又要加条件,如果要加入新方法,比如getFooterText(),又要每个可能生成的对象写一遍条件

<?php
abstract class AppEncoder {/*{{{*/
    abstract function encode();
}/*}}}*/

class BloggsApptEncoder extends AppEncoder {/*{{{*/
    function encode(){
        return "Appointment data encoded in BloggsCal format \n";
    }
}/*}}}*/

class MegaApptEncoder extends AppEncoder {/*{{{*/
    function encode(){
        return "Appointment data encoded in MegaCal format \n";
    }
}/*}}}*/

// 对抽象工厂使用多态
abstract class CommsManager {/*{{{*/
    abstract function getHeaderText();
    abstract function getApptEncoder();
    abstract function getFooterText();
}/*}}}*/

class BloggsCommsManager extends CommsManager {
    function getHeaderText(){
        return "BloggsCal Header";
    }
    function getApptEncoder(){
        return new BloggsApptEncoder();
    }
    function getFooterText(){
        return "BloggsCal Footer";
    }
}

$blog = BloggsCommsManager::getApptEncoder();
print $blog -> encode();

Abstract Factory 抽象工厂模式

<?php
abstract class CommsManager {
    const APPT = 1;
    const TTD = 2;
    const CONTACT = 3;

    abstract function getHeaderText();

//    abstract function getApptEncoder();
//    abstract function getTtdEncoder();
//    abstract function getContactEncoder();

    // 抽象工厂
    abstract function make( $type );
    abstract function getFooterText();

}

class BloggsCommsManager extends CommsManager {

    function getHeaderText(){
        return "BloggsCal header";
    }
/*
    function getApptEncoder(){
        return new BloggsApptEncoder();
    }

    function getTtdEncoder(){
        return new BloggsTtdEncoder();
    }

    function getContactEncoder(){
        return new BloggsContactEncoder();
    }

*/
    function make ( $type ) {
        switch($type){
            case self::APPT:
                return new BloggsApptEncoder();
                break;
            case self::TTD:
                return new BloggsTtdEncoder();
                break;
            case self::CONTACT:
                return new BloggsContactEncoder();
                break;
        }
    }

    function getFooterText(){
        return "BloggsCal footer \n";
    }
}

Prototype 原型模式

<?php
class Sea{
    private $navigability = 0;
    function __construct( $navigability ){
        $this -> navigability = $navigability;
    }

}
class EarthSea extends Sea {}
class MarsSea extends Sea {}

class Plains {}
class EarthPlains extends Plains{}
class MarsPlains extends Plains{}

class Forest{}
class EarthForest extends Forest {}
class MarsForest extends Forest {}

class TerrainFactory {

    private $sea;
    private $forest;
    private $plains;

    function __construct(Sea $sea , Plains $plains ,Forest $forest){
        $this -> sea = $sea;
        $this -> plains = $plains;
        $this -> forest = $forest;
    }

    function getSea(){
        return clone $this -> sea;
    }

    function getPlains(){
        return clone $this -> plains;
    }

    function getForest() {
        return clone $this -> forest;
    }

}

$factory = new TerrainFactory( new EarthSea( -1 ) ,new EarthPlains() ,new EarthForest() );
print_r( $factory -> getSea() );
print_r( $factory -> getPlains() );
print_r( $factory -> getForest() );

深复制 和 浅复制

<?php
class Contained{}

// 浅复制 : $obj_copy = clone $object; 假如被复制对象里面有属性也是对象,也只是复制该属性,也就是一个引用,而不会复制对象本身
// 深复制 : 通过 __clone 魔术方法,指定在clone的时候,将对象内的对象也一并复制

class Container {
    public $con;
    function __construct(){
        $this -> con = new Contained();
    }
    // 深复制
    function __clone(){
        $this -> con = clone $this -> contained;
    }
}

Single Responsibiliy Priciple 单一职责原则

class Cook {
    public function meal(){ echo "meal done", PHP_EOL;}
    public function drink(){ echo "drink done",PHP_EOL;}
    public function ok(){echo "ok",PHP_EOL;}
}

interface Command{
    public function execute();
}

class MealCommand implements Command {
    private $cook;
    public function __construct(Cook $cook){
        $this->cook = $cook;
    }
    public function execute(){
        $this->cook->meal();
    }
}

class DrinkCommand implements Command{
    private $cook;
    public function __construct(Cook $cook){
        $this->cook = $cook;
    }
    public function execute(){
        $this->cook->drink();
    }
}

class cookControl{
    private $mealcommand;
    private $drinkcommand;
    public function addCommand(Command $meal, Command $drink){
        $this->mealcommand = $meal;
        $this->drinkcommand = $drink;
    }
    public function callmeal(){
        $this->mealcommand->execute();
    }
    public function calldrink(){
        $this->drinkcommand->execute();
    }
}

$control = new cookControl();
$cook = new Cook();
$mealcommand = new MealCommand($cook);
$drinkcommand = new DrinkCommand($cook);
$control->addCommand($mealcommand,$drinkcommand);
$control->callmeal();
$control->calldrink();

Interface Segregation Principle 接口隔离原则

Open Close Principle 开闭原则

interface process {
    public function process();
}
class playerencode implements process{
    public function process(){
        echo "encode", PHP_EOL;
    }
}
class playeroutput implements process{
    public function process(){
        echo "output", PHP_EOL;
    }
}
class playProcess{
    private $message = null;
    public function __construct(){}
    public function callback(event $event){
        $this->message = $event -> click();
        if($this->message instanceof process){
            $this->message->process();
        }
    }
}
class mp4{
    public function work(){
        $playProcess = new playProcess();
        $playProcess -> callback(new event('encode'));
        $playProcess -> callback(new event('output'));
    }
}
class event{
    private $m;
    public function __construct($me){
        $this->m = $me;
    }
    public function click(){
        switch ($this->m){
            case 'encode':
                return new playerencode();
                break;
            case 'output':
                return new playeroutput();
                break;
        }
    }
}
$mp4 = new mp4();
$mp4 -> work();

里氏替换原则

依赖倒置原则

interface employee {
    public function working();
}
class teacher implements employee{
    public function working(){
        echo "teaching...", PHP_EOL;
    }
}
class coder implements employee{
    public function working(){
        echo "coding ...", PHP_EOL;
    }
}
// 依赖 teacher 类
class workA{
    public function work(){
        $teacher = new teacher();
        $teacher -> working();
    }
}
// 不依赖 teacher 类
class workB{
    private $e;
    public function set(employee $e){
        $this->e = $e;
    }
    public function work(){
        $this -> e -> working();
    }
}
$a = new workA();
$a -> work();
$b = new workB();
$b -> set(new teacher());
$b -> work();