天天看点

笨方法学Python—ex43:基本的面向对象分析和设计

可遵循的一个流程

Python面向对象编程(OOP)方式实现东西的流程(只是你可以遵守的一种方法): 1. 把要解决的问题写下来,或画出流程图。 2. 将第一条中的关键概念摘录出来并加以研究。 3. 创建一个类和对象的层次结构图。 4. 用代码实现各个类,并写一个测试来运行它们。 5. 重复上述步骤并细化代码。

上述可看做一个“自顶向下”(top down)的流程:从很抽象的概念入手,逐渐细化,直到概念变为具体可用代码实现的东西。

简单游戏分析

利用上述流程实现一个游戏引擎和一个游戏。 游戏名称《来自Percal 25 号行星的哥顿人》,是一款空间冒险游戏。

把问题写下来或画下来

游戏描述:“外星人入侵宇宙飞船,我们的英雄需要通过各种房间组成的迷宫,打败遇到的外星人,这样才能通过救生船回到下面的行星上去。此游戏会与Zork或者Adventure类似,会用文字输出各种有趣的死法。游戏会用到一个引擎,它带动一张充满房间和场景的地图。当玩家进入一个房间时,这个房间会显示出自己的描述,并告诉引擎下一步应到哪个房间去。”

以上对游戏内容和运行方式有了一个很好的概念。下面是描述各个场景。 & 死亡(Death)。 玩家死去的场景,应比较搞笑。 &中央走廊(Central Corridor)。游戏起点,哥顿人已经在这里把守着了,玩家要讲一个笑话才能继续。 &激光武器库(Laser Weapon Armory)。 在这里英雄会找到一个中子弹,在乘坐救生飞船离开时要用它把飞船炸掉。这个房间有一个数字键盘,需猜测密码组合。 &飞船主控仓(The Bridge)。另一个战斗场景,英雄在此埋炸弹。 &救生舱(Escape Pod)。英雄逃离的场景,但需要猜对是哪艘救生船。

至此,已大概画出了它们的关系图,可能还需给每个房间写更详细的描述。

摘录和研究关键概念

有足够信息来摘录一些名词,并分析它们的类层次结构。 1)首先名词列表 外星人(Alien) 玩家(Player) 飞船(Ship) 迷宫(Maze) 房间(Room) 场景(Scene) 哥顿人(Gothon) 救生舱(Escape Pod) 行星(Planet) 地图(Map) 引擎(Engine) 死亡(Death) 中央走廊(Central Corridor) 激光武器库(Laser Weapon Armory) 主控仓(The Bridge) 可能还需摘录动词来看其是否适合做函数名(暂时跳过)。

到此,研究这些概念及其中没弄明白的部分。如,可能会通过玩类似游戏来确认其工作方式;也许会研究飞船怎样设计的,及炸弹是怎样工作的;也许还会研究一些技术细节,如怎样把游戏状态存到数据库里。 等完成这些研究后也许要回到第一步,基于学到的新东西重写游戏描述及重新摘录相关概念。

为各种概念创建类层次结构图和对象关系图

通过思考“和其他东西有哪些类似?”或“哪个其实是某个东西的另一种叫法?” 发现:1)“房间”和“场景”基本是同一个东西,在这个游戏中使用“场景”:“中央走廊”是一个场景,死亡基本也是一个场景,行星也可以是另一个场景 2) “迷宫”和“地图”基本是一个意思,用地图。3)不打算实现一个作战系统,所以“外星人”和“玩家”忽略。 然后,在文本编辑器画一个类的层次结构图。 * map * Engine * Scene    * Death    * Central Corridor    * Laser Weapon Armory    * The Bridge    * Escape Pod 再然后,查看描述中的动词部分,从而知道每一样东西上需要什么样的动作。 例如,需要方法来运行游戏引擎,在地图里转到下一个场景,获得初始场景及进入下一个场景。加到结构图里。 * Map    - next_scene    - opening_scene * Engine    - play * Scene    -enter    * Death    * Central Corridor    * Laser Weapon Armory    * The Bridge    * Escape Pod scene下只添加了enter方法,因为具体场景会继承并覆盖这个方法。

编写和运行各个类

