DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
  1. DZone
  2. Coding
  3. Java
  4. Favor Skeletal Implementation in Java

Favor Skeletal Implementation in Java

Got duplicate code? Learn how to get rid of it while making what's left work for you. A skeletal implementation gets your interface and Abstract class working together.

Shamik Mitra user avatar by
Shamik Mitra
·
Sep. 22, 16 · Tutorial
Like (26)
Save
Tweet
Share
17.51K Views

Join the DZone community and get the full member experience.

Join For Free

Skeletal implementation is a design by which we can use the benefits of the interface and abstract class together.

The Java Collection API has adopted this kind of design: AbstractSet, AbstractMap, etc. are examples of Skeletal interfaces. Also, Joshua Bloch mentions skeletal interfaces in his book “Effective Java.”

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

Let's try to understand it with a problem.

Suppose we want to create different types of vending machines. To get products from the machines, we need to start the vending machine, then choose the product, pay for it, then collect it.

After that, the vending machine should be stopped.

First Approach

We can create a vending machine interface then for different product types. So, to get started, we will create a concrete implementation for a 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 did not divide each step as an individual method. In chooseProduct(), I merged some steps.

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

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

We can create a utility class and put common code into it, but that will break the single responsibility principal and can introduce the 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. Some of the methods might duplicate across the concrete implementation.

Second Approach

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 { 

    @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 { 
    @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 candy =  new CandyVending();
        AbstractVending drink =  new DrinkVending();
        candy.process();
        System.out.println("*********************");
        drink.process();
    }
}


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

As CandyVending and DrinkVending extend the Abstract class, we can't have provision to extends another class or does not support Multiple inheritances.

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

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

Disadvantage of Abstract Class

We can’t support multiple inheritances due to the diamond problem. It would be great if we could use the advantages of both interface and Abstract.

But wait, there is.

Abstract Interface or Skeletal Implementation

To achieve skeletal implementation:

Step 1: Create an interface.

Step 2: Create an Abstract class that implement that interface and provides the implementation of common methods.

Step 3: In the subclass, create a private inner class, which extends the Abstract class. Now this class can extend and implement any interfaces while using the common method by delegating calls to the Abstract class.

Code

package com.example.skeletal;

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

package com.example.skeletal;

public class VendingService {

public void service()
{
System.out.println("Clean the vending machine");
}

}


package com.example.skeletal;
public abstract class AbstractVending implements Ivending {
    public void start()
    {
        System.out.println("Start Vending machine");
    }

    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();

        candy.process();
        System.out.println("*********************");
        drink.process();

        if(drink instanceof VendingService)
        {
            VendingService vs = (VendingService)drink;
            vs.service();
        }
    }
}

/**
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
Clean the vending machine
**/

Looking at the above design, I create an interface, then create an abstract class where I define all common implementations. Then, for each subclass, I implement a delegator class.

And using that delegator, we forward the call to AbstractVending.

Benefits of Skeletal Implementation

  1. A subclass can extend other classes, like DrinkVending.

  2. Get rid of duplicate code by delegating calls to the Abstract class.

  3. If a subclass needs a new implementation of interface, it can do so.

Conclusion

When your interface has some common methods, always create an Abstract class. You can then use subclasses a delegator. Always try to use skeletal implementation.

Implementation Interface (computing) Java (programming language)

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Microservices 101: Transactional Outbox and Inbox
  • Strategies for Kubernetes Cluster Administrators: Understanding Pod Scheduling
  • DevOps for Developers — Introduction and Version Control
  • A Deep Dive Into AIOps and MLOps

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: