Let me explain the scenario:
I'm building a program in which I need to read a file (among other things) and I intend to use object orientation to it's fullest in doing so. I thought of creating an abstract FILE class which has the commonalities, and two subclasses SERVER_FILE and PC_FILE, which implement the abstract method GET_CONTENTS in different ways (OPEN DATASET / GUI_UPLOAD), same for the CHOOSE method which allows to select the file from it's corresponding source.
Initially I've used an interface but since another tasks like setting the file path are common for both, switched to an ABSTRACT class.
Now, the problem is, from the main code I intend to use a FILE reference to handle either type of file. At the instantiation moment I'd like the path attribute to be set; if it was not set by parameter, i'd like to call the CHOOSE method which is abstract for the superclass. Since this is common for either subclass, I need a way to code it once in the superclass. But I get an error because the CHOOSE method is abstract.
This is the problem code (extracts):
*---------------------------------------------------------------------* * CLASS lcl_file DEFINITION *---------------------------------------------------------------------* * ........ * *---------------------------------------------------------------------* CLASS lcl_file DEFINITION ABSTRACT. PUBLIC SECTION. METHODS: constructor IMPORTING i_path TYPE string OPTIONAL EXCEPTIONS no_path_chosen, get_contents ABSTRACT RETURNING value(rt_contents) TYPE string_table EXCEPTIONS read_error. PROTECTED SECTION. DATA: _v_path TYPE string. METHODS: choose ABSTRACT EXCEPTIONS no_path_chosen, set_path IMPORTING i_path TYPE string. ENDCLASS. "lcl_file DEFINITION *---------------------------------------------------------------------* * CLASS lcl_file IMPLEMENTATION *---------------------------------------------------------------------* * ........ * *---------------------------------------------------------------------* CLASS lcl_file IMPLEMENTATION. METHOD constructor. IF i_path IS SUPPLIED. CALL METHOD set_path EXPORTING i_path = i_path. ELSE. *---->>>> PROBLEM CALL - CAN'T BE DONE!! CALL METHOD choose EXCEPTIONS no_path_chosen = 1. IF sy-subrc = 1. RAISE no_path_chosen. ENDIF. ENDIF. ENDMETHOD. "constructor METHOD set_path. _v_path = i_path. ENDMETHOD. "set_path ENDCLASS. "lcl_file IMPLEMENTATION *---------------------------------------------------------------------* * CLASS lcl_server_file DEFINITION *---------------------------------------------------------------------* * ........ * *---------------------------------------------------------------------* CLASS lcl_server_file DEFINITION INHERITING FROM lcl_file. PUBLIC SECTION. METHODS: get_contents REDEFINITION. PROTECTED SECTION. METHODS: choose REDEFINITION. ENDCLASS. "lcl_server_file DEFINITIO *---------------------------------------------------------------------* * CLASS lcl_server_file IMPLEMENTATION *---------------------------------------------------------------------* * ........ * *---------------------------------------------------------------------* CLASS lcl_server_file IMPLEMENTATION. METHOD choose. DATA: l_i_path TYPE dxfields-longpath, l_o_path TYPE dxfields-longpath. CALL FUNCTION 'F4_DXFILENAME_TOPRECURSION' EXPORTING i_location_flag = 'A' " Application server i_path = l_i_path fileoperation = 'R' " Lectura IMPORTING o_path = l_o_path EXCEPTIONS rfc_error = 1 OTHERS = 2. IF sy-subrc = 0 AND l_o_path <> l_i_path. MOVE l_o_path TO _v_path. ELSE. RAISE no_path_chosen. ENDIF. ENDMETHOD. "choose METHOD get_contents. DATA: l_line LIKE LINE OF rt_contents, l_osmsg TYPE string. CHECK NOT _v_path IS INITIAL. OPEN DATASET _v_path FOR INPUT IN TEXT MODE MESSAGE l_osmsg. IF sy-subrc <> 0. MESSAGE e000(oo) WITH l_osmsg RAISING read_error. ELSE. DO. READ DATASET _v_path INTO l_line. IF sy-subrc = 0. APPEND l_line TO rt_contents. ELSE. EXIT. ENDIF. ENDDO. CLOSE DATASET _v_path. ENDIF. ENDMETHOD. "get_contents ENDCLASS. "lcl_server_file IMPLEMENTATION *---------------------------------------------------------------------* * CLASS lcl_pc_file DEFINITION *---------------------------------------------------------------------* * ........ * *---------------------------------------------------------------------* CLASS lcl_pc_file DEFINITION INHERITING FROM lcl_file. PUBLIC SECTION. METHODS: get_contents REDEFINITION. PROTECTED SECTION. METHODS: choose REDEFINITION. ENDCLASS. "lcl_pc_file DEFINITIO *---------------------------------------------------------------------* * CLASS lcl_pc_file IMPLEMENTATION *---------------------------------------------------------------------* * ........ * *---------------------------------------------------------------------* CLASS lcl_pc_file IMPLEMENTATION. METHOD choose. DATA: l_i_path TYPE dxfields-longpath VALUE 'C:\', l_o_path TYPE dxfields-longpath. CALL FUNCTION 'F4_DXFILENAME_TOPRECURSION' EXPORTING i_location_flag = 'P' " PC i_path = l_i_path fileoperation = 'R' " Lectura IMPORTING o_path = l_o_path EXCEPTIONS rfc_error = 1 OTHERS = 2. IF sy-subrc = 0 AND l_o_path <> l_i_path. MOVE l_o_path TO _v_path. ELSE. RAISE no_path_chosen. ENDIF. ENDMETHOD. "choose METHOD get_contents. CHECK NOT _v_path IS INITIAL. CALL METHOD cl_gui_frontend_services=>gui_upload EXPORTING filename = _v_path CHANGING data_tab = rt_contents EXCEPTIONS file_open_error = 1 file_read_error = 2 no_batch = 3 gui_refuse_filetransfer = 4 invalid_type = 5 no_authority = 6 unknown_error = 7 bad_data_format = 8 header_not_allowed = 9 separator_not_allowed = 10 header_too_long = 11 unknown_dp_error = 12 access_denied = 13 dp_out_of_memory = 14 disk_full = 15 dp_timeout = 16 OTHERS = 17. IF sy-subrc <> 0. RAISE read_error. ENDIF. ENDMETHOD. "get_contents ENDCLASS. "lcl_pc_file IMPLEMENTATION *----------------------------------------------------------------------* * Data *----------------------------------------------------------------------* DATA: gr_file TYPE REF TO lcl_file. *----------------------------------------------------------------------- * Main Program *----------------------------------------------------------------------- START-OF-SELECTION. * Get text lines from file IF p_srv = abap_true. CREATE OBJECT gr_file TYPE lcl_server_file EXCEPTIONS no_path_chosen = 1. ELSE. CREATE OBJECT gr_file TYPE lcl_pc_file EXCEPTIONS no_path_chosen = 1. ENDIF.
On a 4.6c system this code gave me a dump, while on my NW7.0 SP it doesn't even activate with the following error:
You cannot call abstract methods in the "CONSTRUCTOR" method.
- Following some suggestions from Java forums i've tried to define the constructor in the base class as PROTECTED or PRIVATE instead, then calling super->constructor from the subclasses, but I get this error in german:
Sichtbarkeit des Konstruktors darf nicht spezieller als die Sichtbarkeit der Instanzerzeugung (CREATE-Zuzatz) sein.
which Altavista translates like:
Visibility of the constructor may not be more special than the
visibility of the instance production (CREATE Zuzatz).
- I've also thought of defining the CHOOSE method as a class (not instance) one, then calling it before creating the file object which maybe solves the problem, but I see that approach more "procedural oriented" which i'm trying to avoid.
- Of course I could define a constructor for each subclass, but both would have exactly the same code.
I'm really lost on how should I code this. My main focus is on avoiding code dupplication.
I hope someone with more OO experience can see what I'm trying to do and sheds some light.
Many thanks for reading all this!