设计模式学习之模版方法模式

2024-06-24 05:32

本文主要是介绍设计模式学习之模版方法模式,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

模板方法模式是一种基于继承的代码复用的行为型模式;在其结构中只存在父类与子类之间的继承关系。通过使用模板方法模式,可以将一些复杂流程的实现步骤封装在一系列基本方法中,在抽象父类中提供一个称之为模板方法的方法来定义这些基本方法的执行次序,而通过其子类来覆盖某些步骤,从而使得相同的算法框架可以有不同的执行结果。本篇博客我们一起来学习模版方法模式。

定义与UML图

定义
模板方法模式:定义一个操作中算法的框架,而将一些步骤延迟到子类中。模板方法模式使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

ULM图
这里写图片描述

模板方法模式包含如下两个角色:
(1) AbstractClass(抽象类):在抽象类中定义了一系列基本操作(PrimitiveOperations),这些基本操作可以是具体的,也可以是抽象的,每一个基本操作对应算法的一个步骤,在其子类中可以重定义或实现这些步骤。同时,在抽象类中实现了一个模板方法(Template Method),用于定义一个算法的框架,模板方法不仅可以调用在抽象类中实现的基本方法,也可以调用在抽象类的子类中实现的基本方法,还可以调用其他对象中的方法。
(2) ConcreteClass(具体子类):它是抽象类的子类,用于实现在父类中声明的抽象基本操作以完成子类特定算法的步骤,也可以覆盖在父类中已经实现的具体基本操作。

对于模板方法模式,父类提供的构建步骤和顺序或者算法骨架,通常是不希望甚至是不允许子类去覆盖的所以在某些场景中,可以直接将父类中提供骨架的方法声明为final类型。

模版方法模式的例子

模版方法模式可以说在我们项目中随处可见,最常见的就是我们平时写的各种Base类,BaseActivity,BaseFragment等,说到模板方法模式,ClassLoader类就使用了模板模式,去保证类加载过程中的唯一性

public class ClassLoader {//这是一个重载方法public Class<?> loadClass(String name) throws ClassNotFoundException {return loadClass(name, false);}//父类算法的定义protected synchronized Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException {Class c = findLoadedClass(name);if (c == null) {try {if (parent != null) {c = parent.loadClass(name, false);} else {c = findBootstrapClass0(name);}} catch (ClassNotFoundException e) {c = findClass(name);}}if (resolve) {resolveClass(c);}return c;}//这里留了一个方法给子类选择性覆盖protected Class<?> findClass(String name) throws ClassNotFoundException {throw new ClassNotFoundException(name);}}

上面中只是展示了ClassLoader中与模版模式相关内容,从上面的代码中,可以看出,findClass这个方法,并不是必须实现的,所以ClassLoader选择留给程序员们自己选择是否要覆盖。
ClassLoader中定义的算法顺序是:
1,首先看是否有已经加载好的类。
2,如果父类加载器不为空,则首先从父类类加载器加载。
3,如果父类加载器为空,则尝试从启动加载器加载。
4,如果两者都失败,才尝试从findClass方法加载。

这是ClassLoader的双亲委派模型,即先从父类加载器加载,直到继承体系的顶层,否则才会采用当前的类加载器加载。这样做的目的是为了JVM中类的一致性。

无疑 ClassLoader 中就定义了模版方法,而ClassLoader 的子类BaseDexClassLoader中就覆盖了ClassLoader 的findClass方法

@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {Class clazz = pathList.findClass(name);if (clazz == null) {throw new ClassNotFoundException(name);}return clazz;
}

Android中的模版方法模式

在Android源码中,View中的Draw()方法就是一个“模板方法”。它定义了一系列“Draw”过程,主要包括这几个步骤:

 /** Draw traversal performs several drawing steps which must be executed* in the appropriate order:**      1. Draw the background*      2. If necessary, save the canvas' layers to prepare for fading*      3. Draw view's content*      4. Draw children*      5. If necessary, draw the fading edges and restore layers*      6. Draw decorations (scrollbars for instance)*/
 // Step 1, draw the background, if neededint saveCount;if (!dirtyOpaque) {drawBackground(canvas);}// skip step 2 & 5 if possible (common case)final int viewFlags = mViewFlags;boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;if (!verticalEdges && !horizontalEdges) {// Step 3, draw the contentif (!dirtyOpaque) onDraw(canvas);// Step 4, draw the childrendispatchDraw(canvas);// Overlay is part of the content and draws beneath Foregroundif (mOverlay != null && !mOverlay.isEmpty()) {mOverlay.getOverlayView().dispatchDraw(canvas);}// Step 6, draw decorations (foreground, scrollbars)onDrawForeground(canvas);// we're done...return;}

其中第三步( Step 3)Draw view’s content函数:


/*** Implement this to do your drawing.** @param canvas the canvas on which the background will be drawn*/
protected void onDraw(Canvas canvas) {
}

第四步( Step 4) draw children

/*** Called by draw to draw the child views. This may be overridden* by derived classes to gain control just before its children are drawn* (but after its own view has been drawn).* @param canvas the canvas on which to draw the view*/
protected void dispatchDraw(Canvas canvas) {
}

从View的Draw()“模板方法”可以看出,当继承View子类中,如果要重写或者扩展这个方法时,整个方法流程和基本内容不能够修改,子类只能通过扩展onDraw(Canvas canvas)和dispatchDraw(Canvas canvas)两个函数,使子类自己的View显示效果和别的具体子类的不同。

我们可以看到,在TextView类中就重写了onDraw方法

    @Overrideprotected void onDraw(Canvas canvas) {if (mPreDrawState == PREDRAW_DONE) {final ViewTreeObserver observer = getViewTreeObserver();observer.removeOnPreDrawListener(this);mPreDrawState = PREDRAW_NOT_REGISTERED;}if (mCurrentAlpha <= ViewConfiguration.ALPHA_THRESHOLD_INT) return;restartMarqueeIfNeeded();// Draw the background for this viewsuper.onDraw(canvas);final int compoundPaddingLeft = getCompoundPaddingLeft();final int compoundPaddingTop = getCompoundPaddingTop();final int compoundPaddingRight = getCompoundPaddingRight();final int compoundPaddingBottom = getCompoundPaddingBottom();final int scrollX = mScrollX;final int scrollY = mScrollY;final int right = mRight;final int left = mLeft;final int bottom = mBottom;final int top = mTop;final Drawables dr = mDrawables;if (dr != null) {/** Compound, not extended, because the icon is not clipped* if the text height is smaller.
.......
}

Android中的AsyncTask也典型的模板方法模式

private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {@Overrideprotected void onPreExecute() {super.onPreExecute();}protected Bitmap doInBackground(String... urls) {return loadImageFromNetwork(urls[0]);}@Overrideprotected void onProgressUpdate(Void... values) {super.onProgressUpdate(values);}protected void onPostExecute(Bitmap result) {mImageView.setImageBitmap(result);}@Overrideprotected void onCancelled() {super.onCancelled();}}

继承AsyncTask的时候只需要根据需要重写上面几个方法就可以,它们就是AsyncTask类的可变部分,我们在子类中只需要实现可变部分就可以了,不变部分AsyncTask已经实现了,所以我们只需要根据这个模板进行使用就行。

总结模版方法模式的适用情况:
(1)一次性实现一个算法的不变部分,并将可变的行为留给子类去实现。
(2)各个子类中公共的行为应该被提取出来并且集中到一个公共的父类中去,这样避免了代码的重复。
(3)扩展子类的扩展。模板方法只在特定点调用操作,这样就只允许在这些点进行扩展。

这篇关于设计模式学习之模版方法模式的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1089294

相关文章

51单片机学习记录———定时器

文章目录 前言一、定时器介绍二、STC89C52定时器资源三、定时器框图四、定时器模式五、定时器相关寄存器六、定时器练习 前言 一个学习嵌入式的小白~ 有问题评论区或私信指出~ 提示:以下是本篇文章正文内容,下面案例可供参考 一、定时器介绍 定时器介绍:51单片机的定时器属于单片机的内部资源,其电路的连接和运转均在单片机内部完成。 定时器作用: 1.用于计数系统,可

问题:第一次世界大战的起止时间是 #其他#学习方法#微信

问题:第一次世界大战的起止时间是 A.1913 ~1918 年 B.1913 ~1918 年 C.1914 ~1918 年 D.1914 ~1919 年 参考答案如图所示

[word] word设置上标快捷键 #学习方法#其他#媒体

word设置上标快捷键 办公中,少不了使用word,这个是大家必备的软件,今天给大家分享word设置上标快捷键,希望在办公中能帮到您! 1、添加上标 在录入一些公式,或者是化学产品时,需要添加上标内容,按下快捷键Ctrl+shift++就能将需要的内容设置为上标符号。 word设置上标快捷键的方法就是以上内容了,需要的小伙伴都可以试一试呢!

AssetBundle学习笔记

AssetBundle是unity自定义的资源格式,通过调用引擎的资源打包接口对资源进行打包成.assetbundle格式的资源包。本文介绍了AssetBundle的生成,使用,加载,卸载以及Unity资源更新的一个基本步骤。 目录 1.定义: 2.AssetBundle的生成: 1)设置AssetBundle包的属性——通过编辑器界面 补充:分组策略 2)调用引擎接口API

