• 设计模式12-行为型设计模式-模板设计模式


    编程是一门艺术,大批量的改动显然是非常丑陋的做法,用心的琢磨写的代码让它变的更美观。

    在面向对象程序设计过程中,常常会遇到这种情况:设计一个系统时知道了算法所需的关键步骤,而且确定了这些步骤的执行顺序,但某些步骤的具体实现还未知,或者说某些步骤的实现与具体的环境相关。未知的步骤可以延迟到子类中实现。可以将固定的步骤设计一个模板设计在父类,未知的操作设置一个抽象方法,让子类去实现具体的操作。

    1.模板设计模式定义

    模板方法(Template Method)模式:定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤,它是一种类行为型设计模式。

    2.模板设计模式有点与不足

    该模式的主要优点:

    • 它封装了不变部分,扩展可变部分。它把认为是不变部分的算法封装到父类中实现,而把可变部分算法由子类继承实现,便于子类继续扩展。

    • 它在父类中提取了公共的部分代码,便于代码复用。

    • 部分方法是由子类实现的,因此子类可以通过扩展方式增加相应的功能,符合开闭原则。

    该模式存在的不足:

    • 对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象,间接地增加了系统实现的复杂度。

    • 父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度。

    • 由于继承关系自身的缺点,如果父类添加新的抽象方法,则所有子类都要改一遍。

    3.模板设计模式的实现思路

    根据需求整理出算法的执行步骤,将不变的固定的步骤实现在父类,可变的定义成抽象方法,让子类去实现,不过抽象方法的执行步骤需要在父类定义好。

    4.模板设计模式使用实例

    场景介绍:购买商品,到收银台结算,整个流程经历:汇总商品费用,处理优惠活动,进行支付,但是支付可以有多个方式:现金、网络与代付,但是支付方式是用户选择的,其他的流程是一样的,所以可以使用模板设计模式,将不变的流程设计成固定的模式,将未知的步骤设计成抽象方法,让子类实现类去实现就可以。

    /**
     * 模板方法模式:真个算法过程是固定的,但是有一些步骤是未知的,需要延迟到子类去实现,需要延迟到子类的需要定义为抽象方法,这就是模板设计模式的
     * 购物车费用结算过程
     *
     * 结算的方式是用户的行为  这个只有当用户真正结算的时候才只能得知,所以需要延迟到子类去实现
     */
    public abstract class ShoppingCart {
        /**
         * 折扣
         */
        private Discount discount;
        /**
         * 购买水果集合
         */
        private List<Fruit> products = new ArrayList<>();
    
        public ShoppingCart(List<Fruit> products){
            this.products = products;
        }
        /**
         * 注入不同的优惠方案
         * @param discount
         */
        public void setDiscount(Discount discount) {
            this.discount = discount;
        }
        /**
         * 提交订单主流程
         */
        public void submitOrder(){
            /*计算商品金额*/
            int money = balance();
            System.out.println("商品总金额为:"+money+"元");
    
            /*优惠减免*/
            money = discount.calculate(money);
            System.out.println("优惠减免后:"+ money+"元,");
    
            /*保存订单*/
            pay(money);
    
        }
    
        /**
         * 计算金额
         * @return
         */
        private int balance(){
            int money = 0;
            System.out.print("商品清单:");
            for (Fruit fruit : products){
                fruit.draw();
                System.out.print(",");
                money += fruit.price();
            }
            return money;
        }
    
    
        /**
         * 支付方式 是用户行为,是用户真正支付的时候才知道的
         * 所以需要延迟到子类去实现
         * @param money
         */
        protected abstract void pay(int money);
    
    }
    
    /**
     * 模板方法模式
     * 会员卡
     */
    public class CartShopping extends ShoppingCart{
    
        public CartShopping(List<Fruit> products) {
            super(products);
        }
    
        @Override
        protected void pay(int money) {
            System.out.println("会员卡结算,立减10,金额:"+ (money - 10)+",增加积分:"+10*money);
        }
    }
    
    /**
     * 模板方法模式
     * 现金
     */
    public class CashShopping extends ShoppingCart{
    
        public CashShopping(List<Fruit> products) {
            super(products);
        }
    
        @Override
        protected void pay(int money) {
            System.out.println("现金结算,假一罚十");
        }
    }
    
    /**
     * 模板方法模式
     * 微信支付结算
     */
    public class OnlineShopping extends ShoppingCart{
    
        private OrderService orderService = new OrderServiceImpl();
    
        public OnlineShopping(List<Fruit> products) {
            super(products);
        }
        @Override
        protected void pay(int money) {
            System.out.println("微信/支付宝结算,减免5元,请支付:"+(money - 5)+"元");
            int orderId = orderService.saveOrder();
    
        }
    }
    
     public static void main(String[] args) {
            List<Fruit> products = new ArrayList();
    
            products.add(StaticFactory.getFruitApple());
            products.add(StaticFactory.getFruitBanana());
            products.add(StaticFactory.getFruitOrange());
    
            ShoppingCart cart = new CartShopping(products);
    
            /*注入优惠方案*/
            cart.setDiscount(new FullDiscount());
    
            cart.submitOrder();
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134

    5.模板设计模式应用场景

    模板方法模式通常适用于以下场景:

    • 算法的整体步骤很固定,但其中个别部分易变时,这时候可以使用模板方法模式,将容易变的部分抽象出来,供子类实现。
    • 当多个子类存在公共的行为时,可以将其提取出来并集中到一个公共父类中以避免代码重复。首先,要识别现有代码中的不同之处,并且将不同之处分离为新的操作。最后,用一个调用这些新的操作的模板方法来替换这些不同的代码。
    • 当需要控制子类的扩展时,模板方法只在特定点调用钩子操作,这样就只允许在这些点进行扩展。
  • 相关阅读:
    全网最牛自动化测试框架系列之pytest(10)-常用执行参数说明
    React基础 (3)
    容器核心技术之Namespace与Cgroup
    SQL监控工具
    PHP中文转拼音实现
    dialog宽度无法占满手机屏幕的解决方案
    完美!字节3-1级别大佬把《数据结构与算法》讲透了,带源码笔记
    Java面试中的常问的多线程问题
    uniapp开发h5 调用微信sdk 全网最全指南!!!! 血泪史!!!
    vue实战入门后台篇五:springboot+mybatis实现网站后台-操作日志功能实现
  • 原文地址:https://blog.csdn.net/u014078003/article/details/125909570