java学习笔记(4)代码设计原则

在我们编写代码过程中,往往会出现一些代码复制和硬编码现象。这很大一定程度上影响代码质量,不利于今后代码的维护修改!!
即使一个内部结构很糟糕的,也能实现一个应用程序,并使之完成所需要的工作。这样的代码在维护人员想要对软件进行修改时,就会发现很难进行工作,所以程序的设计在整个应用软件内就变得举足轻重。

  • 消除代码复制
    程序中存在相同的代码块,是低级的代码不良问题。
    代码复制存在的问题是,如果需要修改一个副本,那么就必须同时修改所有其他的副本,否则就 存在不一致的问题。这增加了维护程序员的工作量,而且存在造成错误的潜在危险。很可能发 生的一种情况是,维护程序员看到一个副本被修改好了,就以为所有要修改的地方都已经改好 了。因为没有任何明显迹象可以表明另外还有一份一样的副本代码存在,所以很可能会遗漏还 没被修改的地方。
    我们从消除代码复制开始。消除代码复制的两个基本手段,就是函数和父类。
package castle;

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();
        
        //**代码复制块**
        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.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();
	}

}


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

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


  • 封装
    要讨论评判一个代码的优劣,会用到两个重要设计术语。对于类的设计来说,有两个核心术语:耦合和聚合。
    耦合是指类与类之间的联系的紧密度,耦合度反映了我们代码维护的难度系数。在一个紧耦合的结构当中,当我们去修改一个类时,会同时需要修改其他类。这是我们要避免的局面,那怎样才能降低类与类之间的耦合度呢?封装时一个好办法!
    在上面的代码中,Room类和Game类中有很多代码相关的接口,尤其是Game类中有大量的Room类的成员变量,这样直接交代自己的心肝肺是不行的,也违背了数据的安全性。
    在这里插入图片描述
    在这里插入图片描述
    聚合与程序中一个单独的单元所承担的任务的数量和种类相对应有关,它是针对类或方法 这样大小的程序单元而言的理想情况下,一个代码单元应该负责一个聚合的任务(也就是说,一个任务可以被看作是 一个逻辑单元)。一个方法应该实现一个逻辑操作,而一个类应该代表一定类型的实体。聚合 理论背后的要点是重用:如果一个方法或类是只负责一件定义明确的事情,那么就很有可能在 另外不同的上下文环境中使用。遵循这个理论的一个额外的好处是,当程序某部分的代码需要 改变时,在某个代码单元中很可能会找到所有需要改变的相关代码段。

接下来让我们挽救一下,降低一下Game和Room的耦合度!
在这里插入图片描述
在这里插入图片描述
根据Game中的需要把Room中的数据进行封装成接口函数,这样就大大降低了两者之间的耦合度。

  • 可扩展性
    优秀的软件设计者的一个素质就是有预见性。什么是可能会改变的?什么是可以假设在软 件的生命期内不会改变的? 在游戏的很多类中硬编码进去的一个假设是,这个游戏会是一个基于字符界面的游戏,通 过终端进行输入输出,会永远是这样子吗? 以后如果给这个游戏加上图形用户界面,加上菜单、按钮和图像,也是很有意思的一种扩 展。如果这样的话,就不必再在终端上打印输出任何文字信息。还是可能保留着命令字,还是会在玩家输入帮助命令的时候显示命令字帮助文本,但是可能是显示在窗口的文字域中,而不 是使用System.out.println?
    可扩展性的意思就是代码的某些部分不需要经过修改就能适应将来可能的变化。
    Room类中的方向是用四个成员变量表示的,增加或者减少方向就要改变代码,如果有Hash表来表示方向,那么方向就不是硬编码了。
package castle;

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.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", bedroom);
        bedroom.setExit("down", lobby);
        currentRoom = outside;  //	从城堡门外开始
    }

    private void printWelcome() {
        System.out.println();
        System.out.println("欢迎来到城堡!");
        System.out.println("这是一个超级无聊的游戏。");
        System.out.println("如果需要帮助,请输入 'help' 。");
        System.out.println();
        showCurrentRoom();
    }
    public void showCurrentRoom() {
    	System.out.println("现在你在" + currentRoom);
        System.out.print("出口有:");
        System.out.println(currentRoom.getExitdesc());
        System.out.println();
	}

    // 以下为用户命令

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

    private void goRoom(String direction) 
    {
        Room nextRoom = currentRoom.getExit(direction);
        

        if (nextRoom == null) {
            System.out.println("那里没有门!");
        }
        else {
            currentRoom = nextRoom;
            showCurrentRoom();
        }
    }
	
	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();
	}

}

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 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 setExit(String dir,Room room) 
    {
        exits.put(dir, room);
    }

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


  • 框架+数据
    从程序中识别出框架和数据,以代码实现框架,将部分功能以数据的方式加载,这样能在很大程度上实现可扩展性。

命令解析是否可以脱离if-else
我们定义一个Handler来处理命令,用Handler的子类HandlerBye来规定bye命令,HandlerHelp来规定hlep命令,HandlerGo来处理go!再将他们的对象放入到Hash表中来实现可扩展性。

展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 数字20 设计师: CSDN官方博客
应支付0元
点击重新获取
扫码支付

支付成功即可阅读