这章断断续续续写了一周,今天放出来......主要是翻译那些场景描述很烦人,毕竟0级英语。

面向对象的分析和设计的基本流程:

  1. 把要解决的问题写下来,或者画下来
  2. 将第一条的关键概念提取出来并加以研究
  3. 创建一个类层次结构和对象图
  4. 用代码实现各个类,并写一个测试来运行它们
  5. 重复上述步骤并细化代码

这一流程可以看成是一个“自顶向下”的,也就是从很抽象的概念入手,逐渐细化,直到概念变成具体的可用代码实现的东西。


作者将用这个流程来实现一个游戏引擎和一个游戏,以作举例:

游戏名叫《来自Percal 25号行星的哥顿人》,这是一个太空冒险游戏。起初脑海里只有这个概念,然后作者沿着这个思路,慢慢完善这个游戏。

1. 先把问题写下来或者画出来

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

接下来要描述各个场景:

  • 死亡(Death):玩家死去的场景。
  • 中央走廊(Central Corridor):这是游戏的起点,哥顿人已经在那儿把守着了,玩家需要讲一个笑话才能继续。
  • 激光武器库(Laser Weapon Armory):在这里英雄会找到一个中子弹,在乘坐逃生舱离开飞船时用它把飞船炸掉。这个房间有个数字键盘,英雄需要猜密码组合。
  • 飞船主控舱(The Bridge):另一个战斗场景,英雄需要在这里埋炸弹。
  • 逃生舱(Escape Pod):英雄逃离的场景,但需要猜对哪艘是好的。

2. 提取和研究关键概念

现在有了足够的信息来提取一些名词,并分析它们的类层次结构。首先整理一个名词列表:

  • 外星人(Alien)
  • 玩家(Player)
  • 飞船(Ship)
  • 迷宫(Maze)
  • 房间(Room)
  • 场景(Scene)
  • 哥顿人(Gothon)
  • 逃生舱(Escape Pod)
  • 行星(Planet)
  • 地图(Map)
  • 引擎(Engine)
  • 死亡(Death)
  • 中央走廊(Central Corridor)
  • 激光武器库(Laser Weapon Armory)
  • 主控舱(The Bridge)

然而并不是是所有的名词都适合作函数名的,然后需要挑选一个合适的名字做函数名。

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 这个方法,因为我知道具体的场景会继承并覆盖这个方法。

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

准备好了类和函数的树,我需要在编辑器里打开一个源文件,并试着编写代码。通常直接把这个树直接复制到源文件,把它扩展写成各个类就行了,这里是一个初始的简单例子,文件最后还包含一点简单的测试。

class Scene(object):   #场景def enter(self):passclass Engine(object):   #引擎def __init__(self, scene_map):passdef play(self):passclass Death(Scene):   #死亡def enter(self):passclass CentralCorridor(Scene):  #中央走廊def enter(self):passclass LaserWeaponArmory(Scene):    #激光武器库def enter(self):passclass TheBridge(Scene):   #主控舱def enter(self):passclass EscapePod(Scene):   #逃生舱def enter(self):passclass Finished(Scene):   #游戏结束def enter(self):passclass Map(object):   #地图def __init__(self, start_scene):passdef next_scene(self, scene_name):passdef opening_scene(self):passa_map = Map('central_corridor')
a_game = Engine(a_map)
a_game.play()

5.重复和细化

完成上一步之后,再回头重复整个流程,基于你从后面步骤中学到的东西来细化你写的内容,在这个步骤中,你不需要把自己锁定在一个层面上去完成某个特定的任务。假如说你不知道怎样写 Engine.play 这个方法,可以停下来,就在这个任务上使用这个流程,直到弄明白为止。

6.完成代码《来自Percal 25号行星的哥顿人》:

from sys import exit
from random import randint
from textwrap import dedent
#为了写房间描述时使用三引号字符串,引入了dedent函数,它会把字符串开头的空白去掉,如果不用这个函数,使用三引号风格字符串就会失败,因为它们会砸屏幕上缩进,和在Python 代码中一样。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_mapdef 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_mapcurrent_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)])   #randint(0,x)函数,在0-x之间(包含)随机生成一个整数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.---Percal25号星球上的哥顿人入侵了你的飞船,杀了你的全体船员。你是最后的幸存者,你最后的任务获得武器库里的中子毁灭炸弹,放在主控舱,然后把船炸成逃生舱。你沿着中央走廊跑去找武器军械库时,一只哥顿人跳出来,红色的鳞片皮肤,全身黑色的污垢。他挡住了军械库的门并用武器攻击你。"""))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.---你迅速拔出你的爆破枪,向Gothon开火。他滑稽的服饰在他的身边流动,这使你无法瞄准。这完全破坏了他的全新服饰,但没有命中他,这使他勃然大怒,不断地朝你脸上猛烈攻击,直到你死去。然后他吃了你。"""))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.---就像一个世界级的拳击手,你躲避、迂回、滑动、向右滚动,Gothon的爆破枪发射的激光在你头上闪过。在巧妙闪避中你的脚滑了一下,你的头撞在了金属墙上之后晕倒了。你醒来后不久就死了,因为Gothon踩着你的头并吃掉了你。"""))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.---幸运的是,他们让你在学院里学会了Gothon侮辱性语言。你讲了一个你知道的Gothon笑话:Lbhe zbgure vf fb sng, jura fur fvgf nebhaq ubhfr, jura fur fvgf nebhaq ubhfr。Gothon停下来,试着不笑,然后突然大笑起来,动弹不得。当他在笑的时候,你跑上去朝他的头部开了一枪把他打倒,然后从武器室的门跳过去。"""))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.---你俯冲翻滚进武器库,蹲伏并扫描房间以寻找可能隐藏的更多Gothon。这里很安静,非常安静,你站起来跑到房间的另一边,在它的容器里找到了中子弹。盒子上有一个键盘锁,你需要密码才能把炸弹取出来。 如果你错了10次,锁就会永远关闭,你就得不到炸弹。 密码是3位数。"""))code = f"{randint(1,9)}{randint(1,9)}{randint(1,9)}"guess = input("[keypad]> ")guesses = 0while guess != code and guesses < 10:print('BZZZZEDDD!')guesses += 1guess = 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.---你胳膊挟着毁灭炸弹突然出现在主控舱,让试图控制这艘船的5个哥顿人大吃一惊。他们每个人都有比上一个更丑的滑稽服饰。他们没拿出武器,因为他们看到你胳膊下的炸弹,不想让它引爆。"""))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.---你惊慌失措地把炸弹扔向哥顿人,然后向门口跳去。就在你扔下它的时候,一只Gothon朝你的背部开枪杀死了你。当你死的时候,你会看到另一只Gothon疯狂地试图拆除炸弹。你死的时候知道它们可能会在拆弹的时候爆炸。"""))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.---你用爆破枪指着你胳膊下的炸弹,哥顿人举起手开始冒汗。你慢慢的向门移动,打开门,然后小心地把炸弹放在地板上,用爆破枪指着它。然后你跳回门里,按下关闭按钮,破坏门锁,让Gothon跑不出去。既然炸弹已经放好了,你就跑到逃生舱去把这个“罐头”弄下来。"""))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?---你拼命冲过飞船,试图在整个飞船爆炸前到达逃生舱。看来船上几乎没有哥顿人,所以你的奔跑没有干扰。你带着逃生舱来到了密室,现在需要选择一个逃生舱。其中一些可能会造成损害,但你没有时间去看。有5个逃生舱,你带哪一个?"""))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.---进入{guess}#舱,然后点击弹出按钮。逃生舱飞出太空,然后在船体破裂时发生内爆,把你的身体压成果冻酱。"""))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 abright star, taking out the Gothon ship at the same time.You won!---进入{guess}#舱,然后点击弹出按钮。逃生舱很容易的飞出太空,,飞向下面的行星。当它飞向地球时,你回头看,你的飞船爆炸了,然后像一颗明亮的星,同时把Gothon飞船干掉了。你赢了!"""))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_scenedef next_scene(self, scene_name):val = Map.scenes.get(scene_name)return valdef opening_scene(self):return self.next_scene(self.start_scene)a_map = Map('central_corridor')
a_game = Engine(a_map)
a_game.play()

自顶向下和自底向上

“自顶向上” 是从抽象的概念(顶层)开始,一直向下到具体的代码实现,另外还有一种解决编码问题的方法,那就是先从代码开始,一直向上做到抽象概念,这种方法叫“自底向上”。

一般步骤如下:

  • 取出要解决的问题中的一小块,写些代码让它差不多能工作。
  • 加上类和自动测试,细化代码让它更加正式。
  • 把关键概念抽取出来然后研究它们。
  • 把真正需要实现的东西描述出来。
  • 回去细化代码,有可能需要全部丢弃重头做起。
  • 在问题的另一小块里重复上述流程。

这个方法对于更加成熟的程序员来说比较好使,各有优势。

END!!!

