一、单例模式
单例模式指的就是使某个类的对象仅允许被创建一次
应用场景: 一般创建一个对象需要消耗过多的资源,如:访问I0和数据库等资源或者有很多个地方都用到了这个实例。
具体代码实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
<?php class MyClass { public $name; public $sex; public static $instance; private function __construct ()//构造函数私有化,防止外部new对象 { $this->name = "我的名字叫小明"; } public static function getInstanceCeshi () { if (empty(self::$instance)) { self::$instance = new self(); return self::$instance; } else { return self::$instance; } } } $myClass=MyClass::getInstanceCeshi(); echo $myClass->name;//输出:我的名字叫小明 |
这里我们需要注意几点:
- 构造函数和析构函数必须声明为私有,防止外部程序new 类从而失去单例模式的意义
- getInstance()方法必须设置为公有的,必须调用此方法 以返回实例的一个引用
- ::操作符只能访问静态变量和静态函数
- 使用单例模式生成一个对象后, 该对象可以被其它众多对象所使用
二:工厂模式
工厂模式是使用工厂类中的方法来生成某个对象的,而不是在代码中直接new该对象。使用工厂模式,可以避免当改变某个类的名字或者方法之后,在调用这个类的所有的代码中都修改它的名字或者参数。
模拟场景:我的一个项目中100个文件都使用到了第三方类库Test.php这个文件,因此在100个文件中我都需要new Test();但是某一天我将这个类的名字重命名后,改为Test1,那么我需要进入100个文件中把new Test();都改成new Test1();吗?
显然这样很不合理
解决方案代码示例说明
我拥有三个文件:(文件1:入口文件index.php)(文件2:工厂类文件:Factory.php)(文件3:第三方类库文件:Test.php)
以下是index.php代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<?php spl_autoload_register('autoload'); //注册autoload函数为自动调用函数 $test = Factory::createDatabase(); //当使用Factory类中的静态方法时,由于该文件中没有Factory这个类,它会自动调用autoload方法,载入Factory这个类 //注:当然也可以选用命名空间的形式调用该静态方法,例如:\app\controller\Factory::createDatabase() 但是请注意的是,如果是使用了命名空间的形式调用,我们需要在autoload方法中修改相应的代码,让其找到该类,并且引入 $test->index(); /** * autoload方法监听当前文件,当出现文件中不存在的类名时,将自动载入该类 * @param $class [当前文件不存在的类名] */ function autoload ($class) { $dir = __DIR__; //获取当前路径地址 $requireFile = $dir . "/" . $class . ".php"; require $requireFile; //引入当前文件中调用的不存在的类 } |
以下是Factory.php代码示例
1 2 3 4 5 6 7 8 9 10 11 |
<?php class Factory { static function createDatabase () { $test = new Test(); //由于该工厂类中也没有Test这个类,当这个方法在index.php中被调用时,Test类也会被监听,并自动载入 return $test; } } |
以下是Test.php代码示例:
1 2 3 4 5 6 7 8 9 |
<?php class Test { public function index () { echo "我已经成功被调用"; } } |
此时,访问index.php这个文件时,我们就能调用Test.php这个类文件。
某一天我需要改变Test这个类的类名,只需要更改Test.php的文件名、Test类名以及Factory工厂类中new Test();中的这个Test名字即可。100个文件要使用Test这个类,仅仅只需要将工厂类引入,并且调用即可。但是前提是要注册监听自动载入函数autoload();
三、注册模式
注册模式,解决全局共享和交换对象。已经创建好的对象,挂在到某个全局可以使用的数组上,在需要使用的时候,直接从该数组上获取即可。将对象注册到全局的树上。任何地方直接去访问。
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
<?php //注册模式类代码 class Registry { protected static $store = array(); private static $instance; private function __construct() { } //用单例模式的方式实例化对象 public static function instance() { if(!isset(self::$instance)) { self::$instance = new self(); } return self::$instance; } public function isValid($key) { return array_key_exists($key, Registry::$store); } public function get($key) { if (array_key_exists($key, Registry::$store)) return Registry::$store[$key]; } public function set($key, $obj) { Registry::$store[$key] = $obj; } } //连接数据库类 class ConnectDB { private $host; private $username; private $password; private $conn; public function __construct($host, $username, $password) { $this->host = $host; $this->username = $username; $this->password = $password; } public function getConnect() { return mysql_connect($this->host,$this->username,$this->password); } } //使用方法 $reg = Registry::instance(); $reg->set('db1', new ConnectDB('localhost', 'root', 'mckee')); $reg->set('db2', new ConnectDB('192.168.1.198', 'test', '0K5Dt@2jdc8#x@')); print_r($reg->get('db1')); print_r($reg->get('db2')); |
四:适配器模式
具体作用:将各种截然不同的函数接口封装成统一的API。
举例说明:PHP中的数据库操作有MySQL,MySQLi,PDO三种,可以用适配器模式统一成一致,使不同的数据库操作,统一成一样的API。类似的场景还有cache适配器,可以将memcache,redis,file,apc等不同的缓存函数,统一成一致。
具体实现方法如下:
首先定义一个接口(有几个方法,以及相应的参数)。然后,有几种不同的情况,就写几个类实现该接口。将完成相似功能的函数,统一成一致的方法。
代码示例:
1 2 3 4 5 6 7 8 9 10 11 |
<?php namespace app\interface; interface DatabaseApi { function connect($host, $user, $passwd, $dbname); function query($sql); function close(); } |
MySQL的API定制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<?php namespace app\database; use app\interface\DatabaseApi; class MySQL implements DatabaseApi { protected $conn; function connect($host, $user, $passwd, $dbname) { $conn = mysql_connect($host, $user, $passwd); mysql_select_db($dbname, $conn); $this->conn = $conn; } function query($sql) { $res = mysql_query($sql, $this->conn); return $res; } function close() { mysql_close($this->conn); } } |
Mysqli定制API
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
<?php namespace app\database; use app\interface\DatabaseApi; class MySQLi implements DatabaseApi { protected $conn; function connect($host, $user, $passwd, $dbname) { $conn = mysqli_connect($host, $user, $passwd, $dbname); $this->conn = $conn; } function query($sql) { return mysqli_query($this->conn, $sql); } function close() { mysqli_close($this->conn); } } |
PDO定制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<?php namespace app\database; use app\interface\DatabaseApi; class PDO implements DatabaseApi { protected $conn; function connect($host, $user, $passwd, $dbname) { $conn = new \PDO("mysql:host=$host;dbname=$dbname", $user, $passwd); $this->conn = $conn; } function query($sql) { return $this->conn->query($sql); } function close() { unset($this->conn); } } |
总结:通过以上案例,PHP与MySQL的数据库交互有三套API,在不同的场景下可能使用不同的API,那么开发好的代码,换一个环境,可能就要改变它的数据库API,那么就要改写所有的代码,使用适配器模式之后,就可以使用统一的API去屏蔽底层的API差异带来的环境改变之后需要改写代码的问题。
五、策略模式
策略模式,将一组特定的行为和算法封装成类,以适应某些特定的上下文环境。
举例说明:假如有一个电商网站系统,针对男性女性用户要各自跳转到不同的商品类目,并且所有的广告位展示不同的广告。在传统的代码中,都是在系统中加入各种if else的判断,硬编码的方式。如果有一天增加了一种用户,就需要改写代码。使用策略模式,如果新增加一种用户类型,只需要增加一种策略就可以。其他所有的地方只需要使用不同的策略就可以。
代码实现:
首先声明策略的接口文件,约定了策略的包含的行为。然后,定义各个具体的策略实现类
以下文件为:UserStrategy.php
1 2 3 4 5 6 7 8 9 10 |
<?php /* * 声明策略文件的接口,约定策略包含的行为。 */ interface UserStrategy { function showAd(); //定义商品方法 function showCategory(); //定义展示类目方法 } |
以下文件为 :FemaleUser.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<?php require_once 'Loader.php'; class FemaleUser implements UserStrategy { function showAd() { echo "2016冬季女装"; } function showCategory() { echo "女装"; } } |
以下文件为 : MaleUser.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<?php require_once 'Loader.php'; class MaleUser implements UserStrategy { function showAd() { echo "IPhone6s"; } function showCategory() { echo "电子产品"; } } |
以下文件为 :Page.php(策略分析文件)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
<?php require_once 'Loader.php'; class Page { protected $strategy; function index() { echo "AD"; $this->strategy->showAd(); echo "<br>"; echo "Category"; $this->strategy->showCategory(); echo "<br>"; } function setStrategy(UserStrategy $strategy) { $this->strategy=$strategy; } } $page = new Page(); if(isset($_GET['male'])) { //rquire(对应性别的类文件,也可以使用自动工厂模式中自动加载的方式) $strategy = new MaleUser(); }else { //rquire(对应性别的类文件,也可以使用自动工厂模式中自动加载的方式) $strategy = new FemaleUser(); } $page->setStrategy($strategy); $page->index(); |
总结:通过以上方式,可以发现,在不同用户登录时显示不同的内容,并且解决了在显示时的硬编码的问题。如果要增加一种策略,只需要增加一种策略实现类,然后在入口文件中执行判断,传入这个类即可。实现了解耦。
六、观察者模式
用模式开发的优点是,能让我们的逻辑结构以及代码更加清晰,便于维护!
而我们为什么要用 “观察者模式”?这就需要从实际运用中来理解才能更好的运用!用如下的情境来说明吧。
事例:开始时我被安排做项目的登录,很快我就完成了。然后产品提出了另一个需求,用户登录后,给他们推送一条实时消息!然后我在登录成功的逻辑后加了一段代码,完成了登录后的实时消息推送。然而事情还没有完,产品又给加了个需求,需要给新登录的用户10块钱红包奖励,这个当然很简单,我又在消息推送后加了代码,完成了新登录用户的红包奖励(钱怎么到账的过程暂且不论),然而事情还没完没了了,产品不断的在加需求了,如非vip用户登录,给他推送10条需要注册VIP才能打开的信息,如根据客户习惯推送10条客户偏好的信息,如vip快到期的客户需要在客户登录后提醒要充值啦。。。。。。。等等,如是这般,那我就得不停的在登录后加代码,变得我开始看不懂哪个xxx写的代码了!
那么此时我们就得考虑用"观察者模式" 了
可以以这样的方式简单明了形容 观察者模式, 某个商场门口安排一个人进行观察,观察到有a类型的顾客进门,立即安排敲锣、打鼓、送鲜花,观察到有b类客户,立即安排购物袋,观察到c类客户,嗯嗯感觉他是来打酱油了,安排不要浪费表情了,什么欢迎仪式也没有。。。。。也就是说 观察者就是个‘势利眼’,看人下彩,根据观察给进来的顾客安排对应的某个服务或者某些服务!也许这个形容还不够恰当,但大体意思差不多了。
接下来就是重点了,描述了观察者模式的轮廓,那么就需要转化为代码来实际运用了!
一、首先得有两个接口类,用以框定观察者模式,一个被观察者接口类(一般申明有三个必须方法)
- 添加观察者对象的方法
- 删除观察者对象的方法
- 通知观察者进行相应执行方法
一个观察者接口类(一般只有一个必须方法,就是执行)
如果直接甩代码可能有点难理解,那么就先给个示意图来明确一下吧!

二、根据观察者接口类的框定我们定下接口类如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<?php // 被观察者接口(即用户接口,我们要根据用户的身份,添加相应的观察者,对其进行服务) interface Subject{ public function register(Observer $observer); //添加(注册)观察者对象 public function detach(Observer $observer); //删除观察者对象 public function notify(); //通知观察者执行相应功能 } // 观察者接口 interface Observer{ public function watch(); //观察者要执行的方法 } |
三、根据框定的结构,大概的理解一下就是,要实现 被观察者对象 存储各种观察者对象(完成各种功能的对象)存储起来,然后通只各观察者执行自己的功能,先看看如下的实现代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
<?php // 被观察者继承类 class Action implements Subject{ public $_observers=array(); //用于存储观察者对象 //用于添加(注册)观察者对象 public function register(Observer $observer){ $this->_observers[]=$observer; } //用于删除观察者对象 public function detach(Observer $observer){ $index = array_search($observer, $this->_observers); if ($index === FALSE || ! array_key_exists($index, $this->_observers)) { return FALSE; } unset($this->_observers[$index]); return TRUE; } //通知各观察者 public function notify(){ //****重点,其实就是循环中执行各观察这对象的watch方法,不同功能方法内容不同但方法名相同 foreach ($this->_observers as $observer) { $observer->watch(); } } } // 观察者1号继承类 class One implements Observer{ public function watch(){ echo "此人是重要用户,我应该主动服务"; echo "<hr/>"; } } //观察者2号继承类 class Two implements Observer{ public function watch(){ echo "此人是vip用户,我应该敲锣打鼓,上去迎接"; echo "<hr/>"; } } //观察者3号继承类 class Three implements Observer{ public function watch(){ echo "此人是潜在用户,我应该去上前去给他推销vip会员服务"; echo "<hr/>"; } } // 应用实例(此时来了个VIP用户,我们应该调用他相应的观察者服务) $action=new Action(); $action->register(new Two()); $action->register(new One()); $action->notify(); //此时来了个普通潜在用户,我们应该调用他相应的推销服务观察者 $action=new Action(); $action->register(new Three()); $action->notify(); |
七、原型模式
1.有些时候,我们需要创建多个类似的大对象。如果直接通过new对象,开销很大,而且new完还得进行重复的初始化工作。可能把初始化工作封装起来的,但是对于系统来说,你封不封装,初始化工作还是要执行。
2.原型模式则不同,原型模式是先创建好一个原型对象,然后通过clone这个原型对象来创建新的对象,这样就免去了重复的初始化工作,系统仅需内存拷贝即可。
举例说明:如果说,我们现在正开发一个游戏,有不同的地图,地图大小都是一样的,并且都有海洋,但是不同的地图温度不一样。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
<?php //抽象原型类 Abstract class Prototype{ abstract function __clone(); } //具体原型类 class Map extends Prototype{ public $width; public $height; public $sea; public function setAttribute(array $attributes) { foreach($attributes as $key => $val) { $this->$key = $val; } } public function __clone() { echo "克隆时自动调用"; } } //海洋类.这里就不具体实现了。 class Sea{} //使用原型模式创建对象方法如下 //先创建一个原型对象 $map_prototype = new Map; $attributes = array('width'=>40,'height'=>60,'sea'=>(new Sea)); $map_prototype->setAttribute($attributes); //现在已经创建好原型对象了。如果我们要创建一个新的map对象只需要克隆一下 $new_map = clone $map_prototype; var_dump($map_prototype); var_dump($new_map); |
八、装饰模式
为什么需要装饰器模式:
- 我们要对一个已有的对象添加新功能,又不想修改它原来的结构。
- 使用子类继承的方法去实现添加新功能,会不可避免地出现子类过多,继承链很长的情况。而且不少书籍都规劝我们竭力保持一个对象的父与子关系不超过3个。
- 装饰器模式,可以提供对对象内容快速非侵入式地修改。
举例说明: 如果有一个游戏角色,他原来是默认没穿衣服的。现在游戏改进了,觉得这个角色,你可以自行搭配穿搭风格~
代码示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
<?php //声明一个装饰抽象类 abstract class Component { //定义一个操作 abstract public function operation (); } //声明一个装饰品抽象类继承装饰抽象类 abstract class Ornament extends Component { //声明一个受保护的变量用来挂载传入的实例 protected $mountClass; //调用setMountClass方法可以挂载对象,并赋给自身属性 public function setMountClass ($mountClass) { $this->mountClass = $mountClass; } //定义一个装饰行为,执行被挂载实例的operation()方法 public function operation () { if ($this->mountClass != null) { $this->mountClass->operation(); } } } //开始声明我准备干吗 class MyOperation extends Component { public function operation () { echo '我准备穿衣服了' . PHP_EOL; } } //定义一个系上围巾的装饰操作 class OrnamentScarf extends Ornament { //重写父类的装饰行为,但是重点是必须执行一次父类装饰行为 public function operation () { parent::operation();//执行了父类的装饰行为 echo '系上围巾' . PHP_EOL; } } //定义一个穿上裤子的装饰操作 class OrnamentTrousers extends Ornament { public function operation () { parent::operation(); echo '穿上裤子 ' . PHP_EOL; } } //定义一个带上帽子的装饰操作 class OrnamentHat extends Ornament { public function operation () { parent::operation();//执行了父类的装饰行为 echo '带上帽子 ' . PHP_EOL; } } //定义一个穿裙子的操作 /* * class OrnamentDress extends Ornament * { * * // 具体方法就不实现了 * * } * */ $MyOperation = new MyOperation();//实例化 $OrnamentScarf = new OrnamentScarf(); $OrnamentTrousers = new OrnamentTrousers(); $OrnamentHat = new OrnamentHat(); //装饰顺序是你的每一个装饰类实例化后的对象,以参数的形式传给下一个对象,如下所示 $OrnamentScarf->setMountClass($MyOperation); $OrnamentTrousers->setMountClass($OrnamentScarf); $OrnamentHat->setMountClass($OrnamentTrousers); $OrnamentHat->operation(); //装饰顺序可以自行组合,也可以自行进行扩展,新的装饰类将不会影响到整体 |
文章评论(0)