PHP常见设计模式

设计模式六大原则

开放封闭原则:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。

里氏替换原则:所有引用基类的地方必须能透明地使用其子类的对象.

依赖倒置原则:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。

单一职责原则:不要存在多于一个导致类变更的原因。通俗的说,即一个类只负责一项职责。

接口隔离原则:客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。

迪米特法则:一个对象应该对其他对象保持最少的了解。

1.单例设计模式(Singleton)

所谓单例模式,即在应用程序中最多只有该类的一个实例存在,一旦创建,就会一直存在于内存中!

应用场景:

单例设计模式常应用于数据库类设计,采用单例模式,只连接一次数据库,防止打开多个数据库连接。

一个单例类应具备以下特点:

单例类不能直接实例化创建,而是只能由类本身实例化。因此,要获得这样的限制效果,构造函数必须标记为private,从而防止类被实例化。

需要一个私有静态成员变量来保存类实例和公开一个能访问到实例的公开静态方法。

在PHP中,为了防止他人对单例类实例克隆,通常还为其提供一个空的私有__clone()方法。

单例模式的例子:

<?php

/**

* Singleton of Database

*/

classDatabase

{

// We need a static private variable to store a Database instance.  

privatestatic $instance;

// Mark as private to prevent it from being instanced.  

privatefunction__construct()

{

// Do nothing.  

}

privatefunction__clone()

{

// Do nothing.  

}

publicstaticfunctiongetInstance()

{

if(!(self::$instance instanceof self)) {

self::$instance =newself();

}

returnself::$instance;

}

}

$a =Database::getInstance();

$b =Database::getInstance();

// true  

var_dump($a === $b);

2.工厂设计模式

要是当操作类的参数变化时,只用改相应的工厂类就可以

工厂设计模式常用于根据输入参数的不同或者应用程序配置的不同来创建一种专门用来实例化并返回其对应的类的实例。

使用场景:使用方法 new实例化类,每次实例化只需调用工厂类中的方法实例化即可。

优点:由于一个类可能会在很多地方被实例化。当类名或参数发生变化时,工厂模式可简单快捷的在工厂类下的方法中 一次性修改,避免了一个个的去修改实例化的对象。

我们举例子,假设矩形、圆都有同样的一个方法,那么我们用基类提供的API来创建实例时,通过传参数来自动创建对应的类的实例,他们都有获取周长和面积的功能。

例子

<?php

interfaceInterfaceShape

{

functiongetArea();

functiongetCircumference();

}

/**

* 矩形

*/

classRectangleimplementsInterfaceShape

{

private$width;

private$height;

publicfunction__construct($width, $height)

{

$this->width = $width;

$this->height = $height;

}

publicfunctiongetArea()

{

return$this->width*$this->height;

}

publicfunctiongetCircumference()

{

return2*$this->width +2*$this->height;

}

}

/**

* 圆形

*/

classCircleimplementsInterfaceShape

{

private$radius;

function__construct($radius)

{

$this->radius = $radius;

}

publicfunctiongetArea()

{

returnM_PI * pow($this->radius,2);

}

publicfunctiongetCircumference()

{

return2* M_PI *$this->radius;

}

}

/**

* 形状工厂类

*/

classFactoryShape

{

publicstaticfunctioncreate()

{

switch(func_num_args()) {

case1:

returnnewCircle(func_get_arg(0));

case2:

returnnewRectangle(func_get_arg(0), func_get_arg(1));

default:

# code...  

break;

}

}

}

$rect =FactoryShape::create(5,5);

// object(Rectangle)#1 (2) { ["width":"Rectangle":private]=> int(5) ["height":"Rectangle":private]=> int(5) }  

var_dump($rect);

echo"<br>";

// object(Circle)#2 (1) { ["radius":"Circle":private]=> int(4) }  

$circle =FactoryShape::create(4);

var_dump($circle);

3.观察者设计模式

观察者模式是挺常见的一种设计模式,使用得当会给程序带来非常大的便利,使用得不当,会给后来人一种难以维护的想法。

使用场景:用户登录,需要写日志,送积分,参与活动 等使用消息队列,把用户和日志,积分,活动之间解耦合

什么是观察者模式?一个对象通过提供方法允许另一个对象即观察者 注册自己)使本身变得可观察。当可观察的对象更改时,它会将消息发送到已注册的观察者。这些观察者使用该信息执行的操作与可观察的对象无关。结果是对象可以相互对话,而不必了解原因。观察者模式是一种事件系统,意味着这一模式允许某个类观察另一个类的状态,当被观察的类状态发生改变的时候,观察类可以收到通知并且做出相应的动作;观察者模式为您提供了避免组件之间紧密耦。看下面例子你就明白了!

<?php

