前段时间看到有人用魔兽来解释设计模式.docx

上传人:b****4 文档编号:5473606 上传时间:2022-12-16 格式:DOCX 页数:15 大小:20.63KB
下载 相关 举报
前段时间看到有人用魔兽来解释设计模式.docx_第1页
第1页 / 共15页
前段时间看到有人用魔兽来解释设计模式.docx_第2页
第2页 / 共15页
前段时间看到有人用魔兽来解释设计模式.docx_第3页
第3页 / 共15页
前段时间看到有人用魔兽来解释设计模式.docx_第4页
第4页 / 共15页
前段时间看到有人用魔兽来解释设计模式.docx_第5页
第5页 / 共15页
点击查看更多>>
下载资源
资源描述

前段时间看到有人用魔兽来解释设计模式.docx

《前段时间看到有人用魔兽来解释设计模式.docx》由会员分享,可在线阅读,更多相关《前段时间看到有人用魔兽来解释设计模式.docx(15页珍藏版)》请在冰豆网上搜索。

前段时间看到有人用魔兽来解释设计模式.docx

前段时间看到有人用魔兽来解释设计模式

使市场

 

前段时间看到有人用魔兽来解释设计模式,感觉很有意思,于是我把它改了改,又添加了些设计模式内容,今天发出来。

有些地方借鉴了前人的内容,没有注明,请前人不要见怪啊。

这里用大家感兴趣的魔兽3来讨论PHP的几种常见的设计模式:

单件模式、策略模式、工厂模式、观察者模式。

今天就讲这四个吧,以后继续。

这些设计模式,都是针对面向对象来说的,所以都用PHP5,另外在这里我想说的是PHP4从2008年8月8日〔我记得是和奥运会同一天,没查证,呵呵〕的时候官方就发了最后一个PHP4的补丁,这意味这PHP4的时代已经终结,所以,我建议大家现在就别理PHP4吧,就以PHP5来说吧。

一、单件模式:

问题的提出:

某些应用程序资源是独占的,因为有且只有一个此类型的资源。

例如,通过数据库句柄到数据库的连接是独占的。

您希望在应用程序中共享数据库句柄,因为在保持连接打开或关闭时,它是一种开销,在获取单个页面的过程中更是如此。

问题的解决:

那么下面我们就开始玩魔兽吧。

首先双击war3.exe,这时候就开始运行魔兽了。

我们用代码来实现吧。

php

classWar3

{

publicfunction__construct()

{

echo"War3isRunning.","
";

}

}

$war=newWar3();

运行!

很好,输出

War3isRunning.

我们已经可以开始游戏了,但是,如果我在代码末尾再参加

$war2=newWar3();

$war3=newWar3();

会怎么样呢?

我们试试,输出结果:

War3isRunning.

War3isRunning.

War3isRunning.

完了,如果不小心双击了两次就开了3个魔兽,那如果再双击几次,那电脑肯定爆掉。

我们还是来想想解决方法吧。

既然我们不能这么随意的就把这个类实例化了,那么我们就把构造函数改成私有方法。

classWar3

{

privatefunction__construct()

{

echo"War3isRunning.","
";

}

}

可是私有变量外部是无法访问的,这样以来,我们就连一个都打不开了啊。

别急,我们再给他加一个不用通过实例化,外部也能访问的函数,那就是静态函数,

classWar3

{

privatefunction__construct()

{

echo"War3isRunning.","
";

}

 

publicstaticfunctionrunWar()

{

 

}

}

通过这个静态的方法runWar()我们来控制类War3的实例化,那么还缺上一个标识,我们再创建一个标识,通过这个标识来表示我们的类是否已经实例化,如果实例化,直接返回句柄就行了。

把类修改成

classWar3

{

protectedstatic$_instance=null;

privatefunction__construct()

{

echo"War3isRunning.","
";

}

 

publicstaticfunctionrunWar()

{

if(null===self:

:

$_instance){

self:

:

$_instance=newself();

}

 

returnself:

:

$_instance;

}

}

当然,我们运行魔兽时的实例化也要换种方法,就通过

$war=War3:

:

runWar();

就能开始玩魔兽了,好了,下面把完整的代码附上来:

php

classWar3

{

protectedstatic$_instance=null;

privatefunction__construct()

{

echo"War3isRunning.","
";

}

publicstaticfunctionrunWar()

{

if(null===self:

:

$_instance){

self:

:

$_instance=newself();

}

 

returnself:

:

$_instance;

}

}

