那些条条框框真的重要么?反编码规范的做法的思考
今天整理文件,看到我早年写的一个编程规范的文档,随便写一点。欢迎大家讨论:
(1)对 for if while 等块结构加上花括号,无论是否只有一条语句;
(2)花括号占一行;
现在发现这两条很教条,有时候反而不利于代码的可读性。现在我倾向在每一行不超过屏幕的宽度上尽可能写多一些代码,而压缩行,使得一个方法,乃至一个类可以在一屏被显示。方块的代码比细长的更容易阅读,对比:
C# code
class Person
{
public string Name
{
get;
set;
}
public int Age
{
get;
set;
}
public string Address
{
get;
set;
}
public string EMail
{
get;
set;
}
public string Phone
{
get;
set;
}
public DateTime Birthday
{
get;
set;
}
public Person()
{
}
public Person(
string name,
int age,
string address,
string email,
string phone,
DateTime birthday
)
{
Name = name;
Age = age;
Address = address;
EMail = email;
Phone = phone;
Birthday = birthday;
}
}
C# code
class Person
{
public string Name { get; set; }
public int Age { get; set; }
public string Address { get; set; }
public string EMail { get; set; }
public string Phone { get; set; }
public DateTime Birthday { get; set; }
public Person() { }
public Person(string name, int age, string address, string email,
string phone, DateTime birthday)
{
Name = name; Age = age; Address = address; EMail = email;
Phone = phone; Birthday = birthday;
}
}
(3)避免编写过长的方法,方法体越小越好
这一条现在看来也不一定,因为过小的方法使得类的方法数过多,而方法可以单独测试,但是方法太多使得组合测试这些方法变得困难,最后过小过多的方法加大了人的理解难度,支离破碎的代码可读性更差。
随着Lambda表达式的引入,使得我们可以定义匿名方法,一个方法如果只在它内部使用,那么就不要公开,哪怕是 private 访问权限:
比如:
C# code
Func<int, int, int> IntPow =
(x, y) => {
int r = x;
for (int i = 1; i < y; i++) r *= x;
return r;
}
Console.WriteLine(IntPow(3, 2));
(4)使用接口降低耦合性
接口是一个很难用的东西,为了接口,我们不得不设计一个类,更糟糕的是,C#不支持匿名类实现接口,这使得接口的代码远离调用。
想象下我们经常为了排序而实现一个 ICompareable 的经历吧。
同样因为 Lambda 表达式,我们可以暴露委托代替接口,比如 OrderBy(),使用起来比 SortList 顺畅很多。
(5)使用泛型代替非泛型接口
我承认泛型有很多足够的优点,但是在设计API的时候,应该考虑使用非泛型的接口,或者保留一个兼容的非泛型版本,这样做好处很多,比如反射调用的时候,非泛型的实现简单很多,在Web Service序列化的时候也是如此。另外考虑到只有C# 4支持逆变和协变,提供向前兼容也是很必要的。
(6)注释很重要
打一个不恰当的比方,很多时候你回答csdn的问题发现,lz说了半天,不知道问什么,相反,他给出些输入输出,一看就懂了。所以,宁可多写测试用例,也不要乱写注释。不好的注释比没有注释更糟,如同2只手表不会给你带来更精确的时间,只会造成模糊。
(7)为专门的问题编写专门的算法
大多时候,Linq提供了我们需要的大部分算法——可能直观但是不直接。但是这不重要。在程序编写的初期,有很多不确定性,这时候获得可以运行和测试的代码,比起编写具体实现重要的多。自己尝试实现算法,浪费了很多时间,而且很可能发现这个功能需求修改,算法就白写了。而且自己实现的算法,可读性,可调试性很差,通用的算法基本可以假定它本身是正确的。虽然可能效率不高,也可以在后期调优的时候解决。越是细节的算法,越不重要。
(8)变量要用有意义的名字表示
随着重构工具的普及,费力想一个变量的名字越来越划不来。变量名需要慎重的地方包括:接口;可能被命名参数调用的参数;共有变量,除此之外,变量名越简单越好。
(9)关于方法重载
对于方法重载,特别要当心2点,一个是子类不正确的覆盖导致不可预料的情况,比如他们忘记调用 base.Method(),或者有的人喜欢先调用,再写覆盖的方法,有的人相反,所以NVI是一个很好的模式。另一个是特别注意在参数中使用类类型,尤其是有继承关系的类类型。
比如:
C# code
class FileSystem
{
public virtual void SetInfo(FileSystemInfo info) { ... }
}
class Fat32FileSystem : FileSystem
{
public virtual void SetInfo(Fat32FileSystemInfo info) { ... }
}
class class FileSystemInfo
{
...
}
class class Fat32FileSystemInfo : FileSystemInfo
{
...
}
(10)使用聚合代替继承
这一条本身没有问题,可是因为 C# 有了扩展方法,所以应该修改为,首先考虑扩展方法,然后是聚合,最后是继承。当然,扩展方法也有一些副作用,比如当类的实现者加上了和扩展方法重名的方法,扩展方法会被忽略。比如滥用扩展方法导致代码可读性差等情况。
------解决方案--------------------