/*

观察者接口

*/

interfaceInterfaceObserver

{

functiononListen($sender, $args);

functiongetObserverName();

}

// 可被观察者接口  

interfaceInterfaceObservable

{

functionaddObserver($observer);

functionremoveObserver($observer_name);

}

// 观察者抽象类  

abstractclassObserverimplementsInterfaceObserver

{

protected$observer_name;

functiongetObserverName()

{

return$this->observer_name;

}

functiononListen($sender, $args)

{

}

}

// 可被观察类  

abstractclassObservableimplementsInterfaceObservable

{

protected$observers =array();

publicfunctionaddObserver($observer)

{

if($observerinstanceofInterfaceObserver)

{

$this->observers[] = $observer;

}

}

publicfunctionremoveObserver($observer_name)

{

foreach($this->observersas $index => $observer)

{

if($observer->getObserverName() === $observer_name)

{

array_splice($this->observers, $index,1);

return;

}

}

}

}

// 模拟一个可以被观察的类  

classAextendsObservable

{

publicfunctionaddListener($listener)

{

foreach($this->observersas $observer)

{

$observer->onListen($this, $listener);

}

}

}

// 模拟一个观察者类  

classBextendsObserver

{

protected$observer_name ='B';

publicfunctiononListen($sender, $args)

{

var_dump($sender);

echo"<br>";

var_dump($args);

echo"<br>";

}

}

// 模拟另外一个观察者类  

classCextendsObserver

{

protected$observer_name ='C';

publicfunctiononListen($sender, $args)

{

var_dump($sender);

echo"<br>";

var_dump($args);

echo"<br>";

}

}

$a =newA();

// 注入观察者  

$a->addObserver(newB());

$a->addObserver(newC());

// 可以看到观察到的信息  

$a->addListener('D');

// 移除观察者  

$a->removeObserver('B');

// 打印的信息:  

// object(A)#1 (1) { ["observers":protected]=> array(2) { [0]=> object(B)#2 (1) { ["observer_name":protected]=> string(1) "B" } [1]=> object(C)#3 (1) { ["observer_name":protected]=> string(1) "C" } } }  

// string(1) "D"  

// object(A)#1 (1) { ["observers":protected]=> array(2) { [0]=> object(B)#2 (1) { ["observer_name":protected]=> string(1) "B" } [1]=> object(C)#3 (1) { ["observer_name":protected]=> string(1) "C" } } }  

// string(1) "D"  

4.适配器模式

将一个类的接口转换成客户希望的另一个接口,适配器模式使得原本的由于接口不兼容而不能一起工作的那些类可以一起工作。

应用场景:老代码接口不适应新的接口需求,或者代码很多很乱不便于继续修改,或者使用第三方类库。

例如:php连接数据库的方法:mysql,,mysqli,pdo,可以用适配器统一

//老的代码       

classUser{

private$name;

function__construct($name){

$this->name = $name;

}

publicfunctiongetName(){

return$this->name;

}

}

//新代码,开放平台标准接口      

interfaceUserInterface{

functiongetUserName();

}

classUserInfoimplementsUserInterface{

protected$user;

function__construct($user){

$this->user = $user;

}

publicfunctiongetUserName(){

return$this->user->getName();

}

}

$olduser =newUser('张三');

echo$olduser->getName()."n";

$newuser =newUserInfo($olduser);

echo$newuser->getUserName()."n";

5.策略模式

将一组特定的行为和算法封装成类,以适应某些特定的上下文环境。

使用场景:个人理解,策略模式是依赖注入,控制反转的基础

例如:一个电商网站系统,针对男性女性用户要各自跳转到不同的商品类目,并且所有广告位展示不同的广告

MaleUserStrategy.php

<?php

namespaceIMooc;

classMaleUserStrategyimplementsUserStrategy{

functionshowAd()

{

echo"IPhone6";

}

functionshowCategory()

{

echo"电子产品";

}

}

FemaleUserStrategy.php

<?php

namespaceIMooc;

classFemaleUserStrategyimplementsUserStrategy{

functionshowAd()

{

echo"2014新款女装";

}

functionshowCategory()

{

echo"女装";

}

}

UserStrategy.php

<?php

namespaceIMooc;

interfaceUserStrategy{

functionshowAd();

functionshowCategory();

}

<?php

interfaceFlyBehavior{

publicfunctionfly();

}

classFlyWithWingsimplementsFlyBehavior{

publicfunctionfly(){

echo"Fly With Wings \n";

}

}

classFlyWithNoimplementsFlyBehavior{

publicfunctionfly(){

echo"Fly With No Wings \n";

}

}

classDuck{

private$_flyBehavior;

publicfunctionperformFly(){

$this->_flyBehavior->fly();

}

publicfunctionsetFlyBehavior(FlyBehavior $behavior){

$this->_flyBehavior = $behavior;

}

}

