ABAP设计模式之---“原型模式(Prototype Pattern)”

1. 定义

通过拷贝原型本身,创建新的对象。

原型模式属于创建型模式的一种。

2. 解读

类比:

  • 这个设计模式在现实生活中也能找到对应的例子。我们经常使用的“复制粘贴”快捷键,其实通过“拷贝”的方式进行的。Ctrl + C 来选中并复制对象,Ctrl + V 粘贴新生成的对象。

解析:

  • 原型模式,其实就是通过复制一个实例对象,来生成另外一个可定制对象的过程。也即,通过复制,来实现类的实例化
  • 当对象的实例化过程非常复杂时,可以考虑使用原型模式,通过克隆的方式,动态地获得对象运行时的状态,并在此基础上操作。
  • 对于某些语言,例如C#, Java等,系统的命名空间中已经提供了ICloneable接口,直接实现这个接口就可以完成原型模型; 对于没有预制ICloneable接口的语言,例如ABAP,我们可以自己定义Cloneable接口即可。
  • 在复制过程中,对于引用类型的属性,要注意“浅复制(Shadow Clone)”和“深复制(Deep Clone)”的问题。

要点:

  • 在Cloneable接口中,定义Clone( )方法,返回值为接口对象本身。在实现类中,实现这个cloneable的接口,通过Clone( )方法依照本实例,复制出一个新的实例。

适用场景:

  • 并不是所有的场景都适合适用原型模式,特别是引用类型较多的类,要因情况而异。因为Cloneable接口的引入,会让类的设计变得更加复杂。

3.举例

定义一个if_prototype_cloneable的接口,在实现类中实现此接口,并在Clone中返回一个新的实例。
在这里插入图片描述

3.1 浅复制

在此例中,通过lcl_resume的clone( )方法来生成新的实例。

REPORT zprototype_pattern.

INTERFACE lif_resume_cloneable.
  METHODS set_name
    IMPORTING
      iv_name TYPE string.
  METHODS
    set_age
      IMPORTING iv_age TYPE i.
  METHODS
    set_company
      IMPORTING iv_company TYPE string.
  METHODS
    display_info.

  " use this clone method to do the initialization of an instance
  METHODS clone
    RETURNING
      VALUE(ro_instance) TYPE REF TO lif_resume_cloneable.
ENDINTERFACE.

CLASS lcl_resume DEFINITION FINAL CREATE PUBLIC.
  PUBLIC SECTION.
    INTERFACES lif_resume_cloneable.

  PRIVATE SECTION.
    DATA mv_name TYPE string.
    DATA mv_age TYPE i.
    DATA mv_company TYPE string.
ENDCLASS.

CLASS lcl_resume IMPLEMENTATION.
  METHOD lif_resume_cloneable~clone.
    ro_instance = NEW lcl_resume( ). " New a fresh instance
    ro_instance->set_name( mv_name ).
    ro_instance->set_age( mv_age ).
    ro_instance->set_company( mv_company ).
  ENDMETHOD.

  METHOD lif_resume_cloneable~set_name.
    mv_name = iv_name.
  ENDMETHOD.

  METHOD lif_resume_cloneable~set_age.
    mv_age = iv_age.
  ENDMETHOD.

  METHOD lif_resume_cloneable~set_company.
    mv_company = iv_company.
  ENDMETHOD.

  METHOD lif_resume_cloneable~display_info.
    WRITE / 'Printing Resume... ...'.
    WRITE / 'Name: ' && mv_name.
    WRITE / 'Age: ' && mv_age.
    WRITE / 'Company:' && mv_company.
    WRITE / '-------'.
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  DATA lo_resume_a TYPE REF TO lif_resume_cloneable.
  DATA lo_resume_b TYPE REF TO lif_resume_cloneable.

  lo_resume_a = NEW lcl_resume( ).
  lo_resume_a->set_name( 'Tom' ).
  lo_resume_a->set_age( 18 ).
  lo_resume_a->set_company( 'XXX Company ' ).

  " lo_resume_b is cloned from lo_resume_a
  lo_resume_b = lo_resume_a->clone(  ).
  lo_resume_b->set_name( 'Jerry' ).

  lo_resume_a->display_info(  ).
  lo_resume_b->display_info(  ).

运行结果:
在这里插入图片描述

3.2 深复制

在此例中,lcl_resume含有的引用对象lcl_work_experience. 在复制过程中,要同样为复制lcl_work_experience.

*&---------------------------------------------------------------------*
*& Report zprototype_pattern2
*&---------------------------------------------------------------------*
*& This case shows the way of deep clone
*&---------------------------------------------------------------------*
REPORT zprototype_pattern2.

INTERFACE lif_work_experience.
  METHODS set_work_experience
    IMPORTING
      iv_experience TYPE string.
  METHODS get_work_experience
    RETURNING VALUE(rv_experience) TYPE string.
  METHODS print_experience.
ENDINTERFACE.

CLASS lcl_work_experience DEFINITION FINAL CREATE PUBLIC.
  PUBLIC SECTION.
    INTERFACES lif_work_experience.

  PRIVATE SECTION.
    DATA mv_experience TYPE string.
ENDCLASS.

CLASS lcl_work_experience IMPLEMENTATION.
  METHOD lif_work_experience~print_experience.
    WRITE / 'Experience:' && mv_experience.
  ENDMETHOD.

  METHOD lif_work_experience~set_work_experience.
    mv_experience = iv_experience.
  ENDMETHOD.

  METHOD lif_work_experience~get_work_experience.
    rv_experience = mv_experience.
  ENDMETHOD.
ENDCLASS.
**********************************************************************
INTERFACE lif_resume_cloneable.
  METHODS set_name
    IMPORTING
      iv_name TYPE string.
  METHODS
    set_age
      IMPORTING iv_age TYPE i.
  METHODS
    set_company
      IMPORTING iv_company TYPE string.
  METHODS
    set_experience
      IMPORTING iv_experience TYPE string.
  METHODS
    display_info.

  " use this clone method to do the initialization of an instance
  METHODS clone
    RETURNING
      VALUE(ro_instance) TYPE REF TO lif_resume_cloneable.
ENDINTERFACE.

CLASS lcl_resume DEFINITION FINAL CREATE PUBLIC.
  PUBLIC SECTION.
    INTERFACES lif_resume_cloneable.

    METHODS
      constructor.
  PRIVATE SECTION.
    DATA mv_name TYPE string.
    DATA mv_age TYPE i.
    DATA mv_company TYPE string.
    DATA mo_work_experience TYPE REF TO lif_work_experience.
ENDCLASS.

CLASS lcl_resume IMPLEMENTATION.
  METHOD lif_resume_cloneable~clone.
    ro_instance = NEW lcl_resume( ). " New a fresh instance
    ro_instance->set_name( mv_name ).
    ro_instance->set_age( mv_age ).
    ro_instance->set_company( mv_company ).

    ro_instance->set_experience( mo_work_experience->get_work_experience(  ) ).
  ENDMETHOD.

  METHOD lif_resume_cloneable~set_name.
    mv_name = iv_name.
  ENDMETHOD.

  METHOD lif_resume_cloneable~set_age.
    mv_age = iv_age.
  ENDMETHOD.

  METHOD lif_resume_cloneable~set_company.
    mv_company = iv_company.
  ENDMETHOD.

  METHOD lif_resume_cloneable~display_info.
    WRITE / '-------'.
    WRITE / 'Printing Resume... ...'.
    WRITE / 'Name: ' && mv_name.
    WRITE / 'Age: ' && mv_age.
    WRITE / 'Company:' && mv_company.
    mo_work_experience->print_experience(  ).
  ENDMETHOD.

  METHOD lif_resume_cloneable~set_experience.
    mo_work_experience->set_work_experience( iv_experience ).
  ENDMETHOD.

  METHOD constructor.
    " always create a new instance of work_experience (to have a separated address space, not a reference pass )
    mo_work_experience = NEW lcl_work_experience(  ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  DATA lo_resume_a TYPE REF TO lif_resume_cloneable.
  DATA lo_resume_b TYPE REF TO lif_resume_cloneable.

  lo_resume_a = NEW lcl_resume( ).
  lo_resume_a->set_name( 'Tom' ).
  lo_resume_a->set_age( 18 ).
  lo_resume_a->set_company( 'XXX Company ' ).
  lo_resume_a->set_experience( 'Tom work experience' ).

  " lo_resume_b is cloned from lo_resume_a
  lo_resume_b = lo_resume_a->clone(  ).
  lo_resume_b->set_name( 'Jerry' ).
  lo_resume_b->set_experience( 'Jerry work experience' ).

  " the cloned object is completed independent from each other
  lo_resume_a->display_info(  ).
  lo_resume_b->display_info(  ).

运行结果:
在这里插入图片描述

3.3 注意引用类型

3.3.1 共用地址空间

在复制时,要注意引用类型对象的处理。

*&---------------------------------------------------------------------*
*& Report zprototype_pattern3
*&---------------------------------------------------------------------*
*& This case shows the effects of reference pass (shadow clone)
*&---------------------------------------------------------------------*
REPORT zprototype_pattern3.

INTERFACE lif_work_experience.
  METHODS set_work_experience
    IMPORTING
      iv_experience TYPE string.
  METHODS print_experience.

*  " use this clone method to do the initialization of an instance
*  METHODS clone
*    RETURNING
*      VALUE(ro_instance) TYPE REF TO lif_work_experience.
ENDINTERFACE.

CLASS lcl_work_experience DEFINITION FINAL CREATE PUBLIC.
  PUBLIC SECTION.
    INTERFACES lif_work_experience.

  PRIVATE SECTION.
    DATA mv_experience TYPE string.
ENDCLASS.

CLASS lcl_work_experience IMPLEMENTATION.
  METHOD lif_work_experience~print_experience.
    WRITE / 'Experience:' && mv_experience.
  ENDMETHOD.

  METHOD lif_work_experience~set_work_experience.
    mv_experience = iv_experience.
  ENDMETHOD.

*  METHOD lif_work_experience~clone.
*    ro_instance = NEW lcl_work_experience(  ).
*    ro_instance->set_work_experience( mv_experience ).
*  ENDMETHOD.
ENDCLASS.

**********************************************************************
INTERFACE lif_resume_cloneable.
  METHODS set_name
    IMPORTING
      iv_name TYPE string.
  METHODS
    set_age
      IMPORTING iv_age TYPE i.
  METHODS
    set_company
      IMPORTING iv_company TYPE string.
  METHODS
    set_experience
      IMPORTING io_experience TYPE REF TO lif_work_experience.
  METHODS
    display_info.

  " use this clone method to do the initialization of an instance
  METHODS clone
    RETURNING
      VALUE(ro_instance) TYPE REF TO lif_resume_cloneable.
ENDINTERFACE.

CLASS lcl_resume DEFINITION FINAL CREATE PUBLIC.
  PUBLIC SECTION.
    INTERFACES lif_resume_cloneable.

  PRIVATE SECTION.
    DATA mv_name TYPE string.
    DATA mv_age TYPE i.
    DATA mv_company TYPE string.
    DATA mo_work_experience TYPE REF TO lif_work_experience.
ENDCLASS.

CLASS lcl_resume IMPLEMENTATION.
  METHOD lif_resume_cloneable~clone.
    ro_instance = NEW lcl_resume( ). " New a fresh instance
    ro_instance->set_name( mv_name ).
    ro_instance->set_age( mv_age ).
    ro_instance->set_company( mv_company ).

    " Here it is only a reference pass
    ro_instance->set_experience( mo_work_experience ).
  ENDMETHOD.

  METHOD lif_resume_cloneable~set_name.
    mv_name = iv_name.
  ENDMETHOD.

  METHOD lif_resume_cloneable~set_age.
    mv_age = iv_age.
  ENDMETHOD.

  METHOD lif_resume_cloneable~set_company.
    mv_company = iv_company.
  ENDMETHOD.

  METHOD lif_resume_cloneable~display_info.
    WRITE / '-------'.
    WRITE / 'Printing Resume... ...'.
    WRITE / 'Name: ' && mv_name.
    WRITE / 'Age: ' && mv_age.
    WRITE / 'Company:' && mv_company.
    mo_work_experience->print_experience(  ).
  ENDMETHOD.

  METHOD lif_resume_cloneable~set_experience.
    mo_work_experience = io_experience.
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  DATA lo_resume_a TYPE REF TO lif_resume_cloneable.
  DATA lo_resume_b TYPE REF TO lif_resume_cloneable.
  DATA lo_experience TYPE REF TO lif_work_experience.

  lo_resume_a = NEW lcl_resume( ).
  lo_resume_a->set_name( 'Tom' ).
  lo_resume_a->set_age( 18 ).
  lo_resume_a->set_company( 'XXX Company ' ).

  " lo_resume_b is cloned from lo_resume_a
  " the reference object is not independent, so it was shared and affect each other
  " you could see 'Jerry work experience' affects lo_resume_a and lo_resume_b.
  lo_resume_b = lo_resume_a->clone(  ).
  lo_resume_b->set_name( 'Jerry' ).

  lo_experience = NEW lcl_work_experience(  ).
  lo_experience->set_work_experience( 'Tom work experience' ).
  lo_resume_a->set_experience( lo_experience ).

  lo_experience->set_work_experience( 'Jerry work experience' ).
  lo_resume_b->set_experience( lo_experience ).


  lo_resume_a->display_info(  ).
  lo_resume_b->display_info(  ).

运行结果:

由于共用lo_experience的地址空间,复制出来的lo_resume_b和原来的lo_resume_a同时指向了lo_experience, 这导致这两个对象并非独立的,而是互相影响的。
在这里插入图片描述

3.3.2 开辟新的地址空间

为每个实例都开辟单独的地址空间。

*&---------------------------------------------------------------------*
*& Report zprototype_pattern4
*&---------------------------------------------------------------------*
*& This case shows the effects of reference pass (shadow clone)
*&---------------------------------------------------------------------*
REPORT zprototype_pattern4.

INTERFACE lif_work_experience.
  METHODS set_work_experience
    IMPORTING
      iv_experience TYPE string.
  METHODS print_experience.

*  " use this clone method to do the initialization of an instance
*  METHODS clone
*    RETURNING
*      VALUE(ro_instance) TYPE REF TO lif_work_experience.
ENDINTERFACE.

CLASS lcl_work_experience DEFINITION FINAL CREATE PUBLIC.
  PUBLIC SECTION.
    INTERFACES lif_work_experience.

  PRIVATE SECTION.
    DATA mv_experience TYPE string.
ENDCLASS.

CLASS lcl_work_experience IMPLEMENTATION.
  METHOD lif_work_experience~print_experience.
    WRITE / 'Experience:' && mv_experience.
  ENDMETHOD.

  METHOD lif_work_experience~set_work_experience.
    mv_experience = iv_experience.
  ENDMETHOD.

*  METHOD lif_work_experience~clone.
*    ro_instance = NEW lcl_work_experience(  ).
*    ro_instance->set_work_experience( mv_experience ).
*  ENDMETHOD.
ENDCLASS.

**********************************************************************
INTERFACE lif_resume_cloneable.
  METHODS set_name
    IMPORTING
      iv_name TYPE string.
  METHODS
    set_age
      IMPORTING iv_age TYPE i.
  METHODS
    set_company
      IMPORTING iv_company TYPE string.
  METHODS
    set_experience
      IMPORTING io_experience TYPE REF TO lif_work_experience.
  METHODS
    display_info.

  " use this clone method to do the initialization of an instance
  METHODS clone
    RETURNING
      VALUE(ro_instance) TYPE REF TO lif_resume_cloneable.
ENDINTERFACE.

CLASS lcl_resume DEFINITION FINAL CREATE PUBLIC.
  PUBLIC SECTION.
    INTERFACES lif_resume_cloneable.

  PRIVATE SECTION.
    DATA mv_name TYPE string.
    DATA mv_age TYPE i.
    DATA mv_company TYPE string.
    DATA mo_work_experience TYPE REF TO lif_work_experience.
ENDCLASS.

CLASS lcl_resume IMPLEMENTATION.
  METHOD lif_resume_cloneable~clone.
    ro_instance = NEW lcl_resume( ). " New a fresh instance
    ro_instance->set_name( mv_name ).
    ro_instance->set_age( mv_age ).
    ro_instance->set_company( mv_company ).

    " Here it is only a reference pass
    ro_instance->set_experience( mo_work_experience ).
  ENDMETHOD.

  METHOD lif_resume_cloneable~set_name.
    mv_name = iv_name.
  ENDMETHOD.

  METHOD lif_resume_cloneable~set_age.
    mv_age = iv_age.
  ENDMETHOD.

  METHOD lif_resume_cloneable~set_company.
    mv_company = iv_company.
  ENDMETHOD.

  METHOD lif_resume_cloneable~display_info.
    WRITE / '-------'.
    WRITE / 'Printing Resume... ...'.
    WRITE / 'Name: ' && mv_name.
    WRITE / 'Age: ' && mv_age.
    WRITE / 'Company:' && mv_company.
    mo_work_experience->print_experience(  ).
  ENDMETHOD.

  METHOD lif_resume_cloneable~set_experience.
    mo_work_experience = io_experience.
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  DATA lo_resume_a TYPE REF TO lif_resume_cloneable.
  DATA lo_resume_b TYPE REF TO lif_resume_cloneable.

  lo_resume_a = NEW lcl_resume( ).
  lo_resume_a->set_name( 'Tom' ).
  lo_resume_a->set_age( 18 ).
  lo_resume_a->set_company( 'XXX Company ' ).

  " lo_resume_b is cloned from lo_resume_a
  " the reference object is not independent, so it was shared and affect each other
  " you could see 'Jerry work experience' affects lo_resume_a and lo_resume_b.
  lo_resume_b = lo_resume_a->clone(  ).
  lo_resume_b->set_name( 'Jerry' ).

  " so in this case, to have 2 independent lo_experience is needed.
  DATA lo_experience_a TYPE REF TO lif_work_experience.
  DATA lo_experience_b TYPE REF TO lif_work_experience.

  lo_experience_a = NEW lcl_work_experience(  ).
  lo_experience_a->set_work_experience( 'Tom work experience' ).
  lo_resume_a->set_experience( lo_experience_a ).

  lo_experience_b = NEW lcl_work_experience(  ).
  lo_experience_b->set_work_experience( 'Jerry work experience' ).
  lo_resume_b->set_experience( lo_experience_b ).

  lo_resume_a->display_info(  ).
  lo_resume_b->display_info(  ).

运行结果:

此时,两个实例会是完全独立的。
在这里插入图片描述

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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

十年铸器

给作者赏杯咖啡

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

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

打赏作者

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

抵扣说明:

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

余额充值