12
Factory-Pattern in ABAP OO When you work with ABAP Objects you always need to create instances of classes. Even if you use "CREATE OBJECT" to create those instances, you probably faced with the problem, that it is not so comfortable as if you have a function for creating objects in the same way. Or, you have a complex logic to create each instance. Background For those cases, there is a design-pattern for creating instances. This pattern is called "Factory"- pattern. There is also a pattern called "factory-method", which is different, because it combines the creation and relationship of objects. Thanks to Naimesh Patel, who advise me of this point. The factory-pattern is one of the most used design pattern. It creates object instances without exposing the concrete instantiation logic. The instantiated objects will be accessable through an interface, which encapsulate the concrete implementation to the users. In the classical GoF-Pattern, the factory-pattern looks like this: A client needs a product (a.k.a. "class") and asks a factory for a concrete instance by passing the neccessary parameters to it. The factory creates a class of a specific interface ( "Product" ) and gives the instance back to the client. The specific interface should also be an abstract class, but by using an interface you will have a much better support for the separations-of-concerns principle. The ABAP-way of the factory pattern Sure, it should be very simple, to transfer the underlying UML to a ABAP class structure. But, you have much more than just a OO-language: The SAP Netweaver AS gives you some nice featues - customizing with a maintenance dialog is one of them. So, here is my approach for the ABAP OO-Factory:

Factory-Pattern in ABAP OO

Embed Size (px)

Citation preview

Page 1: Factory-Pattern in ABAP OO

Factory-Pattern in ABAP OOWhen you work with ABAP Objects you always need to create instances of classes. Even if you use "CREATE OBJECT" to create those instances, you probably faced with the problem, that it is not so comfortable as if you have a function for creating objects in the same way. Or, you have a complex logic to create each instance.

BackgroundFor those cases, there is a design-pattern for creating instances. This pattern is called "Factory"-pattern. There is also a pattern called "factory-method", which is different, because it combines the creation and relationship of objects. Thanks to Naimesh Patel, who advise me of this point.

The factory-pattern is one of the most used design pattern. It creates object instances without exposing the concrete instantiation logic. The instantiated objects will be accessable through an interface, which encapsulate the concrete implementation to the users.

In the classical GoF-Pattern, the factory-pattern looks like this:

A client needs a product (a.k.a. "class") and asks a factory for a concrete instance by passing the neccessary parameters to it. The factory creates a class of a specific interface ( "Product" ) and gives the instance back to the client. The specific interface should also be an abstract class, but by using an interface you will have a much better support for the separations-of-concerns principle.

The ABAP-way of the factory patternSure, it should be very simple, to transfer the underlying UML to a ABAP class structure. But, you have much more than just a OO-language: The SAP Netweaver AS gives you some nice featues - customizing with a maintenance dialog is one of them.

So, here is my approach for the ABAP OO-Factory:

Page 2: Factory-Pattern in ABAP OO

As you can see, the factory use informations out of a customizing table and holds all created instances in a internal table. The rest ( Interface, Concrete Classes) are simple ABAP OO Elements. By the way: If you can be sure, that the instances should not be buffered so far, you do not need an internal table with all created instances.

At least, the "create" Method is not so complex:

Page 3: Factory-Pattern in ABAP OO

The customizing table should look like this:

For your internal table in the factory, you should define a DDIC-Structure and Tabletype which includes the customizing information and a reference to the created instance.

One hint: Create a view on SEOCLASSTX and SEOMETAREL with the used interface as

Page 4: Factory-Pattern in ABAP OO

constant for "REFCLSNAME", use it as foreign-key and you get a DDIC-based check, if the used class implements the correct interface.

And last, but not least: you need a maintenance view, created by the maintenance view generator.

Why should you use a factory-pattern?With the factory pattern you centralize the create of objects within your application. By using this approach, you can manage the instantiation of objects ( "Products" ) and separate the usage of the concrete implementation logic.

Through interfaces you introduce the design principle "separation of concerns" and you give a contract for all users of your classes. By accepting this contract the users of your interface count on the correct implementation of the contract.

Additional, you get an overview about who implements your interface and where is it used - just look in the ABAP Workbench and expand the implementation tree of your interface:

Adding new aspects to your softwareAnother important thing, which I have not mentioned so far: You can build your factory in a way, that you can very easy introduce proxy-classes (another design pattern) which covers new aspects - for example: logging or authentication - without changing the underlying classes.

Page 5: Factory-Pattern in ABAP OO

By using this, your user does not affect, that there is another implementation - the interface, and its underlying contract is stil fulfilled.

ConclusionThe factory-pattern is an important pattern for creating object instances within your own code. With this pattern, you can get a more manageable, extensible and customizable software.

Customize your ABAP Object-FactoriesAfter writing my two blogs Factory-Pattern in ABAP OO and Easy implementation of BEx-Userexit-Variables I was asked, how a efficient customizing table for those classes looks like.

A short history: The factory pattern creates instances of objects which should implement a specific interface. These classes have to be customized, so that the code does not change with new classes or requirements. For this, I use a customizing table, which I create in the dictionary.

The simplest way will be, to create a customer-table ( Z-Table ) and make a maintenenance dialog for this. But some features are still missing:• Only those classes are selectable, which implement the desired interface / at least you can point XYZ in this without any checks.• There is no documentation hint about the class - you only see the technical name• You do not have a semantic link to any other objects ( e.g. Company Code, Material or in my last example BEx-Customerexit-Variables )

