给JAVA设计开发新手的一些建议和意见(3)--转

【空接口的使用】
  在接口使用的时候,空接口有2种情况:
  1. 类似Cloneable,Serializable,他们往往是做一个标记,表示需要某个功能。当然你也可以这么用,来表示你的类具有某个功能,实现了你的某个接口。
  2. 你的接口继承了别的接口(非空),你的接口本身没有声明函数。这种情况一般是你不希望用户使用父接口来作为参数类型,因为他们的用途可能不同,此时就可以用空接口来实现。
  
  第一种情况我们不再多说,搜索一下关于Cloneable,Serializable的文章就会了解很多。
  我们来看下面的代码:
  public interface Text
  {
  String getText();
  }
  
  public interface SqlText extends Text
  {
  }
  可以看到,Text接口是用于返回一个字符串。而SqlText是一个空接口,它继承了Text接口。也就是说SqlText也是一种Text。但是我们可以知道,任何一个字符串不一定是Sql字符串,所以此时声明了一个SqlText接口来用于表名当前的字符串是一个Sql字符串。你的函数可以这样声明:
  public void doQuery(SqlText aSqlText)
  而不是这样
  
  public void doQuery(Text aText)
  避免用户产生歧义的想法,一眼看去,就明白应该传入一个Sql字符串。
  
   【继承层次过多】
  一般来说,继承的层次不要过多,否则使用者可能会讨厌,找一个函数会很麻烦。很多Java语言检查工具都建议你的继承层次不要超过3层。
  
   【Has A ,Is A,不要滥用继承】
  "我是一个Mp3","我有一个Mp3",其实很容易分辨。但是在实际应用中,往往存在把"我有一个Mp3"的情况当作"我是一个Mp3",或者是为了偷懒方便而放松了对自己的要求,甚至还沾沾自喜,感觉找到一个捷径。(scud以前也干过这种事情)。
  
  以前我曾经这样干过:我的逻辑类直接继承了我的数据库访问类,这样我可以直接在逻辑类里面访问:
  
  public MyLogic extends MyDBA
  
  aLogic.getInt("click");
  aLogic.getString("name");
  
  看起来是非常方便,但是你的逻辑类就牢牢绑在了DBA上,是一种非常不好的做法。现在我这样声明:
  public MyLogic
  
  MyDBA adba;
  
  adba.getInt("click");
  adba.getString("name");
  其实代码改动不大,但是你的逻辑类不在牢牢绑在DBA身上了,何乐而不为。
  
  其实这种现象在开发人员中间可能经常见到,我们要尽量避免。下面再来看一个例子:
  
  //一个保存分页信息的类
  
  public class PageInfo
  {
  private int page;
  private int pageCount;
  private int recPerPage;
  private int recCount;
  
  //get,set method list...
  }
  一般的情况是,在Dao中进行分页查询,计算总记录,总页数等等,所以需要把PageInfo传给Dao。而在逻辑类中,把传回来的分页信息数据推到FormBean或者是Action中。
  也许你会这么想,如果我的Action或者FormBean继承了PageInfo,岂不是要省很多事。
  
  千万别这么干。并不是所有的动作都需要分页信息,你的FormBean和PageInfo没有继承的关系。也就是说FormBean Has A PageInfo,但是不是Is A PageInfo。
  
   【保持外观/行为一致】
  外观一致其实很容易理解,例如你用size()表示得到一个List的大小,那么在所有的List类中你都用size()得到它的大小,这就是外观一致。
  外观一致让用户更方便使用你的函数库,不用记住几个不同的表示同一个功能的函数名字。或者几个名字相同功能却不同的函数。那就很糟糕了。
  
  行为一致相对外观一致就相对比较难做到,但是优秀的设计师肯定会让他的成果行为一致,而不是出人意料的行为,也不是一套强行规定的行为。
  
  我们来看下面的代码:
  
  import java.util.HashMap;
  import java.util.Map;
  class UserInfo
  {
  private String realname;
  
  public UserInfo(String sName)
  {
  this.realname = sName;
  }
  
  public void setName(String sName)
  {
  this.realname = sName;
  }
  public String getName()
  {
  return this.realname;
  }
  }
  
  public class MyTest
  {
  
  Map userInfoMap = new HashMap();
  
  public void setUserInfo(String sName,UserInfo aInfo)
  {
  userInfoMap.put(sName,aInfo);
  
  userInfoMap.put(aInfo.getName(),aInfo);
  }
  
  public UserInfo getUserInfo(String sName)
  {
  return (UserInfo)userInfoMap.get(sName);
  }
  
  public static void main(String args[])
  {
  MyTest aTest = new MyTest();
  
  UserInfo aUserInfo = new UserInfo("王小二");
  
  aTest.setUserInfo("儿童团团长",aUserInfo);
  aTest.setUserInfo("三班班长",aUserInfo);
  
  UserInfo 儿童团团长 = aTest.getUserInfo("儿童团团长");
  
  if(null!=儿童团团长)
  {
  System.out.println(儿童团团长.getName());
  }
  else
  {
  System.out.println("儿童团团长 Not Found");
  }
  
  UserInfo 王小二 = aTest.getUserInfo("王小二");
  
  if(null!=王小二)
  {
  System.out.println(王小二.getName());
  }
  else
  {
  System.out.println("王小二 Not Found");
  }
  
  }
  }
  可以看到,上面的代码运行结果是"王小二",也就是说儿童团团长是王小二,王小二本身也是王小二,这一切正常。
  
  现在我们把setUserInfo里面的第一句注释掉:
  
  public void setUserInfo(String sName,UserInfo aInfo)
  {
  //userInfoMap.put(sName,aInfo);
  
  userInfoMap.put(aInfo.getName(),aInfo);
  }
  再次运行上面的代码,我们发现儿童团团长不存在了,但是王小二还在。还可以看出,如果找"三班班长"的话,肯定也找不到,也就是说只有依据王小二的真名才能找到王小二,其他方法就不行了。
  
  从上面的setUserInfo和getUserInfo分析,如果采用修改后的代码,我们的程序就出现了行为表现不一致,而这是令人迷惑不解的,我们set了半天,却找不到,岂不是令人恼火!
  
  当然上面的代码比较简单,通过简单的修改就能做到行为一致,但在实际编程中,往往因为复杂的行为操作,经常会造成行为不一致,从而给开发人员带来困惑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值