0
点赞
收藏
分享

微信扫一扫

Java设计模式之——模板方法

王栩的文字 2022-02-07 阅读 65

模板方法(Template Method作为Java的设计模式之一,一个词概括其优势特点那就是:抽象步骤

接下来以一个问答的形式进行理解记录

提问1:现在你要制作一个蛋糕,你会很多种做法,我们将制作蛋糕具象化到代码中如下,此刻你突发奇想又想出一种制作方案你该怎么办?

public class Cake {
 
	public static int METHOD_1 = 1;

    public static int METHOD_2 = 2;

    public void make(int method) {
		System.out.println("make start");
		if (method == METHOD_1) {
			// 方法1
		} else if (method == METHOD_2) {
			// 方法2
		}
		System.out.println("make finish");
	}
 
}

回答1:你毫不犹豫的会说,追加一个method的static字段,以及后面增加一个if分支条件判断并执行

public class Cake {
 
	......

    public static int METHOD_3 = 3;

    public void make(int method) {
		System.out.println("make start");
		......
		} else if (method == METHOD_3) {
			// 方法2
		}
        ......
		System.out.println("make finish");
	}
 
}

问题2:这确实是一个解决方案,但是你有没有考虑过如果一个if里面有百余行,而且你又有非常多的制作怎么办,怎么解决冗长问题呢?

也许你会想我就加呗,反正我都熟悉。但是如果你不是重头写代码的人,你拿到这个类的时候就有N多种策略超过千行的代码,你是否会吐槽怎么全堆在一起了呢?如果你是一个优秀设计者或leader,review这套代码时能不想去优化吗?

为了更好的可扩展性和可读性我们可以这么做:

首先我们应该抽出共通的东西做一个父类(Base类),其次具体的蛋糕制作由子类进一步实现,每一种制作方式我们就拓展一个子类

仔细观察Cake类,你会发现make方法是这个类的核心,make()中便是执行制作的核心代码,为了尽可能将共通的东西都留在父类(base类),我们要进一步将make()中的代码拆分,让其变成众多子步骤方法如下:

public abstract class Cake {

    final void make() {
        makeStart();//log
        makingCakeGerm();//做蛋糕胚
        makingCream();//制作奶油
        wipeCakeGerm();//抹蛋糕胚
        piping();//裱花
        makeFinish();//log
    }

    private void makeStart() {
        System.out.println("make start");
    }

    protected abstract String makingCakeGerm();

    protected abstract String makingCream();

    protected abstract String wipeCakeGerm();

    protected abstract String piping();

    private void makeFinish() {
        System.out.println("make finish");
    }
}

我们可以看到已经把make方法中的步骤进一步拆分细化,有先做蛋糕胚,再做奶油,然后涂抹到蛋糕胚上,最后裱花

我们来注意几个细节:

1、cake类成为了抽象类,没啥好说的,方法都抽象了

2、make方法增加了final修饰,不希望子类覆写这个方法,防止流程被更改

3、步骤抽象化,这是模板方法的核心,步骤的具体实现交由子类自行实现,每一种蛋糕都可以用不同的子类独立实现,这样可读性就大大提高了,而且扩展也非常方便,只需要实现一个新的子类即可

接下来我们实现具体的蛋糕子类:

我们这里可以这样想,蛋糕胚基本上没什么差别,抹奶油也都是常规操作,不一样的是制作什么样的奶油以及裱花的样式

public abstract class Cake {

    final void make() {
        makeStart();//log
        makingCakeGerm();//做蛋糕胚
        makingCream();//制作奶油
        wipeCakeGerm();//抹蛋糕胚
        piping();//裱花
        makeFinish();//log
    }

    private void makeStart() {
        System.out.println("make start");
    }

    private void makingCakeGerm() {
        System.out.println("making cake germ");
    }

    protected abstract void makingCream();//内容不共通的方法,留在子类实现

    private void wipeCakeGerm() {
        System.out.println("wipe cake germ");
    }

    protected abstract void piping();//内容不共通的方法,留在子类实现

    private void makeFinish() {
        System.out.println("make finish");
    }

}

子类实现蛋糕A蛋糕B

public class Cake_A extends Cake {

    @Override
    public void makingCream() {
        System.out.println("植物奶油 抹茶味");
    }

    @Override
    public void piping() {
        System.out.println("裱花八朵");
    }
}

/******************************************/

public class Cake_B extends Cake {

    @Override
    public void makingCream() {
        System.out.println("动物奶油 巧克力味");
    }

    @Override
    public void piping() {
        System.out.println("裱个生日快乐");
    }
}

最后Main类执行:

public class Test{

  public static void main(String[] args){
      Cake_A a = new Cake_A();
      Cake_B b = new Cake_B();
      a.make(); 
      System.out.println("******************");
      b.make(); 
  }   
}

看一下输出结果:

I/System.out: make start
I/System.out: 制作蛋糕胚
I/System.out: 植物奶油 抹茶味
I/System.out: 将奶油抹到蛋糕胚上
I/System.out: 裱花八朵
I/System.out: make finish
I/System.out: ******************
I/System.out: make start
I/System.out: 制作蛋糕胚
I/System.out: 动物奶油 巧克力味
I/System.out: 将奶油抹到蛋糕胚上
I/System.out: 裱个生日快乐
I/System.out: make finish

总结一下优缺点:

优点:

  • 复用性增强,可读性增强
    将相同部分的代码放在抽象的父类中,部分实现代码在子类中完成
  • 扩展性增强
    通过增加子类来设计新的行为,一子类一行为
  • 遵守了开闭原则,并通过父类调用其子类的扩展行为,实现了反向控制

缺点:

       缺点很显然了,你如果拥有海量的蛋糕设计方法,那你就要实现非常多个子类,那系统实现的复杂度也会变高(但总比写一起强)

应用场景:

       在逻辑设计时,将不变(也称共通)的逻辑做在父类中,变化的逻辑以抽象方法的形式在父类中,由子类后续继承实现,且后续的改动和拓展均应由子类完成,维持父类不动

----------------------------------------------------------

解答时间:

问:有人会说测试类应该是父类型引用指向子类对象,然后调用父类型变量中的.make()方法吧?

答:显然没必要,这种设计模式的核心思想不在多态上,向上转型诚然也可以实现,但是模板方法的核心在于抽象步骤,公共复用,所以用不用向上转型影响不大

问:有人学习了策略模式后会感到疑惑,这和模板方法也太像了吧?

答:不要怀疑确实很像!区别就是策略模式用接口实现算法的扩展性 模板方法模式用继承的方式实现方法的扩展性

到此模板方法就差不多分享完了,但是这就完了?

模板方法还有一个点睛之笔,未完待续......

举报

相关推荐

0 条评论