您的位置:首页 > 其它

你应该掌握的-面向对象的六大原则

2017-06-22 01:52 381 查看

前言:

对于一个使用java语言的程序员而言,面向对象是编码时候的一条必要基准。理解好面向对象六大原则,对于你以后的编码是非常重要的。对于这六大原则,每个人的理解深度可能不同,绝大部分还是依据你自己的工作经验来理解这六大原则。对于我自己而言就是这样,刚开始工作的时候看这个懵懵懂懂,每过一年看一次面向对象的六大原则,都有不同的体会。所以在这我仅仅谈下这六大原则的概念以及实例,或许其中参杂着自己现阶段的理解吧。
在这之前,先说下一个非常有意思的事情,也就是前段时间的一次面试,就谈到了这个。不过不是具体的说这六大原则,而是对我写的某个功能的程序设计。
功能:有一个搜索功能,根据关键词搜索,显示7种不同的列表,当然我这里的列表仅仅是每一种底下有几个item,就相当于展开的expandablelistview。(当时那位面试官是说这种完全用一个RecyclerView就能解决,不可否认实现的方式有很多,主要还是看个人设计的想法)不过在对这个功能重构之前,以前的同事是每一个种类一个listview这样设计的。我当时考虑是通过一个listview来解决,(因为做的项目一直也没涉及到RecyclerView,一直用的都是Listview,不过影响不大,这里可以看下我之前写的一篇博客树形图构建)或者是通过一个Linearlayout然后动态生成view这样来实现。当时我都写到一半了,后来才发现这样并不合适,为什么呢?因为现在做的项目是自主研发的,需求存在多变性,也就是说可能这个版本这样实现可以,但是下个版本就要换个方式来展示了。如果只是为了实现功能而写代码的话,那每次需求改动我们都需要重写一次具体的实现内容,那这样就坑爹了。这样的修修改改要占据我们很大一部分的开发时间,特别是有时候需要添加一点点,或者修改一点点,这样会使你的具体实现的代码变得越来越复杂,增删改动原来的代码,也非常容易造成错误。那下次别人接手你这个功能的时候,哪怕你的注释写的再详细,那别人可能也会感叹一句,"是谁写的这么操蛋的代码"。
先看看我的设计草图:



这是初稿的草图,为什么说是初稿,因为当你架构一个功能或者是一个项目的时候,很多东西不可能考虑的非常全面,最开始你要有一个大致的思路,然后按照这个思路去拓展。当然这个前提是你的这个思路要相对正确。
通过上面的图,可以看到,我们仅仅依赖于抽象,然后通过control来控制抽象从而操作具体的实现。这样哪怕是以后的需求有改动,更换展示(加入listview的方式行不通了或者是其他改动等),我们仅仅要考虑的是增加一个实现接口的具体实现类就行了,这样我们的实现与view就仅仅依赖于抽象,两者的耦合度就大大降低了。(这里传参的话,可以通过泛型来实现)。
这也就是为什么很多书上说的,面向对象编程也就是面向抽象/接口编程了。上面的架构有一点像MVP模式,有时间会写一篇关于MVP模式的博客。所以说很多时候,我们在实现功能的前提下还需考虑以后需求的改动,不应该仅仅为完成任务而工作,多考虑考虑这样才能有提升。很多情况下任务可能不多,但是耗时会很多,还可能要经常加班来完成任务,可能你完成任务的时候多考虑一点,那能大大的节约你的开发时间。在这贴下我定义的interface吧,上面的架构其实就运用到了这六大原则的其中几种。



说了这么久,该进入正题了,分别介绍下六大原则,我这里也没用它的英文缩写,个人习惯不太喜欢去记这些,更习惯用中文表示,最尴尬的是有次面试他们问我IPC是啥,我懵逼了,然后我来一句能不能用中文说下。
1.单一职责:优化代码的第一步。
2.开闭原则:让程序更稳定,更灵活。
3.里氏替换原则:构建扩展性更好的系统。
4.依赖倒置原则:让项目拥有变化的能力。
5.接口隔离原则:系统有更高的灵活性。
6.迪米特原则(最少知识原则):更好的扩展性。

1.单一职责

一个类只有一个原因导致它变化,也就是说一个类仅仅只有一个功能。这个咋说呢,主要还是看你自己,根据自己的经验来划分,例如就拿我以前写的图片框架来说,那时候的我把图片压缩以及缓存都写到同一个类中了,把他们都当作了一个功能来处理。(当然我这里是不太正确的)有兴趣的可以看看我以前的那篇博客通过DiskLruCache以及LruCache来构建自己项目的图片缓存框架。那时候没有一个清晰的架构,所以很多东西冗余到一起了。这样对于以后的扩展来说非常不方便。当然这是个反面教材,所以也是我为什么在最后会说一句可以拆分,减少耦合度了。
这个我就不写例子了,哈,我那图片框架就是个反面教材,这个要具体看自己的理解吧,你在不同的时期可能对于这个单一职责有不同的理解,控制这个度很重要,因为不是越细就越好,越细就代表这你的处理步骤要增多,同时你的类也需要增多。

2.开闭原则

