Finalizing the solution

We started with a solution that solved the problem at hand. After creating a basic pipeline, we created an elaborate pipeline, which made the solution extensible. Now we can add new commands without recompiling the application. This is very important in the case of applications that are governed by amendable laws. To make our code robust, we will add the design by contract strategy to our command objects.

Design by contract and template method pattern

The design by contract idiom, created by Bertrand Meyer (creator of the Eiffel language), extends the ordinary definition of abstract data types with preconditions, post conditions, and invariants. To execute any contract in real life, we need to satisfy some preconditions, followed by execution, and a post execution (verification) phase as listed here:

  • Pre-Execute
  • Execute
  • Post-Execute

At the end of the Post-Execute phase, the invariants are checked to see whether they are violated. The consumer will call PreExecute to determine whether there is a context for the execution of the contract. The invocation will proceed only if PreExecute returns true. To incorporate design by contract, we extend the interface with two additional methods. The resultant interface is given as follows:

    public interface ComputationCommand 
    { 
      bool PreExecute(COMPUTATION_CONTEXT ctx); 
      bool Execute(COMPUTATION_CONTEXT ctx); 
      bool PostExecute(COMPUTATION_CONTEXT ctx); 
    } 

We will create a BaseComputationCommand class, which will stub the methods in the ComputationCommand interface. This will help the concrete, derived command classes to override only those methods which have the respective changes. After redefining the interface, we will create a default implementation of the command pattern with methods marked as virtual. This helps us to override the implementation in the derived class. This is an instance of the template method pattern:

    public class BaseComputationCommand : ComputationCommand 
    { 
      public virtual bool PreExecute(COMPUTATION_CONTEXT ctx) { return      
      true;  }  public virtual bool Execute(COMPUTATION_CONTEXT ctx) {  
      return true; } public virtual bool  
      PostExecute(COMPUTATION_CONTEXT ctx) { return true; } 
    } 

Our commands here will use the template method pattern to override only those methods that are relevant. Otherwise, there is already a fallback in the BaseComputationCommand. The template method pattern defines the program skeleton of an algorithm(s) in a method, and they are called template method(s). These template methods are overridden by sub-classes, which implement the concrete logic:

    public class SeniorCitizenCommand : BaseComputationCommand { 
      public override bool PreExecute(COMPUTATION_CONTEXT ctx) 
      { 
        TaxDTO td = (TaxDTO)ctx.Get("tax_cargo"); 
        //--- Do Some Sanity Checks 
        //--- if some problems => return false; 
        return base.PreExecute(ctx); 
      } 
      public override bool Execute(COMPUTATION_CONTEXT ctx) 
      { 
        TaxDTO td = (TaxDTO)ctx.Get("tax_cargo"); 
        //---- Compute the Tax for Senior Citizens 
        //---- They belong to different Slabs 
        td.taxparams.TaxLiability = 1000; 
        td.taxparams.Computed = true; 
        return true; 
      } 
 
      public override bool PostExecute(COMPUTATION_CONTEXT ctx) 
      { 
        //--- Do the Check on Invariants 
        //--- Return false, if there is violation 
        return base.PostExecute(ctx); 
      } 
    } 

We need not override every method, and yet, the whole scheme would still work:

    public class SeniorCitizenFemaleCommand : BaseComputationCommand 
    { 
      public override bool Execute(COMPUTATION_CONTEXT ctx) 
      { 
        TaxDTO td = (TaxDTO)ctx.Get("tax_cargo"); 
        //---- Compute the Tax for Senior Females 
        //---- They belong to different Slabs 
        double accum = td.taxparams.Basic + 
        td.taxparams.DA + td.taxparams.Allowance + 
        td.taxparams.HRA; 
        double net = accum - td.taxparams.Deductions - 
        td.taxparams.Surcharge; 
        //---- Flat 10% Tax 
        td.taxparams.TaxLiability = net*0.1; 
        return true; 
      } 
    } 

Now we need to rewrite the command pattern to reflect the implementation of the design by contract idiom in the command classes:

    public class CommandDispatcher 
    { 
      private static ObjectFactory obj = new      
      ObjectFactory("Pluggins.xml");  public static bool  
      Dispatch(string archetype,    COMPUTATION_CONTEXT ctx) 
      { 
        ComputationCommand cmd = obj.Get(archetype); 
        if (cmd == null)  
        return false; 
 
        if (cmd.PreExecute(ctx)) 
        { 
          bool rs = cmd.Execute(ctx); 
          cmd.PostExecute(ctx); 
          return rs; 
        } 
        return false; 
      } 
    } 

In some implementations, the clients will check the return value to see whether invariants have been violated. In some cases, a compensating transaction will be executed to restore the state to the previous one.

Using the Facade pattern to expose the computation API

Our computation engine contains a lot of classes that coordinate to implement the application logic. Any client who wants to interact with this implementation would prefer a simplified interface to this subsystem. A facade is an object that provides a simple interface to a large body of code in large classes or modules.

The GoF facade pattern is a mechanism that we can use to expose a coarse-grained API:

    public class TaxComputationFacade 
    { 
       /// <summary> 
       ///  A Rule Engine can do Archetype detection 
       ///  One can write a small Expression Evaluator Engine  
       ///  and GOF terms its Interpreter pattern 
       /// </summary> 
       /// <param name="te"></param> 
       /// <returns></returns> 
       private static string ComputeArchetype(TaxableEntity te) 
       { 
         if ((te.Sex == 'F') && (te.age > 59)) 
         { 
           return "SeniorCitizenFemale"; 
         } 
         else if (te.age<18) { 
           return "JuevenileCitizen"; 
         } 
 
         return (te.age > 60) ? "SeniorCitizen" : "OrdinaryCitizen"; 
       } 
 
       public static bool Compute(TaxableEntity te) 
       { 
         string archetype = ComputeArchetype(te); 
         COMPUTATION_CONTEXT ctx = new COMPUTATION_CONTEXT(); 
         TaxDTO td = new TaxDTO { id = te.id, taxparams = te.taxparams         
         }; 
         ctx.Put("tax_cargo", td); 
         return CommandDispatcher.Dispatch(archetype, ctx); 
       } 
    } 

Now the ViewHandler has become much simpler, as shown in the following code:

    public static void ViewHandler(TaxCalcForm tf) 
    { 
      TaxableEntity te = GetEntityFromUI(tf); 
      if (te == null) 
      { 
        ShowError(); 
        return; 
      } 
      bool rs = TaxComputationFacade.Compute(te); 
      if (rs)  
      { 
        tf.Liabilitytxt.Text =       
        Convert.ToString(te.taxparams.TaxLiability); 
        tf.Refresh(); 
      } 
    } 
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.223.114.142