Java : Extensible Enum with Interface

 Extensible Enum with Interface

 

Overview

In Java Enum is a powerful data type. Many places user uses Enum To get advantages from Enum type. Like In a Static Factory method it wise decision to pass Enum type rather than pass String. Below , I mention some situations where using Enum is advantageous.

Enum Advantages

    1. In a Static Factory method passing Enum type as argument make the method typesafe. Method users can’t pass an arbitrary value to this method.
    2. Instead of using bit fields using Enum and EnumSet make the code cleaner and easy to maintain.
    3. Using Enum one can achieve Singleton pattern which is inherently Thread Safe.
    4. Using Strategy Enum Pattern one can get rid of If/Switch statements.
Although, Enum has several advantages but it has one major disadvantage.

Enum DisAdvantage

    1. Unlike Class, we can not extend Enum.
So the Question is

“Can we Extend Enum and When it is needed?”

Yes we can emulate the extensibility in Enum but we have to play in a strategic way ,
We know that Enum can’t be extended but we know the fact that an Interface can be extended and Enum can implement the Interface. So combining these two statements we can achieve extensibility. Where Enum can’t be extended but an interface can so If we use “Coding to an Interface” Strategy we can easily replace BaseType enum with Our Extended Enum.
Now coming to the use case,
According to Joshua Bloch ,“There is at least one compelling use case for extensible enumerated types, which is operation codes, also known as opcodes. An opcode is an enumerated type whose elements represent operations on some machine, such as the Operation”
Sometimes it is desirable to let the users of an API provide their own operations,effectively extending the set of operations provided by the API.”


According to Josh Bloch In a case of operation, we can provide some basic operation in an Enum like a simple calculator and basic operations are plus ,minus etc but if API Users want more like XOR , Square root, they can achieve it through extending Operation Interface and create a new Enum.

Steps For Achieving Extensibility


Let’s take a Use Case , We have an Enum call Direction in an API ,it has entries for NORTH,SOUTH,EAST,WEST but most of the cases API Users use these basic directions but when it is about to showing the shortest path between source and destination API Users need some advanced direction like NORTH-EAST,NORTH-WEST etc.


How judiciously we implement the API so Users can have the option to extend the Basic Direction Enum?


Steps.
    1.Create an Interface called Direction and declares a method called showDirection().
    2. Create a BasicDirection enum by implement Direction interface.
    3. Put entries for NORTH,SOUTH,EAST,WEST.
    4. Please note that as it implements Direction interface , each Enum has to override showDirection method.
    5. Create an AdVANCEDIRECTION enum with NORTH-EAST,NORTH-WEST etc entries.
    6. Use this Enum anywhere.
    Java Code :
    Direction Interface

package com.example.enumtest;

public interface Direction {
   
    public void showDirection();



BasicDirection Enum 

package com.example.enumtest;

public enum BasicDirection implements Direction{
   
    NORTH{

        @Override
        public void showDirection() {
            System.out.println("I am NORTH");
          
        }
      
    },
    SOUTH{

        @Override
        public void showDirection() {
            System.out.println("I am South");
          
        }
      
    },
    EAST{

        @Override
        public void showDirection() {
            System.out.println("I am EAST");
          
        }
      
    },
    WEST{

        @Override
        public void showDirection() {
            System.out.println("I am WEST");
          
        }
      
    }

}


AdvanceDirection Enum

package com.example.enumtest;

public enum AdvanceDirection implements Direction{
    NORTHEAST{

        @Override
        public void showDirection() {
            System.out.println("I am NORTH-EAST");
          
        }
      
    },
    NORTHWEST{

        @Override
        public void showDirection() {
            System.out.println("I am NORTH-WEST");
          
        }
      
    },
    SOUTHEAST{

        @Override
        public void showDirection() {
            System.out.println("I am SOUTH-EAST");
          
        }
      
    },
    SOUTHWEST{

        @Override
        public void showDirection() {
            System.out.println("I am SOUTH-WEST");
          
        }
      
    }


}
 


DirectionDriver Class 

package com.example.enumtest;

public class DirectionDriver {
   
    public static void printDirection(Direction op)
    {
        op.showDirection();
    }
   
    public static <T extends Enum<T> & Direction> void  printDirections(Class<T> clazz)
    {
        for(Direction direction : clazz.getEnumConstants())
        {
            direction.showDirection();
        }
    }
   
    public static void main(String[] args) {
      
        DirectionDriver.printDirection(BasicDirection.EAST);
        DirectionDriver.printDirection(AdvanceDirection.SOUTHWEST);
      
        DirectionDriver.printDirections(BasicDirection.class);
        DirectionDriver.printDirections(AdvanceDirection.class);
      
    }

}

 

Output

I am EAST
I am SOUTH-WEST
I am NORTH
I am South
I am EAST
I am WEST
I am NORTH-EAST
I am NORTH-WEST
I am SOUTH-EAST
I am SOUTH-WEST





Please pay attention to the class DirectionDriver, here I create two methods
    printDirection which takes Direction as Input and I pass an Enum to it.
    Another version is printDirections method it prints all Enum entries to achieve that
I make a generic type which says T must be an Enum and a subtype of Direction by <T extends Enum<T> & Direction> . then pass class instance so I can iterate over EnumConstants.








 




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