vlambda博客
学习文章列表

“笨方法”学Python3, 习题 43 :基本的面向对象分析和设计

笨方法”学Python3,习题 43:基本的面向对象分析和设计

1、大致理解设计游戏的思维方式


一、面向对象编程(OOP)方式构建的基本流程

这个流程是“自顶向下”(top down)的,从抽象的概念逐步细化到具体的代码

1、把要解决的问题写下来,或者画出来(问题描述、结构关系图等)

2、将第一条中的关键概念提取出来并加以研究(问题名词、动词提取到代码命名中)

3、创建一个类层次结构和对象图(结构关系图、父类子类、函数方法的关系)

4、用代码实现各个类,并写一个测试来运行它们(骨架代码逐步细化)

5、重复上述步骤并细化代码


二、简单游戏引擎的分析


游戏名:《来自 Percal 25 号行星的哥顿人》

1、把问题写下来或者画出来

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

场景描述:

  • 死亡(death):玩家死去的场景,搞笑

  • 中央走廊(central corridor):游戏的起点,有哥顿人把守,要讲一个笑话才能继续

  • 激光武器库(laser weapon armory):有一个中子弹,在乘坐救生船离开时要用它炸飞船,房间有一个数字键盘,需要猜测密码组合

  • 飞船主控舱(the bridge):另一个战斗场景,需要在这里埋炸弹

  • 救生舱(escape pod):逃离的场景,但需要猜对是哪艘救生船

2、提取和研究关键概念

提取名词列表:

  • 外星人(Alien)

  • 迷宫(Maze)

  • 玩家(Player)

  • 房间(Room)

  • 飞船(Ship)

  • 场景(Scene)

  • 哥顿人(Gothon)

  • 死亡(Death)

  • 救生船(Escape Pod)

  • 中央走廊(Central Corridor)

  • 行星(Planet)

  • 激光武器库(Laser Weapon Armory)

  • 地图(Map)

  • 主控舱(The Bridge)

  • 引擎(Engine)

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

4、编写类和运行类的测试代码

根据类结构编写测试代码:

所写的代码:
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()
5、重复和细化

重复整个流程,逐步细化内容


三、自顶向下与自底向上

自底向上的一般步骤:

1、取出要解决的问题中的一小块,写些代码让它差不多能运行

2、加上类和自动测试,细化代码让它更为正式

3、把关键概念抽取出来然后研究它们

4、把真正需要实现的东西描述出来

5、回去细化代码,有可能需要全部丢弃重头做起

6、在问题的另外一小块里重复上述流程


四、《来自 Percal 25 号行星的哥顿人》的代码


1、导入部分:
from sys import exit
from random import randint
from textwrap import dedent

① textwrap 模块的 dedent 函数

取出字符串开头的空白,方便写三引号字符串


2、创建 Scene 基类
class Scene(object):

def enter(self):
print('This scene is not yet configured.')
print('Subclass it and implement enter().')
exit(1)

Scene 基类包含所有场景的通用信息


3、创建 Engine 类
class Engine(object):

def __init__(self, scene_map):
self.scene_map = scene_map

def play(self):
current_scene = self.scene_map.opening_scene()
last_scene = self.scene_map.next_scene('finished')

while current_scene != last_scene:
next_scene_name = current_scene.enter()
current_scene = self.scene_map.next_scene(next_scene_name)

# be sure to print out the last scene
current_scene.enter()

这里用了Map.opening_scene 和 Map.next_scene 方法,是假设这些方法已经写好了的,这里是拿来使用的


4、创建 Death 场景
class Death(Scene):

quips = [
"You died. You kinda suck at this",
"You mom would be proud...if she were smarter.",
"Such a luser.",
"I have a small puppy that's better at this.",
"You're worse than your Dad's jokes."
]

def enter(self):
print(Death.quips[randint(0, len(self.quips)-1)])
exit(1)


5、创建 CentralCorridor 场景
class CentralCorridor(Scene):

def enter(self):
print(dedent("""
The Gothons of planet Percal #25 haveinvaded your ship and desteoyed your entire crew.
You are the last surviving member and your last mission is to get the neutron destruct bomb from the Weapons Armory,
put it in the bridge, and blow the ship up afther getiing into an escape pod.
You're running down the central corridor to the Weapons Armory when a Gothon jumps out ,
red scaly skin, dark grimy filled body.
He's blocking the door to the Armory and about topull a weapon blast you.
"""))

action = input('> ')

