在工作初期,我们可能会有这样的感觉,自己的代码接口设计混乱、代码耦合较为严重、一个类的代码过多等,当自己回过头再看这些代码时可能都会感慨怎么写成那样。再看那些知名的开源库,他们大多有着整齐的代码、清晰简单的接口、职责单一的类,这个时候我们通常会捶胸顿足而感叹:什么时候老夫能写出这样的代码!
面向对象六大原则 在此之前,有一点需要大家知道,熟悉这些原则并不是说你写出的程序就一定灵活、清晰,只是为你的优秀代码之路铺上了一层栅栏,在这些原则的指导下你才能避免陷入一些常见的代码泥沼,从而让你专心写出优秀的东西。 单一职责原则 单一职责原则的英文名称是Single Responsibility Principle,简称是SRP,简单地说就是一个类只做一件事。这个设计原则备受争议却又极其重要。只要你想和别人争执、怄气或者是吵架,这个原则是屡试不爽的。因为单一职责的划分界限并不是如马路上的行车道那么清晰,很多时候都是需要靠个人经验来界定。当然,最大的问题就是对职责的定义,什么是类的职责,以及怎么划分类的职责。
从上述程序中可以看到,HttpStack只有一个performRequest函数,它的职责就是执行网络请求并且返回一个Response。它的职责很单一,这样在需要修改执行网络请求的相关代码时,只需要修改实现HttpStack接口的类,而不会影响其他的类的代码。如果某个类的职责包含有执行网络请求、解析网络请求、进行gzip压缩、封装请求参数等,那么在你修改某处代码时就必须谨慎,以免修改的代码影响了其他的功能。当你修改的代码能够基本上不影响其他的功能。这就在一定程度上保证了代码的可维护性。注意,单一职责原则并不是说一个类只有一个函数,而是说这个类中的函数所做的工作是高度相关的,也就是高内聚。HttpStack抽象了执行网络请求的具体过程,接口简单清晰,也便于扩展。 优点 (1)类的复杂性降低,实现什么职责都有清晰明确的定义。 里氏替换原则 面向对象的语言的三大特点是继承、封装、多态,里氏替换原则就是依赖于继承、多态这两大特性。里氏替换原则简单来说就是所有引用基类、接口的地方必须能透明地使用其子类的对象。通俗点讲,只要父类能出现的地方子类就可以出现,而且替换为子类也不会产生任何错误或异常,使用者可能根本就不需要知道是父类还是子类。但是,反过来就不行了,有子类出现的地方,父类未必就能适应。
HttpStackFactory类的createHttpStack函数负责根据API版本创建不同的HttpStack,实现代码如下: 上述代码中,RequestQueue类中依赖的是HttpStack接口,而通过HttpStackFactory的createHttpStack函数返回的是HttpStack的实现类HttpClientStack或HttlUrlConnStack。这就是所谓的里氏替换原则,任何父类、父接口出现的地方子类都可以出现,这不就保证了可扩展性吗!
优点 (1)代码共享,减少创建类的工作量,每个子类都拥有父类的方法和属性。 缺点 (1)继承是侵入性的。只要继承,就必须拥有父类的所有属性和方法。 依赖倒置原则 依赖倒置原则这个名字看着有点不好理解,“依赖”还要“倒置”,这到底是什么意思?依赖倒置原则的几个关键点如下:
优点 (1)可扩展性好。 开闭原则 开闭原则是Java世界里最基础的设计原则,它指导我们如何建立一个稳定的、灵活的系统。开闭原则的定义是:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。在软件的生命周期内,因为变化、升级和维护等原因需要对软件原有代码进行修改时,可能会给旧代码引入错误。因此,当软件需要变化时,我们应该尽量通过扩展的方式来实现变化,而不是通过修改已有的代码来实现。
JsonRequest通过实现Request抽象类的parseResponse解析服务器返回的结果,这里将结果转换为JSONObject,并且封装到Response类中。
ImageRequest类的parseResponse函数中将Response中的原始数据转换为Bitmap即可。当我们需要添加其他数据格式时,只需要继承自Request类,并且在parseResponse方法中将数据转换为具体的形式即可。这样通过扩展的形式来应对软件的变化或者说用户需求的多样性,既避免了破坏原有系统,又保证了软件系统的可扩展性。依赖于抽象,而不依赖于具体,使得对扩展开放,对修改关闭。开闭原则与依赖倒置原则、里氏替换原则一样,实际上最终都遵循一句话:面向接口编程。 优点 (1)增加稳定性。 接口隔离原则 客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。根据接口隔离原则,当一个接口太大时,我们需要将它分割成一些更细小的接口,使用该接口的客户端仅需知道与之相关的方法即可。
PriorityBlockingQueue类相关代码 : 从PriorityBlockingQueue的代码可知,在元素排序时,PriorityBlockingQueue只需要知道元素是个Comparable对象即可,不需要知道这个对象是不是Request类以及这个类的其他接口。它只需要排序,因此,只要知道它是实现了Comparable接口的对象即可,Comparable就是它的最小接口,也是通过Comparable隔离了PriorityBlockingQueue类对Request类的其他方法的可见性。 优点 (1)降低耦合性。 迪米特原则 迪米特法则也称为最少知识原则(Least Knowledge Principle),虽然名字不同,但描述的是同一个原则:一个对象应该对其他对象有最少的了解。通俗地讲,一个类应该对自己需要耦合或调用的类知道得最少,这有点类似接口隔离原则中的最小接口的概念。类的内部如何实现、如何复杂都与调用者或者依赖者没有关系,调用者或者依赖者只需要知道它需要的方法即可,其他的一概不关心。类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。
Cache接口定义了缓存类需要实现的最小接口,依赖缓存类的对象只需要知道这些接口即可。例如,需要将Http Response缓存到内存中,并且按照LRU的规则进行存储。我们需要LruCache类实现这个功能,代码如下: 在这里,SimpleNet的直接朋友就是Cache或者LruMemCahce,间接朋友就是LruCache类。SimpleNet只需要直接和Cache类交互即可,并不需要知道LruCache的对象的存在,即真正实现缓存功能的对象是LruCache。这就是迪米特原则,尽量少地知道对象的信息,只与直接的朋友交互。 优点 (1)降低复杂度。 面向对象六大原则在开发过程中极为重要,它们给灵活、可扩展的软件系统提供了更细粒度的指导原则。如果能够很好地将这些原则运用到项目中,再在一些合适的场景运用一些经过验证过的设计模式,那么开发出来的软件在一定程度上能够得到质量保证。其实这六大原则最终可以简化为几个关键词:抽象、单一职责、最小化。那么在实际开发过程中如何权衡、实践这些原则,也是需要大家在工作中不断地思考、摸索。 设计模式 设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。这个术语是由Erich Gamma等人在1990年从建筑设计领域引入到软件工程领域,从此设计模式在面向对象设计领域逐渐被重视起来。 设计模式并不直接用来完成代码的编写,而是描述在各种情况下要如何解决软件设计问题。面向对象设计模式通常以类或对象来描述其中的关系和相互作用,它们的相互作用能够使软件系统具有高内聚、低耦合的特性,并且使软件能够应对变化。 |