- 浏览: 57729 次
- 性别:
- 来自: 深圳
最新评论
-
sheldan:
sheldan@126.com 谢谢了啊
Blitz templates 最快的PHP模板引擎 -
msn877763580:
好东西啊………………学习中
做网站的一些定律 -
huhu359819176:
經典!收藏哈!
做网站的一些定律 -
cremains:
不错啊,学习了,张进了
做网站的一些定律 -
beowulf1985:
好多大道理,學習了!!
做网站的一些定律
通过魔兽看PHP设计模式
前段时间看到有人用魔兽来解释设计模式,感觉很有意思,于是我把它改了改,又添加了些设计模式内容,今天发出来。有些地方借鉴了前人的内容,没有注明,请前人不要见怪啊。
这里用大家感兴趣的魔兽3来讨论PHP的几种常见的设计模式:单件模式、策略模式、工厂模式、观察者模式。今天就讲这四个吧,以后继续。这些设计模式,都是针对面向对象来说的,所以都用PHP5,另外在这里我想说的是PHP4从2008年8月8日(我记得是和北京奥运会同一天,没查证,呵呵)的时候官方就发了最后一个PHP4的补丁,这意味这PHP4的时代已经终结,所以,我建议大家现在就别理PHP4吧,就以PHP5来说吧。
一、单件模式:
问题的提出:某些应用程序资源是独占的,因为有且只有一个此类型的资源。例如,通过数据库句柄到数据库的连接是独占的。您希望在应用程序中共享数据库句柄,因为在保持连接打开或关闭时,它是一种开销,在获取单个页面的过程中更是如此。
问题的解决:那么下面我们就开始玩魔兽吧。首先双击war3.exe,这时候就开始运行魔兽了。我们用代码来实现吧。。
< ?php class War3 { public function __construct() { echo "War3 is Running.", } } $war = new War3();
运行!很好,输出War3 is Running.我们已经可以开始游戏了,但是,如果我在代码末尾再加入
$war2 = new War3();
$war3 = new War3();
会怎么样呢?我们试试,输出结果:War3 is Running.War3 is Running.War3 is Running.完了,如果不小心双击了两次就开了3个魔兽,那如果再双击几次,那电脑肯定爆掉。。。我们还是来想想解决方法吧。。。既然我们不能这么随意的就把这个类实例化了,那么我们就把构造函数改成私有方法。
class War3 { private function __construct() { echo "War3 is Running.", } }
可是私有变量外部是无法访问的,这样以来,我们就连一个都打不开了啊。别急,我们再给他加一个不用通过实例化,外部也能访问的函数,那就是静态函数
class War3 { private function __construct() { echo "War3 is Running.", } public static function runWar() { } }
通过这个静态的方法runWar()我们来控制类War3的实例化,那么还缺上一个标识,我们再创建一个标识,通过这个标识来表示我们的类是否已经实例化,如果实例化,直接返回句柄就行了。把类修改成
class War3 { protected static $_instance = null; private function __construct() { echo "War3 is Running.", } public static function runWar() { if (null === self: :$_instance) { self: :$_instance = new self(); } return self: :$_instance; } }
当然,我们运行魔兽时的实例化也要换种方法,就通过
$war = War3: :runWar();
就能开始玩魔兽了,好了,下面把完整的代码附上来:
< ?php class War3 { protected static $_instance = null; private function __construct() { echo "War3 is Running.", } public static function runWar() { if (null === self: :$_instance) { self: :$_instance = new self(); } return self: :$_instance; } } $war = War3: :runWar(); $war2 = War3: :runWar(); $war3 = War3: :runWar(); ?>
运行一下,结果是:War3 is Running.太好了,我双击了这么多次,也就只运行了一个魔兽,现在随便你怎么打开,机子都不会爆掉了。这就是传说中的单价模式,主要用于一些很占资源的而且实例仅有一个实例就够用的东西,比如,zendframework中的Zend_Controller_Front前端控制器,就是采用单价模式来设计的,大家有兴趣的话可以看看那个。
二、策略模式:
问题的提出:在此模式中,算法是从复杂类提取的,因而可以方便地替换。例如,如果要更改搜索引擎中排列页的方法,则策略模式是一个不错的选择。思考一下搜索引擎的几个部分——一部分遍历页面,一部分对每页排列,另一部分基于排列的结果排序。在复杂的示例中,这些部分都在同一个类中。通过使用策略模式,您可将排列部分放入另一个类中,以便更改页排列的方式,而不影响搜索引擎的其余代码。
问题的解决:呵呵,不讲那么复杂,刚才魔兽好不容易打开了,我们还是玩魔兽好了。下面我们选battle,哇好多种族啊,有人族(Human),兽族(ORC),暗夜精灵族(Nighy Elf),不死族(Undead)。我选精灵族(Nighy Elf),再选一个精灵族和两个兽族(ORC),一个兽族和我是一家的,另一个精灵族和兽族是另一家的。每一个玩家在进入游戏后都会得到一些资源,如一个大厅,五个小精灵(苦工)和一个矿山。这些可以称为是初始化的一些东西,这里我们就可以用到策略模式来封装这些初始化。进入正题,首先我们来构建一个玩家类:
< ?php class player { //玩家名字 protected $_name; //种族 protected $_race; //队伍 protected $army; //建筑 protected $building; //人口 protected $population; //黄金 protected $gold; //木材 protected $wood; //构造函数,设定所属种族 public function __construct($race) { $this - >race = $race; } //__get()方法用来获取保护属性 private function __get($property_name) { if (isset($this - >$property_name)) { return ($this - >$property_name); } else { return (NULL); } } //__set()方法用来设置保护属性 private function__set($property_name, $value) { $this - >$property_name = $value; } } ?>
接着,我们再建一个玩家初始化的接口,
< ?php interface initialPlayer { //制造初始化的部队 public function giveArmy($player); //制造初始化的建筑 public function giveBuilding($player); //初始化资源 public function giveSource($player); } ?>
好了,到这里我们就该对这个接口来实现了,为了方便,我只选了两个种族,就只写这两个种族的初始化了:首先是精灵族:
< ?php class NighyElfInitial implements initialPlayer { //制造初始化的部队 public function giveArmy($player) { //五个小精灵 for ($i = 0; $i <= 5; $i++) { $creator = new CreatArms(); //这个是创建部队类,在后面得工厂模式中会用到,这里我就不多说了 $player - >army[] = $creator - >Creat('Wisp', './Arms/'); } } //制造初始化的建筑 public function giveBuilding($player) { $creator = new CreatBuildings(); //一个基地 $player - >building[] = $creator - >Creat('TownHall', './Buildings/'); //一个矿场 $player - >building[] = $creator - >Creat('Mine', './Buildings/'); } //初始化人口上限 public function giveSource($player) { $player - >population = 10; $player - >gold = 1000; $player - >wood = 100; } } ?>
接下来是兽族:
< ?php class ORCInitial implements initialPlayer { //制造初始化的部队 public function giveArmy($player) { //五个苦工 for ($i = 0; $i <= 5; $i++) { $creator = new CreatArms(); //这个是创建部队类,在后面得工厂模式中会用到,这里我就不多说了 $player - >army[] = $creator - >Creat('Peon', './Arms/'); } } //制造初始化的建筑 public function giveBuilding($player) { $creator = new CreatBuildings(); //一个基地 $player - >building[] = $creator - >Creat('TownHall', './Buildings/'); //一个矿场 $player - >building[] = $creator - >Creat('Mine', './Buildings/'); } //初始化人口上限 public function giveSource($player) { $player - >population = 10; $player - >gold = 1000; $player - >wood = 100; } } ?>
好了,到这里初始化代码就写好了,现在还差一个控制这些初始化得类,也就是封装他们:
< ?php class initialController { //构造函数,参数为玩家的数组 public function __construct($playerArray) { foreach($playerArray as $player) { switch ($player - >race) { case 'NighyElf': $initialController = new NighyElfInitial(); break; case 'ORC': $initialController = new ORCInitial(); break; } $initialController - >giveArmy($player); $initialController - >giveBuilding($player); $initialController - >giveSupply($player); } } } ?>
最后就是简单这么一调用,就OK:
< ?php //有两个精灵族两个兽族 $playerArray = array(new player('NighyElf'), new player('NighyElf'), new player('ORC'), new player('ORC')); //进行初始化工作 $initialController = new initialController($playerArray); ?>
这就是策略模式,他将不同情况下的算法封装在一起。Zend framework中的Zend_Application_Resource就是用策略模式来设计的。
三、工厂模式:
问题的提出:最初在设计模式一书中,许多设计模式都鼓励使用松散耦合。要理解这个概念,让我们最好谈一下许多开发人员从事大型系统的艰苦历程。在更改一个代码片段时,就会发生问题,系统其他部分——您曾认为完全不相关的部分中也有可能出现级联破坏。该问题在于紧密耦合。系统某个部分中的函数和类严重依赖于系统的其他部分中函数和类的行为和结构。您需要一组模式,使这些类能够相互通信,但不希望将它们紧密绑定在一起,以避免出现联锁。在大型系统中,许多代码依赖于少数几个关键类。需要更改这些类时,可能会出现困难。例如,假设您有一个从文件读取的User类。您希望将其更改为从数据库读取的其他类,但是,所有的代码都引用从文件读取的原始类。这时候,使用工厂模式会很方便。工厂模式是一种类,它具有为您创建对象的某些方法。您可以使用工厂类创建对象,而不直接使用new。这样,如果您想要更改所创建的对象类型,只需更改该工厂即可。使用该工厂的所有代码会自动更改。
问题的解决:呵呵,估计有些phper没看懂吧,没关系,那是我从其他地方抄的,我们下面还是通过魔兽来进行吧。这一部分,我看都已经有前人写好了,我就基本上照抄了,请前人不要见怪啊。呵呵。前面选了暗夜精灵族(Nighy Elf),和兽族(ORC),因为小精灵(Wisp)能建造建筑,还能自爆。所以根据这个我们下面先写个小精灵(Wisp)的类。
< ?php class Wisp { private $mHealthPoint = 120; //这是小精灵的血量 private $mArmor = 0; //这是小精灵的护甲 //小精灵能建造建筑 public function Build() { echo '精灵建造建筑咯。<br/>'; } //每个小精灵被造出来时还会占用一个人口 public function __construct() { echo '你已经建造了一个小精灵。<br/>'; //这里是增加已有人口的代码 } //每个小精灵死亡会减少你占用的人口 public function __destruct() { //这里是减少已有人口的代码 } } ?>
把这些代码放在Arms / Wisp.php中。啊,还有还有,还有苦工(Peon)的类
< ?php class Peon { private $mHealthPoint = 250; //这是苦工的血量 private $mArmor = 0; //这是苦工的护甲 //苦工能建造建筑 public function Build() { echo '苦工建造建筑咯。<br/>'; } //每个苦工被造出来时还会占用一个人口 public function __construct() { echo '你已经建造了一个苦工。<br/>'; //这里是增加已有人口的代码 } //每个苦工死亡会减少你占用的人口 public function __destruct() { //这里是减少已有人口的代码 } } ?>
把这些代码放在Arms / Peon.php中。等等,这样岂不是很复杂,魔兽里面还有那么多的兵种,另外都还有两个种族,每次创建一个兵就要new一个,要是记不住这个兵的类名,岂不是new不了?而且如果一个兵是一个类,放在一个文件里,那是不是一开始就要把所有的几十上百个文件都include一次啊,那效率可想而知啊。嘿嘿,当然是有解决办法的啊,我们再写一个类把这些类都封装起来,这个把兵种都封装起来的类我们称之为工厂类,他可以像生产产品一样,来创建兵,帮我们对其实例化。下面我们就来看这个类怎么实现吧。
< ?php class CreatArms { public function __construct() {} public function Creat($arms, $path = '') { include $path.$arms.'.php'; //包含要这个类的文件 return new $arms; //返回你创建的兵种对象的句柄 } } ?>
这样,即使在兵种多样的情况下,我们仍然可以很方便地实例化:$creator = new CreatArms();
$w1 = $creator - >Creat('兵种名', '前缀或路径');
例如创建小精灵:$creator = new CreatArms(); //不管创建啥,我都只要使用这个类
$w1 = $creator - >Creat(‘Wisp’, ’. / Arms / ’); //创建一个小精灵
$w1 - >Build(); //让小精灵造建筑
这就是传说中的工厂模式,通过工厂模式,对于如论坛那种有很多种用户的,特别是为了以后扩展比较方便的,采用工厂模式,是个很好的解决方法。在zend framework中的Zend_Form、Zend_Filter、Zend_Validate就是用工厂模式来构架的。
四、观察者模式:
问题的提出:观察者模式为您提供了避免组件之间紧密耦合的另一种方法。该模式非常简单:一个对象通过添加一个方法(该方法允许另一个对象,即观察者注册自己)使本身变得可观察。当可观察的对象更改时,它会将消息发送到已注册的观察者。这些观察者使用该信息执行的操作与可观察的对象无关。结果是对象可以相互对话,而不必了解原因。
问题的解决:呵呵,上面还是抄的,看不懂没关系,我们今天重点是玩魔兽。已经造了很长时间的兵了,现在可以出去带兵打仗了,如果我去打电脑的兽族,那么电脑与那个兽族同盟的精灵族就会过来帮忙。那么如何让他知道自己的同盟受攻击了呢。现在我们就来讨论这个问题。首先我们写一下结盟的抽象类:
< ?php abstract class abstractAlly { //放置观察者的集合,这里以简单的数组来直观演示 protected $oberserverCollection; //增加观察者的方法,参数为观察者(也是玩家) public function addOberserver($oberserver) { $this - >oberserverCollection[] = new oberserver($oberserver); } //将被攻击的电脑的名字通知各个观察者 public function notify($beAttackedPlayerName) { //把观察者的集合循环 foreach($this - >oberserverCollection as $oberserver) { //调用各个观察者的救援函数,参数为被攻击的电脑的名字,if用来排除被攻击的电脑的观察者 if ($oberserver - >name != $beAttackedPlayerName) $oberserver - >help($beAttackedPlayerName); } } abstract public function beAttacked($beAttackedPlayer); } ?>
下面我们就写具体的结盟类:
< ?php class Ally extends abstractAlly { //构造函数,将所有电脑玩家的名称的数组作为参数 public function __construct($allPlayer) { //把所有电脑玩家的数组循环 foreach($allPlayer as $player) { //增加观察者,参数为各个电脑玩家的名称 $this - >addOberserver($player); } } //将被攻击的电脑的名字通知各个观察者 public function beAttacked($beAttackedPlayerName) { //调用各个观察者的救援函数,参数为被攻击的电脑的名字,if用来排除被攻击的电脑的观察者 $this - >notify($beAttackedPlayerName); } } ?>
接着在二、策略模式中我们定义的player类中加入一个help方法
public help($beAttackedPlayerName) { //这里简单的输出,谁去救谁,最后加一个换行,便于显示 echo $this - >name." help ".$beAttackedPlayerName."<br />"; }
这样就行了。最后就是仿真了。
< ?php //先设置敌方电脑 $allComputePlayer = array('NighyElf2', 'ORC2'); //新建电脑结盟 $Ally = new Ally($allComputePlayer); //假设我进攻了电脑的兽族 $Ally - >beAttacked('ORC2'); ?>
这样结盟的另一家就能接到通知,去救援。观察者模式主要就是用在这种情况下。可以将某个状态变化立即通知到相关的对象,相关的对象就可以采用相应的策略。例如,zend framework中的Zend_Message就是用的观察者模式。好了,今天就玩到这里,以后有空再和大家一起玩魔兽啊。
发表评论
-
PHP魔术方法汇总[转]
2010-10-17 13:48 1626从php5以后的版本,php的类就可以使用魔术方法 ... -
[转]PHP:PDO驱动的一些方法属性
2010-09-30 08:35 1535PDO::setAttribute(属性,值) ... -
PHP导出QQ好友
2009-11-23 13:29 2008从昨天晚上开始 ... -
WP主题文章显示摘要
2009-10-31 00:55 1685前几天有个同事突然问 ... -
Sphnix的PHP客户端Demo
2009-10-21 13:12 1630前段时间做JobUI(www.jobui. ... -
Mysql Limit 出现误差?
2009-10-20 21:23 1062在一段更新数据库的代码中,居然出现了虚假的更新动作。 数据库 ... -
Date操作类
2009-10-10 13:03 896<?php /** * 日期工具类,负责日期 ... -
Linux下Apache和Tomcat的整合
2009-09-22 11:48 1201前言:本文是我撰写的关于搭建“linux下整合apache和t ... -
MySql的简单修改表字段SQL语句
2009-09-17 11:25 3456#新增一个字段,默认值为0,非空,自动增长,主键 ... -
PHP转换数据
2009-09-14 13:00 703最近做一个数据转换,需要把大约几G的数据从数据库取出来,然后处 ... -
将XML字符串装换成PHP对象
2009-09-11 14:23 1536最近做一个CRM的接口,需要接受CRM发出的XML数据,再在P ... -
PHP实现大文件上传
2009-09-11 09:13 2844最近在弄大文件上传在网上搜了搜发现两个不错的方法。 1 ... -
Blitz templates 最快的PHP模板引擎
2009-09-09 12:59 2755Blitz--PHP模板引擎的文档翻译 ... -
实用PHP技巧:万能Cache
2009-09-09 10:17 953Web程序员最常使用的数据库封装方式就是DAO,其实和马丁大爷 ...
相关推荐
【PHP5 技巧集-从魔兽看PHP设计模式】是一个关于PHP编程技巧的文章集合,其中涉及了多种实用的PHP开发技术。文章标题暗示了作者可能通过魔兽世界这款游戏中的某些概念来阐述PHP的设计模式,这可能包括如何在PHP中...
总结,WoW Roster Live US是一个实用的魔兽世界行会管理工具,通过PHP与暴雪API的结合,实现了行会信息的实时更新和展示。其开源特性鼓励社区参与,促进了软件的持续发展和优化。无论是行会管理者还是PHP开发者,都...
辅助性公会公告/制度/语音等自由方便的在线编辑设计,使您不需要懂得网站设计也能完善自己的公会网站,免费稳定的系统升级,保证了系统的持久稳定发展,系统完美支持魔兽世界国服/台服,祝您使用愉快。 睿博!魔兽...
Yii 框架是基于组件的高性能 PHP 框架,专为开发Web 2.0应用程序而设计。本项目利用Yii实现了一个魔兽世界(World of Warcraft)的排名网站,旨在展示公会副本进度和个人成就进度,同时也具备装备查询功能。下面我们...
Laravel作为PHP领域的一款热门框架,以其优雅的设计和丰富的功能受到开发者们的喜爱。本篇将详细介绍如何在Laravel项目中集成Battle.net API,为你的应用添加更多的游戏数据和元数据。 **一、Laravel 框架基础** ...
3. **Web应用架构**:该应用可能采用了MVC(Model-View-Controller)设计模式,这是一种常见的Web应用架构,将数据处理(模型)、用户界面(视图)和业务逻辑控制(控制器)分离,提高了代码的可维护性和可扩展性。...
U9 ORPG登陆器是一款专为ORPG(Online Role Playing Game,即在线角色扮演游戏)设计的辅助工具,它为Warcraft III(魔兽争霸III)的游戏地图提供了自动保存和快速读取的功能,极大地提升了ORPG地图的游戏体验。...
WoW DKP Leaderboard是一款基于开源软件理念设计的魔兽世界公会管理工具,主要用于处理和展示EQDKP系统的DKP(Dragon Kill Points)排行榜。在MMORPG(大型多人在线角色扮演游戏)中,DKP是一种常见的资源分配制度,...
TrinityCore通常用于魔兽世界(World of Warcraft)的私人服务器,它处理游戏的服务器端逻辑和数据库交互。 Laravel的模型提供了Active Record模式的实现,允许开发者通过面向对象的方式来操作数据库记录。例如,你...
《WoWRoster_Live-开源:深入探索魔兽世界公会管理工具》 WoWRoster_Live是一个基于开源软件开发的项目,专为魔兽世界(World of Warcraft)的公会提供强大的成员管理工具。通过Armory API,它能够解析游戏数据,...
MaNGOS本身是一个知名的开源MMORPG(大型多人在线角色扮演游戏)服务器软件,它允许用户创建自己的魔兽世界私人服务器。将MaNGOS与CMS结合,意味着这个系统不仅提供了游戏服务器的管理功能,还扩展到了社区交流和...