1

什么是设计模式

在软件开发过程中,经常出现的经典场景的典型解决方案,称为设计模式

如何学习设计模式

典型场景 --> 典型问题 --> 典型解决办法

多态

用来消除逻辑语句.

多态(ploymorphism)是一个生物学上的概念,指同一特种的多种表现形态.

在面向对象中,指某种对象实例的不同表现形态.

<?php

abstract class Tiger {
  public abstract function climb();
}

class XTiger extends Tiger {
  public function climb() {
    echo 'Drop' , '<br/>';
  }
}

class MTiger extends Tiger {
  public function climb() {
    echo 'Up' , '<br/>';
  }
}

class Cat {
  public function climb() {
    echo 'Fly';
  }
}

class Client {
  public static function call(Tiger $animal) { // 参数限定不严格,可以更加灵活, 可以传递一个父类类型,就可以有不同的子类形态
    $animal->climb();
  }
}

Client::call(new XTiger());
Client::call(new MTiger());
Client::call(new Cat());

?>

在23种设计模式中,可以有些模式可以自然消除的.

面向接口开发

减少new 的操作.

熟悉概念:

调用端的概念
客户端(Client)的概念

例如:在Java中,写了一堆类,最终打成一个包。调用端可以把包引入,然后使用类中定义的方法,在开发过程中可以使用查看里面的方法。

假设:开发中引入的是sdk,都不让看。如何调用,必须开放一些接口。存在面向接口开发

接口:共同的规则

面向接口开发

<?php


// 共同接口
// 服务端打包
interface DB {
  function conn();
}


// 服务端开发(不知道将会被谁调用)
class DbMysql implements DB {
  public function conn() {
    echo 'conn mysql <br/>';
  }
}

class DbSqlite implements DB {
  public function conn() {
    echo 'conn sqlite  <br/>';
  }
}


// ***客户端**** (看不到DbMysql,DbSqlite的内部实现细节)
// 只知道,上述两个类实现了db接口

$db = new DbMysql();
$db->conn();

$db = new DbSqlite();
$db->conn();

// 无论是开发者,还是调用者都是为接口负责

简单工厂模式

模式的作用:
发生连接的双方,知道的越少越好.

在面向对象设计法则中,重要的开闭原则:对于修改是封闭的,对于扩展是开放的.

<?php


// 共同接口
// 服务端打包
interface DB {
  function conn();
}


// 服务端开发(不知道将会被谁调用)
class DbMysql implements DB {
  public function conn() {
    echo 'conn mysql <br/>';
  }
}

class DbSqlite implements DB {
  public function conn() {
    echo 'conn sqlite  <br/>';
  }
}


// 简单工厂
class Factory {
  public static function createDB($type) {
    if ($type == 'mysql') {
      return new DbMysql();
    } else if ($type == 'sqlite') {
      return new DbSqlite();
    } else {
      throw new Exception('Error db type', 1);
    }
  }
}


// 客户端不知道服务端到底有哪些类名,
// 只知道对方开放了一个Factory::createDB方法.
// 静态方法允许传递数据库名称

$msqyl = Factory::createDB('mysql');
$msqyl->conn();

$sqlit  = Factory::createDB('sqlite');
$sqlit->conn();


// 如果增加oracle类型,怎么办?
// 服务端要修改Factory的内容(在Java,C++中,改完之后还得再编译)
// 在面向对象设计法则中,重要的开闭原则 --- 对于修改是封闭的,对于扩展是开放的.

// 需求可以扩展子类来实现。

工厂方法

进行扩展,避免对原有数据进行修改,只需要新增代码的子类,就可以完成。

对于修改是封闭的,对于扩展是开放的.

<?php


// 共同接口
// 数据库的接口
interface DB {
  function conn();
}

// 创造数据库的接口
interface Factory {
  function createDB();
}

// 服务端开发(不知道将会被谁调用)
class DbMysql implements DB {
  public function conn() {
    echo 'conn mysql <br/>';
  }
}

class DbSqlite implements DB {
  public function conn() {
    echo 'conn sqlite  <br/>';
  }
}


class MySqlFactory implements Factory {
  public function createDB() {
    return new DbMysql();
  }
}

class SqliteFactory implements Factory {
  public function createDB() {
    return new DbSqlite();
  }
}



// ==== 服务器端添加oracle类
// 进行扩展,避免对原有数据进行修改
class orcale implements DB {
  public function conn() {
    echo 'conn orcal <br />';
  }
}

class orcaleFactory implements Factory {
  public function createDB() {
    return new orcale();
  }
}


// ------客户端开始调用.

$fact = new MysqlFactory();
$db = $fact->createDB();
$db->conn();


$fact = new SqliteFactory();
$db = $fact->createDB();
$db->conn();

单例模式

