ABAP设计模式之---“桥接模式(Bridge Pattern)”

1. 目的

将抽象部分与它的实现部分分离,使得它们都可以独立地变化。

桥接模式(Bridge)也称:柄体模式(Handle and Body)或接口模式(Interface)。

2. 解读

类比:

  • 先举一个生活中的例子,比如“蜡笔”。如果我们需要12种颜色,那么我们准备12支对应颜色的蜡笔即可。但如果我们需要大、中、小号的三种蜡笔,并且都要有12种颜色,我们可能不得不买3盒不同型号的蜡笔,并且每盒都包含12种颜色。
  • 有没有其它的思路呢?如果是毛笔呢?可不可以准备大、中、小三只毛笔,然后配上12种颜色盒呢? 两种方案有什么不同?在这个场景中,如果使用蜡笔,我们将需要准备 3 * 12, 共计36支。但若使用毛笔,则只需要准备3支 + 12种对应的颜料盒即可。哈哈,找到不同点了么?😉
  • 在这个例子中其实包含了两个维度:型号与颜色。对于蜡笔,型号与颜色是“强耦合”关系,型号与颜色的变化是绑定在一起的;但对于毛笔,型号与颜色是“弱耦合”关系,颜色维度(颜料盒)和型号(毛笔大小)维度可以独立变化。

由上面的例子,可以引出我们面向对象世界中的一个重要模式,也即桥接模式(Bridge Pattern),这个模式的核心点在于分离不同维度的变化。例如之前提及到的预留出口类模式,就是桥接模式的一个典型应用场景。在本篇中,我们将进一步阐释这个模式的内核思想。

应用场景:

  • 在某些场景中,当对象有多个维度的变化(最典型的场景就是2个维度),例如抽象/平台、前端/后端等,则可以使用桥接模式。桥接模式使得对象的其中任何一个维度都可以相互结合使用
  • 例如,不同操作系统下,对于不同图片格式的处理和显示。可以使用桥接模式,达成操作系统维度和图片处理维度的分离。
  • 再举一个实际的例子,比如,在不同国家会计准则下,企业税费的计算与报表出具,这个场景下的2个维度即“报表规范” 和“税费的计算”,分别基于这连个维度建模,并使用桥接模式让连个对象实现“聚合”,组成“松耦合”的架构。
  • 此模式的核心在于,在建模过程中,充分理解和应用“聚合/合成”原则。

类图:
在这里插入图片描述

各种角色:

  1. Abstraction - 抽象,定义了客户端可以访问的抽象接口(例如:定义的图片显示接口),并引用Implementor的实例对象。
  2. RefinedAbstraction - 被提炼的抽象,可进一步扩展和丰富Abstraction的行为(例如:实现abstraction接口,并丰富在不同操作系统下,对于显卡的驱动动作)
  3. Implementor - 实现,为具体的实现定义了抽象的接口行为。(例如:图片显示)
  4. ConcreteImplementor - 具体的实现,实现具体的功能。(例如:jpg图片的显示,png图片的显示)

注意:通常情况下,抽象Abstraction部分的接口和实现Implementor部分的接口是不同的, 抽象Abstraction的接口是面向客户端Client的,实现Implementor部分的接口是面向具体的实现对象的。

要点:

  1. “桥接模式” 的优点在于,其分离的抽象和实现之间的耦合关系,让两个维度可以独立的变化,提升的架构的灵活性。
  2. 当然,“桥接模式” 的引入增加了系统设计的复杂性,需要更多的effort。同时,这种间接的引用关系,在一定程度上也对performance带来一些影响。

体现的设计原则:

  • 单一职责原则 - 按维度分工,职责单一而清晰
  • 开放封闭原则 - 不同的维度间,可以独立扩展
  • 除此之外,桥接模式是合成/聚合复用原则的一个典型应用。“合成/聚合复用原则”告诉我们,要尽量使用合成/聚合的关系去完成设计,尽量不要使用类的继承。因为继承是一种“强依赖”关系,父类的变动会直接影响子类的变化,基于继承关系建立模型,如若使用子类时,解决不了新的问题,则父类就会需要被重写、或重新设计,这种“强依赖”关系限制了面向对象的灵活性,并最终限制了复用性。
  • “合成/聚合复用原则”的好处在于,优先使用对象的合成/聚合将有助于保持每个类的封装,并被集中到单个任务上。这样的类和类的继承层次会保持较小的规模,并且不太可能增长成为不可控制的庞然大物。

设计模式辨析:

  • 适配器模式:适配器是从一些现有组件的接口中解耦一个抽象。 相反,桥模式通过设计提前实现解耦,并考虑到未来的可扩展性。 通常,适配器和Adaptee的接口有点相似,而桥模式则并非如此。

3. 举例

3.1 桥接模式的典型代码

下面给出了“桥接模式”的架构代码,abstraction与Implementor进行了解耦合,通过合成关系(桥接)进行弱关联。

REPORT zbridge_pattern.
**********************************************************************
* abstraction per dimensions
**********************************************************************
INTERFACE lif_implementor.
  METHODS do_operation_1.
  METHODS do_operation_2.
ENDINTERFACE.

CLASS lcl_abstraction DEFINITION ABSTRACT.
  PUBLIC SECTION.
    METHODS set_implementor
      IMPORTING
        io_imp TYPE REF TO lif_implementor.
    METHODS do_action_1 ABSTRACT.
    METHODS do_action_2 ABSTRACT.
  PROTECTED SECTION.
    DATA mo_implementor TYPE REF TO lif_implementor.
ENDCLASS.

CLASS lcl_abstraction IMPLEMENTATION.
  METHOD set_implementor.
    mo_implementor = io_imp.
  ENDMETHOD.
ENDCLASS.
**********************************************************************
* implementation
**********************************************************************
CLASS lcl_implementor_a DEFINITION FINAL CREATE PUBLIC.
  PUBLIC SECTION.
    INTERFACES lif_implementor.
ENDCLASS.

CLASS lcl_implementor_a IMPLEMENTATION.
  METHOD lif_implementor~do_operation_1.
    WRITE / 'Do operation 1 in implementor A' .
  ENDMETHOD.

  METHOD lif_implementor~do_operation_2.
    WRITE / 'Do operation 2 in implementor A' .
  ENDMETHOD.
ENDCLASS.

CLASS lcl_implementor_b DEFINITION FINAL CREATE PUBLIC.
  PUBLIC SECTION.
    INTERFACES lif_implementor.
ENDCLASS.

CLASS lcl_implementor_b IMPLEMENTATION.
  METHOD lif_implementor~do_operation_1.
    WRITE / 'Do operation 1 in implementor B' .
  ENDMETHOD.

  METHOD lif_implementor~do_operation_2.
    WRITE / 'Do operation 2 in implementor B' .
  ENDMETHOD.
ENDCLASS.
**********************************************************************
CLASS lcl_refined_abstraction_x DEFINITION FINAL CREATE PUBLIC
INHERITING FROM lcl_abstraction.
  PUBLIC SECTION.
    METHODS:
      do_action_1 REDEFINITION,
      do_action_2 REDEFINITION.
ENDCLASS.

CLASS lcl_refined_abstraction_x IMPLEMENTATION.
  METHOD do_action_1.
    WRITE / 'Do action 1 in abstraction X'.
    mo_implementor->do_operation_1(  ).
  ENDMETHOD.

  METHOD do_action_2.
    WRITE / 'Do action 2 in abstraction X'.
    mo_implementor->do_operation_2(  ).
  ENDMETHOD.
ENDCLASS.

CLASS lcl_refined_abstraction_y DEFINITION FINAL CREATE PUBLIC
INHERITING FROM lcl_abstraction.
  PUBLIC SECTION.
    METHODS: 
      do_action_1 REDEFINITION,
      do_action_2 REDEFINITION.
ENDCLASS.

CLASS lcl_refined_abstraction_y IMPLEMENTATION.
  METHOD do_action_1.
    WRITE / 'Do action 1 in abstraction Y'.
    mo_implementor->do_operation_1(  ).
  ENDMETHOD.

  METHOD do_action_2.
    WRITE / 'Do action 2 in abstraction Y'.
    mo_implementor->do_operation_2(  ).
  ENDMETHOD.
ENDCLASS.
**********************************************************************
START-OF-SELECTION.
**********************************************************************
  DATA(lo_abstraction_x) = NEW lcl_refined_abstraction_x(  ).
  DATA(lo_abstraction_y) = NEW lcl_refined_abstraction_y(  ).
  DATA(lo_imp_a) = NEW lcl_implementor_a(  ).
  DATA(lo_imp_b) = NEW lcl_implementor_b(  ).

  lo_abstraction_x->set_implementor( lo_imp_a ).
  lo_abstraction_x->do_action_1(  ).
  lo_abstraction_x->do_action_2(  ).

  lo_abstraction_y->set_implementor( lo_imp_b ).
  lo_abstraction_y->do_action_1(  ).
  lo_abstraction_y->do_action_2(  ).

运行结果:

可以看到,Abstraction X与 Implementor A进行组合,Abstraction Y与 Implementor B进行组合。当然,也可以直接实现,Abstraction X与 Implementor B进行组合,Abstraction Y与 Implementor A进行组合。

这就是桥接模式带来的灵活性。
在这里插入图片描述

3.2 图片显示与操作系统的分离

下面给出了一个具体的例子,分离“图片显示”和“操作系统”两个维度,从而使得两个维度可以独立变化。

REPORT zbridge_pattern2.
**********************************************************************
* abstraction per dimensions
**********************************************************************
INTERFACE lif_image_imp.
  METHODS display.
ENDINTERFACE.

