[TOC]
原理
示例
泡泡堂(js实现)
2人版本
游戏之初只支持两个玩家同时进行对战
先定义一个玩家构造函数,它有3 个简单的原型方法:Play.prototype.win
、Play.prototype.lose
以及表示玩家死亡的Play.prototype.die
。
当其中一个玩家死亡的时候游戏便结束, 同时通知它的对手胜利
1 | function Player(name) { |
真正的泡泡堂游戏至多可以有8 个玩家,并分成红蓝两队进行游戏。
为游戏增加队伍
现在我们改进一下游戏。因为玩家数量变多,用下面的方式来设置队友和敌人无疑很低效:
1 | player1.partners= [player1,player2,player3,player4]; |
所以我们定义一个数组players 来保存所有的玩家,在创建玩家之后,循环players 来给每个玩家设置队友和敌人
再改写构造函数Player,使每个玩家对象都增加一些属性,分别是队友列表、敌人列表 、玩家当前状态、角色名字以及玩家所在的队伍颜色:
1 | var players = []; |
玩家胜利和失败之后的展现依然很简单,只是在每个玩家的屏幕上简单地弹出提示:
1 | Player.prototype.win = function(){ // 玩家团队胜利 |
玩家死亡的方法要变得稍微复杂一点,我们需要在每个玩家死亡的时候,都遍历其他队友的生存状况,如果队友全部死亡,则这局游戏失败,同时敌人队伍的所有玩家都取得胜利,代码如下:
1 | Player.prototype.die = function () { // 玩家死亡 |
最后定义一个工厂来创建玩家:
1 | var playerFactory = function (name, teamColor) { |
用这段代码创建8 个玩家:
1 | //红队: |
让红队玩家全部死亡:
1 | player1.die(); |
执行结果如图
玩家增多带来的困扰
现在我们已经可以随意地为游戏增加玩家或者队伍,但问题是,每个玩家和其他玩家都是紧紧耦合在一起的。在此段代码中,每个玩家对象都有两个属性,this.partners 和this.enemies,用来保存其他玩家对象的引用。当每个对象的状态发生改变,比如角色移动、吃到道具或者死亡时,都必须要显式地遍历通知其他对象。
如果在一个大型网络游戏中,画面里有成百上千个玩家,几十支队伍在互相厮杀。如果有一个玩家掉线,必须从所
有其他玩家的队友列表和敌人列表中都移除这个玩家。游戏也许还有解除队伍和添加到别的队伍的功能,红色玩家可以突然变成蓝色玩家,这就不再仅仅是循环能够解决的问题了。
用中介者模式改造泡泡堂游戏
现在我们开始用中介者模式来改造上面的泡泡堂游戏, 改造后的玩家对象和中介者的关系
如图 所示:
首先仍然是定义Player 构造函数和player 对象的原型方法,在player 对象的这些原型方法中,不再负责具体的执行逻辑,而是把操作转交给中介者对象,我们把中介者对象命名为playerDirector:
1 | function Player(name, teamColor) { |
再继续改写之前创建玩家对象的工厂函数,可以看到,因为工厂函数里不再需要给创建的玩家对象设置队友和敌人,这个工厂函数几乎失去了工厂的意义:
1 | var playerFactory = function (name, teamColor) { |
最后,我们需要实现这个中介者playerDirector 对象,一般有以下两种方式。
利用发布—订阅模式。将playerDirector 实现为订阅者,各player 作为发布者,一旦player的状态发生改变,便推送消息给playerDirector,playerDirector 处理消息后将反馈发送给其他player。
在 playerDirector中开放一些接收消息的接口,各 player可以直接调用该接口来给playerDirector发送消息,player只需传递一个参数给 playerDirector,这个参数的目的是使 playerDirector可以识别发送者。同样, playerDirector接收到消息之后会将处理结果反馈给其他 player。
在这里我们使用第二种方式,playerDirector 开放一个对外暴露的接口reciveMessage,负责接收player 对象发送的消息,而player 对象发送消息的时候,总是把自身this 作为参数发送给playerDirector,以便playerDirector 识别消息来自于哪个玩家对象,代码如下:
1 | var playerDirector = (function () { |
可以看到,除了中介者本身,没有一个玩家知道其他任何玩家的存在,玩家与玩家之间的耦合关系已经完全解除,某个玩家的任何操作都不需要通知其他玩家,而只需要给中介者发送一个消息,中介者处理完消息之后会把处理结果反馈给其他的玩家对象。我们还可以继续给中介者扩展更多功能,以适应游戏需求的不断变化。
测试
注意每次测试重置状态
测试1:红方全部死亡
1
2
3
4player1.die();
player2.die();
player3.die();
player4.die();测试2:皮蛋和小乖掉线
1
2
3
4player1.remove();
player2.remove();
player3.die();
player4.die();测试3:皮蛋从红队叛变到蓝队
1
2
3
4player1.changeTeam('blue');
player2.die();
player3.die();
player4.die();
找房子(python实现)
人在江湖漂,岂能顺心如意?与大多数毕业生一样,第一份工作很难持续两年以上。Tony 也在一家公司工作了一年半后,换了一个东家。
在北京这个硕大的城市里,换工作基本就意味着要换房子。不得不说,找房子是一件烦心而累人的工作。
- 你首先要知道自己要怎样的房子:多大面积(多少平米),什么价位,是否有窗户,是否有独卫。
- 要去网上查找各种房源信息,找到最匹配的几个户型。
- 之后要去电话咨询,过滤虚假信息和过时信息。
- 最后,也是最累人的一步,要去实地考查,看看真实的房子与网上的信息是否相符,房间是否有异味,周围设施是否齐全。这一步你可能会从东城穿越西城,再来到南城,而后又折腾去北城……想想都累!
- 最后的最后,你还要与各种脾性的房东进行周旋,去讨价还价。
Tony 想了想,还是找中介算了。在北京这座城市,你几乎找不到一手房东,90%的房源信息都掌握在房屋中介手中!既然都找不到一手房东,还不如找一家正规点的中介。
于是 Tony 找到了我爱我家,认识了里面的职员 Vangie。Vangie 问了他对房子的要求。Tony 说:“18平米左右,要有独卫,要有窗户,最好是朝南,有厨房更好!价位在2000左右。”Vangie 立马就说:“上地西里有一间,但没有厨房;当代城市家园有两间,一间主卧,一间次卧,但卫生间是共用的;美和园有一间,比较适合你,但价格会贵一点。” 真是了如指掌啊!说完就带着 Tony 开始看房了……
一天就找到了还算合适的房子。但不得不再次吐槽:北京的房子真 TM 贵啊,18平米,精装修,有朝南窗户,一个超小(1m宽不到)的阳台,卫生间5人共用,厨房共用,价格要2600每月。押一付三,加一个月的中介费,一次交了一万多,要开始吃土了,内心滴了无数滴血……
上面的生活场景中,Tony 通过中介来找房子,因为找房子的过程实在太繁琐了,而且对房源信息不了解。通过中介,他省去了很多麻烦的细节,合同也是直接跟中介签,你甚至可能都不知道房东是谁。
我们将通过程序来模拟一下上面找房子的过程。
源码示例:
1 | class HouseInfo: |
测试代码:
1 | def testRenting(): |
输出结果:
1 | 张三在 我爱我家 发布房源出租信息: |
从网状到星状
类图变化