zoukankan      html  css  js  c++  java
  • 设计模式:模板方法

      模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。

      场景,对于咖啡来说,我们需要把水煮沸,然后用沸水冲泡咖啡,再把咖啡倒入杯子中,最后加入糖和牛奶,对于茶而言,也是先把水煮沸,用沸水去浸泡茶叶,在把茶倒入杯子中,最后加上柠檬。我们可以看这两种方法其实都采用了相同算法,1:把水煮沸 2:用沸水泡咖啡或茶 3:把咖啡因饮料倒入杯子中 4:在杯子中加入相应调料。现在我们用代码去实现看看

      首先先创建一个咖啡因饮料的抽象类

    //定义一个咖啡因饮料的抽象类
    public abstract class CaffeineBeverage {
        
        //浸泡茶和冲泡咖啡其实差不多,我们这就用brew方法来表示
        //声明为final是因为我们不希望子类覆盖这算法型的方法。我们将步骤2和4泛化成方法brew和addCondiments。
        //这里prepareRecipe是我们的模板方法(1:这是一个方法,2:它用作一个方法的模板,这里是制作咖啡因饮料的),在这个模板中,算法内每一个步骤都被一个方法代表了,其中某些方法在这个类中处理,某些需要子类实现
        final void prepareRecipe(){
            boilWater();
            brew();
            pourInCup();
            addCondiments();
        }
        
        abstract void brew();
        abstract void addCondiments();
        
        void boilWater(){
            System.out.println("1:把水煮沸开来");
        }
        
        void pourInCup(){
            System.out.println("3:到入杯子中");
        }
    }

      然后创建茶和咖啡类去实现这个抽象类

    //茶继承咖啡因饮料这抽象类,并实现自己的冲泡和添加调料的方法
    public class Tea extends CaffeineBeverage{
    
        @Override
        void brew() {
            System.out.println("2:将茶浸泡在沸水中");
        }
    
        @Override
        void addCondiments() {
            System.out.println("4:加入柠檬");
        }
    }
    
    //咖啡继承咖啡因饮料这抽象类,并实现自己的冲泡和添加调料的方法
    public class Coffee extends CaffeineBeverage{
    
        @Override
        void brew() {
            System.out.println("2:用沸水冲泡咖啡");
        }
    
        @Override
        void addCondiments() {
            System.out.println("4:加入糖和牛奶");
        }
    }

      最后我们进行测试

    public class Test {
        public static void main(String[] args) {
            //测试茶的制作过程
            Tea tea = new Tea();
            tea.prepareRecipe();
            
            System.out.println("----");
            
            //测试咖啡的制作过程
            Coffee coffee = new Coffee();
            coffee.prepareRecipe();
        }
    }

      运行结果如下:

          

      好了,这就是一个简单的模板方法模式了,我们总结下怎么定义抽象类的,首先抽象类是作为基类的,子类必须实现其操作,然后模板方法被声明了final,以免子类改变这个算法的内容,在模板方法中定义了一连串的步骤,每个步骤有一个方法代表。

      最后,在讲下在模板方法中比较常用的“钩子”,什么是模板方法中的“钩子”?其实就是被声明在抽象类中的方法,但只有空的或者默认的实现。钩子的存在可以让子类有能力对算法的不同点进行挂钩。下面我们将上面场景加入钩子,用钩子来询问客户是否需要加入调料。

      抽象类代码如下:

    public abstract class CaffeineBeverage {
        
        final void prepareRecipe(){
            boilWater();
            brew();
            pourInCup();
            if(customerWantsCondiments()){
                addCondiments();
            }
        }
        
        abstract void brew();
        abstract void addCondiments();
        
        void boilWater(){
            System.out.println("1:把水煮沸开来");
        }
        
        void pourInCup(){
            System.out.println("3:到入杯子中");
        }
        
        //这就是钩子,默认是空的实现,这里返回true不做任何事情,子类可以视情况决定要不要覆盖它们
        boolean customerWantsCondiments(){
            return true;
        }
    }

      接着定义的两个实现类如下

    //茶类,覆盖了使用了钩子方法
    public class TeaWithHook extends CaffeineBeverage {
    
        @Override
        void brew() {
            System.out.println("2:将茶浸泡在沸水中");
        }
    
        @Override
        void addCondiments() {
            System.out.println("4:加入柠檬");
        }
        
        public boolean customerWantsCondiments(){
            String answer = UserInput();
            if(answer.toLowerCase().startsWith("y")){
                return true;
            }
            else{
                System.out.println("不加入任何东西");
                return false;
            }
        }
        
        public String UserInput(){
            String answer = null;
            
            System.out.println("你想在茶中加入柠檬吗?(y/n)");
            
            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
            try{
                answer = reader.readLine();
            }catch(IOException e){
                System.err.println("输入错误!");
            }
            return answer;
        }
    }
    
    //咖啡类,覆盖了使用了钩子方法
    public class CoffeeWithHook extends CaffeineBeverage {
    
        @Override
        void brew() {
            System.out.println("2:用沸水冲泡咖啡");
        }
    
        @Override
        void addCondiments() {
            System.out.println("4:加入糖和牛奶");
        }
        
        public boolean customerWantsCondiments(){
            String answer = UserInput();
            if(answer.toLowerCase().startsWith("y")){
                return true;
            }
            else{
                System.out.println("不加入任何东西");
                return false;
            }
        }
        
        public String UserInput(){
            String answer = null;
            
            System.out.println("你想在咖啡中加入糖和牛奶吗?(y/n)");
            
            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
            try{
                answer = reader.readLine();
            }catch(IOException e){
                System.err.println("输入错误!");
            }
            return answer;
        }
    }

      最后我们进行钩子的测试类编写

    public class Test2 {
        public static void main(String[] args) {
            TeaWithHook teaWithHook = new TeaWithHook();
            teaWithHook.prepareRecipe();
            
            System.out.println("-----");
            CoffeeWithHook coffeeWithHook = new CoffeeWithHook();
            coffeeWithHook.prepareRecipe();
        }
        
    }

      运行结果如下

          

      好了,这就是模板方法中简单的钩子使用了。

      让我们区别下模板方法、策略、工厂方法模式的区别吧,模板方法是子类决定如何实现算法中的某些步骤,策略模式是封装可互换的行为,然后使用委托来决定采用哪一个行为,工厂方法是由子类决定实例化哪个具体类。

      使用原则:好莱坞原则,别调用我们,我们会调用你。在此场景中,高层组件CaffeineBeverage控制冲泡的算法,只有在需要子类实现某个方法时才调用子类,如果Tea和Coffee没有先被调用,绝对不会直接调用抽象类的。

      

      下一节:迭代器模式

    作者:哀&RT
    出处:博客园哀&RT的技术博客--http://www.cnblogs.com/Tony-Anne/
    您的支持是对博主最大的鼓励,感谢您的认真阅读。
    本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    HCIA_R&S-学习_Day05(PPPoE、DHCP、ACL、NAT)
    HCIA_R&S-学习_Day04(链路状态协议OSPF & PPP)
    HCIA_R&S-学习_Day03(路由协议基础与实现)
    【详谈 Delta Lake 】系列技术专题 之 湖仓一体( Lakehouse )
    工作7年,我的10条经验总结
    Hologres揭秘:优化COPY,批量导入性能提升5倍+
    谈谈JVM内部锁升级过程
    如何帮用户管好云账本?阿里云数据库助力收钱吧 | 甲子光年
    重磅 | 数据库自治服务DAS论文入选全球顶会SIGMOD,领航“数据库自动驾驶”新时代
    同程旅行基于 RocketMQ 高可用架构实践
  • 原文地址:https://www.cnblogs.com/Tony-Anne/p/6516475.html
Copyright © 2011-2022 走看看