Favaor Skeletal implementation in java

Skeletal Implementation


Prefer Skeletal implementation..captain Jack sparrow


The skeletal implementation is a design, by which we can use the benefits of interface and abstract class together.
In Java Collection API has adopted this kind of design, AbstractSet , AbstractMap etc. are the example of Skeletal  implementation . Also Joshua Bloch mentions Skeletal implementation into his book “Effective java

In this article, we will see how we can efficiently design our system so it can use the feature of interface and Abstract class both.

Let try to understand it by a problem.

Suppose we want to create different types of Vending machines say candy vending, Soft drink vending etc. To get products from vending we need to start the vending machine then choose the product, pay for the same and collect it.
After that Vending machine should be stopped.

First Approach:
We can create a vending machine interface then for different product type we will create a concrete implementation of vending machine.
Code:
package com.example.skeletal;

public interface Ivending {
      
       void start();
       void chooseProduct();
       void stop();
       void process();
      

}

package com.example.skeletal;

public class CandyVending implements Ivending{

       @Override
       public void start() {
              System.out.println("Start Vending machine");
             
       }

       @Override
       public void chooseProduct() {
              System.out.println("Produce diiferent candies");
              System.out.println("Choose a type of candy");
              System.out.println("pay for candy");
              System.out.println("collect candy");
             
       }

      

       @Override
       public void stop() {
              System.out.println("Stop Vending machine");
             
       }

       @Override
       public void process() {
              start();
              chooseProduct();
              stop();
             
       }
      
      



      
}

package com.example.skeletal;

public class DrinkVending implements Ivending{

       @Override
       public void start() {
              System.out.println("Start Vending machine");
             
       }

       @Override
       public void chooseProduct() {
              System.out.println("Produce diiferent soft drinks");
              System.out.println("Choose a type of soft drinks");
              System.out.println("pay for drinks");
              System.out.println("collect drinks");
             
       }

       @Override
       public void stop() {
              System.out.println("stop Vending machine");
             
       }
      
       @Override
       public void process() {
              start();
              chooseProduct();
              stop();
             
       }

}

package com.example.skeletal;

public class VendingManager {
      
       public static void main(String[] args) {
              Ivending candy = new CandyVending();
              Ivending drink = new DrinkVending();
              candy.process();
              drink.process();
             
       }

}


Output :
Start Vending machine
Produce diiferent candies
Choose a type of candy
pay for candy
collect candy
Stop Vending machine
*********************
Start Vending machine
Produce diiferent soft drinks
Choose a type of soft drinks
pay for drinks
collect drinks
stop Vending machine



For simplicity, I do not divide each step as an individual method. In chooseProduct() I merge some steps.

Although it looks goods, the above code has some problems, if we see the codes carefully we can see There is a lot of duplicate codes. start(), stop() and process() methods do same thing in
Each concrete implementation.

Code duplication increases 3 times when the number of concrete implementation increases.

We can create a utility class and put common code into the same but that will break the single responsibility principal. Can introduce Shotgun surgery code smell.


Disadvantage of Interface:

As the interface is a contract and does not contain method body, each implementation has to fulfill the contract and provide an implementation of all methods. May some of the methods duplicate across the concrete implementation.


Approach 2:

We can overcome it through Abstract class.

Code:
package com.example.skeletal;

public abstract class AbstractVending {
      
       public void start()
       {
              System.out.println("Start Vending machine");
       }
       public abstract void chooseProduct();
       public void stop()
       {
              System.out.println("Stop Vending machine");
       }
       public void process()
       {
              start();
              chooseProduct();
              stop();
       }
      

}


package com.example.skeletal;

public class CandyVending extends AbstractVending{ //implements Ivending{

      

       @Override
       public void chooseProduct() {
              System.out.println("Produce diiferent candies");
              System.out.println("Choose a type of candy");
              System.out.println("pay for candy");
              System.out.println("collect candy");
             
       }     
      



      
}

package com.example.skeletal;

public class DrinkVending extends AbstractVending{ //implements Ivending{

      

       @Override
       public void chooseProduct() {
              System.out.println("Produce diiferent soft drinks");
              System.out.println("Choose a type of soft drinks");
              System.out.println("pay for drinks");
              System.out.println("collect drinks");
             
       }

}






package com.example.skeletal;

public class VendingManager {
      