$war=War3:

:

runWar();

$war2=War3:

:

runWar();

$war3=War3:

:

runWar();

运行一下,结果是:

War3isRunning.

太好了,我双击了这么屡次,也就只运行了一个魔兽,现在随便你怎么打开,机子都不会爆掉了。

这就是传说中的单价模式,主要用于一些很占资源的而且实例仅有一个实例就够用的东西,比如,zendframework中的Zend_Controller_Front前端控制器,就是采用单价模式来设计的,大家有兴趣的话可以看看那个。

二、策略模式:

问题的提出:

在此模式中,算法是从复杂类提取的,因而可以方便地替换。

例如,如果要更改搜索引擎中排列页的方法,如此策略模式是一个不错的选择。

思考一下搜索引擎的几个局部——一局部遍历页面,一局部对每页排列,另一局部基于排列的结果排序。

在复杂的示例中,这些局部都在同一个类中。

通过使用策略模式,您可将排列局部放入另一个类中,以便更改页排列的方式,而不影响搜索引擎的其余代码。

问题的解决:

呵呵,不讲那么复杂,刚刚魔兽好不容易打开了,

我们还是玩魔兽好了。

下面我们选battle,哇好多种族啊,有人族〔Human〕,兽族〔ORC〕,暗夜精灵族〔NighyElf〕,不死族〔Undead〕。

我选精灵族〔NighyElf〕,再选一个精灵族和两个兽族〔ORC〕,一个兽族和我是一家的,另一个精灵族和兽族是另一家的。

每一个玩家在进入游戏后都会得到一些资源,如一个大厅,五个小精灵〔苦工〕和一个矿山。

这些可以称为是初始化的一些东西,这里我们就可以用到策略模式来封装这些初始化。

进入正题,首先我们来构建一个玩家类:

php

classplayer

{

//玩家名字

protected$_name;

//种族

protected$_race;

//队伍

protected$army;

//建筑

protected$building;

//人口

protected$population;

//黄金

protected$gold;

//木材

protected$wood;

//构造函数,设定所属种族

publicfunction__construct($race)

{

$this->race=$race;

 

}

//__get()方法用来获取保护属性

privatefunction__get($property_name)

{

if(isset($this->$property_name)){

return($this->$property_name);

}

else{

return(NULL);

}

}

//__set()方法用来设置保护属性

privatefunction__set($property_name,$value)

{

$this->$property_name=$value;

}

}

接着,我们再建一个玩家初始化的接口,

php

interfaceinitialPlayer

{

//制造初始化的部队

publicfunctiongiveArmy($player);

//制造初始化的建筑

publicfunctiongiveBuilding($player);

//初始化资源

publicfunctiongiveSource($player);

}

好了,到这里我们就该对这个接口来实现了,为了方便,我只选了两个种族,就只写这两个种族的初始化了:

首先是精灵族:

php

classNighyElfInitialimplementsinitialPlayer

{

//制造初始化的部队

publicfunctiongiveArmy($player)

{

//五个小精灵

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

{

$creator=newCreatArms();//这个是创建部队类,在后面得工厂模式中会用到,这里我就不多说了

$player->army[]=$creator->Creat('Wisp','./Arms/');

}

}

//制造初始化的建筑

publicfunctiongiveBuilding($player)

{

$creator=newCreatBuildings();

//一个基地

$player->building[]=$creator->Creat('TownHall','./Buildings/');

//一个矿场

$player->building[]=$creator->Creat('Mine','./Buildings/');

}

//初始化人口上限

publicfunctiongiveSource($player)

{

$player->population=10;

$player->gold=1000;

$player->wood=100;

}

}

接下来是兽族:

php

classORCInitialimplementsinitialPlayer

{

//制造初始化的部队

publicfunctiongiveArmy($player)

{

//五个苦工

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

{

$creator=newCreatArms();//这个是创建部队类,在后面得工厂模式中会用到,这里我就不多说了

$player->army[]=$creator->Creat('Peon','./Arms/');

}

}

//制造初始化的建筑

publicfunctiongiveBuilding($player)

{

 

$creator=newCreatBuildings();

//一个基地

$player->building[]=$creator->Creat('TownHall','./Buildings/');

//一个矿场

$player->building[]=$creator->Creat('Mine','./Buildings/');

}

//初始化人口上限

publicfunctiongiveSource($player)

{

$player->population=10;

$player->gold=1000;

$player->wood=100;

}

}

 