完成以上,再在编辑器里打开一个源文件,并为它编写代码。通常只需将上面的树状结构复制到源文件中,把它扩写成各个类就可以了。(初始简单例子),文件最后还包含一点简单测试。 class Scene(object):

    def enter(self):         pass

class Engine(object):          def __init__(self, scene_map):         pass          def play(self):         pass

class Death(Scene):

    def enter(self):         pass

class CentralCorridor(Scene):

    def enter(self):         pass

class LaserWeaponArmory(Scene):

    def enter(self):         pass

class TheBridge(Scene):

    def enter(self):         pass

class EscapePod(Scene):         def enter(self):         pass

class Map(object):        def __init__(self, start_scene):         pass

    def next_scene(self, scene_name):         pass

    def opening_scene(self):         pass

a_map = Map('central_corridor') a_game = Engine(a_map) a_game.play()

重复和优化

代码的实现不是一蹴而就的,需要不断重复前边的流程,不断优化自己所写的内容。不要局限于具体的步骤,哪突然有灵感了可以继续写下去或跳过某步骤也可以,最终是要使你的代码更完善。

自顶向下与自底向上

自顶向下:从最抽象的概念(顶层)下手,一直向下做到具体的代码实现。要多用这种方法分析问题。

自底向上:先从代码开始,一直向上做到抽象概念。

《来自Percal25号行星的哥顿人》的代码

from sys import exit

from random import randint

class Scene(object):

def enter(self):

print "This scene is not yet configured. Subclass it and implement enter()."

exit(1)

class Engine(object):

def __init__(self, scene_map):

self.scene_map = scene_map

def play(self):

current_scene = self.scene_map.opening_scene()

while True:

print "\n--------"

next_scene_name = current_scene.enter()

current_scene = self.scene_map.next_scene(next_scene_name)

class Death(Scene):

quips = [

"You died. You kinda suck at this.",

"Your mom would be proud...if she were smarter.",

"Such a luser.",

"I have a small puppy that's better at this."

]

def enter(self):

print Death.quips[randint(0, len(self.quips)-1)]

exit(1)

class CentralCorridor(Scene):

def enter(self):

print "The Gothons of Planet Percal #25 have invaded your ship and destroyed"

print "your entire crew. You are the last surviving member and your lasr"

print "mission is to get the neutron destruct bomb from the Weapons Armory,"

print "put it in the bridge, and blow the ship up after getting into an "

print "escape pod."

print "\n"

print "You're running down the central corridor to the Weapons Armory when"

print "a Gothon jumps out, red scaly skin, dark grimy teeth, and evil clown costume"

print "flowing around his hate filled body. He's blocking the doot to the"

print "Armory and about to pull a weapon to blast you."

action = raw_input("> ")

if action == "shoot!":

print "Quick on the draw you yank out your blaster and fire it at the Gothon."

print "His clown costume is flowing and moving around his body, which throws"

print "off your aim. Your laser hits his costume but misses him entirely. This"

print "completely ruins his brand new costume his mother bought him, which"

print "makes him fly into a rage and blast you repeatedly in the face until"

print "you are dead, Then he eats you."

return 'death'

elif action == "dodge!":

print "Like a world class boxer you dodge, weave, slip and slide right"

print "as the Gothon's blaster cranks a laser past your head."

print "In the middle of your artful dodge your foot slips and you"

print "bang your head on the metal wall and pass out."

print "You wake up shortly after only to die as the Gothon stomps on"

print "your head and eats you."

return 'death'

elif action == "tell a joke":

print "Lucky for you they made you learn Gothon insults in the academy."

print "You tell the one Gothon joke you know:"

print "Lbhe zbgure vf fb sng, jura fur fvgf nebhag gur ubhfr, fur fvgf nebhap fur ubkd."

print "The Gothon stops, tries not to laugh , then busts out laughing and can't move."

print "While he's laughing you run up and shoot him square in the head"

print "putting him down, then jump through the Weapon Armory door."

return 'laser_weapon_armory'

else:

print "DOES NOT COMPUTE!"

return 'central_corridor'

class LaserWeaponArmory(Scene):

def enter(self):

print "You do a dive roll into the Weapon Armory, crouch and scan the room"

print "for more Gothons that might be hiding. It's dead quiet, too quiet."

