软件的复杂性
软件的复杂性是一个很宽泛的概念,任何使软件难以理解、难以修改、难以维护的东西,都属于软件的复杂性。软件复杂的根本原因是:变化。这里的变化,包括:客户需求的变化、技术平台的变化、开发团队的变化、市场环境的变化等。
IBM院士、IBM研究院软件工程首席科学家Grady Booch曾经说过下面这段话,有助于我们理解软件的复杂性和需求变化的随意性。
"建筑商从来不会去想给一栋已建好的100层高的楼房底下再新修一个小地下室------这样做花费极大而且注定要失败。然而令人惊奇的是,软件系统的用户在要求作出类似改变时却不会仔细考虑,而且他们认为这只是需要简单编程的事。"
为什么需要设计模式
随着软件复杂性的增加、项目规模的急剧增大,代码的组织、维护和扩展性成为了一项挑战。面对这些挑战,开发者们逐渐总结出了一些最佳实践和通用解决方案,也就是这里提到的"设计模式"。设计模式并不是具体的算法实现或数据结构,而是解决问题的一种模板或蓝图。设计模式描述了如何在特定情况下构建对象和类之间的关系,以解决常见的架构问题。
设计模式描述了在我们周围不断重复发生的问题,以及该问题的解决方案的核心。这样,你就能一次又一次地使用该解决方案而不必重复劳动。设计模式实际上就是类与相互通信的对象之间的组织关系,包括它们的角色、职责、协作方式等各个方面。
设计模式通常和面向对象编程结合起来使用。面向对象设计模式是"好的面向对象设计",所谓"好的面向对象设计"是指那些可以满足 "应对变化,提高复用"的设计。现代软件设计的特征是:需求频繁变化。设计模式的要点是:寻找变化点,然后在变化点处应用设计模式,从而来更好地应对需求的变化。
解决复杂性
设计模式最大的优势在于:抵御变化。为什么能抵御变化呢?原因在于其遵循了以下的设计原则。
单一职责原则:一个类只负责一个职责,只有一个引起它变化的原因。
开放封闭原则:对于功能扩展是开放的,对于修改是封闭的。
里斯替换原则:子类可以替换基类,继承必须确保超类所拥有的性质在子类中仍然成立。
依赖倒置原则:高层次的模块不应该依赖于低层次的模块,它们都应该依赖于抽象。抽象不应该依赖于具体,具体应该依赖于抽象。
接口隔离原则:一个类对另一个类的依赖应该建立在最小的接口上。
迪米特法则:一个对象应当对其他对象有尽可能少的了解,不和陌生人说话。
封装变化点:使用封装来创建对象之间的分界层,让设计者可以在分界层的一侧进行修改,而不会对另一侧产生不良的影响,从而实现层次间的松耦合。
优先使用对象组合,而不是类继承:类继承通常为"白箱复用",对象组合通常为"黑箱复用"。继承在某种程度上破坏了封装性,子类父类耦合度高。而对象组合则只要求被组合的对象具有良好定义的接口,耦合度低。
设计模式的圣经
提起设计模式,就不得不提《设计模式------可复用面向对象软件的基础》这本经典著作。1995年,GOF(Gang Of Four),也就是Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides这四个人,合作出版了《Design Patterns: Elements of Reusable Object-Oriented Software》一书,被奉为设计模式的"圣经"。
该书描述了23种经典的面向对象设计模式,确立了模式在软件设计中的地位,开创了一种新的面向对象设计思潮。从此,参与设计模式研究的人数呈现爆炸性的增长。
总结
使用设计模式时,一定要区分需求的稳定部分和可变部分。一个软件必然有稳定部分,这个部分就是核心业务逻辑。如果核心业务逻辑发生变化,软件就没有存在的必要,核心业务逻辑是我们需要固化的。对于可变的部分,需要判断可能发生变化的程度来确定设计策略和设计风险。考虑你的设计中什么可能会发生变化,考虑你允许什么发生变化而不让这一变化导致重新设计。设计模式的核心在于发现变化点,并封装之。另外,一种可变性不应散落在代码的很多角落,一种可变性也不应当与另一种可变性混合在一起。
在实际工作中,很少会规定必须使用哪些设计模式,这样只会带来限制和条条框框。不能为了使用设计模式而去做架构,而是有了做架构的需求后,发现它符合某一类设计模式的结构,再将两者结合。设计模式从来都不是单个设计模式独立使用的。在实际应用中,通常多个设计模式混合使用,你中有我,我中有你。设计模式要活学活用,不要生搬硬套。死记硬背是没用的,还要从实践中理解。想要游刃有余地使用设计模式,需要打下牢固的程序设计语言基础,夯实自己的编程思想,积累大量的时间经验,提高开发能力。
设计模式解决的是设计不足的问题,但同时也要避免设计过度。一定要牢记简洁原则,要知道设计模式是为了使设计简单,而不是更复杂。如果引入设计模式使得设计变得复杂,只能说我们把简单问题复杂化了,问题本身不需要设计模式。设计模式的应用不宜先入为主,一上来就使用设计模式是对设计模式的最大误用。没有一步到位的设计模式。敏捷软件开发实践提倡的"Refactoring to Patterns"(重构获得模式),是目前普遍公认的最好的使用设计模式的方法。
或许有的人会说,我们不需要设计模式,我们的系统很小,设计模式会束缚我们的实现。实际上,设计模式体现的是一种思想,而思想则是指导行为的一切。理解和掌握了设计模式,并不是说记牢了23种或更多的设计场景和解决策略,实际接受的是一种思想的熏陶和洗礼。等这种思想融入到了你的思想中后,你就会不自觉地使用这种思想去进行你的设计和开发,而这才是最重要的。