if action == "shoot!":
print(dedent("""
Quick on the draw you yank out your blaster and fire it at the Gothon.
His clown costume is flowing and moving around his body, which throws off you aim.
This completely ruins his brand new costume but misses him entirely,
which makes him fly into an insane rage and blast you repeatedly in the face untill you are dead.
Then he eats you.
"""))
return 'death'

elif action == 'dodge!':
print(dedent("""
Like a world class boxer you dodge, weave, slip and slide right as the Gothon's blaster cranks a laser past your head.
In the middle of your artful dodge your foot slips and you bang your head on the metal wall and pass out.
You wake up shortly after only to die as the Gothon stomps on your head and eats you.
"""))
return 'death'

elif action == 'tell a joke':
print(dedent("""
Lucky for you they made you learn Gothon insults in the academy.
You tell the one Gothon joke you know:
Lbhe zbgure vf fb sng , jura fur fvgf nebhaq gur ubhfr, jura fur fvgf nebhaq gur ubhfr.
The Gothon stops, tries not to laugh, then busts out laughing and can't move.
While he's laughing you run up and shoot him square in the head putting him down, then jump through the Weapon Armory door.
"""))
return 'laser_weapon_armory'

else:
print('DOES NOT COMPUTE! ')
return 'central_corridor'

CentralCorridor 场景是游戏的初始位置,这里运用到了 dedent 函数,可以观察其作用


6、创建其他场景
class LaserWeaponArmory(Scene):

def enter(self):
print(dedent("""
You do a dive roll into the Weapon Armory, crouch and scan the room for more Gothons that might be hiding.
It's dead quiet, too quiet. You stand up and run to the far side of the room and find the neutron bomb in its container.
There's a keypad lock on the box and you need the code to get the bomb out.
If you get the code wrong 10 times then the lock closes forever and you can't get the bomb.
The code is 3 digits.
"""))

code = f"{randint(1,9)}{randint(1,9)}{randint(1,9)}"
guess = input("[keypad]> ")
guesses = 0

while guess != code and guesses < 10:
print('BZZZZEDDD!')
guesses += 1
guess = input("[keypad]> ")

if guess == code:
print(dedent("""
The container clicks open and the seal breaks, letting gas out.
You grab the neutron bomb and run as fast as you can to he bridge where you must place it in the right spot.
"""))
return 'the_bridge'

else:
print(dedent("""
The lock buzzes one last time and then you hear a sickening melting sound as the mechanism is fused together.
You decide to sit there, and you die.
"""))
return 'death'


class TheBridge(Scene):

def enter(self):
print(dedent("""
You burst onto the Bridge with the netron destruct bomb under your arm and surprise 5 Gothons who are trying to take control of the ship.
Each of them has an even uglier clown costume than the last.
They haven't pulled their weapons out yet,
as they see the active bomb under your arm and don't want to set it off.
"""))

action = input('> ')

if action == 'throw the bomb':
print(dedent("""
In a panic you throw the bomb at the group of Gothons and make a leap for the door.
Right as you drop it a Gothon shoots you right in the back killing you.
As you die you see another Gothon frantically try to disarm the bomb.
You die knowing they will probably blow up when it goes off.
"""))
return 'death'

elif action == 'slowly place the bomb':
print(dedent("""
You point your blaster at the bomb under your arm and the Gothons put their hands up and start to sweat .
You inch backward to the door, open it ,and then carefully place the bomb on the floor, pointing your blaster at it.
You then jump back through the door, punch the close button and blast the lock so the Gothon can't get out .
Now that the bomb is placed you run to the escape pod to get off this tin can.
"""))
return 'escape_pod'

else:
print('DOES NOT COMPUTE!')
return 'the_bridge'


class EscapePod(Scene):

def enter(self):
print(dedtne("""
You rush through the ship desperately trying to make it to the escape pod before the whole ship explodes.
It seems like hardly any Gothons are on the ship, so your run is clear of interference.
You get to the chamber with the escape pods ,and now need to pick one to take.
Some of them could be damage but you don't have time to look.
There's 5 pods, which one do you take?
"""))

good_pod = randint(1,5)
guess = input('[pod #]> ')

if int(guess) != good_pod:
print(dedent("""
You jump into pod {guess} and hit the eject button.
The escapes pod out into the void of space,
then implodes as the hull ruptures, crushing your body into jam jelly.
"""))
return 'death'
else:
print(dedent("""
You jump into pod {guess} and hit the eject button.
The pod easily slides out into space heading to the planet below.
As it flies to the planet, you look back and see your ship implode then explode like a
bright star, taking out the Gothon ship at the same time.
You won!
"""))
return 'finished'


class Finished(Scene):

