设计模式一


前言

设计模式对于代码结构很有帮助,想提高代码逼格就绕不过设计模式,所以有必要整理一波。

一、设计模式的分类

从应用场景上设计模式的分类:

很多都是常见的,在使用的开源框架中或者原生代码中都有用到,比如:责任链在网络框架Okhttp中拦截器的使用,java中提供了观察者相关的类可以直接使用,RxJava也是基于观察者模式,甚至LiveData也算是观察者模式,工厂模式也经常看到比如BitmapFactory,Glide的双重检验的单列模式等等。

二、设计模式的拙见

设计模式是老生常谈的东西,面试必问,但很多时候一看就懂一用就错。打个比方的话,五花八门的框架技术点是外功,设计模式就属于内功了,外功照着练就基本没多大问题,内功需要理解加实践感悟,还容易练出错,参考欧阳锋修炼的九阴假经,虽然经是假的,但是人家有基础啊,身为五绝,起点高底子好能感悟出新东西,所以练错了还是变强了。

移动端来说其实大部分的业务场景开发并不需要采用严格的设计模式,或者 说需要不是那么多,简单的业务强行过多的设计感觉多此一举。对于一个稍大的模块或者功能组件,整体上的设计的确离不开设计模式的很多标准。

六大原则就是设计模式的口诀,每种设计模式就是演化出来的招式,这些招式用处场景各有不同。

对于业务开发说设计模式最终的目标是让大量的代码看起来整整齐齐,对于繁杂的业务后期能添砖加瓦、有迹可循式的迭代,而不是摸不到头脑只能重新开发,感觉这个过程也并不一定严格遵循一板一眼的设计模式,封装、多态、继承用的合理就是最好的设计模式。

但这个东西只要尝试的去使用,尝试去重构一些代码,模仿开源或者系统代码的用法,总能够信手拈来,虽然现在我还做不到。

三、设计原则

这个属于面试常问的装逼题,概念的东西还是要靠代码去实践理解,不然会快速的忘记,血的教训,一看就会一用就废,看的时间多用的时间少效果减半或者等于没有效果。
这个原则是指设计模式应该有的效果,应该遵循的规范,其实这几个原则才是精华,也可以作为软件设计、业务功能代码的规范,但感觉也不必处处都要强行的去靠。

1.开闭原则

类应该对扩展开放,对修改关闭。
对于一个功能希望的效果是上面说的添砖加瓦式的迭代,而不是每一次功能的改变都需要频繁的改动原有的功能类,在不修改原有类的基础上能够添加新的功能,弹性的应对改变。
也无法做到处处都可以支持扩展,在容易改变的地方可以去增加新的抽象层,从抽象派生来的实现类来进行扩展,当需要发生变化时,只需要根据需求重新派生一个实现类来扩展就可以了。比如观察者模式中可以通过扩展不同的Subject去加新的功能,而不必每次都去Subject中修改,很简单的道理搞个UML图:

2.依赖倒转

要面向接口编程,不要面向实现编程。
要依赖抽象而不是依赖具体的实现类。这里的依赖主要指高层中对低层具体实现类的依赖,而对于高层和底层的区分方法是,高层的行为由低层去实现。
比如在汽车类的启动要依靠引擎类去实现,汽车类就是高层,引擎就是低层。
又比如人这个类吃饭要依靠工具类,人就是高层,工具就是低层。
而这个原则的标准就是高层不应该直接依赖具体的实现类,这样可以减少类间的耦合度,上面的列子中引擎和吃饭的工具可能有很多种,吃饭工具可能是筷子、勺子、叉子或者直接用手也行,如果没种工具都要强行依赖显然是不合理的。只需要依赖抽象的工具类和引擎类就可以了。

其实和第一个类图是一样的,这里更强调Car和Engine的依赖方式,但又没有可能Engine也需要依赖Car呢?而Car是不是也有可能有很多种,所以Car就要提供一个抽象层作为Engine的依赖了。

3.里氏替换

子类可以扩展父类的功能,但不能改变父类原有的功能。
这个原则是对继承的规范,对基类与子类之间的关系的说明,算是对开闭原则的补充,子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。
网上看到的一个列子:企鹅、鸵鸟和几维鸟从生物学的角度来划分,它们属于鸟类;但从类的继承关系来看,由于它们不能继承“鸟”会飞的功能,所以它们不能定义成“鸟”的子类。

