原则一:单一职责原则
单一职责原则(SRP:Single responsibility principle)又称单一功能原则,面向对象五个基本原则(SOLID)之一。它规定一个类应该只有一个发生变化的原因。
单一职责原则:
如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。这种耦合会导致脆弱的设计,在变化发生时,设计会遭到意想不到的破坏。单一职责原则要做的是发现职责并把职责相互分离。
单一职责嘛,一个类一个方法,不就是它的终极目标吗?
的确,这属于‘终极目标’但是此终极目标不现实。因为,一个类一个方法的确是高内聚、低耦合。但是这显然显得代码很臃肿了,维护更加不便,在中大型的项目更加如此。如果你的项目足够简单,类足够少,也可以这么来做。所以,这里要强调一点的是,单一职责原则是一个类处理一类事情,也只有一类事情影响到这个类。并不是一个类处理一个方法。
原则二:开放-封闭原则
开放封闭原则(OCP,Open Closed Principle)是所有面向对象原则的核心。软件设计本身所追求的目标就是封装变化、降低耦合,而开放封闭原则正是对这一目标的最直接体现。
开放-封闭原则: 对于扩展是开放的,对于更改是封闭的。
举例说明:
比如我们平常喝水用的一次性纸杯。平常人只是用来装水。喝完水就扔了。这就是这个纸杯的生命周期。纸杯这一生只完成了它的一个功能:装水。纸杯此时就很封闭了,没有什么扩展性。
此时,我看到身边有一支花苗,我想要拿回家种。但是没有容器呀? 啊?旁边不是有一个纸杯吗,可以用此纸杯来种这朵花苗。
纸杯有了它的另外一个扩展性,就是种花苗。
纸杯不仅有装水、种花苗的用途,以后还可以有装小垃圾、冲茶、回收等功能。对于以后这些功能,我们要想到他们的扩张性。
在纸杯只有一个装水的功能的时候,我们只写一个纸杯功能类,说纸杯能装水。但是以后有扩展呢?这一方面我们要预先判断。预先判断它以后可能会根据需求的变动而扩展。对于纸杯本来的装水功能,不能说不能修改,此功能只能在此函数、类中修改。这就是开闭原则的核心。
所以,纸杯在开闭原则所体现的是:尽量少修改,未来可能扩展的模块、类做好预算的判断。如果要修改,只能在此函数此类修改,不能牵涉到其他地方。
原则三:依赖倒转原则
依赖倒转原则(Dependence Inversion Principle)是程序要依赖于抽象接口,不要依赖于具体实现。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。
对各种概念进行描述:
- 低层模块:不可分割的原子逻辑,可能会根据业务逻辑经常变化。
- 高层模块:低层模块的再组合,对低层模块的抽象。
- 抽象: 接口或抽象类(是底层模块的抽象,特点:不能直接被实例化)
- 与接口或抽象类对应的实现类:低层模块的具体实现(特点:可以直拉被实例化)
依赖倒转原则:
- 高层模块不依赖低层模块,两个都应该依赖抽象。(降低系统耦合性)
- 抽象不应该依赖细节,细节应该依赖抽象。(增强系统内聚性)
实例:
背景1:公司是福特和本田公司的金牌合作伙伴,现要求开发一套自动驾驶系统,只要汽车上安装该系统就可以实现无人驾驶,该系统可以在福特和本田车上使用,只要这两个品牌的汽车使用该系统就能实现自动驾驶。于是有人做出了分析。
分析:我们定义了一个AutoSystem类,一个FordCar类,一个HondaCar类。FordCar类和HondaCar类中各有三个方法:Run(启动Car)、Turn(转弯Car)、Stop(停止Car),当然了一个汽车肯定不止这些功能,这里只要能说明问题即可。AutoSystem类是一个自动驾驶系统,自动操纵这两辆车。
public class HondaCar{
public void Run()
{
Console.WriteLine("本田开始启动了");
}
public void Turn()
{
Console.WriteLine("本田开始转弯了");
}
public void Stop()
{
Console.WriteLine("本田开始停车了");
}
}
public class FordCar
{
publicvoidRun()
{
Console.WriteLine("福特开始启动了")