结构型设计模式: GOF 23个设计模式中,属于结构型设计模式7个。分别为适配器模式
,装饰器模式
,代理模式
,外观模式
,桥接模式
,组合模式
,享元模式
。php
适配器模式(Adapter Pattern):将某个对象的接口适配为另外一个对象所指望的接口。属于结构型设计模式。mysql
(一)为何须要适配器模式sql
1,某个操做数据库的有两套不一样的数据库操做方法,咱们经过适配器统一成一个接口。例如,咱们待会把mysql和mysqli统一成一个接口。数据库
2,咱们有多套数据库对应了多种数据库操做,例如MySQL,SqlServer,Oralce,Redis都有对应的操做函数,或操做类。PDO把这些都统一成一个接口。设计模式
3,系统的增长一些新功能,建立了一个新的接口,可是老的接口并不想废弃。可使用适配器模式,对用户隐藏这两个接口,提供用户所但愿的接口。安全
(二)适配器UML图bash
(三)设计实例微信
把MySQL和mysqli统一成一个接口,用户能够调用一样的方法使用MySQL和mysqli操做数据库。函数
<?php
//MySQL待操做适配类
class MySQLAdaptee implements Target
{
protected $conn; //用于存放数据库链接句柄
//实现链接方法
public function connect($host, $user, $passwd, $dbname)
{
$conn = mysql_connect($host, $user, $passwd);
mysql_select_db($dbname, $conn);
$this->conn = $conn;
}
//查询方法
public function query($sql)
{
$res = mysql_query($sql, $this->conn);
return $res;
}
//关闭方法
public function close()
{
mysql_close($this->conn);
}
}
//MySQLi操做待适配类
class MySQLiAdaptee
{
protected $conn;
public function connect($host, $user, $passwd, $dbname)
{
$conn = mysqli_connect($host, $user, $passwd, $dbname);
$this->conn = $conn;
}
public function query($sql)
{
return mysqli_query($this->conn, $sql);
}
public function close()
{
mysqli_close($this->conn);
}
}
//用户所期待的接口
Interface Target{
public function connect($host, $user, $passwd, $dbname);
public function query($sql);
public function close();
}
//用户期待适配类
Class DataBase implements Target {
protected $db ; //存放MySQLiAdapter对象或MySQLAdapter对象
public function __construct($type){
$type = $type."Adapter" ;
$this->db = new $type ;
}
public function connect($host, $user, $passwd, $dbname){
$this->db->connect($host, $user, $passwd, $dbname);
}
public function query($sql){
return $this->db->query($sql);
}
public function close(){
$this->db->close();
}
}
//用户调用同一个接口,使用MySQL和mysqli这两套不一样示例。
$db1 = new DataBase('MySQL');
$db1->connect('127.0.0.1','root','1234','myDB');die;
$db1->query('select * from test');
$db1->close();
$db2 = new DataBase('MySQLi');
$db2->connect('127.0.0.1','root','1234','myDB');
$db2->query('select * from test');
$db2->close();
复制代码
上面的代码只是一个示例,若是你运行以上的代码报了mysql函数不存在或是被废弃的错误。这是正常的,由于MySQL这套函数在PHP5.5以上的版本已经被废弃了。感兴趣的还能够去了解一下PDO的实现。 经过上面的代码,咱们能够看到,使用适配器能够把不一样的操做接口封装起来,对外显示成用户所指望的接口。post
这就比如你家墙上有一个电源三相插孔,可是插孔的孔距之间过小。你的电器三相插头插脚距太大的插不进去,或许你还有个两相的插头,或许你还有条USB线和type-C线,这些都无法插到三相接口里。因而你买了个插脚适合插到你墙上的排插,而后这个排插是这些年新出的,USB也能插。因而你把你的三相插头,两相插头,USB线,type-c线都插到排插上。实际上就是间接地连在了你墙壁上的三相插孔上。
没错,适配器要作的就是这么回事。
有些书也把适配器模式分为:类的适配器模式,对象的适配器模式,接口的适配器模式
装饰器模式(Decorator Pattern): 容许向一个已有的对象添加新的功能或部份内容,同时又不改变其结构。属于结构型模式,它是做为现有的类的一个包装。
(一)为何须要装饰器模式:
1,咱们要对一个已有的对象添加新功能,又不想修改它原来的结构。
2,使用子类继承的方法去实现添加新功能,会不可避免地出现子类过多,继承链很长的状况。并且很多书籍都规劝咱们竭力保持一个对象的父与子关系不超过3个。
3,装饰器模式,能够提供对对象内容快速非侵入式地修改。
(二)装饰器模式UML图
(三)简单实例
若是有一个游戏角色,他原来就是默认穿一件长衫。如今游戏改进了,以为这个角色,除了穿一件长衫前,还能够在里面穿一件袍子,或是一件球衣。在外面穿一套盔甲或是宇航服。
<?php
/*游戏原来的角色类
class Person{
public function clothes(){
echo "长衫".PHP_EOL;
}
}
*/
//装饰器接口
interface Decorator
{
public function beforeDraw();
public function afterDraw();
}
//具体装饰器1-宇航员装饰
class AstronautDecorator implements Decorator
{
public function beforeDraw()
{
echo "穿上T恤".PHP_EOL;
}
function afterDraw()
{
echo "穿上宇航服".PHP_EOL;
echo "穿戴完毕".PHP_EOL;
}
}
//具体装饰器2-警察装饰
class PoliceDecorator implements Decorator{
public function beforeDraw()
{
echo "穿上警服".PHP_EOL;
}
function afterDraw()
{
echo "穿上防弹衣".PHP_EOL;
echo "穿戴完毕".PHP_EOL;
}
}
//被装饰者
class Person{
protected $decorators = array(); //存放装饰器
//添加装饰器
public function addDecorator(Decorator $decorator)
{
$this->decorators[] = $decorator;
}
//全部装饰器的穿长衫前方法调用
public function beforeDraw()
{
foreach($this->decorators as $decorator)
{
$decorator->beforeDraw();
}
}
//全部装饰器的穿长衫后方法调用
public function afterDraw()
{
$decorators = array_reverse($this->decorators);
foreach($decorators as $decorator)
{
$decorator->afterDraw();
}
}
//装饰方法
public function clothes(){
$this->beforeDraw();
echo "穿上长衫".PHP_EOL;
$this->afterDraw();
}
}
//警察装饰
$police = new Person;
$police->addDecorator(new PoliceDecorator);
$police->clothes();
//宇航员装饰
$astronaut = new Person;
$astronaut->addDecorator(new AstronautDecorator);
$astronaut->clothes();
//混搭风
$madman = new Person;
$madman->addDecorator(new PoliceDecorator);
$madman->addDecorator(new AstronautDecorator);
$madman->clothes();
复制代码
固然,上面的代码没有严格地按照UML图,这是由于当被装饰者只有一个时,那 Component也就是ConcreteComponent。同理,若是,只有一个装饰器,那也不必实现一个implment接口。
若是咱们有两个不一样的被装饰者,那固然就应该抽象出一个Component,让这两个被装饰者去继承它。也许,那继承不是又来了吗。既然外面都用到继承,直接把装饰器的方法放到继承里面不就好了。
但是你想,若是,直接经过继承的话,那装饰过的被装饰者就应该继承自被装饰者,并且被装饰者由于装饰的不一样还要有不少不一样类型的子类。而使用装饰者模式的话,继承链缩短了,并且不一样的装饰类型还能够动态增长。
代理模式(Proxy Pattern):构建了透明置于两个不一样对象以内的一个对象,从而可以截取或代理这两个对象间的通讯或访问。
(一)为何须要代理模式
1,远程代理
,也就是为了一个对象在不一样地址空间提供局部表明。隐藏一个对象存在于不一样地址空间的事实。
2,虚拟代理
,根据须要来建立开销很大的对象,经过它来存放实例化须要很长时间的真实对象。
3,安全代理
,用来控制真实对象的访问对象。
4,智能指引
,当调用真实对象的时候,代理处理一些事情。
(二)代理模式UML图
(三)简单实例
案例一
:你想买一张学友哥的新唱片,之前你都是在县城CD店里买的。如今CD行业不景气,没得卖了。你只能找人去香港帮你代购一张。
<?php
//代理抽象接口
interface shop{
public function buy($title);
}
//原来的CD商店,被代理对象
class CDshop implements shop{
public function buy($title){
echo "购买成功,这是你的《{$title}》唱片".PHP_EOL;
}
}
//CD代理
class Proxy implements shop{
public function buy($title){
$this->go();
$CDshop = new CDshop;
$CDshop->buy($title);
}
public function go(){
echo "跑去香港代购".PHP_EOL;
}
}
//你93年买了张 吻别
$CDshop = new CDshop;
$CDshop->buy("吻别");
//14年你想买张 醒着作梦 找不到CD商店了,和作梦似的,不得不找了个代理去香港帮你代购。
$proxy = new Proxy;
$proxy->buy("醒着作梦");
复制代码
案例二
:经过代理实现MySQL的读写分离,若是是读操做,就链接127.0.0.1的数据库,写操做就读取127.0.0.2的数据库
<?php
class Proxy
{
protected $reader;
protected $wirter;
public function __construct(){
$this->reader = new PDO('mysql:host=127.0.0.1;port=3306;dbname=CD;','root','password');
$this->writer = new PDO('mysql:host=127.0.0.2;port=3306;dbname=CD;','root','password');
}
public function query($sql)
{
if (substr($sql, 0, 6) == 'select')
{
echo "读操做: ".PHP_EOL;
return $this->reader->query($sql);
}
else
{
echo "写操做:".PHP_EOL;
return $this->writer->query($sql);
}
}
}
//数据库代理
$proxy = new Proxy;
//读操做
$proxy->query("select * from table");
//写操做
$proxy->query("INSERT INTO table SET title = 'hello' where id = 1");
//固然对于数据库来讲,这里应该使用单例模式的方法来存放$reader和$writer,但我只是举个例子,不想把单例加进来把代码搞复杂。
//可是若是你要实现这样的一个数据库代理,我以为仍是有必要用上单例模式的知识
复制代码
一句话来讲,代理模式,就是在访问对象时经过一个代理对象去访问你想访问的对象。而在代理对象中,咱们能够实现对访问对象的截断或权限控制等操做。
外观模式 (Facade Pattern): 为子系统中的一组接口提供一个一致的界面,定义一个高层接口,这个接口使得这一子系统更加容易使用。
(一)为何须要外观模式
1,开发阶段,子系统愈来愈复杂,增长外观模式提供一个简单的调用接口。
2,维护一个大型遗留系统的时候,可能这个系统已经很是难以维护和扩展,但又包含很是重要的功能,为其开发一个外观类,以便新系统与其交互。
3,外观模式能够隐藏来自调用对象的复杂性。
(二)外观模式UML图
(三)简单实例
好比说咱们去医院就诊,医院有医生员工系统,有药品系统,有患者资料系统。可是咱们只是在前台挂个号,就能在其余系统里都看到咱们。外观系统就差很少这样。
若是没有挂号系统的话,咱们就先要去医生系统通知一下医生, 而后去患者系统取一下患者资料交给医生,再去药品系统登记一下,最后到药房领药。
<?php
//医院医生员工系统
class DoctorSystem{
//通知就诊医生
static public function getDoctor($name){
echo __CLASS__.":".$name."医生,挂你号".PHP_EOL;
return new Doctor($name);
}
}
//医生类
class Doctor{
public $name;
public function __construct($name){
$this->name = $name;
}
public function prescribe($data){
echo __CLASS__.":"."开个处方给你".PHP_EOL;
return "祖传秘方,药到必死";
}
}
//患者系统
class SufferSystem {
static function getData($suffer){
$data = $suffer."资料";
echo __CLASS__.":".$suffer."的资料是这些".PHP_EOL ;
return $data;
}
}
//医药系统
class MedicineSystem {
static function register($prescribe){
echo __CLASS__.":"."拿处处方:".$prescribe."------------通知药房发药了".PHP_EOL;
Shop::setMedicine("砒霜5千克");
}
}
//药房
class shop{
static public $medicine;
static function setMedicine($medicine){
self::$medicine = $medicine;
}
static function getMedicine(){
echo __CLASS__.":".self::$medicine.PHP_EOL;
}
}
//若是没有挂号系统,咱们就诊的第一步
//通知就诊医生
$doct = DoctorSystem::getDoctor("顾夕衣");
//患者系统拿病历资料
$data = SufferSystem::getData("何在");
//医生看病历资料,开处方
$prscirbe = $doct->prescribe($data);
//医药系统登记处方
MedicineSystem::register($prscirbe);
//药房拿药
Shop::getMedicine();
echo PHP_EOL.PHP_EOL."--------有了挂号系统之后--------".PHP_EOL.PHP_EOL;
//挂号系统
class Facade{
static public function regist($suffer,$doct){
$doct = DoctorSystem::getDoctor($doct);
//患者系统拿病历资料
$data = SufferSystem::getData($suffer);
//医生看病历资料,开处方
$prscirbe = $doct->prescribe($data);
//医药系统登记处方
MedicineSystem::register($prscirbe);
//药房拿药
Shop::getMedicine();
}
}
//患者只须要挂一个号,其余的就让挂号系统去作吧。
Facade::regist("叶好龙","贾中一");
复制代码
外观模式,也叫门面模式。它多用于在多个子系统之间,做为中间层。用户经过Facade对象,直接请求工做,省去了用户调用多个子系统的复杂动做。
外观模式常举的一个例子,就是咱们买了好多支股票,可是时间有限。盯盘很复杂,咱们搞得一团糟。因此,咱们干脆买了股票基金。股票基金就比如于外观模式的Facade对象,而子系统就是股票基金投的各支股票。
上一篇:PHP建立型设计模式
感谢阅读,因为笔者也是初学设计模式,能力有限,文章不可避免地有失偏颇 后续更新** PHP设计模式-结构型设计模式(下) **介绍,欢迎你们评论指正
我最近的学习总结: