learn ruby the hard way (43)

http://learnrubythehardway.org/book/ex43.html

面向对象的分析和设计

  • 用写或者画的方式,对问题进行描述
  • 从中找出关键概念,对它进行研究
  • 对关键概念编写类层次结构和对象映射
  • 实现类的功能和测试
  • 重复整个流程,完善代码

整个过程是从上至下的(top down) ,是从一个抽象的想法出发, 慢慢地通过改善逐渐成为能够编写代码的实例。

实现一个文字游戏

一个太空冒险游戏:Gothons from Planet Percal #25

对问题进行描述

外星人(Gothons)入侵了一艘太空船, 我们的英雄为了能够成功的进入逃生舱驶向地球, 不得不穿过迷宫般的房间.

这个游戏非常像Zork或是其他冒险类游戏, 包括文字输出,玩家输入,以及有趣的死亡方式.

这个游戏将包含运行所有房间或是场景的引擎. 当玩家进入一个房间的时候, 都会出现相应的描述, 并且告诉引擎当出了这个地图后, 哪个房间将被运行.

更多的场景描述

  • Death: 当玩家死亡时会被触发, 应具有一定的娱乐性
  • Central Corridor: 游戏的开始点, 有一个Gothon已经站在了那里, 你需要通过讲笑话击败他
  • Laser Weapon Armory: 英雄能在这里获取中子炸弹, 用于在进入逃生舱之前引爆. 它有一个输入盘, 需要猜测数字
  • Bridge: 另一个有Gothon的战斗场景, 英雄将在那里放置炸弹
  • Escape Pod: 英雄必须选择正确的逃生舱才能逃离

我们可能还需要画一下场景地图

找出关键概念

做一份名词单, 并开始问自己: "这个名词是不是和别的名词意思相同? 这意味着它们有一个相同的父类, 那么这个父类叫什么呢?" 不断地重复提问回答地过程, 直到建立一个类关系图

Alien, Player, Ship, Maze, Room, Scene, Gothon, Escape Pod, Planet, Map, Engine, Death, Central Corridor, Laser Weapon Armory, The Bridge

类层次结构

* 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
          

定义抽象方法


class Scene
  def enter()
  end
end


class Engine
  def initialize(scene_map)
  end

  def play()
  end
end

class Death < Scene
  def enter()
  end
end

class CentralCorridor < Scene
  def enter()
  end
end

class LaserWeaponArmory < Scene
  def enter()
  end
end

class TheBridge < Scene
  def enter()
  end
end

class EscapePod < Scene
  def enter()
  end
end


class Map
  def initialize(start_scene)
  end

  def next_scene(scene_name)
  end

  def opening_scene()
  end
end

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

实现引擎


class Engine
  def initialize(scene_map)
    @scene_map = scene_map
  end

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

    while current_scene != last_scene
      next_scene_name = current_scene.enter()
      current_scene = @scene_map.next_scene(next_scene_name)
    end

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

实现场景


class Scene
  def enter()
    puts "This scene is not yet configured. Subclass it and implement enter()."
    exit(1)
  end
end

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()
    puts @@quips[rand(0..(@@quips.length - 1))]
    exit(1)
  end
end

实现地图


class Map
  @@scenes = {
    'central_corridor' => CentralCorridor.new(),
    'laser_weapon_armory' => LaserWeaponArmory.new(),
    'the_bridge' => TheBridge.new(),
    'escape_pod' => EscapePod.new(),
    'death' => Death.new(),
    'finished' => Finished.new(),
  }


  def initialize(start_scene)
    @start_scene = start_scene
  end


  def next_scene(scene_name)
    val = @@scenes[scene_name]
    return val
  end

  def opening_scene()
    return next_scene(@start_scene)
  end
end

开始游戏


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

测试,修复bug,完善代码

第一个场景,讲完笑话就结束了,没有进入第二个场景

Laser Weapon Armory可以猜测11次,不是设想的10次

给猜3位代码添加一个特定的作弊码(比如888)

添加更多的场景

Q & A

下一课