前言:
最近在写项目的时候,深感设计模式的重要性。一个人的代码写的好不好,别人看的舒不舒服,和会不会设计模式紧密关联的。之前看过四人帮的设计模式。但当时仅限于看,包括现在也仅限于看。有的时候项目中,你都不知道有没有运用到了设计模式。也许用到了单例模式,但你并不知道如何用的,不知不觉就用到了。
《武林外传》老白曾经说过这样一句话。高手就是手里无刀,心中也无刀。类似于设计模式,你不知不觉中已经融进你的代码中了,但你并不知已经运用了。当然我没有达到这个境界,可能五年,十年,或者更久,谁也说不准呢。
这次正好趁这个项目,把用到的涉及模式总结一下,策略模式和模板方法模式。
1:设计模式分类
总体来说设计模式分为三大类:
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
我们这次讲的都属于行为型模式。这类模式负责对象间的高效沟通和职责委派。注意理解下对象间,责任委派。
2:策略模式
策略模式是一种行为设计模式, 它能让你定义一系列算法, 并将每种算法分别放入独立的类中, 以使算法的对象能够相互替换。
在我的项目中有这么一个场景,大家也可以想象一个。用户购买商品支付的时候,可能会使用多种不同的优惠券。比如说,贴息,满减,随机立减等等
这几种返回给前端的金额,文案,以及个个方法都有比较大的差异。我们可以理解为三种不同的算法,选择哪种策略,完全由用户有哪张优惠券所决定的。
因此很简单的,我们可以使用策略模式。
1: 我们首先先定义一个接口
1 @Service 2 public interface CouponStrategy { 3 4 void execute(Coupon coupon); 5 6 }
2:定义不同的实现类
1 class CouponStrategyA implements CouponStrategy { 2 3 @Override 4 public void execute(Coupon coupon) { 5 // 第一种具体算法 6 } 7 8 } 9 10 class CouponStrategyB implements CouponStrategy { 11 12 @Override 13 public void execute(Coupon coupon) { 14 // 第二种具体算法 15 } 16 17 }
3:我们可以根据coupon选择不同的算法。这里我们可以采用工厂模式
1 @Service 2 public class CouponStrategyFactory { 3 4 5 private Map<String, CouponStrategy> serviceMap = new HashMap<>(); 6 7 8 public CouponStrategy getService(CouponInfo couponInfo) { 9 10 CouponStrategy couponStrategy = serviceMap.getOrDefault(coupon.getCouponType(), couponDefault); 11 12 return couponStrategy; 13 } 14 15 }
最后,这种算法看起来是非常的干净整洁舒服的。比如之前很多type=1,则算法A。type=2则算法B。代码虽然实现,但看起来很难受。
并且策略模式让你能将不同行为抽取到一个独立类层次结构中, 并将原始类组合成同一个, 从而减少重复代码。比如我可以把A,B,C算法重复的都抽象到interface接口中,代码最重要一点就是避免写重复性的代码。
3:模板模式
模板方法模式是一种行为设计模式, 它在超类中定义了一个算法的框架, 允许子类在不修改结构的情况下重写算法的特定步骤。
比如我们JDK经典的ArrayList,用到的就是模板模式,我们看一下他的类图如下:
这幅图真的特别的经典,可以说是学习模板方法最好的实践了。
ArrayList继承了AbstractList并且实现了多种的接口。ArrayList只实现了自己特定的算法,其余比较通用的算法全部由AbstractList实现。
比如addAll方法, 它位于absract接口。它定义了一系列的算法,比如首先check,在for循环add等等。
但是具体怎么add他并没有实现,而是交由子类去具体实现。
1 public boolean addAll(int index, Collection<? extends E> c) { 2 rangeCheckForAdd(index); 3 boolean modified = false; 4 for (E e : c) { 5 add(index++, e); 6 modified = true; 7 } 8 return modified; 9 }
比如ArrayList的实现
1 public void add(int index, E element) { 2 rangeCheckForAdd(index); 3 4 ensureCapacityInternal(size + 1); // Increments modCount!! 5 System.arraycopy(elementData, index, elementData, index + 1, 6 size - index); 7 elementData[index] = element; 8 size++; 9 }
又比如LinkedList
1 public void add(int index, E element) { 2 checkPositionIndex(index); 3 4 if (index == size) 5 linkLast(element); 6 else 7 linkBefore(element, node(index)); 8 }
类似于子类可以通过钩子操作可以控制父类的行为,是不是很神奇。
最后,当你只希望客户端扩展某个特定算法步骤, 而不是整个算法或其结构时, 可使用模板方法模式。 模板方法将整个算法转换为一系列独立的步骤, 以便子类能对其进行扩展, 同时还可让超类中所定义的结构保持完整。
4:策略模式和模板模式同与异
这两种模式非常的相像,一不留神可以分不清楚用那种模式了。当然这就是开头所说的,心中有刀,手里已无刀,你不知不觉中已经融进你的代码中了,但你并不知已经运用了。这其实就是最棒的结果了,但对于咱们初学者,刚开始弄清楚还是有必要的。
相同点:
1: 毋庸置疑都可以减少代码的重复,将重复代码抽象到父类即可。
2: 可以很容易的切换算法,根据前端传来的参数,具体算法何种算法,何种模式。
不同点:
1:模板模式基于继承机制: 它允许你通过扩展子类中的部分内容来改变部分算法。
2:策略模式基于组合机制: 你可以通过对相应行为提供不同的策略来改变对象的部分行为。 模板方法在类层次上运作, 因此它是静态的。 策略在对象层次上运作, 因此允许在运行时切换行为。
3:模板更加看重是算法的流程,全部已经规定好了,子类可以修改部分流程算法
4:策略模式是将整个算法全部重写,不是部分,而是整体。
最后祝大家都能写出漂亮,惊艳,眼前一亮的代码。最终做到,手里无刀,心中也无。