常见使用场景:

  1. 需要数据库类的时候

  2. 操作cookie类

  3. 上传图片类

DB.class.php
Upload.class.php
Cookie.class.php
// 这三个类都需要读取配置文件信息,而配置文件是共用的,因此配置读取类有一个对象就够了。 
// (如何保证对象只有一个)

PHP对象什么时候全等

二个对象是一个的时候.

单例模式实现

  1. 封闭外部new操作

  2. 内部开公共接口,负责new操作,控制单一实例

  3. 禁止继承覆盖__construcotr

  4. 防止克隆

<?php

/**
  单例模式
*/

// 第一步,普通类

// class Single {

// }


// $s1 = new Single();
// $s2 = new Single();

// var_dump($s1, $s2);
// var_dump($s1 == $s2);

// ---------------------

// 第二步,封锁new操作
// class Single {
//   // 在new 的时候会触发魔术函数,__constructor,可以在 __constructor 魔术函数中做操作 
//   protected function __constructor() { // 把__constructor()魔术方法保护起来, 导致的后果,一个对象都没法new。(大门关上了,需要开窗)

//   }
// }

// $s1 = new Single();


// ---------------------

// 第三步,留接口来new对象
// class Single {
//    public static function getIns() { // getIns的控制权在class内部,可以在getIns做手脚
//      return new self(); // 返回自身实例
//    } 
//    protected function __constructor() {

//    }
// }

// $s1 = Single::getIns();
// $s2 = Single::getIns();

// var_dump($s1, $s2);
// var_dump($s1 == $s2);



// ---------------------

// 第四步,getIns要预先判断实例
// class Single {
//    protected static $ins = null;
//    public static function getIns() { // getIns的控制权在class内部,可以在getIns做手脚
//      if (self::$ins === null) {
//        self::$ins = new self();
//      }
//      return self::$ins;  // 返回自身实例
//    } 
//    protected function __constructor() {

//    }
// }

// $s1 = Single::getIns();
// $s2 = Single::getIns();

// var_dump($s1, $s2);
// var_dump($s1 == $s2); // true

// 问题 :继承之后 constructor 被公开, 可以使用final
// class multi extends Single {
//   public function __constructor() { // 继承之后 constructor 被公开

//   }
// } 




// ---------------------

// 第五步,final,防止继承时,被修改权限
// class Single {
//    protected static $ins = null;
//    public static function getIns() { // getIns的控制权在class内部,可以在getIns做手脚
//      if (self::$ins === null) {
//        self::$ins = new self();
//      }
//      return self::$ins;  // 返回自身实例
//    } 
//    final protected function __constructor() { // 方法前加 final,则方法不能被覆盖,类前加final,则类不能被继承。

//    }
// }

// class multi extends Single {
//   public function __constructor() { // 继承之后 constructor 被公开

//   }
// } 

// $s1 = Single::getIns();
// $s2 = clone $s1; // 克隆了,又产生了多个对象.

// var_dump($s1, $s2);
// var_dump($s1 === $s2); // true




// ---------------------

// 第六步,禁止clone
class Single {
   protected static $ins = null;
   public static function getIns() { // getIns的控制权在class内部,可以在getIns做手脚
     if (self::$ins === null) {
       self::$ins = new self();
     }
     return self::$ins;  // 返回自身实例
   } 
   final protected function __constructor() { // 方法前加 final,则方法不能被覆盖,类前加final,则类不能被继承。

   }

   // 封锁clone
   final protected function __clone() {

   }
}

$s1 = Single::getIns();
$s2 = clone $s1; // 克隆了,又产生了多个对象.

var_dump($s1, $s2);
var_dump($s1 === $s2); // true

观察者模式

一个对象变化,引起其它对象的反应。可以让其它几个对象观察变化的对象的反应.

一对多的关系.

优点:解耦。

观察者模式中的三者: Subject, Observer, Client;

// Subject
attach() // 记忆多个的对象
detach()  // 告知记忆的对象,变化情况
notify()  // 更新通知

// Observer
update() // 更新对象中执行的逻辑

// Client
// 调用添加观察者`attach()`

JavaScript实现观察者模式:

var select = document.querySelector('select');
var content = document.querySelector('.content');
var ad = document.querySelector('.ad');

// Subject
select.observer = {};
// 添加观察者
select.attach = function(key, obj) {
  this.observer[key] = obj;    
}

// 删除观察者
select.detach = function(key) {
  delete this.observer[key];
}

// 更新通知
select.onchange = select.ontify = function() {
  for (var key in this.observer) {
    this.observer[key].update(this);
  }
}

// Observer  
// 观察者
content.update = function(observer) { // 参数是被观察者对象
  alert('content');
  if (observer.value) {
    // 逻辑代码
  }
}

