和设计模式相关的是设计原则。设计原则更象是理论,而设计模式是这这理论的具体体现。只有了解了面象对象理论,才可以去学习设计模式。
下面是几个基本的设计原则:
1. OCP (开放-封闭原则): Software entities should be open for extension,but closed for modifycation.
一 个软件实体应该对扩展开放,对修改关闭. 开闭原则的关键是抽象化,然后从抽象化导出具体实现. 通过抽象基类或接口来提供一个标准调用规范,每个实质子类都继承或者实现这个规范以达到不同的应用操作。在实际使用中我们一般通过工厂模式 (Abstract Factory / Factory Method / Simple Factory)来达到可扩展而不修改现有代码的目的。
2. LSP (里氏替换原则):子类型必须能替换掉他们的基类型。
一 个软件实体如果使用的是一个基类的话,那么适用于其子类,而且它根本不能觉察出基类对象和子类对象的区别. 这个原则实质上就是多态的一种体现,子类继承或者覆盖基类的方法,具有和基类完全相同的调用规范。不过这个原则提醒我们在C#中尽可能不要使用new关键 字改写基类的方法和属性,因为这样做会造成潜在的多态错误.
3. DIP (依赖倒置原则):要针对接口编程,不要针对实现编程. 抽象不应该依赖于细节,细节应该依赖于抽象。高层模块不应该依赖于低层模块,二者都应该依赖于抽象。
如果开闭原则是面向对象设计的目标的话,依赖倒置原 则就是面向对象设计的主要机制. 高层模块一般是指那些实现业务规则的单元,而所谓低层模块是具体的一些功能代码。我们在设计的时候,一般高层模块会直接依赖于低层模块来实现其具体功能, 这势必会造成相关干扰,任何一个模块的修改都会影响其关联的模块。因此DIP原则建议我们在两个模块之间添加一个抽象,两者通过依赖和实现抽象来达到沟通 和调用,从而确保在抽象不变动的情况下,各自隔离而互不影响。抽象本身应该是个干净的协议,它具备完整的独立性,如果依赖于某个功能细节,那么抽象本身就 变成不稳定因素,造成以后修改的污染。
4. ISP (接口隔离原则):一个类对另一个类的依赖应当是建立在最小的接口上的. 不应该强迫客户依赖于他们不用的方法。
我们设计的一个类会提供给多个客户调用,那么势必造成客户会“看到”一些他们根本用不上的方法,这对于客户是一种干扰,会加大他们的负担,甚至造成错误调 用而影响系统的功能实现。在C#中我们可以继承多个接口,每个客户只使用其特定的接口,更具体一点,我们返回给客户的是一个接口,而不是一个类实例来达到接口隔离。在类编码中可能还会用到接口方法这个概念。
5. CARP(合成/聚合原则): 在一个新的对象里面使用已有的对象,使之成为新对象的一部分;新的对象通过向这些对象委派达到复用已有功能的目的.
要尽量使用合成/聚合,尽量不要使用继承.
6. LOD(迪米特法则) : 只与你直接的朋友们通信. 不要跟"陌生人"说话. 每一个软件单位对其它单位都具有最少的知识,而且局限于与本单位密切相关的软件单位.
7. SRP (单一职责原则):就一个类而言,应该仅有一个引起它变化的原因。
这 个原则实质上是对于一个类的粒度划分作出了一个规定。一个具备强大功能的庞大类并不是一个好的做法,不如将其分割成多个小类,每个小类仅提供某个单一的功 能,这样我们的编码和测试都会简化,避免了因为复杂而造成潜在的错误,然后使用Facade或者Adapter模式对外提供一个聚合的操作接口就可以了。 软件设计上组合和继承一样重要,非类库开发的时候推荐更多的使用组合而不是继承。
开闭原则 --> --> (OCP)、里氏代换原则(LSP)、依赖倒转原则(DIP)关系
开闭原则说:软件实体应该是扩展开放,对修改关闭。找到系统中的可变因素,并将之封装起来。
开闭原则的关键是抽象化,利用抽象化来达到开闭原则的目标。抽象化只所以是关键,因为只有很好的抽象化后,才能在此基础上导出具体化实现。
里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。开闭原则的关键是抽象化,而基类与子类的继承关系就是抽象化的具体体现。可以说里氏代换原则是对实现抽象化具体步骤的规范。
依赖倒转原则讲的是要依赖抽象,不要依赖具体。如果开闭原则是目标的话,那么依赖倒转原则便是实现这个目标的一种手段。
另外依赖倒转使用后,为了避免对具体类的直接使用可能会使用到对象工厂。由于依赖倒转还会导致大量类,对于不熟悉面向对象技术的工程师来说,维护这样的系统需要较好的面向对象设计的知识。