Tuesday 24 January 2012

Open Closed Principles - Applicable to JAIN SLEE SBBs?

One of the most important principles in Software Engineering is arguably the Open Closed Principle (OCP). Simply put, OCP stated that modules should be written in a manner that makes it possible to extend or change their behavior without having to modify their source code. In this sense, such modules are "open for extension" but "closed for modification".

This principle requires that the details of the workings of the modules should be hidden from potential "extension builders". The techniques for achieving OCP are necessarily based on Abstraction.

Robert C. Martin (2000) lists:
Dynamic Polymorphism - in this case the "function" or "behavior" is made to depend on an interface and not on a concrete object - this means the function will need no modification when a new "Interface implementation" is added, thus extending how the module behaves.

In JAIN SLEE SBBs, the SBBs are abstract classes, implementing the javax.slee.Sbb (which defines mainly Life-cycle methods such as:

    public void sbbCreate() throws javax.slee.CreateException {...}
    public void sbbPostCreate() throws javax.slee.CreateException {...}


So by having something like this: public abstract class IshmaelExampleSbb implements javax.slee.Sbb
I am extending how the "Sbb" behaves (to the person invoking methods on the javax.slee.Sbb Interface) by adding IshmaelExample-specific behavior - which is hidden from the user of javax.slee.Sbb Interface.

Now between SBBs (say IshmaelExampleSbb and IshmaelAnotherExampleSbb), JAIN SLEE Spec allows for a "Local Object" interface through which the developer can define some functions which these other SBBs can use to invoke certain behavior of the implementing SBB. Such an interface is define through the descriptor file (sbb-jar.xml) thus:

 <sbb-local-interface>
          <sbb-local-interface-name>

                 co.za.ishmael.labs.XmppServiceBuildingBlockLO
          </sbb-local-interface-name>
 </sbb-local-interface>


Again, following the OCP, by changing what happens  in the implementing SBB (which defines the interface) - I will be extending, from the viewpoint of the SBB that uses the said interface, how the function exposed by SBB behaves. For example -

public interface XmppServiceBuildingBlockLO {
       public void incrementXmppMessagesSent(long value);    
       public long getXmppMessagesSent();
}

The implementing SBB would invoke the above method thus (I am omitting JAIN SLEE specific details of attaining the Local Object):

_xmppServiceBuildingBlockLO.incrementXmppMessagesSent(300);



Assume that the above statement results in the value 300 added to the total number of XMPP Messages ("stanzas"). Now, say someone decides that it the value reached a certain threshold, after incrementing, the operation should be aborted. This changed how the incrementXmppMessagesSent(long value) behaves, but requires no code changes on the part of the module that invokes the method.
Another technique mentioned is the Static Polymorphism - technically, this is achieved through the use of Templates or Generics:

public class SomeGenericStuff<T extends SomeCommonGenericStuff>
{
   
    /**
     * T is the formal parameter of SomeGenericStuff 
         and stands for all "extensions" of  SomeCommonGenericStuff
     */
    private T genType;
    String defaultStuffFromBase;  
    public SomeGenericStuff(T concreteObject){
         this.genType = concreteObject;
          //assuming this is defined in the SomeCommonGenericStuff. I can do this... 
          this.defaulStuffFromBase = concreteObject.getStuff();
          ...
    }

In fact the above example speaks to to the next principle that Robert C. Martin discusses - The Liskov Substitution Principle (LSP). It states that the subclasses (that extend others) must be substitutable for their base classes (which they extend). So, from the above, I should be able to treat T as though it were SomeCommonGenericStuff.

Don't be caught in the midst of the The Circle/Ellipse Dilemma dilemma though.....

Have fun coding...

Ishmael





1 comment:

  1. The subtleties of the LSP lie in the statement that "the user of the base class should continue to function PROPERLY even if a derived class is passed to it":

    The keyword here is "properly" - that is, whatever the user/client expects to be true of the base class should still hold for derived classes as well. Derived classes are "instances" of the base class by definition, therefore the method that accepts the base would be quite happy to receive the derived classes (withough knowing or caring). The properties of the base class should make sense to the derived classes as well.

    Let's look at an example:
    It might be quite acceptable to consider a Cricle to be some derivative of an Ellipse - until you realize that a Circle only needs two data elements, the center and radius. Moreover, a person dealing with Ellipse (base) would rightly expect our Circle to pass the Ellipse-test. It would be nice to simply set the value of both Ellipse foci to the same value when we construct a circle - however this will fail the Ellipse-test:

    For instance:

    Ellipse(Point focusA, Point focusB, double majorAxis){
    setFoci(focusA, focusB);
    setMajorAxis(majorAxis);
    }
    ...assume we have getters for the properties: FocusA, FocusB and MajorAxis

    When constructing a Circle, we might do this:
    Circle(Point center, double radius){
    super(center,center, radius); }

    The equivalent of this is when the Circle object overrides the setFoci(Point,Point) method, ignoring the second argument, considering the first as its center like this:
    public void setFoci(Point a, Point b){
    focusA = a;
    focusB = a;
    }

    This is ok, until the user/client who expects all derivatives of Ellipse to be sustitutable for Ellipses does this when passed Circle object:
    public void checkIfEllipse(Ellipse e){
    if(e.getFocusA() == e.getFocusB(){
    throw new NotAnEllipseException("Not an Ellipse!!!");
    }
    }

    The above method expects all Ellipses to pass this particular test - but our Circle won't.

    It therefore goes without saying that properties such as Focus-A and Focus-B do not make sense in the case of a Circle, so the center property of Circle is taken to be Focus-A, and for completeness sake, the same value is assigned to Focus-B

    ReplyDelete