classRubberDuckextendsDuck{

}

// Test Case  

$duck =newRubberDuck();

/*  想让鸭子用翅膀飞行 */

$duck->setFlyBehavior(newFlyWithWings());

$duck->performFly();

/*  想让鸭子不用翅膀飞行 */

$duck->setFlyBehavior(newFlyWithNo());

$duck->performFly();

6.装饰器模式

使用场景:当某一功能或方法draw,要满足不同的功能需求时,可以使用装饰器模式;实现方式:在方法的类中建addDecorator(添加装饰器),beforeDraw,afterDraw 3个新方法, 后2个分别放置在要修改的方法draw首尾.然后创建不同的装器类(其中要包含相同的,beforeDraw,afterDraw方法)能过addDecorator添加进去,然后在beforeDraw,afterDraw中循环处理,与观察者模式使用有点相似

1.装饰器模式(Decorator),可以动态地添加修改类的功能

2.一个类提供了一项功能,如果要在修改并添加额外的功能,传统的编程模式,需要写一个子类继承它,并重新实现类的方法

3.使用装饰器模式,仅需在运行时添加一个装饰器对象即可实现,可以实现最大的灵活性

DrawDecorator.php

<?php

namespaceIMooc;

interfaceDrawDecorator

{

functionbeforeDraw();

functionafterDraw();

}

Canvas.php

<?php

namespaceIMooc;

classCanvas

{

public$data;

protected$decorators =array();

//Decorator  

functioninit($width =20, $height =10)

{

$data =array();

for($i =0; $i < $height; $i++)

{

for($j =0; $j < $width; $j++)

{

$data[$i][$j] ='*';

}

}

$this->data = $data;

}

functionaddDecorator(DrawDecorator $decorator)

{

$this->decorators[] = $decorator;

}

functionbeforeDraw()

{

foreach($this->decorators as $decorator)

{

$decorator->beforeDraw();

}

}

functionafterDraw()

{

$decorators = array_reverse($this->decorators);

foreach($decorators as $decorator)

{

$decorator->afterDraw();

}

}

functiondraw()

{

$this->beforeDraw();

foreach($this->data as $line)

{

foreach($line as $char)

{

echo$char;

}

echo"<br />\n";

}

$this->afterDraw();

}

functionrect($a1, $a2, $b1, $b2)

{

foreach($this->data as $k1 => $line)

{

if($k1 < $a1 or $k1 > $a2)continue;

foreach($line as $k2 => $char)

{

if($k2 < $b1 or $k2 > $b2)continue;

$this->data[$k1][$k2] =' ';

}

}

}

}

ColorDrawDecorator.php

<?php

namespaceIMooc;

classColorDrawDecoratorimplementsDrawDecorator

{

protected$color;

function__construct($color ='red')

{

$this->color = $color;

}

functionbeforeDraw()

{

echo"<div style='color: {$this->color};'>";

}

functionafterDraw()

{

echo"</div>";

}

}

index.php

<?php

define('BASEDIR',__DIR__);

includeBASEDIR.'/IMooc/Loader.php';

spl_autoload_register('\\IMooc\\Loader::autoload');

$canvas =newIMooc\Canvas();

$canvas->init();

$canvas->addDecorator(new\IMooc\ColorDrawDecorator('green'));

$canvas->rect(3,6,4,12);

$canvas->draw();

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,265评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,274评论 1 288
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,087评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,479评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,782评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,218评论 1 207
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,594评论 2 309
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,316评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,955评论 1 237
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,274评论 2 240
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,803评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,177评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,732评论 3 229
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,953评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,687评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,263评论 2 267
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,189评论 2 258

推荐阅读更多精彩内容

  • DesignPattern_PHP PHP 基础设计模式学习,默认自动加载类 单例模式 一个类只允许实例化一次:如...
    初秋夏末阅读 546评论 0 0
  • 更新:这篇文章已经不推荐阅读,大家想了解 PHP 的设计模式,建议阅读这篇文章: https://laravel-...
    冰淤阅读 11,439评论 2 12
  • 设计模式分类 总体来说设计模式分为三大类:创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原...
    lifeline丿毅阅读 1,129评论 0 2
  • 五岁,不愿上幼儿园,每天清晨抓着你和哥哥的衣服大哭。你问我为什么不愿上学,我已然忘记怎么回答的你,只记得说自己想回...
    邬俣阅读 773评论 2 3
  • “陈凉!你有种别跑!” “略略略~我怕你啊。反正我跑的快,你这辈子也追不上我~” 嗯。我是叶烨,今年15岁,高一,...
    子宁的命是奶茶阅读 231评论 0 0