策略模式引发的一些思考

[b]一、前言[/b]
在这里直言不讳,这篇博文是看了某位老师新出的一段Java SE视频中的一道作业题,题目很简单,要求用策略模式实现排序;其中有三个字段,需要对这个三个字段(ID,NAME,AGE)进行排序,并且还要求有升序和降序的方式。对此有兴趣的朋友可以在网上搜索获取,在此不做任何说明,避免7+7的厄运。
也许是因为无聊吧,我做了这道简单不能再简单的题目。但是却给了我全新的思考感受,[b]使我能够更有明确目的地在使用设计模式[/b]。
上面加粗的一句话貌似不好理解,太抽象,但又没有比这更能涵盖我所表达的意思了。换句话来说就是又增加了一条使用设计模式的心里暗示,即更有针对性地使用设计模式来解决应用场景中出现的问题。
我用一个更容易理解的比喻来解释一下:应用场景好比是一道菜,设计模式就好比是做这道菜所用的调料,用设计模式解决应用场景的问题也就好比是不同调料搭配起来会让这道菜具有不同的口味。由此引发的一个官方言论,即不要过分依赖设计模式,就好比是盐放的太多了,心里就该咒骂“打死卖盐的”了。

[b]二、寻找应用场景中隐藏的问题[/b]
首先让我们来分析一下这道题目,题目要求很明确,就是排序。单拿这道题目作为应用场景,该应用场景中需要对三个字段进行排序;注意应用场景中的问题出现了
1.不仅仅实现排序功能,并且还要实现升序和降序的排序方式,详细点地说就意味着要实现3*2个排序算法。
2.应用场景中的三个字段在未来或许还会增加,并伴随着需要进行排序的可能性,也就是那个“3”会变化。
3.应用场景中的排序方式也是会变化的,即使这个假设放在这个应用场景中不太合适,但是也能说明一些问题的,问题本质上没太大的区别。
4.前三条是针对实现来说地,下面的几条针对可维护性层面来说。我之所以写出这篇博文,是因为我终于思考出设计模式对今后代码的维护也是有很大好处的。为什么说是“终于”?以往看过很多设计模式相关的书籍,里面也曾提到可维护性之类的话题;但是看了之后,也是能够理解,在解决实际问题的时候这些话题可就不翼而飞了,脑海里面只想着我要使用设计模式,而不是我该怎样使用设计模式。我在一番思考之后,对此类话题就很敏感了,换句话来说,选择使用哪种设计模式之前,又多了一条思考路径(前言也提到了这个话题)。
5.结合上述4条,用第五条明确指出代码维护带来的问题。就本文的应用场景,它要实现3*2个排序算法,也就是说以后要维护6个算法;如果两个乘数都在不断的增长,那就要维护N*N个算法。为此需要用更优雅的方式来设计,解决今后对代码维护引发的超大工作量问题。

[b]三、三种设计方案[/b]
针对这个应用场景,我脑海里浮现出了三种设计方案。前两种方案是在没太理解策略模式之前经常用到的。后一种设计方案,看上去跟第二个很类似,但是却比前者更具内涵。下面是三种方案的方案以伪代码的方式介绍,就不放代码介绍了,一太占篇幅(好吧,我那是懒!),二此文最后提供第三种解决方案的源码。[b]本文只阐述我思考后的成果,请勿沉浸在问题和代码表面上![/b]
[b]1.不使用模式[/b]
  if (升序) {
if (升序比较ID) {
// 比较后的算法
} else if (升序比较NAME) {
// 比较后的算法
} else if (升序比较AGE) {
// 比较后的算法
}
} else if (降序) {
if (降序比较ID) {
// 比较后的算法
} else if (降序比较NAME) {
// 比较后的算法
} else if (降序比较AGE) {
// 比较后的算法
}
}
这个设计可谓很老很糟糕,也算个懒人专用方式(好吧,我承认经常这么干,前提要保证这个需求是千年不变的)。这个设计需要维护6个算法,如果连把比较算法封成一个方法都不弄的话,那就会更悲剧了。请参见第二节第五条里的说明。
[b]2.不完整的策略模式[/b]
鉴于上面那个悠久而又古老的设计,再来进一步的优化。
        //(1)定义6个实现自Comparator接口的排序算法实现类,有3个针对字段升序排序, 另外3个针对字段降序排序。