       public static void main(String[] args) {
              AbstractVending candynew CandyVending();
              AbstractVending drinknew DrinkVending();
              candy.process();
              System.out.println("*********************");
              drink.process();
             
       }

}



Here, I provide common code implementation in to the abstract class. And Candyvending and DrinkVending extends this AbstractVending. This implementation gets rid of duplicate code but adds a new problem.

As, Candyvending and DrinkVending extends the Abstract class we can’I have provision to extends another class or does not support Multiple inheritance.

Say I want to add VendingServicing class which will clean and check the vending machine.

In this scenario I can’t extend VendingServicing as I have already extends AbstractVending. One thing I can do, create a composition but again we have to pass vending Machine into it which will strongly couple VendingServicing and Vending machine.

Disadvantage of Abstract class:

We can’t support Multiple inheritance due to diamond problem.


So It will be good if we can use advantages of both interface and Abstract class.


We call it Abstract Interface or Skeletal Implementation .

To achieve Skeletal Implementation ,

Step 1: Create an interface.
Step 2:  Create an Abstract class implement that interface and provide the implementation of Common method.
Step 3. In subclass create a private inner class which extends the Abstract class, Now this class can extend class implement any interfaces while using the common method by delegation call to Abstract class.









Let check the code,


package com.example.skeletal;

public interface Ivending {
      
       void start();
       void chooseProduct();
       void stop();
       void process();
      

}


package com.example.skeletal;

public abstract class AbstractVending implements Ivending{
      
       public void start()
       {
              System.out.println("Start Vending machine");
       }
       //public abstract void chooseProduct();
       public void stop()
       {
              System.out.println("Stop Vending machine");
       }
       public void process()
       {
              start();
              chooseProduct();
              stop();
       }
      

}

package com.example.skeletal;

public class CandyVending  implements Ivending{

      
       private class AbstractVendingDelegator extends AbstractVending
       {

              @Override
              public void chooseProduct() {
                     System.out.println("Produce diiferent candies");
                     System.out.println("Choose a type of candy");
                     System.out.println("pay for candy");
                     System.out.println("collect candy");
                    
              }
             
       }
      
       AbstractVendingDelegator delegator = new AbstractVendingDelegator();
      
       @Override
       public void start() {
             
              delegator.start();
             
       }
      
      

       @Override
       public void chooseProduct() {
             
              delegator.chooseProduct();
       }

      

       @Override
       public void stop() {
             
              delegator.stop();
             
       }

       @Override
       public void process() {
             
              delegator.process();
             
       }
      
      



      
}







package com.example.skeletal;



package com.example.skeletal;



public class DrinkVending extends VendingService  implements Ivending {

       private class AbstractVendingDelegator extends AbstractVending
       {

              @Override
              public void chooseProduct() {
                     System.out.println("Produce diiferent soft drinks");
                     System.out.println("Choose a type of soft drinks");
                     System.out.println("pay for drinks");
                     System.out.println("collect drinks");
                    
              }
             
       }
      
       AbstractVendingDelegator delegator = new AbstractVendingDelegator();
              @Override
       public void start() {
              delegator.start();
             
       }

       @Override
       public void chooseProduct() {
              delegator.chooseProduct();
             
       }

       @Override
       public void stop() {
              delegator.stop();
             
       }
      
      
      
       @Override
       public void process() {
              delegator.process();
             
       }

}










package com.example.skeletal;

public class VendingManager {
      
       public static void main(String[] args) {
              Ivending candy = new CandyVending();
              Ivending drink = new DrinkVending();
              //AbstractVending candy =  new CandyVending();
              //AbstractVending drink =  new DrinkVending();
              candy.process();
              System.out.println("*********************");
              drink.process();
             
              if(drink instanceof VendingService)
              {
                     VendingService vs = (VendingService)drink;
                     vs.service();
              }
             
       }

}

Please look the above design I create an interface then create an abstract class where I define all common implementation, then for each subclass implement a delegator class
And using that delegator we forward the call to AbstractVending.


Benefits of Skeletal Implementation :

1.    A subclass can extend other class like DrinkVending.
2.    Get rid of duplicate code by delegation call to Abstract class.
3.    If a subclass needs a new implementation of interface it can do so.








Conclusion: when your interface has some common method always create an Abstract class
And in a subclass, you can use this subclass as a delegator. Always try to use Skeletal Implementation 



Code Smell and Shotgun Surgery

