一、概述:
没有参加武斌老师组织的BJDP之前对代码质量的理解只限于实现功能和自动调整格式。在BJDP活动中,武斌老师和王老师对代码要求到极致,甚至不会多出一个空行,再想想自己写的那些热带雨林式的代码简直是糟糕透顶。王老师的说中曾经提到过代码的效率的高低也行和程序员的实际经验职业技能有关系,但是不整洁代码确实工作习惯和态度问题,今天开始从代码整洁做起吧!
二、下面我们总结一下编程过程中习惯问题:
1.习惯使用默认访问修饰符
类或者接口的默认修饰符为internal(同一程序集内)。
类成员默认修饰符为private。
class SampleClass { string name; } //相当于 internal class SampleClass { private string name; }
2. 大括号:能用就用
如:if语句下只有一行语句,每次都会纠结用还是不用,答案是要用。
当出现多层if语句嵌套的时候不用大括号会使你的代码看起看很不工整,而且会因为遗忘作用域导致逻辑错误。
3. 命名要有意义
除非有特殊原因,否则永远不要为自己的代码提供无意义命名。
做到见名知意最好,本着好的注释不如好的命名原则,做到会说话的代码。
4. 抽象级别应该在同一层
方法抽象级别应该在同一层上执行,我们来看下面代码:
class SampleClass { public void Init() { //本地初始化代码 RemoteInit(); } void RemoteInit() { //远程初始化代码 } } //应该写成 class SampleClass { public void Init() { LocalInit(); RemoteInit(); }
void LocalInit() { //本地初始化代码 } void RemoteInit() { //远程初始化代码 } }
5. 一个方法只能做一件事
“单一职责原则”(SRP)要求每一个类型只负责一件事,我们把概念扩展到方法上就变成了:一个方法只能做一件事。
什么样的代码才能称得上“做一件事”?我们看一下上一个建议中的代码,其中LocalInit方法和RemoteInit方法是两件事,但是在同一抽象层上,在类型这个层次对外又可以将其归并为“初始化”这一件事上。所以,“同一件事”要看抽象所处地位。
6. 避免过长的方法和过长的类
如果方法过长,意味着可以站在更高的层次上重构出若干的小方法。那么没有具体的指标提示方法是否过长?方法不长于30行(1页),类不超过300行(10页)。
7. 只对外公布必要的操作
那些不必要的公开方法和属性,Private之。
8. 重构多个相关属性为一个类
若存在多个相关属性,就该考虑是否将其重构为另一个类。
class Person { public string Address { get; set;} public string ZipCode{ get; set;} public string Mobile{ get; set;} public string Hotmail{ get; set;} //其他省略 } //重构后代码 class Person { public Contact Contact{ get; set;} //其他省略 }
class Contact { public string Address { get; set;} public string ZipCode{ get; set;} public string Mobile{ get; set;} public string Hotmail{ get; set;} }
上面的四个属性都是和联系地址有关,所以应该重构成Contact类。
9. 不要重复代码
如果我们发现重复代码,则意味着我们需要整顿一下,再继续前进。
重复代码会让我们的软件行为变得不一致。举例来说,如果存在两处相同的加密代码,结果某一天我们发现加密代码有个小Bug并改之,却忘记另一份相同代码,那么这个Bug就出出现隐患。
10.使用表驱动法避免过程if和Switch分支
enum Week { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, sunday } //表驱动法 static string GetChineseWeekInTable(Week week) { string[] chineseWeek={"星期一","星期二","星期三","星期四","星期五","星期六","星期日"}; return chineseWeek[(int)week]; } static void Main(string[] arge) { Console.WriteLine(GetChineseWeekInTable(Week.Monday)); }
这种方法有局限性,完成功能很有限,如果换成星期一Abel扫房间,星期二Lucy清洗衣物,星期三陪Tomson打篮球......那么事情就复杂了。
遇到这种情况,我们会想到多态,在这让然使用表驱动法再加上反射来实现这类动态行为,代码如下:
enum Week { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, sunday } //表驱动法 static string ActionInTable(Week week) { string[] methods={"Cleaning","CleanCloset","Quarrel","shopping","A","B","C"}; return methods[(int)week]; } static void Main(string[] arge) { Actionmake action=new Actionmake(); var addMethod=type(Actionmake).GetMethod(ActionInTable(Week.Monday)); addMethod.Invoke(action,null); } class Actionmake() { public void Cleaning() { Console.WriteLine("打扫"); } public void CleanCloset() { Console.WriteLine("清理"); } public void Quarrel() { Console.WriteLine("吵架"); } public void shopping() { Console.WriteLine("购物"); } }
11.使用匿名方法、Lambda表达式代替方法
static void sampleMethod() { List<string> list = new List<string>() {"Abel","Lucy","Tomson"}; var getname=list.Find((value)=>{ return value.Length==5; }); Console.writeLine(getname); }
12.使用事件访问器代替公开的事件成员变量
使用事件访问器的好处是,提供对赋值更多细粒度的控制。这就好比应该使用属性而不使用字段一样:
class SampleClass { EventHandlerList events=new EventHandlerList(); public envet EventHandler Click { add { events.AddHandler(null,value); } remove { events.Removehandler(null,value); } } }
13.最少,甚至不要注释
即使在详细的注释也不会优化糟糕的代码。当然注释可能不得不加,如版权信息,自主开发的API便于生成参考手册。
14.若抛出异常,则必须要注释
/// <summary> /// SampleMethod /// </summary> /// <param name="value">输入参数注释</param> /// <returns>返回注释</returns> /// <exception cref="System.IO.IOException">异常原因,则抛出IOException</exception> public string SampleMethod(int value) { if (true) { throw new IOException("一个IO异常"); } }