What to do? Use the existing dictonary features ( and they are also avaiable within a BW ).

In the following I will show, how to build a customizing table for the BEx-Customerexit-Implementation. For your case, you have to change the keyfields of the customizing table and at least the used search-helps and views to run with your concrete scenario.

Create the concrete customizing Table

Page 6: Factory-Pattern in ABAP OO

The delivery and maintenance flags are: Customizing and changes are allowed.

Ok, no rocket sience. Look at the entry helps/check-Tabular:

I marked the check table for our variable - which goes to a view, which selects the avaiable variables (described later) and the search help for our classes.

First Step: The check table of type ZBIU001_V_BVAR selects only valid entries of the BEx-Variable-Table RSZGLOBV. With this, I can ensure that only variables which are declared as customer-exit can be choosen.

The view is very simple: First select the variables from table RSZGLOBV and do a join with the corresponding text-table RSZELTTXT ( just for the user ;-) ).

Join:

Page 7: Factory-Pattern in ABAP OO

View Fields:

Selections:

With this selection, you can ensure, that only customer-exit variables are chosen.

Next Step: Create a searchhelp, which delivers the correct class - and even this search-help looks

Page 8: Factory-Pattern in ABAP OO

after inheritence and abstract classes.

The function-module will be build in the way, classical search-help modules are developed.

FUNCTION zbiu001_sh_exit_classname. *"---------------------------------------------------------------------- *"*"Local Interface: *" TABLES *" SHLP_TAB TYPE SHLP_DESCT *" RECORD_TAB STRUCTURE SEAHLPRES *" CHANGING *" REFERENCE(SHLP) TYPE SHLP_DESCR *" REFERENCE(CALLCONTROL) TYPE DDSHF4CTRL *"---------------------------------------------------------------------- * EXIT immediately, if you do not want to handle this step IF callcontrol-step <> 'SELONE' AND callcontrol-step <> 'SELECT' AND " AND SO ON callcontrol-step <> 'DISP'. EXIT. ENDIF.

IF callcontrol-step = 'SELONE'. * PERFORM SELONE ......... EXIT. ENDIF. IF callcontrol-step = 'PRESEL'.

Page 9: Factory-Pattern in ABAP OO

EXIT. ENDIF. *"---------------------------------------------------------------------- * STEP SELECT (Select values) *"---------------------------------------------------------------------- IF callcontrol-step = 'SELECT'. FIELD-SYMBOLS: <line> TYPE string. DATA: lt_result TYPE stringtab, ls_result TYPE seahlpres. CLEAR: record_tab. * For this example: the code is here * For real-world scenario: export this coding to a utility-class * BTW: This is an example, how to write a recursive function using two stacks. *____________________________ FIELD-SYMBOLS: <class> TYPE string. DATA: lt_classes_stack TYPE stringtab, lt_subclasses TYPE stringtab, lc_abaptype TYPE REF TO cl_abap_classdescr.

SELECT DISTINCT clsname FROM seometarel INTO TABLE lt_classes_stack WHERE refclsname = 'ZIF_BIU001_VARIABLE'. "<= Replace this in other cases with your interface!!! WHILE lt_classes_stack IS NOT INITIAL. LOOP AT lt_classes_stack ASSIGNING <class>. * Only those classes, which can be instantiated lc_abaptype ?= cl_abap_classdescr=>describe_by_name( <class> ). IF lc_abaptype IS BOUND AND lc_abaptype->is_instantiatable( ) = abap_true. ls_result-string = <class>. APPEND ls_result TO record_tab. ENDIF. SELECT DISTINCT clsname FROM seometarel APPENDING TABLE lt_subclasses WHERE refclsname = <class>. ENDLOOP.

lt_classes_stack = lt_subclasses. CLEAR: lt_subclasses. ENDWHILE. *____________________________ callcontrol-step = 'DISP'. EXIT. "Don't process STEP DISP additionally in this call. ENDIF. ENDFUNCTION.

The most important thing is your selection-event. There you have to traverse the class-tree (build up within the table SEOMETAREL) and you will have to check wether a class is instantiatable via CL_ABAP_TYPEDESCR.

Create a "cool" maintenance view

Page 10: Factory-Pattern in ABAP OO

After creating the customizing-table, you will need a view for the maintenance dialog. Why a view? Because the maintenance-views of the DDIC are very powerful and easy to enhance.

The maintenance-view will be build up over your customizing table and the table SEOCLASSTX. Why this? Because I want to have the texts of the class description during the maintenance, so the user/developer can immediatly see, which class he has selected - and he does not have to enter any descriptions.

Join-Definition

View-Fields. Regard: DESCRIPT has the flag "R" for read-only.

Now: Start you table maintenance generator:

Page 11: Factory-Pattern in ABAP OO

Just apply your settings of authorization group and function-group.

The final maintenance viewStart the transacition SM30 and look at your new maintenance view:

Even with great search helps:

Page 12: Factory-Pattern in ABAP OO

ConclusionI hope, I could show, how easy it is, to build own customizing-dialogs for classes if you are using them in a dynamic-environment.

With this approach, you have the chance, to build applications, which are maintainable and easy to enhance. By using this kind of customizing you are even in the situation to ensure, that only those classes are used, which implements a concrete interface.

Another point: Do you have counted, how many lines of code are necessary to get this result? At least a small function module, which can easy encapsulated, so that it will work for many others too.