//(2)调用Collection.sort(List,Comparator)方法针对List进行排序。
实现Comparator接口也是在使用策略模式,具体参见本文最后的源码附件,同时引发的问题请参见第二节第五条里的说明。
这个设计方式看上去靠谱了很多,但是需要维护的算法(类)还是N*N。
[b]3.比较完整的策略模式[/b]
那就继续优化,下面的三步思路足以说明,具体参见本文最后的源码附件。
//(1)定义1个排序方式接口(策略接口)。针对本文所引用的应用场景,该接口提供两个方法,分别对Integer和String类型字段做比较操作;该方法返回的是int类型。
//(2)定义2个实现自先前定义的排序方式接口的实现类(具体策略),分别针对于正序和降序实现各自的算法。因该算法内部使用JDK提供的比较方法,其内部采用的是减法操作,故接口中的两个方法需返回int类型。
//(3)定义3个实现自Comparator接口的排序算法实现类(环境),分别针对三个字段的排序。每个类都分别保有先前定义的排序方式接口的引用,并且实现的方法中需调用该接口引用所提供的相应方法。
特别要注意本节用阿拉伯数字表明的地方。这个设计与第二个设计同样也定义了6个算法类,表面上看是差不多的;不过这里使用了两次策略模式,使维护的算法(类)变为N+N。
[b]4.结论[/b]
首先让我们来假设一下:现在需求有变化了,客户方又突然想增加100个字段,并且这新增的字段都需要提供可排序功能;第一种设计暂且不考虑,那就考虑后两种。针对这个新需求,两者的差别慢慢地浮现出来。
请仔细看,第二种设计方式中的每个字段都需要有对应的且类似的2个类,并且应对这样的需求显然愈发无力了,因为维护数量是以乘积计算的;而第三种设计方案,连用了两个策略模式,其中一个是将排序方式和待排序的字段分离开来,使二者的变化不会互相影响对方,使代码维护量变为加法计算,明显比前者达到的效果要好得多。
这时客户方那边又提出了一个新需求,这次不是再加100个字段了,而仅仅是再要增加1个排序方式,如果换成第二种设计方案,是不是要抓狂了?
此番思考之后,得出了这样一个结论:使用抽象的东西要尽可能得将其转换成实际的东西;理解一个实际的东西要尽可能得将其转换成抽象的东西。

[b]四、总结[/b]
本文的题目其实有些迷惑性,是说策略模式呢?还是在说思考呢?其实由策略模式引发的思考,不仅是对策略这个单个模式的思考,也是对整个设计模式的全面思考;现阶段的思考重心完全倾向于怎么更给力的使用,绝对的要有目的性的去用;还不会用?有了目的就很好办了,把这个模式的UML图找出来,再把场景问题往里套,就完全OK了。不过说实话,本文还是侧重于对思考后的阐述。
有些幽默的是本文所引发的这个思考是我在不经意间摸索出来的,不过看起来还是很靠谱的,又为我提供了一个思考的路径(不思考永远不会有不经意)。
因本文完全是我思考后的文字,也就是说是用我的思维方式来描述思考的过程,不见得您能读的明白;别着急,只要理解大致意思,用您的思维方式来理解就好了。看书也是同样的道理,用自己的思维方式去理解书中的内容,而不要一味地随着作者的思维方式;换句话说思维方式可以借鉴,但不要模仿。
[b]思考需从不同的角度无限延伸,定能有所收获。[/b]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值