软件中的对象/类/模块等,应该对于扩展是开放的,但是对于修改是封闭的。当然这对于实际开发而言,仅仅只是个理想的状态,往往修改原有的代码和扩展新的代码是 同时存在的。我们只能尽量避免对原有模块进行修改来实现我们的功能,或者说是扩展我们的功能。
看到这个原则,(如果你仔细看了我上面那个搜索功能的实现的话)是不是有点感觉了。拿我那个搜索功能来说,如果需求有改动,需要增加一个历史记录的展示,或者是其他的功能,我们仅仅只需增加一个实现我们接口的实现类,具体的历史记录的显示就直接在这个实现类中处理就行了,这样不就遵守了我们的开闭原则吗?通过扩展来实现我们的需求变化。根本不需要改动原有代码,哪怕是我们的control层都不需要变动。或者换种需求,我们原有的7种显示方式不合适了,需要更改,那我们也可以通过新增实现类来改变。不过基本没人这么做,因为这样又会新增7种实现类,有点纯傻逼的行为,这仅仅是举个例子让你能更好的理解这个开闭原则,如果真有这种需求,我们还是老老实实的改变我们的实现类吧,这也是为什么说很多时候真正要做到开闭原则是种理想情况,我们只能尽量的去遵守。

3.里氏替换原则

只要父类能出现的地方,子类就能出现,而且替换为子类也不会产生任何错误,使用者根本不需要知道是父类还是子类,但是反过来就不行了,有子类出现的地方并不一定父类就能出现。其实它利用的就是抽象这个原理,也就是我们java中的多态与继承。
在我们android中View以及我们的TextView,EditText...等就能完美的展现这个,而我前面说的那个搜索功能中也存在,就是我们的Interface与我们的具体实现,在control层中,通过传递进去的不同具体实现类都能代替SearchResultAgent这个interface的工作,并且展示我们不同的搜索结果。这样就保证了我们这个搜索功能的扩展,我们仅仅只需在具体运行的时候把抽象的SearchResultAgent接口替换成我们的实现就行了。它往往与开闭原则是相互依赖的,都是依据抽象,如果我们的需求变动,我们只需新增一个实现类就行了。这样就保证了对扩展开发对修改封闭。这里可以好好体会一下,为什么说两者存在相互依赖关系。

4.依赖倒置原则

它是一种特定的解耦形式,高层次的模块不依赖于低层次的模块的具体实现。举个例子来说,客户要求实现一个产品,他仅仅依赖的是能做好这个产品的公司,而并不需要知道这个公司是如何实现这个产品的,只要在指定日期内交货就行了。这样我们的客户就是高层次模块,而公司的具体实现就是低层次模块。两者依赖的是这个公司。对于程序而言就是我们编码要对抽象进行编程,而不是对实现进行编程。
而对于依赖倒置原则的实例,还是拿上面那个搜索功能来说,Activity就相当于客户,具体的实现类就相当于我们的具体实现(从名字上也好理解)。两者的依赖就是通过control层的SearchResultAgent的接口。我们通过SearchResultAgent来抽象出要执行的实现方法,当到具体实现的时候,仅仅只需通过依赖注入的方式传入具体的实现类就行了。

5.接口隔离原则

我们编写代码的时候,一个类不应该依赖它不需要的接口,或者说类间的依赖应该建立在最小的接口上。它的作用是把复杂的接口,拆分成更小的或者说是更具体的接口,(这与单一职责有点相似)这样能让你的项目更好的重构。
上面的搜索功能也有体现,不过这里是个反面教材,当时设计的时候为了方便没考虑这一点,当然这也是我之前所说的这仅仅只是初稿,这也是为啥代码需要重构的原因。可以看到SearchResultAgent接口中有一个方法void setInfoData(T t),这个方法仅仅只为7种显示中的一种显示提供的设值方法,其他6种根本没有使用到,这样就导致了其他6中过多的依赖了他不需要的接口违背了接口隔离原则,解决方式就是把这个接口中的setInfoData(T
t)方法单独提取出来。

6.最少知识原则

它的定义就是一个对象应该对其他对象有最少的了解,或者说一个类需要与它耦合的/调用的类知道的最少。这样大大的降低了类之间的耦合度,提高代码的稳定性。
还是拿上面的搜索功能来说,Activity--control--实现类,Activity与control耦合,而control控制SearchResultAgent的具体实现类来展示不同的view,control就与我们的SearchResultAgent耦合。我们的activity只需依赖注入一个具体的实现类到control中,control来操作SearchResultAgent来实现具体的展示。这样我们的activity只需与control来进行通信,而control又直接与SearchResultAgent来进行通信。activity根本不知道具体的实现,这样就很好的对activity隐藏了具体的实现。

好了,以上就是面向对象的六大原则,其中也参杂我自己这对于这六大原则的理解,或许你会认为,对于一个搜索功能,为毛能用一个expandableListview或者是RecyclerView/ListView能解决的问题要花费这么大的周章来处理。我只能说单单的完成一个任务并不是你的价值体现,你应该更多的考虑,这个功能之后的扩展,这个需求的变动带来的开发时间,如何能做到更好更优的重构是一个程序员更应该考虑的事。除非是这个功能基本不会再变动了,不过我想这种情况应该很少很少吧。(如果把这个功能给放大点,扩展到整个搜索模块,重构下这个设计思路,那岂不是能自己搭建一个自己项目的搜索模块框架?当然这里仅仅是提供一种思路)理解好这六大原则,同时控制设计的"度",就像前面说的并不是越细分越好,这样往往会导致"设计过度"。对于编码的提升是有很大的帮助的,并且能极大的缩小由于需求变动而带来的额外开发时间。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: