首先了解一下什么是程序设计模式:
程序设计模式指的是一套被反复使用,被多数人知晓的,经过分类编目的,代码设计经验的总结
其实通过漫漫的开发之路,程序员们逐渐的总结出了程序开发的原则,可以总结为以下六大原则
1.单一职责原则
单一职责就是一个类只负责一个功能,更加具体的说就是对一个类而言,应该是一组相关性很高的函数、数据的封装,是高内聚低耦合的,对外界而言应该仅有一个引起它变化的原因。但是如何划分一个类、甚至是一个函数的职责,这就需要每一个开发者自己去设计。
2.里氏替换原则
只要父类出现的地方子类就可以出现,而且替换为子类也不会产生任何的错误或者异常,里氏替换原则的核心是抽象。
3.依赖倒置原则
高层模块不应该依赖底层模块,都应该依赖于抽象;抽象不应该依赖于具体,具体依赖于抽象
依赖倒置原则指定了一种特定的解耦形式,使得高层次的模块不依赖与低层次模块的实现细节的目的,依赖模块被颠倒了。依赖倒置原则有以下几个关键点:
1.高层模块不应该依赖于低层模块,两者都应该依赖其抽象
2.抽象不应该依赖于细节
3.细节应该依赖于抽象
在Java语言中,抽象就是指接口或者抽象类,二者都是不能够被直接实例化的:细节就是实现类,实现接口或者抽象类而产生的类就是细节,其特点就是可以直接被实例化,也就是可以使用关键字new产生一个对象。高层模块就是指调用端,底层模块就是指具体的实现类。依赖倒置原则在Java语言中的表现就是:模块间的依赖通过抽象产生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或者抽象类产生的。使用一句话概括就是:面向接口编程或者说是面向抽象编程。、
例子:公司是福特和本田公司的金牌合作伙伴,现要求开发一套自动驾驶系统,只要汽车上安装该系统就可以实现无人驾驶,该系统可以在福特和本田车上使用,只要这两个品牌的汽车使用该系统就能实现自动驾驶。
福特汽车类,包含三个方法,启动、停止、转弯
class FordCar
{
public void Run()
{
Console.WriteLine("FordCar run");
}
public void Stop()
{
Console.WriteLine("FordCar stop");
}
public void Turn()
{
Console.WriteLine("FordCar turn");
}
}
宏达汽车类,包含三个方法,启动、停止、转弯
class HondaCar
{
public void Run()
{
Console.WriteLine("HondaCar run");
}
public void Stop()
{
Console.WriteLine("HondaCar stop");
}
public void Turn()
{
Console.WriteLine("HondaCar turn");
}
}
自动驾驶系统,有三个方法启动汽车、停止汽车、汽车转向
class AutoSystem
{
HondaCar hondaCar = new HondaCar();
FordCar fordCar = new FordCar();
CarType type;
public AutoSystem(CarType type)
{
this.type = type;
}
public void RunCar()
{
if (type == CarType.Honda)
{
hondaCar.Run();
}
else if (type == CarType.Ford)
{
fordCar.Run();
}
}
public void StopCar()
{
if (type == CarType.Honda)
{
hondaCar.Stop();
}
else if (type == CarType.Ford)
{
fordCar.Stop();
}
}
public void TurnCar()
{
if (type == CarType.Honda)
{
hondaCar.Turn();
}
else if (type == CarType.Ford)
{
fordCar.Turn();
}
}
}
目前来看,是满足需求的,但是随着发展业务也在发展,现在如果发展了伙伴,需要对其他品牌的汽车添加自动驾驶系统,比如红旗、奇瑞等品牌,那么如果沿用以前的方式,就需要去修改AutoSystem了,先增加两个新的品牌汽车的对象,然后在启动汽车、停止汽车、汽车转向中进行修改增加分支语句对不同的品牌来进行判断然后加上各种操作,这样就违背的OCP,而且复杂的分支语句也会容易造成错误,如果以后再继续扩展其他的品牌的话,那么这样的程序肯定是不易于维护的,程序的健壮性是较差的,大大的增加了开发的成本。那么敏感的同学已经看出来,既然不同的汽车品牌之间都拥有相同的行为,那么为什么不定义一个接口呢?现在我们先来定义一个接口,然后将各个品牌的汽车实现这个接口实现,那么在AutoSystem中我们就可以针对定义的接口操作了。
interface ICar
{
void Run();
void Stop();
void Turn();
}
自动驾驶系统也就是高层模块现在针对的是这个抽象的接口,无论什么汽车,只要实现了ICar接口,就能进行相关的操作。
class AutoSystem
{
ICar car;
public AutoSystem(ICar car)
{
this.car = car;
}
public void RunCar()
{
car.Run();
}
public void StopCar()
{
car.Stop();
}
public void TurnCar()
{
car.Turn();
}
}
福特汽车类也就是底层模块,实现了ICar接口,现在依赖的是抽象的接口
class FordCar : ICar
{
public void Run()
{
Console.WriteLine("FordCar run");
}
public void Stop()
{
Console.WriteLine("FordCar stop");
}
public void Turn()
{
Console.WriteLine("FordCar turn");
}
}
宏达汽车类也就是底层模块,实现了ICar接口,现在依赖的是抽象的接口
class HondaCar : ICar
{
public void Run()
{
Console.WriteLine("HondaCar run");
}
public void Stop()
{
Console.WriteLine("HondaCar stop");
}
public void Turn()
{
Console.WriteLine("HondaCar turn");
}
}
当高层模块依赖底层的时候,那么高层的复用性就较差,就如上例所说的增加汽车品牌种类。如果高层与底层都是依赖于抽象的话,那么高层复用性就较好,因为通过继承象出来的接口实现多态,那么复用的地方就多了,这样的设计无疑是较为稳定的。
4.接口隔离原则
接口隔离原则的定义是:客户端不应该依赖于他不需要的接口。另一种定义是:类之间的依赖关系应该建立在最小的接口上。接口隔离原则将非常庞大,臃肿的接口拆分成更小的和更具体的接口,这样客户端将会值需要知道它们感兴趣的方法。接口隔离原则的目的是系统解开耦合,从而容易重构、更改和部署。
5.迪米特法则
一个对象应该对其他对象有最少的了解,通俗的讲,一个类应该对自己需要耦合或调用的类知道的最少,类的内部如何实现与调用者或者依赖者没有关系,调用者或者依赖者只需要知道他需要的方法即可,其他的一概不管。类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。
6.开闭原则
软件中的对象(类、模块、函数等)应该对于扩展是开放的,但是对于修改是封闭的。在软件的生命周期内,因为变化、升级和维护等原因需要对软件的原有代码进行修改时,可能会将错误的代码引入,从而破坏原有系统。因此当软件需求发生变化时,我们应该尽量通过扩展的方式 来实现变化,而不是通过修改已有的代码。