ABAP设计模式之---“模板方法模式(Template Method Pattern)”

1.目的

定义一个操作中算法的骨架,将一些步骤延迟到子类中。

模板方法模式使得子类可以在不改变算法结构的前提下,重写算法的某些步骤。

2.解读

类比:

  • 我们的日常生活中,可以遇到很多的模板。例如,编写简历时,我们会使用一些简历模板;填写表格时,会有一个填表的参照模板;编写文章时,根据叙事方式的不同,也可参照相应的写作模板。可以说,“模板”其实充斥在我们日常生活中。
  • 想一想,模板的好处是什么?模板可以为使用者提供一个参照对象,并将某些方面固化下来,形成“套路”。这种方式简化了操作步骤,并提升了效率。在面向对象的程序世界里,我们将这个“模板”抽象成了一种设计模式,也即“模板方法模式”。

解析:

  • 模板方法模式其实是一种基于“继承”的代码复用技术,它属于行为型这一类的设计模式
  • 对于“继承”,可能大多数同学对它是又爱又恨。问什么爱它呢?因为它能很方便地实现代码的复用。为什么恨它呢?因为继承的入侵性,父类的改动会直接影响到子类,破坏了“开放-封闭原则”。同时“里式替换原则”和“合成/聚合复用原则”都在提醒我们继承带来的风险和副作用。其实,这些原则说的都是对的,关键是我们如果合理地应用这些原则,“继承”不是程序设计的禁令,关键在于平衡各个原则之间的关系,合理地使用“继承”。
  • 模板方法模式,其实就是很好地平衡了这几个原则的一个应用场景

万事万物没有绝对的好与坏,关键在于使用的人、使用的方式。把合适的人或物,放到/应用到合适的位置/场景,才是真正的高手。

类图:
ABAP设计模式之---“模板方法模式(Template Method Pattern)”_第1张图片

各种角色:

  1. Abstract Class: 提供模板的抽象类,定义并实现了一个模板方法。这个模板方法给出了一个逻辑框架,而具体逻辑的组成步骤,推迟到子类中实现
  2. Concrete Class: 模板的实现类,实现模板类的一个或多个抽象方法

要点:

  • 模板类定义的算法的框架,对于不同的场景,可以继承模板类,并在子类中实现定制化的逻辑。 模板类通常应定义为抽象类,不允许实例化,仅其具体的实现子类方可实例化。也即,应将模板仅是一个通用参照对象,具体的实际对象的实例才有具体的含义
  • 模板类中的方法,应有合理的限定。例如模板方法应当不允许重写,子类仅可重写具体的步骤实现方法;在模板方法中使用Hook Method(钩子方法),可以让子类反向控制模板方法的执行逻辑
  • 在使用时,应当尽量限定可重写方法的数量,因为随着数量的增加,代码的复杂度和子类的实现复杂度都会随之升高。同时,为了避免“继承”带来的代码侵入性,对于留给子类实现的方法应当尽可能定义为“抽象方法”,父类不要给出实现,对于钩子方法和特殊用的方法除外
  • 良好的命名规范,可以使得模板方法模式的设计变得更加清楚。例如对于留给子类实现的方法可以用do_someting( ), 对于钩子方法可以用Is_xxx( ), Is_pre_xxx( ), Is_post_xxx( )等命名

下图展示了,加入Hook Method后,模板方法的执行流程图。
ABAP设计模式之---“模板方法模式(Template Method Pattern)”_第2张图片

设计模式对比:

  1. 模板方法适用于一些复杂算法的分割,固定的部分设计为模板类,而细节实现留个子类。 当模板类中变化的部分过多时,应当考虑使用“桥接模式”替代,因为过多的变化意味着更多可能子类的实现,“桥接模式”更适合处理这种多维度变化的问题
  2. 模板方法模式同策略模式的异同:模板方法模式是通过“继承”实现的代码复用,策略模式是通过“接口”实现算法家族的定义。对客户端而言,策略模式使得客户端可独立于具体算法的变化;而模板方法模式,则要求客户端应该直接使用具体的实现子类

3. 示例代码

下面给出一个利用模板方法模式,制作三明治的例子。

三明治制作的步骤是固定的,需要加面包片,奶油,蔬菜等等,但对于不同类型的三明治,其具体的搭配可以是不同的。

在使用模板方式,应注意对于各个方法的关键字限定

REPORT ztemplate_method_pattern.

CLASS lcl_template_sandwich DEFINITION ABSTRACT CREATE PROTECTED.
  PUBLIC SECTION.
    METHODS prepare_sandwich FINAL. " do not allow to redefine

  PROTECTED SECTION.
    METHODS add_butter. " can be overwritten
    METHODS add_extra ABSTRACT. " needs to be redefined
    METHODS add_veggetables ABSTRACT. " needs to be redefined

  PRIVATE SECTION.
    METHODS slice_bread.
