要评判某些设计比其他的设计优秀,就得定义一些在类的设计中重要的术语,以用来讨论 设计的优劣。
对于类的设计来说,有两个核心术语:耦合和聚合。 耦合这个词指的是类和类之间的联系。程序设计的目标是一系列通 过定义明确的接口通信来协同工作的类。耦合度反映了这些类联系的紧密度。我们努力要获得 低的耦合度,或者叫作松耦合(loose coupling)。
耦合度决定修改应用程序的容易程度。在一个紧耦合的结构中,对一个类的修改也会导致 对其他一些类的修改。这是要努力避免的,否则,一点小小的改变就可能使整个应用程序发生 改变。另外,要想找到所有需要修改的地方,并一一加以修改,却是一件既困难又费时的事情。 另一方面,在一个松耦合的系统中,常常可以修改一个类,但同时不会修改其他类,而且 整个程序还可以正常运作。
聚合与程序中一个单独的单元所承担的任务的数量和种类相对应有关,它是针对类或方法 这样大小的程序单元而言的理想情况下,一个代码单元应该负责一个聚合的任务(也就是说,一个任务可以被看作是 一个逻辑单元)。一个方法应该实现一个逻辑操作,而一个类应该代表一定类型的实体。
从Java设计原则1—城堡游戏之消除代码复制 中的源码中可以看出,在Room类中 的成员变量都为public。很明显这不是一个好的处理板发,除非万不得已,否则不能将自己的成员变量设置为public,成员变量一般都会为private!!
public class Room {
public String description;
public Room northExit;
public Room southExit;
public Room eastExit;
public Room westExit;
······
所以毋庸置疑要把public转换成private 即:
public class Room {
private String description;
private Room northExit;
private Room southExit;
private Room eastExit;
private Room westExit;
······
把public转换成private,在其他类里面访问到这四个成员变量的地方势必会抱怨(即报错,在Game类中goRoom()会报错,如下所示代码)。
public class Game{
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;
showPrompt();
}
}
············
public void showPrompt() {
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():
}
}
解决这个问题办法,有一种方式是:可以通过在Room类中实现一个访问权限为public 的函数,通过这个访问权限为public函数来获取Room的私有成员变量,这是一种可行的办法,但不是最好的办法,除非已经无计可施,否则别用哈!!!(ps:以前我是这样用的,too young too simple!)
下来看看另外一种解决办法:让拥有私有成员的类 完成 对私有成员在其他类中需要的操作,给要访问私有成员的类提供接口,也就是用封装来降低耦合和用接口来实现聚合!
用接口来实现聚合,策略:给Room类实现新的方法,把方向的细节彻底隐藏在Room类内部,今后方向如何实现与外部无关!
看下面代码:
public class Game {
······
private void goRoom(String direction)
{
Room nextRoom = currentRoom.goExit(direction);
if (nextRoom == null) {
System.out.println("那里没有门!");
}
else {
currentRoom = nextRoom;
showPrompt();
}
}
·······
public void showPrompt() {
System.out.println("你在" + currentRoom);
System.out.print("出口有: ");
System.out.println(currentRoom.getExitDesc());
System.out.println():
}
}
public class Room {
······
public Room goExit(String direction) {
Room nextRoom = null;
if(direction.equals("north")) {
nextRoom = northExit;
}
if(direction.equals("east")) {
nextRoom = eastExit;
}
if(direction.equals("south")) {
nextRoom = southExit;
}
if(direction.equals("west")) {
nextRoom = westExit;
}
return nextRoom;
}
······
public String getExitDesc() {
StringBuffer sb = new StringBuffer();
if( northExit != null)
sb.append("north");
if( eastExit != null)
sb.append("east");
if( southExit != null)
sb.append("south");
if( westExit != null)
sb.append("weat");
System.out.println();
return sb;
}
}
上面的策略将public成功转为private完成封装,不但消除了Game类中抱怨的地方,而且实现不错的高内聚。
下来面对这样一个问题,如果要给城堡游戏中添加up和down两个方向怎么办?这就是我们考虑的一个很重的设计原则:可控扩展性!下一篇blog会记录。实现了不错的高内聚和低耦合,给可扩展性打下了良好的基础。