def enter(self):
print("You won! Good job.")
return 'finished'


创建 Map 基类
class Map(object):

scenes = {
'central_corridor': CentralCorridor(),
'laser_weapon_armory': LaserWeaponArmory(),
'the_bridge': TheBridge(),
'escape_pod': EscapePod(),
'death': Death(),
'finished': Finished(),
}

def __init__(self, start_scene):
self.start_scene = start_scene

def next_scene(self, scene_name):
val = Map.scenes.get(scene_name)
return val

def opening_scene(self):
return self.next_scene(self.start_scene)


运行代码
a_map = Map('central_corridor')
a_game = Engine(a_map)
a_game.play()

五、整段脚本及运行结果

所写的代码:
from sys import exit
from random import randint
from textwrap import dedent



class Scene(object):

def enter(self):
print('This scene is not yet configured.')
print('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()
last_scene = self.scene_map.next_scene('finished')

while current_scene != last_scene:
next_scene_name = current_scene.enter()
current_scene = self.scene_map.next_scene(next_scene_name)

# be sure to print out the last scene
current_scene.enter()


class Death(Scene):

quips = [
"You died. You kinda suck at this.",
"You mom would be proud...if she were smarter.",
"Such a luser.",
"I have a small puppy that's better at this.",
"You're worse than your Dad's jokes.",
]

def enter(self):
print(Death.quips[randint(0, len(self.quips)-1)])
exit(1)


class CentralCorridor(Scene):

def enter(self):
print(dedent("""
The Gothons of planet Percal #25 haveinvaded your ship and desteoyed your entire crew.
You are the last surviving member and your last mission is to get the neutron destruct bomb from the Weapons Armory,
put it in the bridge, and blow the ship up afther getiing into an escape pod.
You're running down the central corridor to the Weapons Armory when a Gothon jumps out ,
red scaly skin, dark grimy filled body.
He's blocking the door to the Armory and about topull a weapon blast you.
"""))

action = input('> ')

if action == "shoot!":
print(dedent("""
Quick on the draw you yank out your blaster and fire it at the Gothon.
His clown costume is flowing and moving around his body, which throws off you aim.
This completely ruins his brand new costume but misses him entirely,
which makes him fly into an insane rage and blast you repeatedly in the face untill you are dead.
Then he eats you.
"""))
return 'death'

elif action == 'dodge!':
print(dedent("""
Like a world class boxer you dodge, weave, slip and slide right as the Gothon's blaster cranks a laser past your head.
In the middle of your artful dodge your foot slips and you bang your head on the metal wall and pass out.
You wake up shortly after only to die as the Gothon stomps on your head and eats you.
"""))
return 'death'

elif action == 'tell a joke':
print(dedent("""
Lucky for you they made you learn Gothon insults in the academy.
You tell the one Gothon joke you know:
Lbhe zbgure vf fb sng , jura fur fvgf nebhaq gur ubhfr, jura fur fvgf nebhaq gur ubhfr.
The Gothon stops, tries not to laugh, then busts out laughing and can't move.
While he's laughing you run up and shoot him square in the head 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(dedent("""
You do a dive roll into the Weapon Armory, crouch and scan the room for more Gothons that might be hiding.
It's dead quiet, too quiet. You stand up and run to the far side of the room and find the neutron bomb in its container.
There's a keypad lock on the box and you need the code to get the bomb out.
If you get the code wrong 10 times then the lock closes forever and you can't get the bomb.
The code is 3 digits.
"""))

code = f"{randint(1,9)}{randint(1,9)}{randint(1,9)}"
guess = input("[keypad]> ")
guesses = 0

while guess != code and guesses < 10:
print('BZZZZEDDD!')
guesses += 1
guess = input("[keypad]> ")

if guess == code:
print(dedent("""
The container clicks open and the seal breaks, letting gas out.
You grab the neutron bomb and run as fast as you can to he bridge where you must place it in the right spot.
"""))
return 'the_bridge'

else:
print(dedent("""
The lock buzzes one last time and then you hear a sickening melting sound as the mechanism is fused together.
You decide to sit there, and you die.
"""))
return 'death'


class TheBridge(Scene):

def enter(self):
print(dedent("""
You burst onto the Bridge with the netron destruct bomb under your arm and surprise 5 Gothons who are trying to take control of the ship.
Each of them has an even uglier clown costume than the last.
They haven't pulled their weapons out yet,
as they see the active bomb under your arm and don't want to set it off.
"""))

action = input('> ')

if action == 'throw the bomb':
print(dedent("""
In a panic you throw the bomb at the group of Gothons and make a leap for the door.
Right as you drop it a Gothon shoots you right in the back killing you.
As you die you see another Gothon frantically try to disarm the bomb.
You die knowing they will probably blow up when it goes off.
"""))
return 'death'

elif action == 'slowly place the bomb':
print(dedent("""
You point your blaster at the bomb under your arm and the Gothons put their hands up and start to sweat .
You inch backward to the door, open it ,and then carefully place the bomb on the floor, pointing your blaster at it.
You then jump back through the door, punch the close button and blast the lock so the Gothon can't get out .
Now that the bomb is placed you run to the escape pod to get off this tin can.
"""))
return 'escape_pod'

else:
print('DOES NOT COMPUTE!')
return 'the_bridge'


class EscapePod(Scene):

def enter(self):
print(dedtne("""
You rush through the ship desperately trying to make it to the escape pod before the whole ship explodes.
It seems like hardly any Gothons are on the ship, so your run is clear of interference.
You get to the chamber with the escape pods ,and now need to pick one to take.
Some of them could be damage but you don't have time to look.
There's 5 pods, which one do you take?
"""))

good_pod = randint(1,5)
guess = input('[pod #]> ')

if int(guess) != good_pod:
print(dedent("""
You jump into pod {guess} and hit the eject button.
The escapes pod out into the void of space,
then implodes as the hull ruptures, crushing your body into jam jelly.
"""))
return 'death'
else:
print(dedent("""
You jump into pod {guess} and hit the eject button.
The pod easily slides out into space heading to the planet below.
As it flies to the planet, you look back and see your ship implode then explode like a
bright star, taking out the Gothon ship at the same time.
You won!
"""))
return 'finished'


class Finished(Scene):

def enter(self):
print("You won! Good job.")
return 'finished'


class Map(object):

scenes = {
'central_corridor': CentralCorridor(),
'laser_weapon_armory': LaserWeaponArmory(),
'the_bridge': TheBridge(),
'escape_pod': EscapePod(),
'death': Death(),
'finished': Finished(),
}

def __init__(self, start_scene):
self.start_scene = start_scene

def next_scene(self, scene_name):
val = Map.scenes.get(scene_name)
return val

def opening_scene(self):
return self.next_scene(self.start_scene)


a_map = Map('central_corridor')
a_game = Engine(a_map)
a_game.play()
---------------------------------------------------------------------------------------------------
应该看到的结果:
PS D:\代码> & D:/软件/python/python.exe d:/代码/test.py

The Gothons of planet Percal #25 haveinvaded your ship and desteoyed your entire crew.
You are the last surviving member and your last mission is to get the neutron destruct bomb from the Weapons Armory,
put it in the bridge, and blow the ship up afther getiing into an escape pod.
You're running down the central corridor to the Weapons Armory when a Gothon jumps out ,
red scaly skin, dark grimy filled body.
He's blocking the door to the Armory and about topull a weapon blast you.

> tell a joke

Lucky for you they made you learn Gothon insults in the academy.
You tell the one Gothon joke you know:
Lbhe zbgure vf fb sng , jura fur fvgf nebhaq gur ubhfr, jura fur fvgf nebhaq gur ubhfr.
The Gothon stops, tries not to laugh, then busts out laughing and can't move.
While he's laughing you run up and shoot him square in the head putting him down, then jump through the Weapon Armory door.


You do a dive roll into the Weapon Armory, crouch and scan the room for more Gothons that might be hiding.
It's dead quiet, too quiet. You stand up and run to the far side of the room and find the neutron bomb in its container.
There's a keypad lock on the box and you need the code to get the bomb out.
If you get the code wrong 10 times then the lock closes forever and you can't get the bomb.
The code is 3 digits.

[keypad]> 153
BZZZZEDDD!
[keypad]> 123
BZZZZEDDD!
[keypad]> 999
BZZZZEDDD!
[keypad]> 122
BZZZZEDDD!
[keypad]> 111
BZZZZEDDD!
[keypad]> 000
BZZZZEDDD!
[keypad]> 999
BZZZZEDDD!
[keypad]> 777
BZZZZEDDD!
[keypad]> 888
BZZZZEDDD!
[keypad]> 555
BZZZZEDDD!
[keypad]> 444

The lock buzzes one last time and then you hear a sickening melting sound as the mechanism is fused together.
You decide to sit there, and you die.

You mom would be proud...if she were smarter.

总结:

1、设计游戏脚本要从抽象的概念开始,逐步细化到具体的代码

2、要游戏中的场景动作分别与脚本中的类方法匹配,弄清楚中间的关系

3、重复编写,逐步细化