ENDCLASS.

CLASS lcl_template_sandwich IMPLEMENTATION.
  METHOD add_butter.
    WRITE: / 'Add thin layer of butter'.
  ENDMETHOD.

  METHOD prepare_sandwich.
    slice_bread(  ).
    add_butter(  ).
    add_extra(  ).
    add_veggetables(  ).
  ENDMETHOD.

  METHOD slice_bread.
    WRITE:/ 'Slice bread'.
  ENDMETHOD.
ENDCLASS.
**********************************************************************
CLASS lcl_cheese_sandwich DEFINITION FINAL CREATE PUBLIC
INHERITING FROM lcl_template_sandwich.
  PROTECTED SECTION.
    METHODS:
      add_butter REDEFINITION,
      add_extra REDEFINITION,
      add_veggetables REDEFINITION.
ENDCLASS.

CLASS lcl_cheese_sandwich IMPLEMENTATION.
  METHOD add_butter.
    WRITE: / 'Add thick layer of butter'.
  ENDMETHOD.

  METHOD add_extra.
    WRITE: / 'Add slices of cheese'.
  ENDMETHOD.

  METHOD add_veggetables.
    WRITE: / 'Add tomato slices'.
  ENDMETHOD.
ENDCLASS.
**********************************************************************
CLASS lcl_ham_sandwich DEFINITION FINAL CREATE PUBLIC
INHERITING FROM lcl_template_sandwich.
  PROTECTED SECTION.
    METHODS: add_extra REDEFINITION,
      add_veggetables REDEFINITION.
ENDCLASS.

CLASS lcl_ham_sandwich IMPLEMENTATION.
  METHOD add_extra.
    WRITE: / 'Add slices of ham'.
  ENDMETHOD.

  METHOD add_veggetables.
    WRITE: / 'Add salad leaves'.
    WRITE: / 'Add onions'.
  ENDMETHOD.
ENDCLASS.
**********************************************************************
START-OF-SELECTION.
**********************************************************************
  DATA lt_sandwich TYPE TABLE OF REF TO lcl_template_sandwich.
  lt_sandwich = VALUE #( ( NEW lcl_cheese_sandwich(  ) )
                         ( NEW lcl_ham_sandwich(  )    ) ).
  LOOP AT lt_sandwich INTO DATA(lo_sandwich).
    WRITE: / '...This is sandwich :' && sy-tabix && '...'.
    lo_sandwich->prepare_sandwich(  ).
  ENDLOOP.

在上例中,我们利用模板三明治,制作了奶酪三明治和火腿三明治。程序运行结果如下:
ABAP设计模式之---“模板方法模式(Template Method Pattern)”_第3张图片
下面给出一个简单的示例,展示了如何使用Hook Method钩子方法控制模板方法中的执行逻辑:

CLASS lcl_template_sandwich DEFINITION ABSTRACT CREATE PROTECTED.
  PUBLIC SECTION.
    METHODS prepare_sandwich FINAL. " do not allow to redefine

  PROTECTED SECTION.
    METHODS add_butter. " can be overwritten
    METHODS is_extra_required RETURNING VALUE(rv_yes) TYPE abap_bool. " hook method
    METHODS add_extra ABSTRACT. " needs to be redefined
    METHODS add_veggetables ABSTRACT. " needs to be redefined

  PRIVATE SECTION.
    METHODS slice_bread.
ENDCLASS.

CLASS lcl_template_sandwich IMPLEMENTATION.
  METHOD add_butter.
    WRITE: / 'Add thin layer of butter'.
  ENDMETHOD.

  METHOD prepare_sandwich.
    slice_bread(  ).
    add_butter(  ).

    IF is_extra_required(  ). " hook to control whether a specific step needs to be executed
      add_extra(  ).
    ENDIF.

    add_veggetables(  ).
  ENDMETHOD.

  METHOD slice_bread.
    WRITE:/ 'Slice bread'.
  ENDMETHOD.

  METHOD is_extra_required.
    rv_yes = abap_false.
  ENDMETHOD.
ENDCLASS.

此例中的方法 :

* METHODS is_extra_required RETURNING VALUE(rv_yes) TYPE abap_bool. *

就是一个hook method,它使得子类可以有选择地实现模板类中定义的步骤方法,并通过返回值,控制模板类中算法的执行。
.
.
.
以上,是本篇对模板方法模式的总结,欢迎分享、留言。

本博客专注于技术分享,干货满满,持续更新。
欢迎关注❤️、点赞、转发!

你可能感兴趣的:(设计模式,sap,abap,设计模式,模板方法)