设计原则

如果写不出设计优良的类,就发挥不出OOP的优势。发挥不出OOP的优势,很容易就会成为量产型程序员。

下面给出一份糟糕的简易文字游戏的代码,然后进行优化

//Room.java
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 = south;
        if(west != null)
            westExit = west;
    }

    @Override
    public String toString()
    {
        return description;
    }
}
//Game.java
import java.util.Scanner;

public class Game {
    private Room currentRoom;
        
    public Game() 
    {
        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.setExits(null, lobby, study, pub);
        lobby.setExits(null, null, null, outside);
        pub.setExits(null, outside, null, null);
        study.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();
    }

    // 以下为用户命令

    private void printHelp() 
    {
        System.out.print("迷路了吗?你可以做的命令有: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.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();
        }
    }
	
	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();
	}

}
消除代码复制

明显看到下面这段代码在Game.java中有重复

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();

可以做成函数在原处调用

//Game.java
public void printchoice(){
	System.out.println("现在你在" + currentRoom);
    System.out.print("出口有:");
    ......
}
用封装来降低耦合
//Room.java
public class Room {
    private String description;
    private Room northExit;
    private Room southExit;
    private Room eastExit;
    private Room westExit;

    public Room(String description){
        this.description = description;
    }

    public  String showExit(){
        StringBuffer re =new StringBuffer();
        if(this.eastExit!=null){
            re.append("east ");
        }
        if(this.westExit!=null){
            re.append("west ");
        }
        if(this.southExit!=null){
            re.append("south ");
        }
        if(this.northExit!=null){
            re.append("north ");
        }
        return re.toString();
    }
    
    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 = south;
        if(west != null)
            westExit = west;
    }

    public  Room nextRoom(String obj){
        Room re=null;
        if(obj.equals("north"))
            re = this.northExit;
        else if(obj.equals("east"))
            re = this.eastExit;
        else if(obj.equals("south"))
            re = this.southExit;
        else if(obj.equals("west"))
            re = this.westExit;
        return re;
    }

    @Override
    public String toString(){
        return description;
    }
}

当然此时的Game.java

import java.util.Scanner;

public class Game {
    private Room currentRoom;

    public Game(){
        createMap();
    }

    private void createMap(){
        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(null, outside, null, null);
        study.setExits(outside, bedroom, null, null);
        bedroom.setExits(null, null, null, study);

        currentRoom = outside;  //	从城堡门外开始
    }

    private  void printChoice(){
        System.out.println("现在你在" + currentRoom);
        System.out.print("出口有:");
        String info = currentRoom.showExit();
        if(info != null){
            System.out.printf(info);
        }
        else{
            System.out.print("you have jump into a trap.over.");
        }
        System.out.println();
    }

    private void printWelcome() {
        System.out.println();
        System.out.println("欢迎来到城堡!");
        System.out.println("这是一个超级无聊的游戏。");
        System.out.println("如果需要帮助,请输入 'help' 。");
        System.out.println();
        printChoice();
    }

    // 以下为用户命令

    private void printHelp(){
        System.out.print("迷路了吗?你可以做的命令有:go bye help");
        System.out.println("如:\tgo east");
    }

    private void goRoom(String direction){
        Room nextRoom = currentRoom.nextRoom(direction);
        if (nextRoom == null) {
            System.out.println("那里没有门!");
        }
        else {
            currentRoom = nextRoom;
            printChoice();
        }
    }
	
	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
import java.util.HashMap;

public class Room {
    private String description;
    private HashMap<String,Room> exit = new HashMap<String, Room>();

    public Room(String description){
        this.description = description;
    }

    public void setExit(String direction , Room room){
        exit.put(direction,room);
    }

    public  String showExit(){
        StringBuffer re =new StringBuffer();
        for(String s :exit.keySet()){
            re.append(s);
            re.append(" ");
        }
        return re.toString();
    }

    public  Room nextRoom(String obj){
        Room re=null;
        re = exit.get(obj);
        return re;
    }

    @Override
    public String toString()
    {
        return description;
    }
}

//Game.java中的设置地图部分
private void createMap(){
        Room outside, lobby, pub, study, bedroom;
      
        //	制造房间
        outside = new Room("城堡外");
        lobby = new Room("大堂");
        pub = new Room("小酒吧");
        study = new Room("书房");
        bedroom = new Room("卧室");
        
        //	初始化房间的出口
        outside.setExit("west",lobby);
        outside.setExit("south",study);
        outside.setExit("north", pub);
        lobby.setExit("east",  outside);
        pub.setExit("west", outside);
        study.setExit("west",outside);
        study.setExit("south",bedroom);
        bedroom.setExit("north",study);
		//此处我们可随意增加down,up,等等的新的Room关系,而不用修改Room类
		//容器使得Room类更加灵活
        currentRoom = outside;  //	从城堡门外开始
    }

还有一处硬编码为命令的判断,即go,bye,help
若增加新的命令,实现命令本身的功能后,还要维护命令分发过程的代码
同样用容器修改

import java.util.HashMap;
import java.util.Scanner;

interface Handler{
    public void handle(String str);
}

public class Game {
    private Room currentRoom;
    private HashMap<String,Handler> handlers = new HashMap<>();

    public Game(){
        createMap();
        handlers.put("go", this::goRoom);
        handlers.put("help", (str -> printHelp()));
        handlers.put("bye", str -> {
            System.out.println("感谢您的光临。再见!");
            System.exit(0);
        });
    }

    //Game.java中的设置地图部分
    private void createMap(){...}

    private  void printChoice(){...}

    private void printWelcome() {...}

    // 以下为用户命令

    private void printHelp(){...}

    private void goRoom(String direction){...}

    private void play(){
        Scanner in = new Scanner(System.in);
        while ( true ) {
            String line = in.nextLine();
            String[] words = line.split(" ");
            String value = null;
            if(words.length>1){
                value = words[1];
            }
            Handler handler = handlers.get(words[0]);
            if(handler!=null){
                handler.handle(value);
            }else {
                System.out.println("我不明白你的意思,请使用help命令");
            }
//            if ( words[0].equals("help") ) {
//                printHelp();
//            } else if (words[0].equals("go") ) {
//                goRoom(words[1]);
//            } else if ( words[0].equals("bye") ) {
//                break;
//            }
        }
    }

    public static void main(String[] args) {
        Game game = new Game();
        game.printWelcome();
        game.play();
    }

}

通过以上设计,当要加入新的命令时,只需定义新的handler,主程序逻辑无需变化


2019/7/25更新