"写代码和写好代码是不一样的,如果要写好的代码,考虑的问题更多了,考虑稳定性,扩展性和耦合性,当然也要考虑上游和下游关联的问题,让你做事的时候知道怎么做,也知道为什么这么做,这就是学习设计模式带来的好处。"
First Of All
(这篇文章提炼于孟大佬的 C#设计模式 ---- 总结汇总.在保留原文精髓的基础上添加了自己的理解.)
做任何事情都必须要有原则,写代码也是如此!当你能严格遵守设计原则之后你会发现你的代码维护成本变低了,更容易扩展和复用了.所谓的设计模式不过是在这些设计原则只上总结出来的架构罢了.所以为了写出更优雅的代码,学习设计原则是极为重要的.
单一职责原则(SRP:Single Responsibilities Principle)
概述:这个原则主要是对类的定义作出规范.它要求类的功能必须要单一!
例子:Human类只能包含人类本身的一些特征(动物,有智慧,直立行走),而Clothes类的特征是:材料加工而成,有口子.如果你因为人可以穿衣服就把Clothes类的特征放到Human里面,这样就违背了单一职责原则了.因为其它生物也是可以穿衣服的.
功能:职责相互分离从而降低系统的耦合度,强化系统的稳固性!
开放关闭原则(OCP:Open-Close Principle)
概述:对代码的扩展开放,对代码的修改关闭(允许你在原有的程序加入新的代码,限制你去修改原有程序的代码)
是面向对象设计的核心思想之一!
例子:在实际的开发场景中.产品经理不可避免地会增加或者修改已定功能,如果你每次都在原有的代码上做更改的话你的程序逻辑会变得越来越复杂,越来越混乱.维护难度也会随之提高.
功能:遵循这个原则可以保证系统的可维护性,可扩展性,可复用性以及灵活性
里氏代替原则(LSP:Liskov Substitution Principle)
概述:父类必须可以被子类覆盖.
其实我个人觉得这个说法太模糊了.我个人对LSP的理解是在方法定义入参的时候,尽量把入参定义为父类方法,因为子类是可以覆盖父类的.如果你把入参定义为子类的话,那每一个子类你都得去写一个方法了.如果你的理解跟我不一样的话可以在下方评论区讨论一下.
例子:既然定义是父类必须可以被子类覆盖,那么是不是存在父类可以不被子类覆盖的情况呢?答案是可以的!有两种情况:
1:父类中的字段或者方法用了private关键字修饰
2:当父类中的方法或者字段没有用virtual关键字修饰的时候.这个时候如果子类是不能去覆盖父类方法/字段的,只能隐藏.
功能:里氏代换原则是是面向接口的基础,是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。
依赖倒置原则(DIP:Dependence Inversion Principle)
概述:抽象不依赖细节,细节依赖抽象.
抽象是稳定的,设计好之后几乎不会有改动.
而细节的改动是相对频繁的.如果抽象依赖的细节,那么在细节发生变动的时候抽象也会被迫发生改动.
说白了就是要面向接口编程且接口里不能出现实例,不能依赖实例.
功能:DIP会把代码分为上层代码(接口)和下层代码(实现),如果需求出现变动只需替换掉下层代码即可.且只要下层代码实现了上层接口,那么下层可随意替换,方便快捷!
接口隔离原则(ISP:Interface Segregation Principle)
概述:多个职责单一的接口优于一个多职责的接口.
这一点其实和单一职责原则非常类似,只不过单一职责原则是面向细节的,接口隔离原则是面向抽象的.所以这里就不再赘述了.
组合(聚合)复用原则(CRP:Composite Reuse Principle)
概述:在新的对象里使用已知对象优于继承已知对象.继承过多会增加维护难度以及系统复杂度
例子:
class C
{
class B {get;set;}
}
class B:A
{
}
class A
{
}
//上面的方式要优于下面的方式
class C:B
{
}
class B:A
{
}
class A
{
}
功能:组合/聚合复用原则可以使系统更加灵活,类与类之间的耦合度降低,一个类的变化对其他类造成的影响相对较少,因此一般首选使用组合/聚合来实现复用;其次才考虑继承,在使用继承时,需要严格遵循里氏替换原则,有效使用继承会有助于对问题的理解,降低复杂度,而滥用继承反而会增加系统构建和维护的难度以及系统的复杂度,因此需要慎重使用继承复用
迪米特法则(Law of Demeter) 或者说是 最少知识原则(LKP:Least Knowledge Principle)
概述:该原则主张使用中介类来达到一个对象应尽量减少与其他实体之间的相互作用的目的;
例子:
class C
{
class D d=new class D;
d.FuncOne();
}
class B
{
FuncOne(){}
FuncTwo(){};
}
class A
{
FuncOne(){}
FuncTwo(){};
}
class D //中介类
{
FuncOne()
{
class B b=new class B;
class A a=new class A;
B.FuncOne();
A.FuncTwo();
}
}
//上面的方式优于下面的方式
class C
{
class B b=new class B;
class A a=new class A;
B.FuncOne();
A.FuncTwo();
}
class B
{
FuncOne(){}
FuncTwo(){};
}
class A
{
FuncOne(){}
FuncTwo(){};
}
功能:
迪米特法则的初衷是降低类之间的耦合,实现类型之间的高内聚,低耦合,这样可以解耦。但是凡事都有度,过分的使用迪米特原则,会产生大量这样的中介和传递类,导致系统复杂度变大。所以在采用迪米特法则时要反复权衡,既做到结构清晰,又要高内聚低耦合.