我们在进行软件开发时要想实现可维护、可扩展,就需要尽量复用代码,并且降低代码的耦合度。设计模式就是一种可以提高代码可复用性、可维护性、可扩展性以及可读性的解决方案。大家熟知的23种设计模式,可以分为创建型模式、结构型模式和行为型模式三大类。本专题着眼于实际开发过程中常用的几种设计模式,从理论和实战两个角度进行讨论和分享,力求逻辑清晰、表述简洁,帮助大家在项目中合理运用设计模式,保障代码的可靠性。
第一篇:浅析设计模式1——工厂模式
第二篇:浅析设计模式2——策略模式
第三篇:浅析设计模式3——装饰者模式
概述
大家熟知的23种设计模式,可以分为创建型模式、结构型模式和行为型模式三大类。其中,行为型模式可用于描述程序中多个类和多个对象如何协作完成复杂的任务,涉及不同对象间的职责分配、算法的抽象化。模版方法模式是一种应用广泛的行为型模式,本文将着眼于模版方法模式进行学习分享,阐述模板方法模式的核心概念和应用实践。认识和使用模板方法模式,可以帮助我们在设计代码时更好的控制算法流程,避免大量重复代码,从而有效提升编程效率。
基本概念
模版方法模式的结构相对简单,主要包含两大类:抽象类、具体类,抽象父类中首先定义好算法流程,具体的步骤细节延迟到具体子类中执行。
角色 | 关系 | 作用 |
抽象类 Abstract Class | 具体类的父类 | 定义一个抽象的模版类,给出算法的骨架,包含一个模版方法和若干个基本方法(抽象方法、具体方法、钩子方法) |
具体类 Concrete Class | 抽象构件的接口实现类 | 定义一个具体的实现类,实现抽象类中定义的抽象方法和钩子方法。 |
有了上述的基本概念,我们将装饰者模式的使用步骤概括为:
step1:创建抽象类,定义一个算法骨架,包含一个模版方法和若干个基本方法:
模版方法:算法流程,定义基本方法的执行顺序;
基本方法 - 具体方法:在抽象类中实现,可被具体类继承或重写;
基本方法 - 抽象方法:在抽象类中声明,由具体类实现;
基本方法 - 钩子方法:在抽象类中实现,包含一种用于判断的方法、一种由具体类重写的空方法。
step2:创建具体类,实现抽象类中定义的抽象方法和钩子方法;
使用示例
// 创建抽象父类,定义算法骨架 public abstract class TMAbstractClass { public final void shopOnline() { selectItems(); checkoutItems(); if(isReturnItemsHook1()) { returnItems(); } else if(isExchangeItemsHook2()) { exchangeItems(); //假设一次性换到满意的商品 confirmTheReceipt(); } else { confirmTheReceipt(); } terminateTransaction(); } protected abstract void selectItems(); protected void checkoutItems() { System.out.println("下单支付"); } public boolean isReturnItemsHook1() { return false; } protected void returnItems() { } public boolean isExchangeItemsHook2() { return false; } protected void exchangeItems() { } protected void confirmTheReceipt() { System.out.println("确认收货"); } protected void terminateTransaction() { System.out.println("终止交易"); } } // 定义具体子类1:购买商品1 public class TMConcreteClass1 extends TMAbstractClass{ @Override protected void selectItems() { System.out.println("选择商品1"); } @Override public boolean isReturnItemsHook1() { return true; } @Override protected void returnItems() { System.out.println("退货退款"); } } // 定义具体子类2:购买商品2 public class TMConcreteClass2 extends TMAbstractClass{ @Override protected void selectItems() { System.out.println("选择商品2"); } @Override public boolean isExchangeItemsHook2() { return true; } @Override protected void exchangeItems() { System.out.println("换货"); } } //客户端调用 public class userPayForItem() { public static void main(String[] args) { System.out.println("购物记录1:"); TMAbstractClass shopRecord1 = new TMConcreteClass1(); shopRecord1.shopOnline(); System.out.println(); System.out.println("购物记录2:"); TMConcreteClass2 shopRecord2 = new TMConcreteClass2(); shopRecord2.shopOnline(); } }
购物记录1: 选择商品1 下单支付 退货退款 终止交易 购物记录2: 选择商品2 下单支付 换货 确认收货 终止交易
JDK源码赏析
模板方法模式在框架源码中使用也很广泛,比如:JDK 源码中 AbstractList 抽象类、Mybatis 源码中 BaseExecutor 抽象类。本文以 AbstractList 为例,分析模版方法模式在源码中如何应用。AbstractList 是 ArrayList 的父类,也就是模版类,它包含的方法有很多,这里主要介绍一下 addAll() 方法。
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> { public boolean addAll(int index, Collection<? extends E> c) { rangeCheckForAdd(index); boolean modified = false; for (E e : c) { add(index++, e); modified = true; } return modified; } }
事实上,AbstractList 中的方法除了一些私有方法不能被子类访问,大多数方法都和 addAll() 一样,可由子类选择是否修改:如果子类需要做个性化的实现就要修改,如果不需要则直接按照父类方法的逻辑执行。
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { public boolean addAll(int index, Collection<? extends E> c) { rangeCheckForAdd(index); Object[] a = c.toArray(); int numNew = a.length; ensureCapacityInternal(size + numNew); // Increments modCount int numMoved = size - index; if (numMoved > 0) System.arraycopy(elementData, index, elementData, index + numNew, numMoved); System.arraycopy(a, 0, elementData, index, numNew); size += numNew; return numNew != 0; } }
rivate final AbstractList<E> l; private final int offset; abstract public E get(int index); public E get(int index) { rangeCheck(index); checkForComodification(); return l.get(index+offset); }
左边是父类AbstractList中的,右边是ArrayList中的方法。在父类中没有直接写出实现代码,而是让子类自己手动去实现。除此之外其实还有一个方法就是AbstractList父类AbstractCollection中的toString方法。在ArrayList中是没有的,但是平常在写代码时候,是可以直接调用的,这就是一个公共的方法。
优缺点及适用场景
-
封装不变部分, 扩展可变部分。把认为是不变部分的算法封装到父类实现, 而可变部分的则可以通过继承来继续扩展。 -
提取公共部分代码, 便于维护。 -
行为由父类控制, 子类实现。基本方法是由子类实现的, 因此子类可以通过扩展的方式增加相应的功能, 符合开闭原则。
-
子类执行结果影响父类结果,这违背了我们平时设计代码的习惯,在复杂项目中,很可能会带来阅读上的难度。 -
可能引起子类泛滥、为了继承而继承的问题。
-
算法的整体步骤相对固定、其中个别方法容易变化时,这时候可以使用模板方法模式,将易变部分抽象出来,供子类实现。 -
当多个子类存在公共行为时,可以将其提取出来并集中到一个公共父类中以避免代码重复。 -
当需要控制子类的扩展时,模板方法只在特定点调用钩子操作,即可进行个性化的功能扩展。
总结
本文仅供学习!所有权归属原作者。侵删!文章来源: 大淘宝技术
文章评论