Saturday, 26 May 2012

Object Oriented Design Principles (OODP): Open-Closed Principle(OCP)

Definition:

The name itself sounds like contradicting – OPEN & CLOSE, but by definition, A module should be OPEN for extension but CLOSED for modification. The Open-Closed Principle (OCP) is one of the most important Design Principle. OCP originated from the work of by the Bertrand Meyer.

In simple words: We can add new features in the module without changing the existing code (CLOSED) but by adding new code (OPEN).  By following this principle, we can make sure that by changing the code in the method we are not going to break existing functionality. Let us try to see it using the example.

Example:

First of all we would see the code which doesn’t follow this principle. Consider we have a class which prints the shape information of different shapes based on the input.

Object Oriented Design Principles (OODP): Open-Closed Principle(OCP)

Code snippet which violates this principle.

*——————————————————–* *&  Author : Naimesh Patel *&  Purpose: Open-Closed Principle (violation) *——————————————————–*  REPORT  ztest_np_ocp.   *——————————————————–* *       CLASS lcl_circle DEFINITION *——————————————————–* *       Circle *——————————————————–* CLASS lcl_circle DEFINITION.    PUBLIC SECTION.     METHODS:       write_circle.  ENDCLASS.                    “lcl_circle DEFINITION  *——————————————————–* *       CLASS lcl_square DEFINITION *——————————————————–* *       Square *——————————————————–* CLASS lcl_square DEFINITION.    PUBLIC SECTION.     METHODS:       write_square.  ENDCLASS.                    “lcl_square DEFINITION  *——————————————————–* *       CLASS lcl_report DEFINITION *——————————————————–* *       Report *——————————————————–* CLASS lcl_report DEFINITION.    PUBLIC SECTION.     METHODS:       create_shape         IMPORTING           if_type TYPE char30,        write_shape         IMPORTING           if_type TYPE char30.    PRIVATE SECTION.     DATA: o_circle TYPE REF TO lcl_circle.     DATA: o_square TYPE REF TO lcl_square.  ENDCLASS.                    “lcl_report DEFINITION  PARAMETERS: p_shape TYPE char30.  START-OF-SELECTION.   DATA: o_report TYPE REF TO lcl_report.    CREATE OBJECT o_report.    o_report->create_shape( p_shape ).   o_report->write_shape( p_shape ).  *——————————————————–* *       CLASS lcl_circle IMPLEMENTATION *——————————————————–* * *——————————————————–* CLASS lcl_circle IMPLEMENTATION.    METHOD write_circle.      WRITE: / ‘I am a Circle’.    ENDMETHOD.                    “write_circle  ENDCLASS.                    “lcl_circle IMPLEMENTATION  *——————————————————–* *       CLASS lcl_square IMPLEMENTATION *——————————————————–* * *——————————————————–* CLASS lcl_square IMPLEMENTATION.    METHOD write_square.      WRITE: / ‘I am a Square’.    ENDMETHOD.                    “write_square  ENDCLASS.                    “lcl_square IMPLEMENTATION   *——————————————————–* *       CLASS lcl_report IMPLEMENTATION *——————————————————–* * *——————————————————–* CLASS lcl_report IMPLEMENTATION.    METHOD create_shape.     CASE if_type.       WHEN ‘CIRCLE’.         CREATE OBJECT: me->o_circle.       WHEN ‘SQUARE’.         CREATE OBJECT: me->o_square.     ENDCASE.    ENDMETHOD.                    “create_shapes    METHOD write_shape.      CASE if_type.       WHEN ‘CIRCLE’.         me->o_circle->write_circle( ).       WHEN ‘SQUARE’.         me->o_square->write_square( ).     ENDCASE.    ENDMETHOD.                    “write_shapes  ENDCLASS.                    “lcl_report IMPLEMENTATION

Now the problem with this code is, when ever we need to add a new type of Shape, we need to change the code in the CREATE_SHAPE, WRITE_SHAPE method, which is clear violation of the OCP. According to OCP, we should only change the method to add new functionality. Here we are not adding a new functionality. Creation of the Shape Object and writing the information of the different Shape is not a new functionality. So, we must re-design our class and methods to be able to follow the Open Closed Principle.

We can use the dynamic polymorphism to achieve Open-Closed design. Check the UML for the new design.

Object Oriented Design Principles (OODP): Open-Closed Principle(OCP)

Now we would create an interface LIF_SHAPE which we will use to create different shapes like circle using LCL_CIRCLE, square using LCL_SQUARE and so on.

*——————————————————–* *       INTERFACE lif_shape *——————————————————–* *       Shpae Interface *——————————————————–* INTERFACE lif_shape.   METHODS:     write_shape. ENDINTERFACE.                    “lif_shape  *——————————————————–* *       CLASS lcl_circle DEFINITION *——————————————————–* *       Circle uses interface *——————————————————–* CLASS lcl_circle DEFINITION.   PUBLIC SECTION.     INTERFACES lif_shape. ENDCLASS.                    “lcl_circle DEFINITION  *——————————————————–* *       CLASS lcl_square DEFINITION *——————————————————–* *       Square uses interface *——————————————————–* CLASS lcl_square DEFINITION.   PUBLIC SECTION.     INTERFACES lif_shape. ENDCLASS.                    “lcl_square DEFINITION

Implementation of the class LCL_CIRCLE and LCL_SQUARE implements the method