这里的列子是几维鸟重写了父类的setSpeed方法,会导致多态调用getFlytime的时候出错。几维鸟应该继承更高一层的父类Animal。
感觉这个原则貌似怪怪的,它强调继承关系的约束,子类要保证父类非抽象方法原始性,因为重写的话会有出错的场景,但这种出错是可以通过其他方式规避的,而不是说非要重新定义基类,毕竟父类中可以声明protected字段,像kotlin中声明open字段表示这个方法可以被子类覆盖重写,而且必须是显示的这个和java不同。而在重写的时候入参和返回值的问题,入参要比父类宽松,返回值要比父类更严格。但还是应该尽量遵循不要改变父类非抽象方法的行为。

4.接口隔离

不要试图去建立一个很庞大的接口供所有依赖它的类去调用
这个原则强调接口的设计约束,应该规避庞大臃肿的接口,接口应该合理的拆分,变的更小更具体。
相对明了,不同的功能模块业务需求应该建立独立颗度更小的接口,不应该实现不需要的功能。

5.单一职责

一个类应该有且仅有一个引起它变化的原因
这个原则的目的是为了提高类的内聚性,通常的业务功能划分应该有所区别。
就好像数学老师的工作不应该包括门卫的安保工作或者食堂大厨做饭的工作,这些额外职能的工作应该抽离到单独的类中处理。

6.迪米特法则

不跟“陌生人”说话
这个原则强调类之间的“通信””关系,不是混一个圈子的类不应该认识甚至还吃饭喝酒打诨,应该保持洁身自好。

打个比方,古代皇帝幼弱的话就需要太后垂帘听政,而封建社会的女人不因该抛头露面,尤其是皇室的女性,更不应该和大臣有眉来眼去,不然不自宫不能解决问题,所以要垂帘,有啥话通过太监传,同意了喊个准,大臣心理就有数了。

再举个皇帝和大臣的栗子,皇帝作为政权的中心,无法做到对每一个大臣都做出相应的事务批复,这个时候就需要一个中转职能的机构,于是明朝就有文渊阁,设立首辅,首辅的职责就是保持下级官员和上级的皇帝之间的联系,鸡毛蒜皮的事务或者地方小官是没有资格或者必要让皇帝处理的,皇帝只需对重大的事情根据中间机构做出的处理建议给出批复,同理后朝的军机处也有类似的功能,就是为了皇帝能够及时处理需要上报的事务设立的。

这里的首辅和皇帝是依赖关系,没有皇帝,首辅处理不了上报的事务。

7.合成复用

组合大于继承
关于复用:分为继承复用及合成复用
继承复用存在的问题:
1.继承破坏了类的封装性,父类对于子类来说变的透明,暴露了实现的细节;
2.子类和父类耦合,父类的行为改变子类也会发生改变;
3.灵活性,继承的实现编译时无法发生变化;

关于组合和聚合关系:
组合的话每个成员脱离了所在的地方还可以单独存在,就像汽车的轮子,引擎;
聚合是每个成员脱离所在就无法独存或者变的没有意义,鼻子、眼、嘴聚合在一起才有头

感觉问题是相对于场景而言的,如果场景不会存在这些问题那就不是问题,也不需要强行的使用合成的方式复用。

这里用一个网上看到的汽车的栗子,汽车按动力源和颜色分类会有很多的组合,

可以发现对于颜色的区分,如果用继承的方式会出现大量的子类,且每次添加新的颜色要进行大量的修改。

采用组合关系的话就可以解决问题。

总结

看过很多次设计模式,但都是走马观花,平时开发的时候也很少思考是否符合设计原则,所以代码的结构很难提高上去。
这次的梳理对于每个例子都单独的画了类图,毕业之后就很少画UML了,可能还有错误的地方,但还是有新的收获的。
设计原则是作为软件开发的参考,并不是强行必须的约束条件,也结合实际的业务场景做出做好的处理,再考虑重构时可以参考当前的代码是否有违背的地方,是否有可以符合原则改进的地方。总有一天逼格会提高上去。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值