好了,到这里初始化代码就写好了,现在还差一个控制这些初始化得类,也就是封装他们:

php

classinitialController{

//构造函数,参数为玩家的数组

publicfunction__construct($playerArray)

{

foreach($playerArrayas$player)

{

switch($player->race)

{

case'NighyElf':

$initialController=newNighyElfInitial();break;

case'ORC':

$initialController=newORCInitial();break;

}

$initialController->giveArmy($player);

$initialController->giveBuilding($player);

$initialController->giveSupply($player);

}

}

}

最后就是简单这么一调用,就OK:

php

//有两个精灵族两个兽族

$playerArray=array(newplayer('NighyElf'),newplayer('NighyElf'),newplayer('ORC'),newplayer('ORC'));

//进展初始化工作

$initialController=newinitialController($playerArray);

这就是策略模式,他将不同情况下的算法封装在一起。

Zendframework中的Zend_Application_Resource就是用策略模式来设计的。

三、工厂模式:

问题的提出:

最初在设计模式一书中,许多设计模式都鼓励使用松散耦合。

要理解这个概念,让我们最好谈一下许多开发人员从事大型系统的艰辛历程。

在更改一个代码片段时,就会发生问题,系统其他局部——您曾认为完全不相关的局部中也有可能出现级联破坏。

该问题在于严密耦合。

系统某个局部中的函数和类严重依赖于系统的其他局部中函数和类的行为和结构。

您需要一组模式,使这些类能够相互通信,但不希望将它们严密绑定在一起,以防止出现联锁。

在大型系统中,许多代码依赖于少数几个关键类。

需要更改这些类时,可能会出现困难。

例如,假设您有一个从文件读取的User类。

您希望将其更改为从数据库读取的其他类,但是,所有的代码都引用从文件读取的原始类。

这时候,使用工厂模式会很方便。

工厂模式是一种类,它具有为您创建对象的某些方法。

您可以使用工厂类创建对象,而不直接使用new。

这样,如果您想要更改所创建的对象类型,只需更改该工厂即可。

使用该工厂的所有代码会自动更改。

问题的解决:

呵呵,估计有些phper没看懂吧,没关系,那是我从其他地方抄的,我们下面还是通过魔兽来进展吧。

这一局部,我看都已经有前人写好了,我就根本上照抄了,请前人不要见怪啊。

呵呵。

前面选了暗夜精灵族〔NighyElf〕,和兽族〔ORC〕,因为小精灵〔Wisp〕能建造建筑,还能自爆。

所以根据这个我们下面先写个小精灵〔Wisp〕的类。

php

classWisp

{

private$mHealthPoint=120;//这是小精灵的血量

private$mArmor=0;//这是小精灵的护甲

//小精灵能建造建筑

publicfunctionBuild()

{

echo'精灵建造建筑咯。


';

}

//每个小精灵被造出来时还会占用一个人口

publicfunction__construct()

{

echo'你已经建造了一个小精灵。


';

//这里是增加已有人口的代码

}

//每个小精灵死亡会减少你占用的人口

publicfunction__destruct()

{

//这里是减少已有人口的代码

}

}

把这些代码放在Arms/Wisp.php中。

啊,还有还有,还有苦工〔Peon〕的类

php

classPeon

{

private$mHealthPoint=250;//这是苦工的血量

private$mArmor=0;//这是苦工的护甲

 

//苦工能建造建筑

publicfunctionBuild()

{

echo'苦工建造建筑咯。


';

}

 

//每个苦工被造出来时还会占用一个人口

publicfunction__construct()

{

echo'你已经建造了一个苦工。


';

//这里是增加已有人口的代码

}

//每个苦工死亡会减少你占用的人口

publicfunction__destruct()

{

//这里是减少已有人口的代码

}

}

把这些代码放在Arms/Peon.php中。

等等,这样岂不是很复杂,魔兽里面还有那么多的兵种,另外都还有两个种族,每次创建一个兵就要new一个,要是记不住这个兵的类名,岂不是new不了?

而且如果一个兵是一个类,放在一个文件里,那是不是一开始就要把所有的几十上百个文件都include一次啊,那效率可想而知啊。