*——————————————————–* *       CLASS lcl_circle IMPLEMENTATION *——————————————————–* * *——————————————————–* CLASS lcl_circle IMPLEMENTATION.    METHOD lif_shape~write_shape.     WRITE: ‘I am a Circle’.   ENDMETHOD.                    “lif_shape~write_shape  ENDCLASS.                    “lcl_circle IMPLEMENTATION  *——————————————————–* *       CLASS lcl_square IMPLEMENTATION *——————————————————–* * *——————————————————–* CLASS lcl_square IMPLEMENTATION.    METHOD lif_shape~write_shape.     WRITE: ‘I am a Square’.   ENDMETHOD.                    “lif_shape~write_shape  ENDCLASS.                    “lcl_square IMPLEMENTATION 

Design of the report class is also changed. LCL_REPORT would now accept the class name instead of the simple type to instantiate required object as the parameter of the method CREATE_SHAPE.

*——————————————————–* *       CLASS lcl_report DEFINITION *——————————————————–* *       Report *——————————————————–* CLASS lcl_report DEFINITION.    PUBLIC SECTION.     METHODS:       create_shape         IMPORTING           if_type TYPE char30,        write_shape         IMPORTING           if_type TYPE char30.    PRIVATE SECTION.     DATA: o_shape TYPE REF TO lif_shape.  ENDCLASS.                    “lcl_report DEFINITION 

Implementation of the class LCL_REPORT needs to be changed. CREATE_SHPAE would create the object and WRITE_SHAPE would call the method WRITE_SHAPE from the object O_SHAPE.

*——————————————————–* *       CLASS lcl_report IMPLEMENTATION *——————————————————–* * *——————————————————–* CLASS lcl_report IMPLEMENTATION.    METHOD create_shape.  *   Instantiate the object based on the type     CREATE OBJECT me->o_shape TYPE (if_type).    ENDMETHOD.                    “create_shapes    METHOD write_shape.  *   write shape     me->o_shape->write_shape( ).    ENDMETHOD.                    “write_shapes  ENDCLASS.                    “lcl_report IMPLEMENTATION

Full code Snippet which shows how to achieve OCP design.

*——————————————————–* *&  Author  : Naimesh Patel *&  Purpose : Open-Closed Principle (Achieved) *——————————————————–*  REPORT  ztest_np_ocp.  *——————————————————–* *       INTERFACE lif_shape *——————————————————–* *       Shpae Interface *——————————————————–* INTERFACE lif_shape.   METHODS:     write_shape. ENDINTERFACE.                    “lif_shape  *——————————————————–* *       CLASS lcl_circle DEFINITION *——————————————————–* *       Circle uses interface *——————————————————–* CLASS lcl_circle DEFINITION.   PUBLIC SECTION.     INTERFACES lif_shape. ENDCLASS.                    “lcl_circle DEFINITION  *——————————————————–* *       CLASS lcl_square DEFINITION *——————————————————–* *       Square uses interface *——————————————————–* CLASS lcl_square DEFINITION.   PUBLIC SECTION.     INTERFACES lif_shape. ENDCLASS.                    “lcl_square DEFINITION  *——————————————————–* *       CLASS lcl_report DEFINITION *——————————————————–* *       Report *——————————————————–* CLASS lcl_report DEFINITION.    PUBLIC SECTION.     METHODS:       create_shape         IMPORTING           if_type TYPE char30,        write_shape         IMPORTING           if_type TYPE char30.    PRIVATE SECTION.     DATA: o_shape TYPE REF TO lif_shape.  ENDCLASS.                    “lcl_report DEFINITION  *……selection screen……………………………..* PARAMETERS: p_shape TYPE char30.  *….. START-OF_SELECTION……………………………* START-OF-SELECTION.   DATA: o_report TYPE REF TO lcl_report,         w_type   TYPE char30.  * Name of the Class   CONCATENATE ‘LCL_’ p_shape INTO w_type.  * Instantiate the object   CREATE OBJECT o_report.  * Shape   o_report->create_shape( w_type ).   o_report->write_shape( w_type ).  *——————————————————–* *       CLASS lcl_report IMPLEMENTATION *——————————————————–* * *——————————————————–* CLASS lcl_report IMPLEMENTATION.    METHOD create_shape.  *   Instantiate the object based on the type     CREATE OBJECT me->o_shape TYPE (if_type).    ENDMETHOD.                    “create_shapes    METHOD write_shape.  *   write shape     me->o_shape->write_shape( ).    ENDMETHOD.                    “write_shapes  ENDCLASS.                    “lcl_report IMPLEMENTATION  *——————————————————–* *       CLASS lcl_circle IMPLEMENTATION *——————————————————–* * *——————————————————–* CLASS lcl_circle IMPLEMENTATION.    METHOD lif_shape~write_shape.     WRITE: ‘I am a Circle’.   ENDMETHOD.                    “lif_shape~write_shape  ENDCLASS.                    “lcl_circle IMPLEMENTATION  *——————————————————–* *       CLASS lcl_square IMPLEMENTATION *——————————————————–* * *——————————————————–* CLASS lcl_square IMPLEMENTATION.    METHOD lif_shape~write_shape.     WRITE: ‘I am a Square’.   ENDMETHOD.                    “lif_shape~write_shape  ENDCLASS.                    “lcl_square IMPLEMENTATION 

Now, if we want to add a new shape, say TRIANGLE. We can easily add a new shape without changing any existing code.
1. Create a new class LCL_TRIANGLE which implements the interface LIF_SHAPE
2. Implement the method LIF_SHAPE~WRITE_SHAPE in this class.