人物:大鸟,小菜
事件:小菜和大鸟探讨学习,回忆小时候抄黑板知识点时,容易把题目抄错,说着说着大鸟就引入了模板方法模式
模板方法模式:
1.不用设计模式直接设计(结合事件)
2.用模板方法模式去设计(结合事件)
3.看有什么地方继续改进(结合事件)
4.为模板方式模式做小结
大鸟让小菜设计:一个粗心的学生和一个细心的学生去抄同一份试卷
小菜给出的第一版
学生a抄好的试卷:
@Slf4j public class TestPaperA { /** * 试题1 */ public void testQuestion1() { log.info("哥尔·D·罗杰开启了大航海时代,最后的秘宝叫[], a.超级宝藏 b.One Piece c.永恒黄金 d.荣耀之石"); log.info("答案:b"); } /** * 试题2 */ public void testQuestion2() { log.info("下面哪一个不是真的D:[], a.蒙奇·D·路飞 b.波特卡斯·D·罗杰 c.马歇尔·D·蒂奇 d.特拉法尔加·D·瓦铁尔·罗"); log.info("答案:b"); } /** * 试题3 */ public void testQuestion3() { log.info("本博主磕哪一对cp:[],a.佐罗和香吉士 b.香吉士和娜美 c.佐罗和罗宾 d.乔巴和佐罗 "); log.info("答案:c"); } }
学生b抄好的试卷
@Slf4j public class TestPaperB { /** * 试题1 */ public void testQuestion1() { log.info("哥尔·D·罗杰开启了大航海时代,最后的秘宝叫[], a.永恒钻石 b.哇塞 c.永恒黄金 d.大秘宝"); log.info("答案:d"); } /** * 试题2 */ public void testQuestion2() { log.info("下面哪一个不是真的D:[], a.蒙奇·D·罗杰 b.波特卡斯·D·艾斯 c.马歇尔·D·蒂奇 d.特拉法尔加·D·瓦铁尔·罗"); log.info("答案:a"); } /** * 试题3 */ public void testQuestion3() { log.info("本博主磕哪一对cp:[],a.佐罗和香吉士 b.路飞和娜美 c.佐罗和罗宾 d.乔巴和佐罗 "); log.info("答案:b"); } }
客户端代码:
@Slf4j public class TestPaper { public static void main(String[] args) { log.info("学生甲抄的试卷: "); TestPaperA studentA = new TestPaperA(); studentA.testQuestion1(); studentA.testQuestion2(); studentA.testQuestion3(); log.info("学生乙抄的试卷: "); TestPaperB studentB = new TestPaperB(); studentB.testQuestion1(); studentB.testQuestion2(); studentB.testQuestion3(); } }
大鸟:你看现在抄出来的两份试卷大体一致,但有很多细节上的地方不一样,而且非常难以维护,为了防止有人抄错,可以抽象一个父类,让子类继承它,然后公共试题代码写到父类中即可
小菜结合模板方法模式的第一版
公共试题部分:
@Slf4j public class TestPaper { /** * 试题1 */ public void testQuestion1() { log.info("哥尔·D·罗杰开启了大航海时代,最后的秘宝叫[], a.超级宝藏 b.One Piece c.永恒黄金 d.荣耀之石"); } /** * 试题2 */ public void testQuestion2() { log.info("下面哪一个不是真的D:[], a.蒙奇·D·路飞 b.波特卡斯·D·罗杰 c.马歇尔·D·蒂奇 d.特拉法尔加·D·瓦铁尔·罗"); } /** * 试题3 */ public void testQuestion3() { log.info("本博主磕哪一对cp:[],a.佐罗和香吉士 b.香吉士和娜美 c.佐罗和罗宾 d.乔巴和佐罗 "); } }
学生a和学生b的答题:
@Slf4j public class TestPaperA extends TestPaper{ /** * 试题1 */ @Override public void testQuestion1() { super.testQuestion1(); log.info("答案:b"); } /** * 试题2 */ @Override public void testQuestion2() { super.testQuestion2(); log.info("答案:b"); } /** * 试题3 */ @Override public void testQuestion3() { super.testQuestion3(); log.info("答案:b"); } }
@Slf4j public class TestPaperB extends TestPaper{ /** * 试题1 */ @Override public void testQuestion1() { super.testQuestion1(); log.info("答案:b"); } /** * 试题2 */ @Override public void testQuestion2() { super.testQuestion2(); log.info("答案:b"); } /** * 试题3 */ @Override public void testQuestion3() { super.testQuestion3(); log.info("答案:c"); } }
大鸟:虽然这样只填写答案即可,但是这只是初步的泛华,其实除了选项abcd,其他的都是重复的。我们既然用了继承,并且肯定这个继承有意义,就应该要成为子类的模板,所有重复的代码都应该要上升到父类去,而不是让每个子类都去重复
小菜:那应该怎么做呢?
大鸟:这时就要用到模板方法模式了,当我们要完成在某一细节层次,一致的一个过程或一系列步骤,但其个别步骤在更详细的层次上的实现可能不同时,我们通常考虑用模板方法模式来处理
小菜结合模板方法模式的第二改进版
试题卷(父类):
@Slf4j public abstract class TestPaper { private String answer; /** * 试题1 */ public void testQuestion1() { log.info("哥尔·D·罗杰开启了大航海时代,最后的秘宝叫[], a.超级宝藏 b.One Piece c.永恒黄金 d.荣耀之石"); log.info("答案为:{}", answer1()); } public abstract String answer1(); /** * 试题2 */ public void testQuestion2() { log.info("下面哪一个不是真的D:[], a.蒙奇·D·路飞 b.波特卡斯·D·罗杰 c.马歇尔·D·蒂奇 d.特拉法尔加·D·瓦铁尔·罗"); log.info("答案为:{}", answer2()); } public abstract String answer2(); /** * 试题3 */ public void testQuestion3() { log.info("本博主磕哪一对cp:[],a.佐罗和香吉士 b.香吉士和娜美 c.佐罗和罗宾 d.乔巴和佐罗 "); log.info("答案为:{}", answer3()); } public abstract String answer3(); }
学生a,b自己答题:
@Slf4j public class TestPaperA extends TestPaper{ /** * 试题1 */ @Override public String answer1() { return "b"; } /** * 试题2 */ @Override public String answer2() { return "b"; } /** * 试题3 */ @Override public String answer3() { return "c"; } }
@Slf4j public class TestPaperB extends TestPaper{ /** * 试题1 */ @Override public String answer1() { return "b"; } /** * 试题2 */ @Override public String answer2() { return "b"; } /** * 试题3 */ @Override public String answer3() { return "d"; } }
客户端:
@Slf4j public class TestPaperLog { public static void main(String[] args) { log.info("学生甲抄的试卷: ");
//这里将子类改为了父类TestPaper,这里就可以用多态性实现代码的复用了 TestPaper studentA = new TestPaperA(); studentA.testQuestion1(); studentA.testQuestion2(); studentA.testQuestion3(); log.info("学生乙抄的试卷: "); TestPaper studentB = new TestPaperB(); studentB.testQuestion1(); studentB.testQuestion2(); studentB.testQuestion3(); } }
模板方法模式小结:
1.概念:它定义了一个操作中的算法骨架,而将一些步骤延迟到子类中。模板方法模式可以使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤
2.优点:最大优点为去重复代码
(1)封装不变部分,扩展可变部分
(2)提取公共代码,便于维护
(3)行为由父类控制,子类实现