PHP设计模式
1. 学习目标
2. 设计模式介绍
2.1 设计模式是什么?
- 设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结,是一种在编程过程中发现的习惯性用法
- 使用设计模式可以更快开发出健壮的程序。能够有效的提高代码的可重用性、让代码更容易被他人理解、保证代码的可靠性
3. 常用的设计模式
- 单例模式
- 确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例单例模式
- 工厂模式
- 客户类和工厂类分开。客户需要某种产品,只需向工厂请求即可。客户无须修改就可以接纳新产品。缺点是当产品修改时,工厂类也要做相应的修改
- 观察者模式
- 定义一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象在发生变化时,会通知所有观察者,并使他们自动更新
- 命令链模式
- 命令链中每个处理程序都会自行判断自己能否处理请求。如果可以,该请求被处理,进程停止
- 策略模式
- 针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换
3.1 单例模式
- 确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例单例模式
- 单例模式的要点有三个:
- 某个类只能有一个实例/只生成一个对象;
- 它必须自行创建这个实例;
- 它必须自行向整个系统提供这个实例。
- 根据上述要点,我们可以总结出一些特点:
- 构造函数私有
- 当构造函数为私有时,不能直接实例化类,只能在类内部实例化,这是要防止对象在类的外部被实例化
<?php
header("Content-type:text/html;charset=utf-8");
class Db
{
private static $_instance = null;
private function __construct()
{
var_dump('数据库连接');
var_dump('被创建了');
}
public static function getInstance(){
if(self::$_instance === null){
self::$_instance = new self;
var_dump('第一次被调用');
}else{
var_dump('已创建');
}
return self::$_instance;
}
private function __clone(){}
public function select()
{
var_dump('查询数据库');
}
public function insert()
{
var_dump('插入数据库');
}
}
$db = Db::getInstance();
$db = Db::getInstance();
$db = Db::getInstance();
$db->select();
$db->insert();
?>
注:要加上 私有克隆函数 __clone,防止外部克隆对象,
因为克隆对象是值传递,那就变成2个对象了,和单例模式的概念不符。
3.2 工厂模式
- 客户类和工厂类分开。客户需要某种产品,只需向工厂请求即可。客户无须修改就可以接纳新产品。缺点是当产品修改时,工厂类也要做相应的修改
- 工厂模式的最大优点在于创建对象上面,就是把创建对象的过程封装起来,这样随时可以产生一个新的对象。
- 减少代码进行复制粘帖,耦合关系重,牵一发动其他部分代码。
- 通俗的说,以前创建一个对象要使用new,现在把这个过程封装起来了,全部交由工厂类创建。
- 假设不使用工厂模式:那么很多地方调用类a,代码就会这样子创建一个实例:new a(), new a(),假设某天需要把a类的名称修改,意味着很多调用的代码都要修改。
- 工厂模式的优点就在创建对象上。建立一个工厂(一个函数或一个类方法)来制造新的对象,它的任务就是把对象的创建过程都封装起来, 创建对象不是使用new的形式了。而是定义一个方法,用于创建对象实例。
<?php
header("Content-type:text/html;charset=utf-8");
abstract class Db
{
abstract public function connect();
abstract public function query();
}
class Mysql extends Db
{
public function connect()
{
echo 'Mysql';
}
public function query()
{
echo '';
}
}
class Oracle extends Db
{
public function connect()
{
echo 'Oracle';
}
public function query()
{
echo '';
}
}
class MsServer extends Db
{
public function connect()
{
echo 'MsServer';
}
public function query()
{
echo '';
}
}
class MongoDB extends Db
{
public function connect()
{
echo 'MongoDB';
}
public function query()
{
echo '';
}
}
class DbFactory
{
public static function createFactory($dbName){
$db = null;
switch ($dbName) {
case 'mysql':
$db = new Mysql;
break;
case 'oracle':
$db = new Oracle;
break;
case 'oracle':
$db = new Oracle;
break;
case 'msserver':
$db = new MsServer;
break;
case 'mongodb':
$db = new MongoDb;
break;
}
$db->connect();
return $db;
}
}
$mysql = DbFactory::createFactory('mysql');
$oracle = DbFactory::createFactory('oracle');
3.3 观察者模式
- 定义一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象在发生变化时,会通知所有观察者,并使他们自动更新
- 场景:一个事件发生后,要执行一连串更新操作。传统的编程方法,就是在事件的代码之后直接加入处理逻辑。当更新的逻辑增多之后,代码会变得难以维护。这种方式是耦合的,侵入式的,增加新的逻辑需要修改事件主体的代码。
- 观察者模式实现了低耦合,非侵入式的通知与更新机制。
- 例如:京东商城有个功能叫做
降价通知
,就是你登录后点击进入商品详情页,里面有个叫做降价通知,我们点击降价通知后,京东就会帮我们做一个登记,当商品真正降价的时候,调用短信接口,发短信通知我们。下面使用观察者模式模拟一下场景:
- 本模式中有两类类,一种是被观察类,一种是观察类
- 在被观察类中注册观察类,当被观察类变化时,通知所有已注册的观察类
- 被观察类 商品
- 观察类 要降价商品的人
interface People
{
function sendMsg( $product );
}
interface Product
{
function addPeople( $people );
}
class UserList implements Product
{
private $_userList = array();
public function send( $name )
{
foreach( $this->_userList as $val ){
$val->sendMsg( $name );
}
}
public function addPeople( $userame )
{
$this->_userList[] = $userame;
}
}
class gg implements People
{
public function sendMsg( $product )
{
echo '亲爱的gg,'.$product.'降价啦'.'<br />';
}
}
class zz implements People
{
public function sendMsg( $product )
{
echo '亲爱的zz,'.$product.'降价啦'.'<br />';
}
}
$ul = new UserList();
$ul->addPeople(new gg());
$ul->addPeople(new zz());
$ul->send('iphone');
3.4 命令链模式
- 命令链中每个处理程序都会自行判断自己能否处理请求。如果可以,该请求被处理,进程停止。
- 注册多个下属,当老板发生变化时,所有的下属就像接收到命令那样执行
- 命令链模式跟观察者模式,虽然都是设计模式。但是两者很不一样。命令链模式是一系列像链式一样的操作。而观察者模式,是事件监听一样,在某个操作前,操作中,或操作后,触发一个其他的动作,来处理相应的逻辑。
- 例如:淘宝APP有个功能叫做
88会员
,就是你登录后点击进入个人中心,里面有个88会员
,我们点击88会员
后,就可以看到88会员
带来的各种权限,以及使用这些权限。下面使用命令链模式模拟一下场景,我们下单买一件商品88会员
3500,普通用户
4000,那么当我们下单的时候,就会检测是否88会员
,是则3500可以购买,不是那只能4000购买了;
interface MyCommand{
public function onCommand($name);
}
class Register{
private $_commands = array();
public function addCommand($cmd){
$this->_commands[] = $cmd;
}
public function runCommand($name){
foreach($this->_commands as $key => $val){
if($val->onCommand($name)){
return;
}
}
}
}
class CommonCommand implements MyCommand{
public function onCommand($name){
if($name != '普通会员'){
return false;
}
echo '我是普通会员,买这件商品需要4000<br />';
return true;
}
}
class Vip88Command implements MyCommand{
public function onCommand($name){
if($name != '88会员'){
return false;
}
echo '我是88会员,买这件商品需要3500<br />';
return true;
}
}
$shell = new Register();
$shell->addCommand(new CommonCommand);
$shell->addCommand(new Vip88Command);
$shell->runCommand('普通会员');
$shell->runCommand('88会员');
3.5 策略模式
- 它体现面向对象重要思想,封装变化点!通过传入不同策略类,分离并封装变化点(这里的变化点就是――――算法)
- 针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。
- 下面模拟一个超市打折场景,vip用户打8折,vip10周年纪念卡折上5折,超市清仓装修大甩卖全场5折;
interface Customer
{
function Count($total);
}
class Common implements Customer
{
public function Count($total)
{
return $total;
}
}
class Vip implements Customer
{
public function Count($total)
{
return 0.8 * $total;
}
}
class Vip10 implements Customer
{
public function Count($total)
{
$vip = new Vip;
$v = $vip->Count($total);
return 0.5 * $v;
}
}
class Clear implements Customer
{
public function Count($total)
{
return 0.5 * $total;
}
}
class Calculator
{
public $total = 0;
public function getTotal($customer)
{
return $customer->Count($this->total);
}
}
$cal = new Calculator;
$cal->total = 100;
echo $cal->getTotal(new Vip);
echo $cal->getTotal(new Vip10);
echo $cal->getTotal(new Clear);
4、课堂总结
5、课后练习