嘿嘿,当然是有解决方法的啊,我们再写一个类把这些类都封装起来,这个把兵种都封装起来的类我们称之为工厂类,他可以像生产产品一样,来创建兵,帮我们对其实例化。

下面我们就来看这个类怎么实现吧。

php

classCreatArms

{

publicfunction__construct(){}

publicfunctionCreat($arms,$path='')

{

include$path.$arms.'.php';//包含要这个类的文件

returnnew$arms;//返回你创建的兵种对象的句柄

}

}

这样,即使在兵种多样的情况下,我们仍然可以很方便地实例化:

$creator=newCreatArms();

$w1=$creator->Creat('兵种名','前缀或路径');

例如创建小精灵:

$creator=newCreatArms();//不管创建啥,我都只要使用这个类

$w1=$creator->Creat(‘Wisp’,’./Arms/’);//创建一个小精灵

$w1->Build();//让小精灵造建筑

这就是传说中的工厂模式,通过工厂模式,对于如论坛那种有很多种用户的,特别是为了以后扩展比拟方便的,采用工厂模式,是个很好的解决方法。

在zendframework中的Zend_Form、Zend_Filter、Zend_Validate就是用工厂模式来构架的。

四、观察者模式:

问题的提出:

观察者模式为您提供了防止组件之间严密耦合的另一种方法。

该模式非常简单:

一个对象通过添加一个方法〔该方法允许另一个对象,即观察者注册自己〕使本身变得可观察。

当可观察的对象更改时,它会将消息发送到已注册的观察者。

这些观察者使用该信息执行的操作与可观察的对象无关。

结果是对象可以相互对话,而不必了解原因。

问题的解决:

呵呵,上面还是抄的,看不懂没关系,我们今天重点是玩魔兽。

已经造了很长时间的兵了,现在可以出去带兵打仗了,如果我去打电脑的兽族,那么电脑与那个兽族同盟的精灵族就会过来帮助。

那么如何让他知道自己的同盟受攻击了呢。

现在我们就来讨论这个问题。

首先我们写一下结盟的抽象类:

php

abstractclassabstractAlly

{

//放置观察者的集合,这里以简单的数组来直观演示

protected$oberserverCollection;

//增加观察者的方法,参数为观察者〔也是玩家〕

publicfunctionaddOberserver($oberserver)

{

$this->oberserverCollection[]=newoberserver($oberserver);

}

//将被攻击的电脑的名字通知各个观察者

publicfunctionnotify($beAttackedPlayerName)

{

//把观察者的集合循环

foreach($this->oberserverCollectionas$oberserver)

{

//调用各个观察者的救援函数,参数为被攻击的电脑的名字,if用来排除被攻击的电脑的观察者

if($oberserver->name!

=$beAttackedPlayerName)

$oberserver->help($beAttackedPlayerName);

}

}

 

abstractpublicfunctionbeAttacked($beAttackedPlayer);

}

下面我们就写具体的结盟类:

php

classAllyextendsabstractAlly

{

//构造函数,将所有电脑玩家的名称的数组作为参数

publicfunction__construct($allPlayer)

{

//把所有电脑玩家的数组循环

foreach($allPlayeras$player)

{

//增加观察者,参数为各个电脑玩家的名称

$this->addOberserver($player);

}

}

//将被攻击的电脑的名字通知各个观察者

publicfunctionbeAttacked($beAttackedPlayerName)

{

//调用各个观察者的救援函数,参数为被攻击的电脑的名字,if用来排除被攻击的电脑的观察者

$this->notify($beAttackedPlayerName);

}

}

接着在二、策略模式中我们定义的player类中参加一个help方法

publichelp($beAttackedPlayerName)

{

//这里简单的输出,谁去救谁,最后加一个换行,便于显示

echo$this->name."help".$beAttackedPlayerName."
";

}

这样就行了。

最后就是仿真了。

php

//先设置敌方电脑

$allputePlayer=array('NighyElf2','ORC2');

//新建电脑结盟

$Ally=newAlly($allputePlayer);

//假设我进攻了电脑的兽族

$Ally->beAttacked('ORC2');

这样结盟的另一家就能接到通知,去救援。

观察者模式主要就是用在这种情况下。

可以将某个状态变化立即通知到相关的对象,相关的对象就可以采用相应的策略。

例如,zendframework中的Zend_Message就是用的观察者模式。

好了,今天就玩到这里,以后有空再和大家一起玩魔兽啊

 

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 解决方案 > 学习计划

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1