CLASS lcl_image DEFINITION ABSTRACT.
  PUBLIC SECTION.
    METHODS set_image_implementor
      IMPORTING
        io_imp TYPE REF TO lif_image_imp.
    METHODS output_image ABSTRACT.
  PROTECTED SECTION.
    DATA mo_image_imp TYPE REF TO lif_image_imp.
ENDCLASS.

CLASS lcl_image IMPLEMENTATION.
  METHOD set_image_implementor.
    mo_image_imp = io_imp.
  ENDMETHOD.
ENDCLASS.
**********************************************************************
* implementation
**********************************************************************
CLASS lcl_image_imp_jpg DEFINITION FINAL CREATE PUBLIC.
  PUBLIC SECTION.
    INTERFACES lif_image_imp.
ENDCLASS.

CLASS lcl_image_imp_jpg IMPLEMENTATION.
  METHOD lif_image_imp~display.
    WRITE / 'Display a .jpg file' .
  ENDMETHOD.
ENDCLASS.

CLASS lcl_image_imp_png DEFINITION FINAL CREATE PUBLIC.
  PUBLIC SECTION.
    INTERFACES lif_image_imp.
ENDCLASS.

CLASS lcl_image_imp_png IMPLEMENTATION.
  METHOD lif_image_imp~display.
    WRITE / 'Display a .png file' .
  ENDMETHOD.
ENDCLASS.
**********************************************************************
CLASS lcl_image_windows DEFINITION FINAL CREATE PUBLIC
INHERITING FROM lcl_image.
  PUBLIC SECTION.
    METHODS: output_image REDEFINITION.
ENDCLASS.

CLASS lcl_image_windows IMPLEMENTATION.
  METHOD output_image.
    WRITE / 'Prepare windows system environment...'.
    mo_image_imp->display(  ).
  ENDMETHOD.
ENDCLASS.

CLASS lcl_image_linux DEFINITION FINAL CREATE PUBLIC
INHERITING FROM lcl_image.
  PUBLIC SECTION.
    METHODS: output_image REDEFINITION.
ENDCLASS.

CLASS lcl_image_linux IMPLEMENTATION.
  METHOD output_image.
    WRITE / 'Prepare Linux system environment...'.
    mo_image_imp->display(  ).
  ENDMETHOD.
ENDCLASS.
**********************************************************************
START-OF-SELECTION.
**********************************************************************
  DATA(lo_image_windows) = NEW lcl_image_windows(  ).
  DATA(lo_image_linux) = NEW lcl_image_linux(  ).
  DATA(lo_image_jpg) = NEW lcl_image_imp_jpg(  ).
  DATA(lo_image_png) = NEW lcl_image_imp_png(  ).

  lo_image_windows->set_image_implementor( lo_image_jpg ).
  lo_image_windows->output_image(  ).
  lo_image_windows->set_image_implementor( lo_image_png ).
  lo_image_windows->output_image(  ).

  lo_image_linux->set_image_implementor( lo_image_jpg ).
  lo_image_linux->output_image(  ).
  lo_image_linux->set_image_implementor( lo_image_png ).
  lo_image_linux->output_image(  ).

运行结果:

可以看到,由于独立了“操作系统”与“图片显示”两个维度,使得两种维度可以独立变化和组合。
在这里插入图片描述

以上,是本篇对桥接模式的总结,欢迎分享、留言。😉

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

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
ABAP是一种用于SAP系统开发的编程语言,其中pp模块是SAP系统中的生产计划模块,主要用于生产计划、生产过程控制和生产数据记录等。 ABAP开发在pp模块中的要点如下: 1. 熟悉pp模块的基础知识:了解pp模块的功能和流程,包括物料需求计划、生产订单、工作中心、生产版本等。这些知识对于正确理解和开发与pp模块相关的程序非常重要。 2. 掌握ABAP语言特性:ABAP作为SAP系统的开发语言,开发人员需要掌握其语法和特性,例如数据类型、变量声明、循环语句、条件语句等。这些知识将帮助开发人员编写高效且可靠的pp模块程序。 3. 理解生产计划数据结构:pp模块涉及的数据结构较为复杂,包括物料清单、工艺路线、生产订单等。开发人员需要了解这些数据结构的组成和关系,以便能够正确地读取和处理这些数据。 4. 开发报表和界面程序:PP模块通常需要输出各种类型的报表,如生产订单报表、工序报表等。开发人员需要根据用户需求设计和开发这些报表,并保证它们的准确性和易用性。 5. 数据验证和错误处理:在pp模块开发中,数据的准确性和可靠性非常重要。开发人员需要编写适当的代码来验证用户输入的数据,并能够处理各种可能的错误情况,以保证系统的稳定性和可靠性。 总之,ABAP开发在pp模块中需要掌握相关的知识和技术,包括pp模块的基础知识、ABAP语言特性、数据结构、报表和界面开发以及数据验证和错误处理等方面。只有掌握这些要点,才能够开发出高质量和高效率的pp模块程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

十年铸器

给作者赏杯咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值