参考资料:《大话设计模式》
PS:引用了很多书中的内容,特此标注出处。
面向对象思想是软件工程领域的一种哲学世界观,相比于更为直观的面向过程化,它处理问题的角度和方法都有些不同。而设计模式就相当于在这种世界观指导下而形成的方法论。通俗来说,就是当遇到了某种特定环境下的问题,设计模式提供了一种符合面向对象原则的处理办法,它是无数程序员经历了许久历练而总结出来的经验论,依循这些设计模式的思路,你可以对面向对象思想有更加深刻的理解,你可以对面向对象的设计原则有着更加深刻的把握,甚至于,你可以淡化设计模式中的具体方法,提取出其中的精髓,创造出适合于自己所遇到的实际问题中的设计模式!
面向过程化的思想是指在面对问题的时候,通过分析和把握问题的本质,对问题的解决方案进行处理,总结出一个流程,流程中的具体步骤就对应了一个function。从代码角度来看待这个思想,那么它的基本组成单位就是function,通常的面向过程化语言需要有一个主function,称之为main,它作为函数的入口。因此,倘若你遇到的是一个算法问题(如ACM算法题),那么你会发现面向过程化语言在处理这个问题上体现出的简洁与高效。
然而当问题发展到一定的规模时候,简洁与高效的优先级也许就没有那么高了。比如,你的需求发生了变更,需要修改代码时候,怎么办?在面向过程化的代码中,我需要打开代码,将整个问题的流程进行更正,运气不好甚至要重新编写一份代码。再比如,你的数据关系到了一些机密的信息,比如银行账户的密码,怎么办?在面向过程化的代码中,你无法处理这种情况,因为变量总是轻易地暴露在调用者的程序中,他可以随意地使用修改它。又比如,你发现代码中有很多地方重复书写了好多遍,但是其中又有少许不同,怎么办?在面向过程化的代码中,也许你会想到提取公共的部分到一个function中,然后对于不同部分使用参数列表去适应它的变化,然后这种变化的情况太复杂了,参数的个数会有不定个,种类会有不定种,总之你无法调配出一个合适于所有情况的参数列表。
综上所述,面向过程化在处理一些大规模的现实应用问题方面,将显得力不从心。然而这并不意味着面向过程化的思想和语言需要摒弃,事实上,C语言仍然是世界上使用最多最为优秀的编程语言之一。究其原因,个人理解,这是一个“什么更加重要”的问题,当你去写一个操作系统的内核,你更愿意考虑它的什么因素?当你去写一个企业级网站,你更愿意考虑它的什么因素?要知道,一个云平台的应用,是面向了亿万级别的用户,那么其中的万分之一秒的时间优化,也许就为它的公司省下了一部大型机器。这个时候,时间和效率的优先级很高。
所以,面向过程化和面向对象都有着它们的适用领域,二者之间也并非是对立关系。面向对象建立在了更粗的粒度上面,它将function之间的联系互动提升到了object这个级别,一个object有其attribution和operation,每个object可以看成是一个特殊的个体,那么个体的共性,就可以抽象成为class。
既然要谈面向对象,那么就要基于class和object去谈,就像化学家对世界的认知是:万物是原子、分子构成的一样,我们不需要去费劲地解释那些名词的概念,从代码的角度来研究也许会是更好的理解方法,有时候,说不出来不代表不懂,只是那种理不清道不明的感觉很奇特,然而当你实际地敲上几行代码,是朽木还是真金就高下立判。后续的详细篇内容将会加入实际的代码与UML图去帮助理解。
面向对象的三大基本特征:
1、封装:隐藏实现的细节,隐藏私密的数据,暴露共有的接口与方法来进行class、object之间的互动。
2、继承:由父类衍生出行为与其相似但不完全相同的子类,为了提升代码的可扩展性。
3、多态:对象的具体行为由它的运行时类型来决定,一个相同的对象,在不同的环境下表现出不同的行为。
这里再次强调,对一些概念费劲地去解释,不是一个学习它的好办法,因为解释概念只能记住一时,不易理解,所以很快就会遗忘。
面向对象设计的六大原则:
这里强调一下,原则并不是规则,不是说违反了某条原则就不是一个很好的面向对象设计。这里的原则是指在遇到这类的问题时候,最好去依循这些原则的想法来处理,但万不得已要违反,也就真的违反了。比如:当需求变更时候,我需要依循开放封闭原则,对扩展开放,对修改封闭,也就是说尽量通过添加代码的方式来实现对变更需求的响应,但是,需求变更就意味着你的代码一定要发生修改,不可能一成不变。所以这在一定角度来看,也确实违反了对修改封闭这条原则。但是这是不可避免的必要过程,因此不要将原则视为法则一样不可违背。设计模式的精髓就在于面对不可逆转的情况下,让一个问题的解决更多更好的遵循这些原则,而遵循这些原则会带来的主要好处就是:软件的安全性、可扩展性、可复用性提升了N个数量级。
1、单一职责原则:类的设计要功能单一,引起类变化的原因最好是只有一个。简单说,就是一个类最好只有一个功能,不要把杂七杂八的通通写在一起。
2、开放封闭原则:软件模块要对扩展开放,对修改封闭。意思是,当你的功能要改或者要拓展的时候,最好是添加代码来实现这个变更的响应,而不是通过打开原有代码去修改来实现这个变更的响应。
3、里氏代换原则:一个父类在任何时候要能够被其子类替换,并且让人看不出来。在生物学上,有遗传和变异,因此一个父亲可能有着与他差异很大的子女,但是这违背了里氏代换原则,里氏代换并不包括了那些变异。简单来说,就是一个子类不能够包含违反其父类定义的属性或方法,比如,鸵鸟作为鸟的子类,鸟有个operation是fly(),而鸵鸟并不会飞,那么鸵鸟作为鸟的子类这个设计就违反了里氏代换原则,它将不会是一个好的面向对象设计。
4、迪米特原则:也称为最少知识原则。意思是,如果两个类之间没有必要直接互相联系,那么就没有必要让两个类耦合,最好的处理方式是通过第三方来作为中介来联系。也许你看了之后会觉得这不是更加麻烦了么。但试想,如果这样的类多了起来,那么它们之间的关系将显得极其复杂,就如同现今的国家间关系一样。而联合国的存在就类似于这个中介者,它将各个国家之间的复杂关系通过它来进行传达,从而大大简化了国家之间的联系关系。
5、依赖倒置原则:高层组件不要依赖于底层组件,抽象不要依赖具体实现,具体实现要依赖抽象。简单点说,就是在设计类的时候,先提供它的接口。具体的实现则不在考虑范围之内,反正你无论怎样去实现,功能都是一样的。这也便于当我们发现实现可以进行优化时候,只需要修改它的具体实现,而不需要去变更其它的部分。
6、合成复用原则:尽量多地去使用合成,而不要使用继承。继承天生是一种父类与子类的高耦合性设计,因此,当父类需要进行变更的时候,子类就难以避免会随之产生变更,有可能需要重新设计子类,这样维护代价就太高了。而合成则没有这种情况,因此更多时候优先考虑合成。然而如果要使用继承通常也就是必须使用继承了,因此,这条原则并不需要刻意地去默念遵守。
站在面向对象的角度看问题,class和object是它的基本单位。那么设计模式就是人们在研究class和object的创建、组成和行为的过程。
首先,人们研究如何地创建一个类或者对象,能够使得创建的成本更低、更易于维护、更易于复用,于是就有了创建对象的一些设计模式:
1、简单工厂模式
2、工厂方法模式
3、抽象工厂模式
4、建造者模式
5、单例模式
6、原型模式
创建好了类和对象之后,对于它们之间的组合关系进行研究,去发掘它们更好的组合形式,于是有了组合对象的一些设计模式:
1、组合器模式
2、桥接模式
3、装饰器模式
4、适配器模式
5、享元模式
6、代理模式
7、外观模式
创建好了,也找到了它们之间合适的组合方式,那么下一步便是对它们之间的行为进行一个研究,于是有了行为型的设计模式:
1、策略模式
2、模板方法模式
3、命令模式
4、观察者模式
5、备忘录模式
6、状态模式
7、迭代器模式
8、责任链模式
9、解释器模式
10、中介者模式
11、访问者模式
之后的详细篇将会对以上内容进行详细地展示与说明。