shotgun surgery

The Code Smells are similar in concept to Development-level Anti-patterns. Sometimes in our code, we introduce code smell unintentionally those makes our design fragile.

Definition of Code smell
Code smell, also known as a bad smell, in computer programming code, refers to any symptom in the source code of a program that possibly indicates a deeper problem.

Martin fowler says:
 "a code smell is a surface indication that usually corresponds to a deeper problem in the system"


Code smell creates lot of problems while introducing new feature or maintains the codebase,
Often developer has to write repeatable code, breaking encapsulation, breaking abstraction etc.
If code smells are not corrected.

So always refactor code smell while developing.
 In this article, we discuss one of the popular code smell “SHOTGUN SURGERY
Shotgun surgery says, to introduce a small new change, a developer has to change many classes and methods, most of the time has to write duplicate codes which violate “Don’tRepeatYourself” principle.

Cause of Shotgun surgery smell:
1.    Due to poor separation of concern.
2.    Fail to understand the responsibility. Often due to misunderstanding (Single responsibility principle)
3.    Not identifying the common behavior or behavior with a slight change.
4.    Fail to introduce proper design pattern.
Consequences of Shotgun Surgery:
1.    Lots of duplicates code
2.    Taking more time to develop a small feature.
3.    Unmaintainable code base.


Refactoring strategy:

1.    We can do it by “Move Method”, “Move Field” or “Inline class

We will discuss above strategies in another article.


Let see an example, where “Shotgun Surgery” smell is present

package com.example.codesmell;

public class Account {
      
       private String type;
       private String accountNumber;
       private int amount;
      
       public Account(String type,String accountNumber,int amount)
       {
              this.amount=amount;
              this.type=type;
              this.accountNumber=accountNumber;
       }
      
      
       public void debit(int debit) throws Exception
       {
              if(amount <= 500)
              {
                     throw new Exception("Mininum balance shuold be over 500");
              }
             
              amount = amount-debit;
              System.out.println("Now amount is" + amount);
             
       }
      
       public void transfer(Account from,Account to,int cerditAmount) throws Exception
       {
              if(from.amount <= 500)
              {
                     throw new Exception("Mininum balance shuold be over 500");
              }
             
              to.amount = amount+cerditAmount;
             
       }
      
       public void sendWarningMessage()
       {
              if(amount <= 500)
              {
                     System.out.println("amount should be over 500");
              }
       }
      
      

}








package com.example.codesmell;

public class ShotgunSurgery {
      
       public static void main(String[] args) throws Exception {
              Account acc = new Account("Personal","AC1234",1000);
              acc.debit(500);
              acc.sendWarningMessage();
              //acc.debit(500);
       }

}

If we pay attention in Account.java file, we can see every operation debit(), transfer(),sendWarningMessage() has one validation, account balance should be more than 500.
And we copy the same validation in every method because of we not able to identify the common validation, so we introduce a “Shotgun Surgery” code smell.
The real problem occurs when we add another criterion in validation logic that is if account type is personal and balance is over 500 then we can perform above operations.

In this scenario, we have to make change in all methods which is not intended so let’s see how to solve it

Create a common method call isAccountUnderflow() that will solve the problem, all validation related stuff goes there.

Refactored code:

package com.example.codesmell;

public class AcountRefactored {
      
       private String type;
       private String accountNumber;
       private int amount;
      
      
      
       public AcountRefactored(String type,String accountNumber,int amount)
       {
              this.amount=amount;
              this.type=type;
              this.accountNumber=accountNumber;
       }
      
       private boolean isAccountUnderflow()
       {
              if(amount <= 500)
              {
                     return true;
              }
              return false;
             
       }
      
      
       public void debit(int debit) throws Exception
       {
              if(isAccountUnderflow())
              {
                     throw new Exception("Mininum balance shuold be over 500");
              }
             
              amount = amount-debit;
              System.out.println("Now amount is" + amount);
             
       }
      
       public void transfer(AcountRefactored from,AcountRefactored to,int cerditAmount) throws Exception
       {
              if(isAccountUnderflow())
              {
                     throw new Exception("Mininum balance shuold be over 500");
              }
             
              to.amount = amount+cerditAmount;
             
       }
      
       public void sendWarningMessage()
       {
              if(isAccountUnderflow())
              {
                     System.out.println("amount should be over 500");
              }
       }
      
      



}






Picture :