print "You stand up and run to the far side of the room and find the "

print "neutron bomb in its container. There's a keypad lock on the box"

print "and you need the code to get the bomh out. If you get the code"

print "wrong 10 times then the lock closes forever and you can't"

print "get the bomb. The code is 3 digits."

code = "%d%d%d" % (randint(1, 9), randint(1, 9), randint(1, 9))

a = 0

for s in code:

x = int(s)

a = a + x

print "The sum of these 3 digit is %d" % a

guesses = 1

guess = raw_input("[keypad]> ")

while guess != code and guesses < 10:

print "BZZZZEDDD!"

guesses += 1

guess = raw_input("[keypad]> ")

if guess == code:

print "The container clicks open and the seal breaks, letting gas out."

print "You grab the neutron bomb and run as fast as you can to the"

print "bridge where you must place it in the right spot."

return 'the_bridge'

else:

print "The lock buzzes one last time and then you hear a sickening"

print "melting sound as the mechanism is fused together."

print "You decide to sit there, and finally the Gothons blow up the"

print "ship from their ship and you die."

return 'death'

class TheBridge(Scene):

def enter(self):

print "You burst onto the bridge with the netron destruct bomb"

print "under your arm and surprise 5 Gothons who are trying to"

print "take control of the ship. Each of them has an even uglier"

print "clown costume than the last. They haven't pulled their"

print "weapons out yet, as they see the active bomb under your"

print "arm and don't want to set it off."

action = raw_input("> ")

if action == "throw the bomb":

print "In a panic you throw the bomb at the group of Gothons"

print "and make a leap for the door. Right as you drop it a"

print "Gothon shoots you right in the back killing you."

print "As you die you see another Gothon frantically try to disarm"

print "the bomb. You die knowing they will probably blow up when"

print "it goes off."

return 'death'

elif action == "slowly place the bomb":

print "You point your blaster at the bomb under your arm"

print "and the Gothons put their hands up and start to sweat."

print "You inch backward to the door, open it, and the carefully"

print "place the bomb on the floor, pointing your blaster at it."

print "You then jump back through the door, punch the close button"

print "and blast the lock so the Gothons can't get out."

print "Now that the bomb is placed you run to the escape pod to"

print "get off this tin can."

return 'escape_pod'

else:

print "DOES NOT COMPUTE!"

return "the_bridge"

class EscapePod(Scene):

def enter(self):

print "You rush through the ship desperately trying to make it to"

print "the escape pod before the whole ship explodes. It seems like"

print "hardly any Gothons are on the ship, so your run is clear of"

print "interferrnce. You get to the chamber with the escape pods, and"

print "now need to pick one to take. Some of them could be damaged"

print "but you don't have time to look. There's 5 pods, which one"

print "do you take?"

good_pod = randint(1, 5)

guess = raw_input("[pod #]> ")

if int(guess) != good_pod:

print "You jump into pod %s and hit the eject button." % guess

print "The pod escapes out into the void of space, then"

print "implodes as the hull ruptures, crushing your body"

print "into jam jelly."

return 'death'

else:

print "You jump into pod %s and hit the eject button." % guess

print "The pod easily slides out into spase heading to"

print "the planet below. As it flies to the planet, you look"

print "back and see your ship implods then explode like a"

print "bright star, taking out the Gothons ship at the same"

print "time, You win!"

return 'finished'

class Map(object):

scenes = {

'central_corridor': CentralCorridor(),

'laser_weapon_armory': LaserWeaponArmory(),

'the_bridge': TheBridge(),

'escape_pod': EscapePod(),

'death': Death()

}

def __init__(self, start_scene):

self.start_scene = start_scene

def next_scene(self, scene_name):

return Map.scenes.get(scene_name)

def opening_scene(self):

return self.next_scene(self.start_scene)

a_map = Map('central_corridor')

a_game = Engine(a_map)

a_game.play()

附加练习

1 代码中有个bug, 武器库的门锁密码为啥要猜11次而不是10次?

2 解释一下房间切换的原理

3 为难度大的房间添加通过秘籍(一行代码,两个词就可做出来)

4 回到描述和分析部分,为英雄和哥顿人创建一个简单的格斗系统

5 这其实是一个小版本的“有限状态机”(finite state machine)