形式一."见兔撒鹰".
如果有一个类有一个集合成员,可以在为此成员添加元素时再把具体集合建立起来,以免浪费空间和时间.
例:
2 private List members = null ;
3
4 public void addMember(Member member){
5 if (members = null ){
6 members = new ArrayList();
7 }
8
9 members.add(member);
10 }
11 }
12
异曲同工的另外一个例子:
2 private static Singleton instance = null ;
3
4 public static synchronized Singleton getInstance(){
5 // 要用的时候再把Singleton建立起来
6 if (instance == null ){
7 instance = new Singleton();
8 }
9
10 return instance;
11 }
12 }
13
形式二."只管结果,不顾过程"
从集合中取区间元素时,可以直接从上下限之间取来,让try...catch...去处理越界的事.
题设:有个数不定元素的列表(allTodoes),需要从中取N个,起始位置不限,你怎么编写程序.
很多人开始进行越界的判断,出来一堆if else,有时还需要在纸上写好思路,完毕后还有多方测试,生怕出错,即使编写好后其他人维护起来也是小心翼翼的.其实没必要这么麻烦.
例.
2 int end = start + pageSize;
3
4 for ( int i = start;i < end;i ++ ){
5 try {
6 todoResult.addTodo((Todo)allTodoes.get(i));
7 }
8 catch (Exception ex){
9 continue ;
10 }
11 }
题外话:分支和循环语句天生就不容易理解,尤其是在嵌套较深的时候,因为这是机器的思维特性.还是try...catch...比较贴近人类思维.
形式三."奉旨行事"
在查询中,如果把查询条件和遴选过程分开来更有益,程序也因此解耦合.这才是OO化的查询.
需求:从公司的职员列表中,找出男性且年龄大于22的成员.
传统写法:
2 List results = new ArrayList(); // 结果列表
3
4 for (Iterator it = allmembers.iterator();it.hasNext();){
5 Member member = (Member)it.next();
6
7 if (member.getAge() > 22 && member.isMale()){ // 筛选,这里是把查询条件和遴选过程融合在一起,条件一变立即就得加个分支.
8 results.add(member);
9 }
10 }
11
这种写法没有错,但是不是面向对象的写法,它有以下缺陷:
1.查询条件和筛选过程没有分离.
2.这样写的后果使Company变成了一个失血模型而不是领域模型.
3.换查询条件的话,上面除了"筛选"一句有变化外其它都是模板代码,重复性很高.
真正符合OO的查询应该是这样:
2 public boolean accept(Member member) {
3 return member.isMale() && member.getAge() > 22 ;
4 }
5 };
6
7 List ls = company.listMembers(filter1);
这段代码成功的把查询条件作为一个接口分离了出去,接口代码如下:
2 public boolean accept(Member member);
3 }
而类Company增加了这样一个函数:
2 List retval = new ArrayList();
3
4 for (Iterator it = members.iterator();it.hasNext();){
5 Member member = (Member)it.next();
6
7 if (memberFilter.accept(member)){
8 retval.add(member);
9 }
10 }
11
12 return retval;
13 }
这就把模板代码归结到了类内部,外面不会重复书写了.Company也同时拥有了数据和行为,而不是原来的数据容器了.
形式四."化繁为简"
这种结构将多个分支语句变换为一个查表结构,这样做对扩充程序结构,修改具体数额都很方便,使程序更易于维护.还可以把归结出的表结构放在持久介质中如XML文件,数据库等,用到的时候再取,这样做在条件变化时不需要修改程序.
原始代码(VB代码,但应该不妨碍理解):
2 count1 = salary.Value + USA.Value * Drate + JAN.Value * Jrate - 4000
3 If count1 < 500 Then
4 tax.Value = count1 * 0.05
5 ElseIf count1 < 2000 Then
6 tax.Value = count1 * 0.1 - 25
7 ElseIf count1 < 5000 Then
8 tax.Value = count1 * 0.15 - 125
9 ElseIf count1 < 20000 Then
10 tax.Value = count1 * 0.2 - 375
11 ElseIf count1 < 40000 Then
12 tax.Value = count1 * 0.25 - 1375
13 ElseIf count1 < 60000 Then
14 tax.Value = count1 * 0.3 - 3375
15 Else
16 tax.Value = count1 * 0.3 - 3375
17 End If
变换如下:
这是个税率计算的语句段,公式是确定的:税=月薪*税率-折扣,税率又和月薪有关系,月薪越高税率越高,首先这里可以归纳出一个基本类:
2 float limit; // 月薪界限
3 float ratio; // 税率
4 float discount; // 折扣
5
6 public TaxItem( float limit, float ratio, float discount){
7 this .limit = limit;
8 this .ratio = ratio;
9 this .discount = discount;
10 }
11
12 public TaxItem(){
13 this ( 0.0f , 0.0f , 0.0f );
14 }
15
16 public float getDiscount() {
17 return discount;
18 }
19
20 public float getLimit() {
21 return limit;
22 }
23
24 public float getRatio() {
25 return ratio;
26 }
27 }
28
29
然后就是税计算类:
2 private static ArrayList list = new ArrayList();
3
4 public TaxCaculator(){
5 // 这里把各个等级加入到链表中,注意添加顺序是由小到大
6 list.add( new TaxItem( 500.0f , 0.05f , 0.0f ));
7 list.add( new TaxItem( 2000.0f , 0.1f , 25.0f ));
8 list.add( new TaxItem( 5000.0f , 0.15f , 125.0f ));
9 list.add( new TaxItem( 20000.0f , 0.2f , 375.0f ));
10 list.add( new TaxItem( 40000.0f , 0.25f , 1375.0f ));
11 list.add( new TaxItem( 60000.0f , 0.3f , 3375.0f ));
12 }
13
14 // 这个函数用来计算所得税
15 public float getTax( float salary){
16 TaxItem item = new TaxItem();
17
18 for ( int i = 0 ;i < list.size();i ++ ){
19 item = (TaxItem)list.get(i);
20
21 if (salary > item.getLimit()){
22 continue ;
23 }
24 else {
25 break ;
26 }
27 }
28
29 // 返回最终结果,当然,这个公式也可以放在TaxItem 类中,这里就见仁见智了。
30 return salary * item.getRatio() - item.getDiscount();
31 }
32 }
调用如下 :
2
3 float salary = 1000 .f;
4 System.out.println( " Salary= " + salary + " Tax= " + taxCaculator.getTax(salary));
5
6 salary = 2000 .f;
7 System.out.println( " Salary= " + salary + " Tax= " + taxCaculator.getTax(salary));
8
9 salary = 3000 .f;
10 System.out.println( " Salary= " + salary + " Tax= " + taxCaculator.getTax(salary));
形式五."分而治之"
该结构将分支语句的执行部分分散到单独的类中处理,降低了系统耦合度,程序也更容易维护.
举例如下:
在日常工作中,我们经常需要解析一段字符串并交由相应的函数进行处理,例如TCP/IP通信中的命令解析和用户自定义文件解析等场合,通常的处理方法是这样:
if(命令==”AAA”){
函数AAA执行;
}
else if(命令==”BBB”){
函数BBB执行;
}
.
.
.
else{
函数XXX执行;
}
这种方法在命令较少时是有效的,当命令众多时,if语句和相关的函数将会形成一个巨集,给检查,维护和扩充带来了很大的不便,久而久之将会成为系统性能提升的瓶颈。
一个成功的软件程序必须尽可能简单并易于重构和扩展,在命令模式和Java反射机制的帮助下,我们可以从容解决上述问题,达到简单并易于重构和扩展的要求。以下将简要说明解决方案。
1. 制作一个命令的抽象接口.
2 public abstract void execute(String[] args);
3 }
2. 让每种命令都实现这个接口.
2 public class CommandType01 implements Command{
3 public void execute(String[] args){
4 System.out.println( " \n commandType01 start! " );
5 System.out.print( " \t commandType01 Length= " + args.length);
6 System.out.println( " \n commandType01 End! " );
7 }
8 }
9
10 // 命令二
11 public class CommandType02 implements Command{
12 public void execute(String[] args){
13 System.out.println( " \n commandType02 start! " );
14
15 System.out.print( " \t commandType02 is: " );
16 for (String item:args){
17 System.out.print( " \t " + item);
18 }
19
20 System.out.println( " \n commandType02 End! " );
21 }
22 }
23
24 // 命令三
25 public class CommandType03 implements Command{
26 public void execute(String[] args){
27 System.out.println( " \n commandType03 start! " );
28 System.out.print( " \t commandType03 last Nation= " + args[args.length - 1 ]);
29 System.out.println( " \n commandType03 End! " );
30 }
31 }
32
33
让每种命令都实现execute接口的用意是强制每个命令的执行方式一致,简化调用时的处理,但执行内容应该根据实际情况决定.
例如
命令一的执行内容是输出参数的个数
命令二的执行内容是输出参数的内容
命令二的执行内容是输出最后一个参数
3. 将命令防置到命令中心中去
命令中心类的代码如下:
2 Command cmmd; // 命令对象的引用
3 String[] cmmdArgs; // 参数列表
4
5 public Mediation(){
6
7 }
8
9 public void fetchCommand(String strCmmd){
10 cmmdArgs = strCmmd.split( " \s+ " ); // 分析原始命令
11
12 String className = " Command " + cmmdArgs[ 0 ]; // 根据分析后命令的第一个参数得到类名
13
14 &am