对设计模式六大原则的理解

来源:互联网 发布:兄贵pat it 编辑:程序博客网 时间:2024/05/01 12:58

以下内容都是我个人的一些理解,并不代表官方或主流的说法,欢迎大家一起讨论。


写在前面:有一些最基本的理解是需要首先说明的。设计模式并不是规定,也不是唯一的准则,你可以完全不用设计模式而完成你的代码,保证它能实现你想要的功能。所以设计模式不是非用不可的。那我们为什么要用它呢?因为它使我们的程序结构更清晰,更有扩展性,而且它可以解决某些特定的问题。就像你可以顶着一头鸟窝快乐的生活,你也可以把它好好梳理一下,做个造型,这样既好看,又能解决你的头发总往眼睛里跑的问题:)

设计模式的六大原则也不是规定,而是类似规范的东西,你应该尽量遵守。很多时候你并不能100%依照原则做事,但它们最大的好处是为你提供了一种倾向,告诉你往这个方向靠拢是好的。


开闭原则

内容:软件应对扩展开放,对修改关闭

依赖倒转原则

内容:实现应该依赖于抽象,抽象不应该依赖于实现


理解:开闭原则和依赖倒转原则是六大原则中最基本的两条,他们是设计模式的基础。设计模式的根本思想可以通俗地理解成:把底层架构搭好,然后就不要去动它,其它的一切都是建立在这个底层的基础上的。

举个实际的例子,你要建造一座房子,你预期它有两层,那么当你开始建造之前,你必须画一张非常详细的图纸,它有几面围墙,它的地基有几层,哪些位置应该留着接通电线、水管、天然气,哪些位置应该做特别的设计以便与上层衔接……你必须把它们全部实现确定好,因为你明白,一旦开始建造,这些东西是无法再做修改的——任何改动都可能带来危险的因素甚至导致返工,比如你发现你忘了留出天然气管道的位置,于是你不得不把已经铺好的地板挖开;比如你发现你其实更想要三层这样你可以把父母也接来,但你当初打的地基根本就承受不了再多一层楼,于是你推到一切重新来过了。

再举个贴近工作的例子,你要写一个软件(或者是跟别人合作),你确认了用户的需求以后开始搭建软件的框架(各种接口、抽象类等)。等你搭建完成开始编写上层代码时,你就不能再修改搭建好的框架了。因为你的上层代码大量的继承和调用了这些底层的类和方法,它们之间又发生了无数的引用,你对底层的任何修改都会带来安全隐患,留下定时炸弹。


这两条原则是相辅相成的,开闭原则是依赖倒转的原则,依赖倒转又为开闭提供了基础。可以说为了让程序满足开闭原则,你必须先遵从依赖倒转原则。抽象不依赖于实现是对修改关闭的基础,实现依赖于抽象是对扩展开放的核心。


里氏代换原则

内容:在任何使用基类的地方,都可以使用其子类,而不影响程序的功能。反之不一定成立

理解:这里说任何并不很精确,但大致意思是这样。比如一个方法接受的参数类型是IPara这个接口,那么ParaImp作为IPara的子类,它的实例是绝对可以被传给这个方法的。里氏代换原则最大的好处就是,它使得一个只能接受单一类的位置可以接受无限多个不同的类(当然这些类要继承自同一父类),大大提高了程序的扩展性。

举个我自己工作的例子,我在做单元测试的时候,常常需要改变一些类的行为以实现测试目的。比如一个类中会检查时间过了24小时才会发送一条消息,我要发送这条消息当然不能等24小时,所以我需要把这里改成10s。但我并不能修改被测的源码(这样很可能引入新的bug不是吗?),所以我会去mock这个类。那么我怎么用mock的类去换掉原来的真实类呢?

很多情况下这个类就想我开始说的,是一个方法的参数。如果写这个方法的人遵循了里氏代换原则,他就会使用父类作为这个方法的参数,这样我的mock类就可以直接传进去,或者不是为了测试,他发现有一种情况需要6小时发送一条消息,他也可以方便的把这个新类的实例传进去,而不用改动已有的代码。而如果他一开始没有这么做,他直接把24h这个类作为参数的类型了,那我的测试是没希望了,他也无法再扩展这里的功能了。


所以里氏代换原则其实可以理解成这样一条原则:尽量多使用基类,而不是子类。


里氏代换原则是满足开闭原则的。


合成/聚合原则

内容:尽量使用合成/聚合而不是继承

理解:比较简单的理解是继承是一种强耦合,而合成聚合是弱耦合,这条原则告诉我们要尽可能的降低类与类之间的耦合度。先解释下什么是合成聚合吧,合成聚合其实就是”引用“的关系,类A的一个方法接受一个类B类型的参数,那么类A就引用了类B,这是一种“聚合”;类A中有一个域是类C类型的,那么类A也引用了类C,这是一种“合成”。合成与聚合间的界限没那么清晰,可以把它们都当做引用。两个类之间有之间的关系,那么不是继承就是引用。

为什么要避免使用继承呢?因为java是单继承的,一个类只能有一个父类,一旦继承就不能再更改,功能就被限定了,这显然是不利于程序的扩展性的。如果想获得某个类的功能,可以去引用它,这样你想增加别的功能时,就不会受到限制。比如某个位置只接受类A类型的参数,而你为了获得类B的功能而去继承了它,显然你的类就无法再传入这个位置了。


这条原则也是满足开闭原则的。


迪米特法则

内容:一个类应该与尽量少的类发生联系

理解:这条原则可以在上一条原则的基础上理解为:尽可能少的使用合成聚合。虽然你可以通过引用很多类来获取不同的功能,但如果你引用了A,而A又可以访问B,那你就不应该再引用B,而是应该通过A来访问B,以降低引用的类的数目。

合成/聚合原则与迪米特法则其实表达了同一个思想:要尽可能降低类与类间的耦合性。至于为什么要降低耦合,这个不难理解,耦合性越低,代码结构就越清晰,修改的成本也越低(想想吧,如果你的代码耦合性很高,那么你修改了一处bug,你就很难确定它的影响范围,很可能在你修改bug的同时又埋下了新的bug)。


接口隔离原则

内容:将不同的功能分离成多个单一的接口,而不是一个总接口

理解:这条原则的本质也是降低耦合性。举个例子,一个接口的所有方法中,一部分是为“通电”服务的,一部分是为“通网”服务的,那么这个接口就应当被拆成两个接口,分别服务于这两个功能。如果不这样的话,如果我有一个类继承自这个接口而这个类的功能是通电,它就必须把通网的方法也实现了,这显然是没有必要的。

使用设计模式也许很大可能会是你的代码量增加很多,但这不意味着像上面说的那种包含在内。不提供任何贡献的代码没有存在的必要。


总结:按我个人理解,设计模式六大原则其实可以“浓缩”为两条,

1、依赖于抽象,并对扩展开放

2、尽量降低类与类之间的耦合

0 0
原创粉丝点击