Javascript高级程序设计(第四版)--学习记录之变量、内存

原始值与引用值 原始值:简单的数据即基础数据类型,按值访问。 引用值:由多个值构成的对象即复杂数据类型,按引用访问。 动态属性 对于引用值而言,可以随时添加、修改和删除其属性和方法。 let person = new Object();person.name = 'Jason';person.age = 42;console.log(person.name,person.age);//'J

大学湖北中医药大学法医学试题及答案,分享几个实用搜题和学习工具 #微信#学习方法#职场发展

今天分享拥有拍照搜题、文字搜题、语音搜题、多重搜题等搜题模式,可以快速查找问题解析,加深对题目答案的理解。 1.快练题 这是一个网站 找题的网站海量题库,在线搜题,快速刷题~为您提供百万优质题库,直接搜索题库名称,支持多种刷题模式:顺序练习、语音听题、本地搜题、顺序阅读、模拟考试、组卷考试、赶快下载吧! 2.彩虹搜题 这是个老公众号了 支持手写输入,截图搜题,详细步骤,解题必备

如何开启和关闭3GB模式

https://jingyan.baidu.com/article/4d58d5414dfc2f9dd4e9c082.html

C++必修:模版的入门到实践

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ 🎈🎈养成好习惯,先赞后看哦~🎈🎈 所属专栏:C++学习 贝蒂的主页:Betty’s blog 1. 泛型编程 首先让我们来思考一个问题,如何实现一个交换函数? void swap(int& x, int& y){int tmp = x;x = y;y = tmp;} 相信大家很快就能写出上面这段代码,但是如果要求这个交换函数支持字符型

电脑不小心删除的文件怎么恢复?4个必备恢复方法!

“刚刚在对电脑里的某些垃圾文件进行清理时,我一不小心误删了比较重要的数据。这些误删的数据还有机会恢复吗?希望大家帮帮我,非常感谢!” 在这个数字化飞速发展的时代,电脑早已成为我们日常生活和工作中不可或缺的一部分。然而,就像生活中的小插曲一样,有时我们可能会在不经意间犯下一些小错误,比如不小心删除了重要的文件。 当那份文件消失在眼前,仿佛被时间吞噬,我们不禁会心生焦虑。但别担心,就像每个问题

《offer来了》第二章学习笔记

1.集合 Java四种集合:List、Queue、Set和Map 1.1.List:可重复 有序的Collection ArrayList: 基于数组实现,增删慢,查询快,线程不安全 Vector: 基于数组实现,增删慢,查询快,线程安全 LinkedList: 基于双向链实现,增删快,查询慢,线程不安全 1.2.Queue:队列 ArrayBlockingQueue: