大观设计模式(一)

看了不少讲解设计模式的帖子和资料,要么语言晦涩难懂,要么就直接复制,连UML都照贴不误,实在是给不了人任何参考,更不用说学会设计模式了。本贴子会是一个系列,争取将自己学习的心得整理好,给他人一些学习借鉴。

有人觉得用不上设计模式,因为他们工作项目的需求是固定的,代码可以“从一而终”,耦合度再高也无所谓。可是随着互联网的兴起,这样的企业会越来越少,大多数程序员要面对需求迭代或者需求变更的命运。为了让码农们在面对需求变更时少加点班,更为了保证代码产品的质量,设计模式可以说是救世主般的存在。

在学习具体的设计模式之前,我们需要了解两个基本的原则,可以说,这两个原则是设计模式存在的基石。

  • 开-闭原则

  • 依赖倒转原则

    下面,我来说明这两个原则的具体内容,以及它们如此重要的原因:

开-闭原则(The Open-Closeed Principle OCP)

即开放封闭原则,是说软件实体(类,模块,函数等等)应该可以扩展,但是不可修改。

从这个原则出发,对了解设计模式存在的原因是非常有帮助的,设计模式本身就是一整套的软件构架思路,也可以叫做套路。从经典设计模式的个数来看,多达23种,而且还有越来越多的趋势,这导致我们学起设计模式是很费劲的。而了解了开闭原则,我们就不必拘泥于花样繁多的设计模式种类了。

为什么好好的平铺直叙型的代码不写,要整什么设计模式呢,毕竟设计模式在很多时候会加大设计难度,这一点也是很多人不理解的地方。从开闭原则看,我们应该像对待基础库一样来对待我们的已有代码,只做扩展,不做修改。正如我们盖一个大房子,已有的地基是不改动的,只在地基上继续盖新的建筑。现在 ,新需求来了,使用旧的代码是无法满足新需求的,我们肯定要新添代码。或者需求直接变了,而旧的代码却是不让改的!面对这一切变化,平铺型的代码肯定是无法满足要求的。因此,作为一员优秀的代码工程师,你需要引入设计模式,是的,适当的设计模式的使用,可以让需求的变更变得兵来将挡,水来土掩。

开闭原则 既是 设计模式存在的理由,也是设计模式在设计时的指导原则,所以,请记住这个原则。

依赖倒转原则

  1. 高层模块不应该依赖底层模块,两个都应该依赖抽象
  2. 抽象不应该依赖细节,细节应该依赖抽象。

乍一看,你可能会觉得,字我都认识,可是组合在一起就莫名其妙了。是的,作为oop的初级实践者,很多人都不明白这两句话的意思,至少,无法完全明白。很多人还以为用C写代码就是面向过程,而用C++,java写代码就是面向对象了,事实上,即使你完全使用面向对象语言写代码,你的编码思路也可能是面向过程的。oop作为一个思维方式,并不是靠语言本身的语法就可以逆转的。

回到以上两句话,这和OOP的关系大么?我的回答是很大,如果你知晓OOP思维,你就会知道模块有高低之分,而实现是分抽象和细节(具体)的,我先提个论点:

  • 一切在流水线上组装的产品都是OOP思维的集大成者

这个话可能有些绝对,但是可以举出很多例子,比如,PC,手机,家电等。原因何在,因为他们的发明者都将变化因子封装了,将其抽象成接口,而具体的细节则作为一个个零件存在着。以触控手机说明,屏幕,主板,外放喇叭等的接口都是高度抽象的,不同的手机厂商可以选择用不同生产商的器件去组装自己的品牌手机,手机厂商也可以自由的安排这些接口的位置以及去留。等到了组装的流水线上,产线工人不需要理解具体的手机组件的原理,只需要按sop(操作指导书)组装器件即可完成一部复杂的手机的生产了;这里说个题外话,不懂设计模式的程序员,在工位上码代码的时候 和 产线上的组装工人 做的工作其实并无不同。而今天的手机厂商上至apple下至国内的三流小厂做的事无非是调换接口位置,换手机壳,换组成芯片等等,他们是一群设计模式的使用者,区别仅在于熟练程度。而设计模式的发明者,他们是规则的发明家,少之又少。

下面,我还是具体解释下依赖倒转原则,很多人认为 这不可能啊,没有地基怎么可能有上层建筑,没有图纸,怎么可能建房子呢?有这种认识的人,其实是误会了模块 ,抽象,细节的意思了:

  • 模块之间的拼接不该是强耦合的直接联系,而是同时以第三方抽象接口为连接点,
  • 而这个第三方抽象接口是可以扩展的,但是这种扩展并不影响原来的模块之间的拼接关系,但是却可以改变他们的拼接属性。
  • 具体到设计类之间的关系的时候要从抽象着手,留出补位和扩展的空间,这是对开闭原则的另一种遵循。
  • 具体的细节实现都只以设计好的抽象接口为准绳,不逾越接口的功能点。

接下来,又需要说下,类的设计里需要遵守的两个原则了:

  • 单一职责

    就一个类而言,应该仅有一个引起它变化的原因。

    不遵守这一原则的代码,最终都可能走向无法复用,不易扩展,甚至重构的命运

  • 里氏代换

    子类型必须能够替换它们的父类型

在软件里,把父类都替换成它的子类,程序的行为没有变化。在面向对象语法里,也确实是遵守这一原则的,子类继承了父类,确实就可以直接以父类的身份出现了。只是,语法是语法,原则是原则,很多类的设计没有遵守这一原则,就会导致一些歧义。比如设计一个鸭子类,在里面 有一个 叫的方法,可是现在有一个木头鸭子,继承该鸭子类,现在木头鸭子也能叫了,因此此时的木头鸭子是不能替换鸭子的,不然就违反里氏代换原则了,从这个角度看,鸭子类的设计是有问题的。

继续回到 依赖倒转,单一职责原则 让我们有了设计类功能时的边际约法,而里氏代换则说明了抽象接口的可行性,很多的设计模式都能看到里氏代换原则的身影。遵守了依赖倒转原则,实际上也就是遵守了里氏代换原则了。

总结一下:依赖倒转其实可以说是面向对象设计的标志,用什么语言码代码并不重要,如果编写的代码考虑的都是面向抽象而不是面向细节,也就是说所有的依赖关系都是终止于抽象类或者是抽象接口,那么这样的编码方式才是面向对象的,否则就是面向过程的。

如果,你看到这里,说明你基本明白了这两个OOP的设计原则,他们既是设计模式的起点也是终点。从这两个原则出发,你将更好的开展设计模式的学习,如果你的代码 写就后不需要改变,那你可以 不学习设计模式,否则,你应该以终为始的学习后面的知识。

posted @ 2018-09-15 16:38  Lckfa  阅读(152)  评论(0编辑  收藏  举报