NEWS 新闻资讯
系统重构注意事项36条之25-32
文章来源:本站原创作者:Deepfisher发布时间:2012年4月29日浏览次数:935
第二十五点:函数体最多不超过100行
记得以前看过一个函数有上万行,相当壮观,从那以后看到长函数时再也不觉得奇怪了,个人认为过长函数的主要缺点是:
1、严重影响代码的阅读,使用到某个变量的地方可能间隔几百甚至上千行,如果if-else嵌套层次较多的话那就更噩梦了。
2、不利于代码的重用,短小而独立的逻辑处理单元更容易被重用,而过长的代码段则需要经过进一步分解才行。
个人觉得函数最好不要超过100行,对于过长的函数要尽可能地进行分解,如果实在不能分解,那么就通过注释的方式增加该函数处理步骤的说明,例如:
public void foo(){ // 1、验证参数、内部状态的有效性 ... // 2、开始倾斜角度 ... // 2.1 计算角度1 ... // 2.2 计算角度2 ... // 3、输出计算说明书 ... }
第二十六点:使用语言的修饰符确保变量的不可变性
当声明一个变量时,如果能十分确定该变量不会被修改或者不应该被修改,那最好把它声明为不可变的,如使用Java中的final、C++中的const修饰符,这样可以防止本该不变的变量被意外地修改。
第二十七点:对象状态共享
大量对象的同时存在会占用系统宝贵的内存,如果这些对象中某些状态是相同的,那么可以将这些状态提取出来让所有需要它的对象共享,这可以大大减少冗余对象,从而达到节省内存的目的,设计模式中的Flyweight模式可以解决这个问题。
第二十八点:用对象代替普通常量
由于普通常量本质上是一个简单的数字或者字符串,当我们错误地将某个类别的常量在另一个类别的常量的场景中使用时,就会产生问题,但是编译器并不会提示有错误,所以,这可能是一个不小的隐患,例如:
// 表示用户状态的常量声明 public static int USER_STATE_ACTIVE = 0; public static int USER_STATE_DELETE = 1; // 表示用户角色的常量声明 public static int USER_ROLE_NORMAL = 2; public static int USER_ROLE_MANAGER = 3; // 下面用户是否被激活的判断 if(userState == USER_ROLE_NORMAL){ }
这个判断本应该使用USER_STATE_ACTIVE和USER_STATE_DELETE两个常量之一,却意外地使用了其他常量,可能直到Bug产生后才能被发现。
可以使用对象常量来避免这种情况,例如:
public class State{ private int state; public State(int s){ state = s; } } // 表示用户状态的常量声明 public static State USER_STATE_ACTIVE = new State(0); public static State USER_STATE_DELETE = new State(1); public class Role{ private int role; public Role(int r){ role = r; } } // 表示用户角色的常量声明 public static Role USER_ROLE_NORMAL = new Role(2); public static Role USER_ROLE_MANAGER = new Role(3);
下面的判断是无法通过编译的,因为userState是State类型的。
if(userState == USER_ROLE_NORMAL){ }
第二十九点:查询函数中尽量不要有修改操作
我们一般都是根据函数的名字来判断它的功能,“表里不一“的函数可能会引起一些问题,例如我们调了一个查询函数(获取类的成员变量值的函数):getName(),但是它内部却修改了其他成员变量的值,当查找Bug的原因时,很可能会忽略这个函数,从它的名字看,觉得它不会引起问题,到最后发现就是它捣的鬼,心里估计会骂这个函数的作者:他奶奶的,代码如其人,表里不一!
第三十点:尽量封装对象的创建过程
本文之前曾提到过要尽量为成员变量增加set和get函数,主要目的是为了掌握成员变量的控制权,对象的创建过程也是如此,如果提供者掌握了对象创建过程的控制权,那么就可以屏蔽具体的实现类,并且任意修改对象的创建过程。
第三十一点:置空不用的对象
在C++中,销毁一个对象后,一定要把指针置为NULL,否则会出现野指针,最好写成下面这样,delete后立马置为NULL,
delete pObject;
pObject = NULL;
在Java中,当不再需要一个对象时,最好能把它置为null,这样有利于垃圾回收。
第三十二点:善于利用接口
1、 回调型接口
在C语言中,回调函数可以通过函数指针来实现,Java中没有指针的概念,可以利用接口来达到同样的目的,例如:
public interface Callback{ public void onChanged(); } public void execute(Callback callback){ ... callback.onChanged(); ... }
2、标记型接口
这种类型的接口中不包含函数的声明,即接口是空的,主要用来让实现这个接口的类表明自身具有某种特性,起一个标记的作用,例如下面的接口:
public interface Millionaire{ } public class Member extends User implements Millionaire{ ... } public boolean check(User user){ if(user instanceOf Millionaire) // 这是百万富豪,直接让它pass return true; ... }
3、依赖注入型接口
这种接口一般用在工厂方法中,有选择性地为要创建的对象注入对象引用或者数据,例如:
public interface DataAware{ public void setData(Data data); } public interface DataBaseConnectionAware{ public void setConnection(Connection conn); } // 用户服务类 public class UserService extends Service implements DataAware,DataBaseConnectionAware{ public void setData(Data data); public void setConnection(Connection conn); } // 创建服务对象 public Service createService(int type){ Service service = null; if(type == USER){ service = new UserService(); if(service instanceOf DataAware) // 如果它想要数据,则注入数据对象 ((DataAware)service).setData(data); if(service instanceOf DataBaseConnectionAware) // 如果它想要数据库连接,则注入连接对象 ((DataBaseConnectionAware)service).setConnection(conn); return service; }
4、常量型接口
接口中定义的全是常量,这样,相关类都可以实现这个接口,相当于把这些常量都定义在了这些类中,而其他类则可以通过常量接口来引用这些常量