title: Java进阶学习-7 面向对象程序设计原则
date: 2020-02-02 18:02:27
tags:
以一个城堡游戏为例子谈面向对象程序设计原则
Game.java
package castle;
import java.util.Scanner;
public class Game {
private Room currentRoom;
public Game()
{
creatRooms();
}
private void creatRooms()
{
Room outside, lobby,pub,study,bedroom;
// 制造房间
outside = new Room("城堡外");
lobby = new Room("大堂");
pub = new Room("小酒吧");
study = new Room("书房");
bedroom = new Room("卧室");
// 初始化房间的出口
outside.setExits(null,lobby,study,pub);
lobby.setExits(null,null,null,outside);
pub.setExits(outside,bedroom,null,null);
bedroom.setExits(null,null,null,study);
currentRoom = outside; //从城堡门外开始
}
private void printWelcome()
{
System.out.println();
System.out.println("欢迎来到城堡!");
System.out.println("这是一个超级无聊的游戏。");
System.out.println("如果需要帮助,请输入'help'。");
System.out.println();
System.out.println("现在你在:" + currentRoom);
System.out.print("出口有:");
if(currentRoom.northExit != null)
System.out.print("north ");
if(currentRoom.eastExit != null)
System.out.print("east ");
if(currentRoom.southExit != null)
System.out.print("south ");
if(currentRoom.westExit != null)
System.out.print("west ");
System.out.println();
}
// 以下为用户命令
private void printHelp()
{
System.out.println("迷路了吗?你可以做的命令有:go bye help");
System.out.println("如:\tgo east");
}
private void goRoom(String direction)
{
Room nextRoom = null;
if(direction.equals("north")){
nextRoom = currentRoom.northExit;
}
if(direction.equals("east")){
nextRoom = currentRoom.eastExit;
}
if(direction.equals("south")){
nextRoom = currentRoom.southExit;
}
if(direction.equals("west")){
nextRoom = currentRoom.westExit;
}
if(nextRoom == null){
System.out.println("那里没有门!");
}
else{
currentRoom = nextRoom;
System.out.println("你在"+ currentRoom);
System.out.println("出口有:");
if(currentRoom.northExit != null)
System.out.print("norh ");
if(currentRoom.eastExit != null)
System.out.print("east ");
if(currentRoom.southExit != null)
System.out.print("south ");
if(currentRoom.westExit != null)
System.out.print("west ");
System.out.println();
}
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
Game game = new Game();
game.printWelcome();
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;
}
}
System.out.println("感谢您的光临。再见!");
in.close();
}
}
Room.java
package castle;
public class Room {
public String description;
public Room northExit;
public Room southExit;
public Room eastExit;
public Room westExit;
public Room(String description)
{
this.description = description;
}
public void setExits(Room north,Room east,Room south,Room west)
{
if(north != null)
northExit = north;
if(east != null)
eastExit = east;
if(south != null)
southExit = east;
if(west != null)
westExit = west;
}
@Override
public String toString()
{
return description;
}
public void southExit(Object object, Room lobby, Room study, Room pub) {
// TODO Auto-generated method stub
}
}
原则一:代码复制问题
消除代码复制
将showPrompt()引入将重复的代码部分使用showPrompt()代替
public void showPrompt() {
System.out.println("你在"+ currentRoom);
System.out.println("出口有:");
if(currentRoom.northExit != null)
System.out.print("norh ");
if(currentRoom.eastExit != null)
System.out.print("east ");
if(currentRoom.southExit != null)
System.out.print("south ");
if(currentRoom.westExit != null)
System.out.print("west ");
System.out.println();
}
原则二:可维护性—封装
自己的代码是否可以很好的维护,他人可以在这个代码上继续做事情。是否适合于扩展
增加可扩展性
可以运行的代码!=良好的代码
对代码做维护的时候最可以看出代码的质量
如果想要增加一个方向,如down或者up
用封装来降低耦合
耦合 类和类的关系
Room类和Game类都有大量的代码和出口相关
尤其是Game类中大量使用了Room类的成员变量
类和类之间的关系叫做耦合
耦合越低越好,保持距离是形成良好代码的关键
此处的问题是Room中的所有的成员变量都要改为private
在Room增加两个函数 在Game中做相应处理
getExitDesc()
public String getExitDesc() {
StringBuffer sb = new StringBuffer();
if( northExit != null )
sb.append("north");
if( eastExit != null )
sb.append("east");
if( westExit != null )
sb.append("west");
if( southExit != null )
sb.append("south");
return sb.toString();
}
getExit()
public Room getExit(String direction) {
Room ret = null;
if(direction.equals("north")){
ret = northExit;
}
if(direction.equals("east")){
ret = eastExit;
}
if(direction.equals("south")){
ret = southExit;
}
if(direction.equals("west")){
ret = westExit;
}
return ret;
}
用接口实现聚合
给Room类实现的新的方法,把方向的细节彻底隐藏在Room内部
今后方向如何实现和外部无关
用容器来实现灵活性
Room的方向使用成员变量表示的,如果想要增加或者减少方向都要大面积的改代码
如果用Hash表来表示方向,那么方向就不是硬编码。可以任意的增加方向。
private Room northExit;
private Room southExit;
private Room eastExit;
private Room westExit;
用下代替
private HashMap<String, Room> exit = new HashMap<String, Room>();
修改
public void setExits(Room north,Room east,Room south,Room west)
{
if(north != null)
northExit = north;
if(east != null)
eastExit = east;
if(south != null)
southExit = east;
if(west != null)
westExit = west;
}
用下代替 一次输出一个方向
public void setExit(String dir, Room room) {
exits.put(dir, room);
}
获取方向的时候 直接遍历一遍hashMap 使用
public String getExitDesc() {
StringBuffer sb = new StringBuffer();
for( String dir : exits.keySet() ) {
sb.append(dir);
sb.append(' ');
}
return sb.toString();
}
getExit()修改为
public Room getExit(String direction) {
return exits.get(direction);
}
则在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; //从城堡门外开始
同时也很方便的进行对上下层的扩展 加上
lobby.setExit("up", pub);
pub.setExit("down", lobby);
就可以很好的扩展地图。
原则三:以框架+数据来提高可扩展性
命令的解析是否可以脱离if-else
定义一个Handler来处理命令
用Hash表来保存命令和Handler之间的关系
将硬编码转化为框架和数据
框架使用Hash表 和 函数接口 代表
数据为放在hashMap中的东西
main函数中的硬编码 循环
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;
}
}
函数不是对象 但是类里面有函数
故最后修改城堡游戏为:
Game.java
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("go", new HandlerGo());
handlers.put("bye", new HandlerBye(this));
handlers.put("help", new HandlerHelp(this));
handlers.put("go", new HandlerGo(this));
creatRooms();
}
private void creatRooms()
{
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();
}
// 以下为用户命令
public void goRoom(String direction)
{
Room nextRoom = currentRoom.getExit(direction);
if(nextRoom == null){
System.out.println("那里没有门!");
}
else{
currentRoom = nextRoom;
showPrompt();
}
}
public void showPrompt() {
System.out.println("你在"+ currentRoom);
System.out.println("出口有:");
System.out.print(currentRoom.getExitDesc());
System.out.println();
}
public void play() {
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;
}
}
// if ( words[0].equals("help")){
// printHelp();
// }else if ( words[0].equals("go")){
// goRoom(words[1]);
// }else if ( words[0].equals("bye")){
// break;
// }
}
in.close();
}
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
Game game = new Game();
game.printWelcome();
game.play();
System.out.println("感谢您的光临。再见!");
}
}
HandlerHelp.java
package castle;
public class HandlerHelp extends Handler {
public HandlerHelp(Game game) {
super(game);
}
@Override
public void doCmd(String word) {
System.out.println("迷路了吗?你可以做的命令有:go bye help");
System.out.println("如:\tgo east");
}
}
Room.java
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);
}
public void southExit(Object object, Room lobby, Room study, Room pub) {
// TODO Auto-generated method stub
}
}
HandlerHelp.java
package castle;
public class HandlerGo extends Handler {
public HandlerGo(Game game) {
super(game);
}
@Override
public void doCmd(String word) {
game.goRoom(word);
}
}
HandlerGo.java
package castle;
public class HandlerGo extends Handler {
public HandlerGo(Game game) {
super(game);
}
@Override
public void doCmd(String word) {
game.goRoom(word);
}
}
HandlerBye.java
package castle;
public class HandlerBye extends Handler{
public HandlerBye(Game game) {
super(game);
}
@Override
public boolean isBye() {
return true;
}
}