从程序中识别出框架和数据,以代码实现框架,将部分功能以数据方式加载,这样在很大程度上实现了可拓展性.
以框架+数据来提高可拓展性
●命令的解析是否可以脱离 if - else
●定义一个Handler来处理命令
●用Hash表来保存命令&Handler之间的关系
我们之前刚开始城堡游戏做的是硬编码,就是把方向什么都当作Room的成员变量,然后调用,后来我们利用HashMap函数, 将方向和房间联系起来,这样我们就可以随意的增加不同的方向,
我们也利用接口函数,传入数据,返回数据,这些我们形成了一个框架,在这些框架里面,我们如果要增加新的出口, 很容易.
所以这是一种思路, 把程序的硬编码, 尽可能的分解成框架和数据的结构, 框架规定说,我们有一个HashMap , 我们有这几个接口函数, 数据就是我们放在HashMap里面的东西.
那么在这个程序里面, 还有一部分是硬编码. 就是我们的命令解析 , 我们现在有三个变量
"go" ,"help ", "bye".
我们在Game的主函数里面 , 有一个判断输的字符
这是一种硬编码, 当然我们的HashMap 里面存放的是键值和元素,都必须是对象, 而我们这里需要的是, 输入字符串, 然后去调用函数, 函数不是对象.
那类里面可以有函数, 所以我们可以定义一个新的类
public class Handler{ public void doCmd(String word){} }
我们这是为不同操作定义的一个基础类
那接下来, 我们要定义一个对象,用来保存 字符串 和 Handler对象之间的对应关系
在Game里面,定义成员变量
private HashMap<String , Handler> handlers = new HashMap<String , Handler>();
当然我们想将输入的"help" "go" "bye " 和对应的操作联系起来, 需要在HashMap里面创建对象,
那我们需要在Game初始化的时候,就把对象填进去, 那我们就那Game的构造函数开刀.
public Game(){ handlers.put("bye" , new HandlerBye(this)); handlers.put("help" , new HandlerHelp(this)); handlers.put("go" , new HandlerGo(this)); createRooms(); }
我们new 创建了一个关于HadlerGo 、HandlerBye 、HandlerHelp的类的对象
那我们就需要创建这几个类,来实现调用相关的指令
开始之前 , 我们还需要把主函数里面的判断输入,调用函数的工作 , 去交给成员函数
你叉叉
替换成 game.play();
交给成员变量:
public void play(){ Scanner in = new Scanner(System.in); while ( true ) { String line = in.nextLine(); String[] words = line.split(" "); if ( words[0].equals("help") ) { game.printHelp(); } else if (words[0].equals("go") ) { game.goRoom(words[1]); } else if ( words[0].equals("bye") ) { break; } } }
此时,如果我们要根据输入的字符串 , 来调用指定的对象的类的操作, 就需要我们的 handler了
public void play(){
Scanner in = new Scanner(System.in);
while ( true ) {
String line = in.nextLine();
String[] words = line.split(" ");
Handler handler = handlers.get(word[0]);
if( handler != null){
handler.doCmd(word[1]);
if( handler.isBye())
break;
}
}
}
所以, 我们结束的时候,需要 Handler 类里面有一个成员函数
public boolean isBye(){ return false ;}
默认返回 false
那什么时候返回true 呢,就是结束的时候, 我们可以让另一个 HandlerBye 类来继承Handler类,然后我们就可以覆盖 父类的public boolean isBye(){ return false ;} 成员函数了
定义的Handler类,
父类
public class Handler{ public void doCmd(String word){} public boolean isBye(){ return false;} }
子类继承父类
public class HandlerBye extends Handler{ @Override public boolean isBye(){ return true; } }
那现在我们要实现 , 输入bye , 然后就调用对应容器里的函数, 需要我们在容器 handlers里面放入 字符串bye 和HandlerBye 对象之间的关系
在Game的构造函数里面
public Game(){
handlers.put("bye" , new HandlerBye());
}
因为我们需要用到handlerBy 这个类的属性,所以要创建一个对象,然后将这个对象放在容器handlers里面,容器里面放的是字符串和对象之间的关系
这里,
当我们输入 “bye” 的时候, 第一个单词就是,然后
Handler handler = handlers.get("bye");
把容器里面的 bye对应的 HandlerBye 对象传递给了 handler , 变成
if(HandlerBye != null){
HandlerBye.doCmd(words[1]);
if(HandlerBye.isBye()) / /调用了Handler的子类HandlerBye覆盖了public booleanisBye()
{
beak ; //返回了true,然后结束了游戏
}
那我们其他命令,比如 go east go west ,是第二个字符,我们也要进行赋值,怎么办,我们做了一点小修改
public void play(){
@SuppressWarnings("resource")
Scanner in = new Scanner(System.in);
while ( true ) {
String line = in.nextLine();
String[] words = line.split(" ");
Handler handler = handlers.get(words[0]);
String value = "";
if(words.length > 1)
value = words[1];
if( handler != null){
handler.doCmd(value);
if( handler.isBye())
break;
}
}
}
现在 bye 命令可以用了,但是 help 还是不能用的
我们要覆盖Handler类的 doCmd 成员函数
HandlerHelp类
public class HandlerHelp extends Handler{ @Override public void doCmd(String word){ System.out.println("迷路了吗?你可以做的命令有: go bye help"); System.out.println("如: \tgo east"); } }
Game 里面的 printHelp成员函数就不存在了
要让help起作用
我们需要在Game里面的handlers容器里面,放入
handlers.put("help" , new HandlerHelp(this));
自然的,当我们输入 help 的时候
public void play(){
Scanner in = new Scanner(System.in);
while ( true ) {
String line = in.nextLine();
String[] words = line.split(" ");
Handler handler = handlers.get(word[0]);
String value = "";
if(words.length > 1)
value = word[1];
if( handler != null){
handler.doCmd(value);
if( handler.isBye())
break;
}
}
}
handlers.get("help") 对应 对象HandlerHelp()
然后Handler的子类HandlerHelp就会调用 doCmd 函数,覆盖父类,输出帮助信息。
同理,我们接下来,给 go 添加操作
我们让HandlerGo继承 Handler,然后覆写
public class HandlerGo extends Handler{ public void doCmd(String word){ game.goRoom(word); } }
我们现在要的是,实现Game里面的goRoom 成员函数的功能,如果这里game 是指针的话,这种方法是可以的,我们需要拿到的是game的对象。
由于,作者能力有限,这里我们采用比较曲折的办法,
我们首先在Handler里面定义Game 的变量
public class Handler{
protected Game game; public Handler(Game game){ this.game = game; }
public void doCmd(String word){}public boolean isBye(){ return false;}
}
接下来,他的每一个子类都需要实现 ,带有Game的构造函数
public class HandlerBye extends Handler{
public HandlerBye(Game game) { super(game); // TODO Auto-generated constructor stub }
@Override
public boolean isBye(){
return true;
}
}
HandlerHelp 类:
public class HandlerHelp extends Handler{
public HandlerHelp(Game game) { super(game); // TODO Auto-generated constructor stub }
@Override
public void doCmd(String word){
System.out.println("迷路了吗?你可以做的命令有: go bye help");
Sysem.out.println("如: \tgo east");
}
}
那对于go来说,也会记下Game
HandlerGo类
public class HandlerGo extends Handler{
public HandlerGo(Game game){ super(game); }
public void doCmd(String word){
game.goRoom(word);
}
}
还有一个问题,我们在各个类创建构造函数的时候,需要传入参数,我们在
Game 的构造函数里面,往容器里面传入 字符串和 用各个类创建的对象 赋予对应关系的时候, 在括号里传入(this)就可以了。
public Game(){ handlers.put("bye" , new HandlerBye(this)); handlers.put("help" , new HandlerHelp(this)); handlers.put("go" , new HandlerGo(this)); createRooms(); }
当然,为了让我们能调用Game里的 goRoom成员函数,我们需要将private 改成 public ,这样我们就可以调用,并传入参数了 ,接着覆写函数了。
做完这个之后,如果我们的代码今后想要增加新的命令,我们需要做到事情只有两个地方,
第一,要有新的指令的 Handler类型
第二,在Game的构造器里面,把新的指令Handler对象放进去
然后,在public void play { }里面的代码不需要改变,这就是可扩展性。
我们之所以把操作分开进行输入,是为了尽可能让每一个指令形成一个类,尽可能聚合,让功能更清晰。
源代码如下:
Game类:
package castle; import java.util.HashMap; import java.util.Scanner; public class Game { private Room currentRoom; private HashMap<String , Handler> handlers = new HashMap<String , Handler>(); public Game(){ handlers.put("bye" , new HandlerBye(this)); handlers.put("help" , new HandlerHelp(this)); handlers.put("go" , new HandlerGo(this)); createRooms(); } private void createRooms() { Room outside, lobby, pub, study, bedroom; // 制造房间 outside = new Room("城堡外"); lobby = new Room("大堂"); pub = new Room("小酒吧"); study = new Room("书房"); bedroom = new Room("卧室"); // 初始化房间的出口 outside.setExit("east" , lobby); outside.setExit("south", study); outside.setExit("west", pub); lobby.setExit("west" , outside); pub.setExit("east" , outside); study.setExit("north" , outside); study.setExit("east" , bedroom); bedroom.setExit("west" , study); lobby.setExit("up" , pub); pub.setExit("down" , lobby); currentRoom = outside; // 从城堡门外开始 } private void printWelcome() { System.out.println(); System.out.println("欢迎来到城堡!"); System.out.println("这是一个超级无聊的游戏。"); System.out.println("如果需要帮助,请输入 'help' 。"); System.out.println(); showPrompt(); } // 以下为用户命令 private void printHelp() { System.out.print("迷路了吗?你可以做的命令有:go bye help"); System.out.println("如:\tgo east"); } public void goRoom(String direction) { Room nextRoom = currentRoom.getExit(direction); if (nextRoom == null) { System.out.println("那里没有门!"); } else { currentRoom = nextRoom; showPrompt(); } } public void play(){ @SuppressWarnings("resource") Scanner in = new Scanner(System.in); while ( true ) { String line = in.nextLine(); String[] words = line.split(" "); Handler handler = handlers.get(words[0]); String value = ""; if(words.length > 1) value = words[1]; if( handler != null){ handler.doCmd(value); if( handler.isBye()) break; } } } public void showPrompt(){ System.out.println("现在你在" + currentRoom); System.out.print("出口有:"); System.out.println(currentRoom.getExitDesc()); System.out.println(); } public static void main(String[] args) { Game game = new Game(); game.printWelcome(); game.play(); System.out.println("感谢您的光临。再见!"); } }
Room类:
package castle; import java.util.HashMap; public class Room { private String description; private HashMap<String,Room> exits = new HashMap<String, Room>(); public Room(String description) { this.description = description; } public void setExit(String dir, Room room){ exits.put(dir, room); } @Override public String toString() { return description; } public String getExitDesc(){ StringBuffer sb = new StringBuffer(); for( String dir : exits.keySet()){ sb.append(dir); sb.append(' '); } return sb.toString(); } public Room getExit(String direction){ return exits.get(direction); } }
Handler类:
package castle; public class Handler{ protected Game game; public Handler(Game game){ this.game = game; } public void doCmd(String word){} public boolean isBye(){ return false;} }
HandlerHelp类:
package castle; public class HandlerHelp extends Handler{ public HandlerHelp(Game game) { super(game); // TODO Auto-generated constructor stub } @Override public void doCmd(String word){ System.out.println("迷路了吗?你可以做的命令有: go bye help"); System.out.println("如: \tgo east"); } }
HandlerBye类:
package castle; public class HandlerBye extends Handler{ public HandlerBye(Game game) { super(game); // TODO Auto-generated constructor stub } @Override public boolean isBye(){ return true; } }
HandlerGo类:
package castle; public class HandlerGo extends Handler{ public HandlerGo(Game game){ super(game); } public void doCmd(String word){ game.goRoom(word); } }