ad.update = function(observer) { // 参数是被观察者对象
  alert('ad');
  if (observer.value) {
    // 逻辑代码
  }
}


// Client
// 监听 
select.attach('content', content); // 只需要把独特的表示加入 对象key中
select.attach('ad', ad);

图片描述

PHP实现观察者模式:

单一功能原则:类或者一个方法,完成一个功能即可.

PHP中内置接口:
Splsubject
Splobserver
内置类:
Splobjectstorage

<?php

// PHP实现观察者

// PHP5中提供了观察者(observer)和被观察者(subject)接口

/**
  被观察者
 */
class User implements SplSubject {
  public $lognum;
  public $hobby;

  protected $observers = null;

  public function __construct($hobby) {
    $this->lognum = rand(1, 10);
    $this->hobby = $hobby;
    $this->observers = new SplObjectStorage();
  }

  public function login() { // 类或者一个方法,完成一个功能即可. (单一功能原则)
    // 操作session
    $this->notify();
  }

  public function attach(SplObserver $observer) {
    $this->observers->attach($observer);
  }

  public function detach(SplObserver $observer) {
    $this->observers->detach($observer);
  }

  public function notify() {
    $this->observers->rewind();
    while($this->observers->valid()) {
      $observer = $this->observers->current();
      $observer->update($this);
      $this->observers->next();      
    }
  }
}

/**
  观察者
*/

class Secrity implements SplObserver {
  public function update(SplSubject $subject) { // 传入的 对象是$subject,$subject是干什么,随你的意.
    if($subject->lognum < 3) {
      echo '这个第' . $subject->lognum . '次安全登录<br/>';
    } else {
      echo '这个第' . $subject->lognum . '次登录,异常<br/>';
    }
  }    
}


class Ad implements SplObserver {
  public function update(SplSubject $subject) {
    if ($subject->hobby == 'sport') {
      echo 'sport,nba <br/>';
    } else {
      echo 'good good study, day day up<br/>';
    }
  }
}


/**
  Client
*/
$user = new User('study');
$user->attach(new Secrity());
$user->attach(new Ad());

$user->login();

职责链模式

也称之为责任链模式(chain of resionbility)

对象产生的过程中,放在逻辑判断中.
面向过程和面向对象混杂在一块,没有充分拆开。

责任链模式:

  1. 权利越大的人管理越多,越严重的问题,越得往上级找,底层的人只能管理鸡毛蒜皮的小事。

  2. 每个人都有处理事情的,权利范围.

  3. 责任链模式最终都需要都一级能够处理.

  4. 先处理最近的一级

每个对象中有职责功能,上级.
每个对象,储存着对自己上级的引用,如果自己处理不了,交给上一级。

优点:使用到那一级才会new 出那一级。

<?php

header('content-type: text/html; charset=utf-8');

// 责任链模式

// 权利越大的人管理越多,越严重的问题,越得往上级找,底层的人只能管理鸡毛蒜皮的小事。
// 每个人都有处理事情的,权利范围.

class borad {
  public $power = 1; // 处理范围的权限
  protected $top = 'admin'; // 上级的范围

  public function process($lev) {
    if ($lev <= $this->power) {
      echo '删除';
    } else {
      $top = new $this->top;
      $top->process($lev);
    }
  }
}

class admin {
  public $power = 2;
  protected $top = 'police';
  public function process($lev) {
    if ($lev <= $this->power) {
      echo '封闭';
    } else {
      $top = new $this->top;
      $top->process($lev);
    }
  }
}

class police {
  protected $power;
  protected $top = null;
  public function process() {
    echo '抓!~';
  }
}

$lev = 1;

$judge = new borad(); // 距离最近的一级
$judge->process($lev);

策略模式

和工厂模式相似的一种模式

<?php

/**
  Strategy
  策略模式

  完成:加减乘除功能
*/

interface Math {
  public function calc ($op1, $op2);
}

class MathAdd implements Math {
  public function calc($op1, $op2) {
    return $op1 + $op2;
  }
}

class MathSub implements Math {
  public function calc($op1, $op2) {
    return $op1 - $op2;
  }
}

class MathMul implements Math {
  public function calc($op1, $op2) {
    return $op1 * $op2;
  }
}

class MathDiv implements Math {
  public function calc($op1, $op2) {
    if ($op2 !=0 )  return;
    return $op1 / $op2;
  }
}

// 一般思路: 根据`op`的值,制造对象,并且调用
$op = 'Add';

// 封装一个虚拟计算器,中可以 调用到实际计算器
class CMath {
  protected $calc = null;

  public function __construct($type) {
    $calc = 'Math' . $type;
    $this->calc = new $calc();
  }

  public function calc($op1, $op2) {
    return $this->calc->calc($op1, $op2);
  }
}


$cmath = new CMath($op);
var_dump($cmath->calc(10, 100));

