1. 什么是好的框架
2. 框架设计原则
3. 命名规范
4. 类型设计规范
5. 成员设计规范
6. 扩展性设计
7. 异常
8. 使用规范
9. 设计模式
1.1 简单的
一个好的框架必定是使用起来非常简单的,如果超过2个小时都还没能写出基本的使用代码,就一定不够好用。我们自己设计的框架很多时候都要使用者去了解框架设计的原理才能用起来,就是一种失败。
1.2. 充满利弊权衡的
一个好的框架必然要对很多方面进行取舍,比如灵活性和易用性,性能和开发效率,内存使用和CPU使用等等。如果只考虑其中一方面,会大大降低框架的可用范围,考虑过多极端情况会让框架变得一无是处。
1.3. 借鉴过去经验的
我们的框架主要是为了解决那些以前碰到过的问题,并且考虑的是那些习惯于己有工具的一般用户,所以不适合在框架上对使用方式做很大的创新。但是也要设法解决以前一直存在的不合理状态。
1.4. 需要很多设计工作,代价不菲的
框架的设计工作本身是一项艰巨的任务,比起只是实现一个功能要困难的多,所以不能简单的认为框架设计仅仅是开发了一些可重用的代码而已。它还包括了对于最优实践的总结,对于未来需求的扩展性规划。
1.5. 考虑未来发展
在设计框架的时候,应该判断最终的决定对框架在将来的发展会有怎样的影响。
1.6. 良好的集成性
简单来说就是要能方便的部署、安装、编译、链接、运行。对于外部的依赖应该尽量的少和尽量的简单。
1.7. 一致性
一致的框架可以让开发人员举一反三,从框架中已知的部分推理知道不了解的部分。一致性还可以帮助开发人员很快的认识到,设计的哪些部分是某个特定领域独有的,需要特别加以注意,而哪些仅仅是常有的模式和惯用法。
2.1 渐进式框架原则
2.1.1 学习成本随功能复杂程度提升
开发人员能够从较简单的使用场景中积累知识,并应用到更高级的使用场景中去。
2.1.2 强大,同时易用
由多个部分组成的框架,可以针对不同的目标来设计,从而达到强大和易用的平衡。
用户群体的技术水平会有很大差别,框架既要能提供简单易学的使用方法,也要提供复杂高级的用法。
在设计框架时,必须从一组使用场景以及实现这些场景的样例代码开始。框架设计者最常犯的错误就是一开始就设计对象模型。没有明确的场景,就不会有已用的API设计,也没有决定冲突权衡的依据。框架如果把在针对各种不同的使用场景下提供的API,全部混合到一起,这个框架将会变得非常庞大复杂,同时没有人能学会如何使用。所以我们必须要按照场景来把框架的设计逐步划分开来。
不是其内部实现方案,API是影响用户的关键部分,也是整个框架最大的约束条件部分。必须认真对待API的设计。
否则框架设计将缺乏目标和重点,沦为大堆功能代码的集合,最终既不强大也不灵活。
框架代码所抽象的概念,最好能和使用场景一一对应,而不是提供一套需要理解和解释的抽象。比如某个业务功能(比如读取配置)需要从文件中加载内容,应该直接提供一个LoadFromFile(),而不是提供一个OpenFile()、ReadFromFile()、CloseFile()的文件操作套件。
我们喜欢开发有趣而强大的新功能,但是要发现新功能的需求和最佳使用方法,最好的莫过于实际的使用场景。在有客户需求的情况下来设计API,会让这些程序变得更加实用。当一个功能我们觉得很有用的时候,应该先不考虑加入,直到有客户强烈的需求,此时我们对需求的理解将会更加深入,加入框架的方式会更恰当。
框架必须以易于试验的方式来为普通用户提供一个低门槛。
类型本身是对功能的一种说明。用户可以通过类型的描述了解使用的方法。
此条规范同时符合自说明对象原则。
2.3.2 简单的初始化
复杂的初始化让用户难以开始正确的“试验性”编程,从而更难以学习
2.3.3 尽量提供默认参数,减少参数个数
参数个数越多越难以学习,API的格式也越复杂难以记忆。
命名空间中比较单一的包含有限的类,便于用户浏览所有的类来学习如何使用。
2.3.5 用异常提示对API的误用
比起用文档和返回值,使用异常能让用户在试验性开发时就教育他们如何正确使用API。
同时符合自说明对象原则
同时符合低门槛原则
同时符合低门槛原则
分层设计使得在单个框架中同时提供强大的功能性和易用性成为可能。
2.5.1 名字空间可以成为分层的界限
不同的名字空间可以成为分层的标记,这种名字空间代表了一个常用层级中的所有概念,可以更容易被学习。大多数框架应该使用这种方法来划分名字空间。
2.5.2 高层API提供最佳的开发效率
2.5.4 避免把非常复杂的低层API和高层API混合到一个命名空间中
3. 命名规范
3.1 大小写约定
3.1.6 不要把闭合的复合词中的字母大写
ID
OK
EndPoint
CallBack
HashTable
3.2 通用命名约定
3.2.1 单词的选择
GetWindow
GetWin
UI
HTML
GetInt
GetLength
ToLong
ToInt64
void Write(double value)
void Write(doubledouble)
AppDomain
AppDomainSetup
AppDomain
AppDomainSetup
Date
Calendar
TimeZone
TimeZone2
TimeZoneInfo
尽量不用带64后缀的名字,特别是在新写的,直接同时支持32和64位的API的时候。
3.3.2. 考虑遵循命名空间
3.4 名字空间的命名
3.4.1 要使用公司名字作为前缀
Debug::Debug
3.4.7 名字空间和类型名的冲突
Element
Node
Log
Message
System.Web.UI::Page
System.Web.UI.Adapter::Page
System.IO.Stream
MyNameSpace::Stream
Microsoft.VisualBasic::Binding
System.Windows.Forms::Binding
3.5.4 考虑在派生类的末尾使用基类的名字
ArgumentOutOfRangeException
Exception
FileStream
Stream
有些人喜欢接口前有I,因为COM是这样做的
一个字母还算简洁,所以也可以使用
IComponent
Component
3.5.7 泛型类型参数命名:
TSession 、TInput
3.5.8 常用类型命名,遵循.Net核心类型的命名规则
3.5.9 枚举类型的命名
[Flags]
public enum ConsoleModifiers{
Alt,
Control,
Shift
}
ImageMode
ImageMode
3.6 类型成员的命名
publicstring TextWriter{ get{...} set{...} }
publicstring GetTextWriter(int value){...}
publicItemCollection Itmes{ get; };
publicItemCollection ItemCollection { get;}
Enable
Disable
3.6.3 事件的命名
Closing:关闭前
Closed:关闭后
大多数情况下应该直接使用.NET的标准类模板:EventHandler<T>
public delegate void<EventName>eventHandler(object sender, <EventName>EventArgs e);
public class ClickedEventArgs :EventArgs{
int x;
int y;
public ClieckedeventArgs(int x, int y){
this.x = x;
this.y = y;
}
public int X { get { return x; } }
public int Y { get { return y; } }
}
不要用g_或者s_表示静态字段
如果参数没有具体的含义
如果参数没有具体的含义
d1, d2
ArgumentExceptionIllegalCharacters
ArgumentExceptionInvalidName
ArgumentExceptionFileNameIsMalformed
感谢大家的阅读,如觉得此文对你有那么一丁点的作用,麻烦动动手指转发或分享至朋友圈。