2008 Mar 19 3:34 AM
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!
2008 Mar 19 9:54 AM
Hi Alejandro,
I tried some changes in your code and atleast got rid of your error
" You cannot call abstract methods in the "CONSTRUCTOR" method. "
heres how ....
1) Change One--
CLASS lcl_file DEFINITION ABSTRACT.
PUBLIC SECTION.
METHODS:
constructor
IMPORTING
i_path TYPE string OPTIONAL
i_ref TYPE REF TO lcl_file ">>>>>>>> gaurav
EXCEPTIONS
no_path_chosen,
;
;
2) Change TWO--
CALL METHOD i_ref->choose ">>>>>>>> gaurav
3) Change THREE--
defined two more reference variables as
DATA: gr_file TYPE REF TO lcl_file. "declared by YOU
DATA gr_server_file TYPE REF TO lcl_server_file.
DATA gr_pc_file TYPE REF TO lcl_pc_file.
START-OF-SELECTION.
Get text lines from file
IF p_srv = abap_true.
CREATE OBJECT gr_server_file. ">>>>>>>> gaurav
CREATE OBJECT gr_file
TYPE
lcl_server_file
EXPORTING
i_ref = gr_server_file ">>>>>>>> gaurav
EXCEPTIONS
no_path_chosen = 1.
ELSE.
create object gr_pc_file. ">>>>>>>> gaurav
CREATE OBJECT gr_file
TYPE
lcl_pc_file
exporting
iref = gr_pc_file ">>>>>>>> gaurav
EXCEPTIONS
no_path_chosen = 1.
ENDIF.
4) After this I got some error in the If condition in
" IF sy-subrc = 0 AND l_o_path EQ l_i_path. " I added the EQ
5) When changed this also I got error for .....
OPEN dataset STATEMENT..... of GET_CONTENTs method....
addiding "ENCODING DEFAULT WITH SMART LINEFEED" at the end of this open dataset statement .....there are other errors
6) like IF sy-subrc 0. without EQ or NE operator.......
I didn't do much changes there after..... You can get those all sorted.
Hope this helps,
Regds,
Gaurav
Edited by: Gaurav P Fadnis on Mar 19, 2008 10:57 AM
2008 Mar 19 10:43 AM
Dear Alejandro,
When i saw your code, you are trying to access an astract method CHOOSE(which is actually implemented in sub class) from the constructor of the base class which is not possible. By this time, we don't know which sub class it is refering to, so it gives an error. I see two solutions for this..
1. To define constructor in sub class and call the choose method from the consturctor of the sub class(which in this case is reputation of the same again for each sub class)
2. Remove the calling of choose method from the constructor of the main class and call it separately(after creating the object). By now we know which sub class we are refering to. I would have designed the program in the following way.
*---------------------------------------------------------------------*
* 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_errorm,
set_path ABSTRACT
EXCEPTIONS
no_path_chosen.
PROTECTED SECTION.
DATA:
_v_path TYPE string.
* METHODS:
* choose ABSTRACT
* EXCEPTIONS
* no_path_chosen,
* set_path ABSTRACT
* IMPORTING
* i_path TYPE string.
ENDCLASS. "lcl_file DEFINITION
*---------------------------------------------------------------------*
* CLASS lcl_file IMPLEMENTATION
*---------------------------------------------------------------------*
* ........ *
*---------------------------------------------------------------------*
CLASS lcl_file IMPLEMENTATION.
METHOD constructor.
IF i_path IS SUPPLIED.
_v_path = i_path.
* 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,
set_path REDEFINITION.
* PROTECTED SECTION.
* METHODS:
* choose REDEFINITION.
ENDCLASS. "lcl_server_file DEFINITIO
*---------------------------------------------------------------------*
* CLASS lcl_server_file IMPLEMENTATION
*---------------------------------------------------------------------*
* ........ *
*---------------------------------------------------------------------*
CLASS lcl_server_file IMPLEMENTATION.
METHOD set_path.
DATA:
l_i_path TYPE dxfields-longpath,
l_o_path TYPE dxfields-longpath.
CHECK _v_path IS INITIAL.
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. "set_path
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,
set_path REDEFINITION.
* PROTECTED SECTION.
* METHODS:
* choose REDEFINITION.
ENDCLASS. "lcl_pc_file DEFINITIO
*---------------------------------------------------------------------*
* CLASS lcl_pc_file IMPLEMENTATION
*---------------------------------------------------------------------*
* ........ *
*---------------------------------------------------------------------*
CLASS lcl_pc_file IMPLEMENTATION.
METHOD set_path.
DATA:
l_i_path TYPE dxfields-longpath VALUE 'C:\',
l_o_path TYPE dxfields-longpath.
CHECK _v_path IS INITIAL.
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. "set_path
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 abap_true = 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.
gr_file->set_path( ).
Regards
Kesava
Edited by: Kesava Chandra Rao on Mar 19, 2008 11:44 AM
2008 Mar 19 2:36 PM
Hello,
Many thanks to both for taking the time to help.
Here go my replies:
@Gaurav: Your changes 1 to 3 surely work, but as I said I intend to have only an lcl_file reference, which polimorphically knows how to execute the GET_CONTENTS and CHOOSE methods (depending on how it was instantiated at runtime rather than how was declared statically).
The 4th error was caused by the forum, the = was there but the formatting tag removed it somehow 😮
The remaining errors you got (like with the OPEN DATASET) are probably caused because i'm coding this on a 4.6C system, which may have other obsolete statements. Hence the ugly "CALL METHOD" syntax everywhere. I intend to finish the code on this system and then port it to 7.0.
@Kesava: I think I'll go with the first option...the way I'm thinking of a File object (as merely a file pointer, which doesn't hold the contents) implies that when created, it should have the path set by any means. That's why I don't like to have to call a method afterwards to do it, I see it like a second "initialization" which shouldn't be necessary.