《笨方法学 Python 3》43.基本的面向对象分析和设计相关推荐

  1. 17.笨方法学python 习题43

    这章断断续续续写了一周,今天放出来......主要是翻译那些场景描述很烦人,毕竟0级英语. 面向对象的分析和设计的基本流程: 把要解决的问题写下来,或者画下来 将第一条的关键概念提取出来并加以研究 创 ...

  2. 《 笨方法学 Python 》_ 目录

    < 笨方法学 Python >(第 3 版)书中代码是 Python 2 版本,本着学习 Python 3 的目的,用 Python 3 完成本书的习题,代码也已上传到 Github. 作 ...

  3. 笔记 | 笨方法学Python

    整理 | 阿司匹林 出品 | 人工智能头条(公众号ID:AI_Thinker) Python 有多好应该不用多说了,毕竟它是"钦定的"最接近 AI 的语言.(当然,PHP 才是最好 ...

  4. 笨方法学Python(二)

    笨方法学Python,习题16 - 21 版本:3.8.0 编辑器:Visual Studio Code 习题16到21讲的是文件的读写和函数的基础,可以通过一个实例来同时练习他们.在下列情景中,我将 ...

  5. 笔记37 笨办法学python练习43面向对象OOP的游戏代码(二)代码的反复理解

    笔记37 笨办法学python练习43面向对象OOP的游戏代码(二)代码的反复理解 连续贴着这个练习43的代码折腾了整整两天,把那些英文文本翻译为中文文本,重新装进这个代码之中.本想一段一段的运行,发 ...

  6. 笔记36 笨办法学python练习43面向对象OOP的文字理解(一)

    笔记36 笨办法学python练习43面向对象OOP的文字理解(一) 先仔细看了本练习的文本,感到一个笔记写不下来这个复杂的练习过程,那就先把文字理解的部分先来完成,再用一篇笔记来完成代码的理解. 一 ...

  7. 笨方法学python 习题37

    还是在笨方法学python中... 本节的习题是看一下作者列出的python中的各种运算符,尝试来理解这些符号. 在这里,我只列出了一些自己不会的,通过查百度得到得答案,这里来列举一下. (另外有不怎 ...

  8. 笨方法学python 15章疑问

    ** 笨方法学python 15章疑问 在15张中教我们读取文件,但是当我测试能否打开我之前写的py格式的文本时出现了这一幕 文件打开后然后又出现了 File "15.py", l ...

  9. 笨办法学python3 学习笔记 习题43 基本的面向对象分析和设计

    习题43 基本的面向对象分析和设计 通过面向对象编程(OOP)构建一些东西的流程(常用)(自顶向下) 把要解决的问题写下来,或者画出来. 将第一条中的关键概念提取出来并加以研究. 创建一个类层次结构和 ...

  10. 《笨方法学python》_《笨办法学Python》 第46课手记

    <笨办法学Python> 第46课手记 这节课制作了一个Python的项目骨架,花了我一个晚上和一个早上的时间,原因是我下载的pdf里面只有OX S的命令行,而没有win下的.我为此在知道 ...

最新文章

  1. Docker不香吗,为啥还要K8s?
  2. rpm包安装apache发布多个虚拟主机
  3. uboot环境变量实现分析
  4. 【c++】0.C++笔记
  5. Java 多线程:InheritableThreadLocal 实现原理
  6. 崛起吧,亲爱的,该背单词了!!!
  7. PHP正则匹配效率,PHP 正则表达式效率 贪婪、非贪婪与回溯分析(推荐)
  8. 用手写一个工具的过程讲清楚Go反射的使用方法和应用场景
  9. 矩阵乘法c语言蓝桥杯,[蓝桥杯]ALGO-86.算法训练_矩阵乘法
  10. 打印时显示预服务器,域服务器如何设置打印机
  11. UC浏览器书签导入Chrome的详解
  12. MVC、POJO、PO、DTO、TO、BO、VO、DAO、domian、delegate、sql
  13. leetcode 第1题【两数之和】C语言
  14. 【天天读书】2023 年,科技行业的创业者们推荐这 47 本书 | 创始人书单
  15. linux 内核2.6.35.3,linux-2.6.35.3内核移植(s3c2440)
  16. PDF分割成两页怎么弄?来看看这3个工具
  17. 智能语音时代,需要了解什么?
  18. python树状图可视化_如何可视化(树状图)一个层次项目字典?
  19. Zigbee 无线传感网络 无线通信技术 协议体系结构 OSAL
  20. xfplay(先锋影音) v8.9.6 官方版​

热门文章

  1. Cesium基本使用,包括加载影像,地形,模型等,设置地图样式
  2. ssh信任与scp自动传输
  3. css中min-height和max-height的区别
  4. [Java] Java基础
  5. Spring Cloud Netfilx Ribbon(负载均衡工具)
  6. 面试题之数据库脏读、不可重复读、幻读
  7. 基于STM32,TB6612,TCRT5000的简易红外循迹小车
  8. Android Fragment异常:java.lang.IllegalStateException: Fragment XXXFragment not attached to Activity
  9. 努力学Java挣小钱钱
  10. 康耐视In-Sight操作流程