工厂方法和策略模式的区别:
工厂方法是传递不同参数,直接把零件(子类)进行操作。
策略模式是,真实的子类,虚拟成一个父类, 父类中操作(零件)子类。不需要直接碰子类,聚合成一个父类。

装饰器模式

问题:继承层次越来越多、

<?php

// 场景:发布一篇文章

class Article {
  protected $content;

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

  public function decorator() {
    return $this->content;
  }
}

$art = new Article('goods goods study, day day up <br/>');
echo $art->decorator();

// -----------------------------------------------------
// 文章需要, 需要编辑人员专门编辑
class BianArt extends article {
  // 重新加工对decorator
  public function summary() {
    return $this->content . '编辑摘要 <br/>';
  }
}

$art = new BianArt('goods goods study, day day');
echo $art->summary();

// -----------------------------------------------------
// 文章需要, 需要做SEO
class SeoArt extends BianArt {
  public function seo() {
    $content = $this->summary();
    return $content . 'seo <br />';
  }
}

$art = new SeoArt('lz');
echo $art->seo();

// -----------------------------------------------------
// 文章需要,广告部单独管理
class Ad extends SeoArt {
  // 层次越来越深,目的是:给文章添加各种内容

}

// 继承层次越来越多,装饰器模式可以解决,变成俩级继承

使用装饰器模式,修改多级继承

父类负责主要逻辑,子类负责装饰,修饰。(重写父类的方法,装饰之后再次返回)

<?php

// 装饰器模式

class BaseArt {
  protected $content; // 文章内容
  protected $art = null; // 文章对象

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

  public function decorator() {
    return $this->content;
  }
}  

// 编辑文章
class BianArt extends BaseArt {
  
  public function __construct(BaseArt $art) {
    $this->art = $art;
    $this->decorator();
  } 

  public function decorator() {
    return $this->content = $this->art->decorator() . '编辑人员,文章摘要';
  }
}

// SEO人员
class SeoArt extends BaseArt {
  public function __construct(BaseArt $art) {
    $this->art = $art;
    $this->decorator();
  } 

  public function decorator() {
    return $this->content = $this->art->decorator() . 'SEO';
  }
}


$art = new BaseArt('day day up');
// echo $art->decorator();

$art1 = new BianArt($art);
// echo $art1->decorator();

$art2 = new SeoArt($art1);
echo $art2->decorator();

适配器模式

把不适用的格式或者数据类型,转换成适用目前场景

<?php

// 适配器模式

// 服务器端代码
class tianqi {
  public static function show() {
    $today = array('tep' => 28, 'wind' => 7, 'sun' => 1);
    return serialize($today);
  }
}   


// 增加一个代理,适配器
class AdapterTainqi extends tianqi {
  public static function show() {
    $today = parent::show();
    $today = unserialize($today);
    $today = json_encode($today);
    return $today;
  }
}



// =====客户端调用=====
$taiqni = unserialize(tianqi::show());
echo '温度:',$taiqni['tep'], '风力:', $taiqni['wind'] , '<br/>';



// java客户端,并不认识PHP的串行化后的字符串。使用适配器模式,转成想通模式。

$tq = AdapterTainqi::show();
$td = json_decode($tq);
echo '温度:',$td->tep, '风力:', $td->wind;

桥接模式

在某种场景下,多个条件有共同作用,增加耦合关系,减少逻辑复杂程度
这个世界上的因素都不是单一的,都是相互耦合的.

<?php

// 桥接模式 bridge
// 功能:论坛站内给用户发送消息,可以是站内短信,emial,手机信息

abstract class info {
  protected $send = null;

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

  abstract public function msg($content);

  public function send($to, $content) {
    $content = $this->msg($content);
    $this->send->send($to, $content);
  }
}

class zn {
  public function send($to, $content) {
    echo 'zn: ', $to, '内容是:', $content;
  }
}

class email {
  public function send($to, $content) {
    echo 'email: ', $to, '内容是:', $content;
  }
}

class sms {
  public function send($to, $content) {
    echo 'sms: ', $to, '内容是:', $content;
  }
}


class commonInfo extends info {
  public function msg($content) {
    return 'common'. $content;
  }
}

class warnInfo extends info {
  public function msg($content) {
    return 'warn'. $content;
  }
}

class dangerInfo extends info {
  public function msg($content) {
    return 'danger'. $content;
  }
}


// 站内发普通信息
$commonInfo = new commonInfo(new zn());
$commonInfo->send('小明', '吃晚饭');


// sms发普通信息
$commonInfo = new commonInfo(new sms());
$commonInfo->send('小红', '吃晚饭');

alogy
1.3k 声望121 粉丝

// Designer and Developer


« 上一篇
ECMA_OOP
下一篇 »
ECMA_RegExp