Java Abstraction: abstract class delegates abstractions vs. concrete class uses common abstractions

sum3sh

Say i am implementing a simple interface like Animal. Broadly speaking, there are two ways to go about doing that:

  1. implement the interface's method in the abstract class, and create abstract methods for any implementation specific logic needed.
  2. implement the interface method in the concrete classes, and move the common logic in the abstract class with protected access.

I'd like to understand if-

  • there are any objective benefits of one way over the other in terms of design.
  • is it recommended to stick with just one way in a single project (code maintainability/readability).
public interface Animal {
    void makeSound();
}

public abstract class BaseAnimal implements Animal {
    @Override
    public void makeSound() {
        //do something which is common for all implementaions
        doSomeImplementaionSpecificStuff();
    }

    public abstract void doSomeImplementaionSpecificStuff();
}

public class Dog extends BaseAnimal implements Animal {
    public void doSomeImplementationSpecificStuff(){
       //do something specific to a dog
    }
}
public abstract class BaseAnimal implements Animal {
    public void doCommonStuff() {
        //any common logic that can be shared between concrete  implementation goes here
    }
}

public class Dog extends BaseAnimal implements Animal {
    @Override
    public void makeSound() {
       doCommonStuff();
       //do something specific to a dog
    }
}

davidxxx

The two ways are not always interchangeable.
Your first example sets a constraint for subclass that requires to implement a specific method that is a part of the makeSound() method.
Using that way couples strongly the implementation of the subclass to which one of the parent class.
Besides, the subclass may still subclass makeSound() as it is not final.
So I would use that way only for very specific scenarios :

  • to define a factory method that subclass have to define because parent class relies on that (abstract factory)
  • to define a general algorithm and let subclass to define some specific parts of that algorithm (template method).

In the general case you want to use the code of the second example but by doing BaseAnimal a Animal too :

public abstract class BaseAnimal implements Animal {
    public void doCommonStuff() {
        //any common logic that can be shared between concrete  implementation goes here
    }
}

public class Dog extends BaseAnimal implements Animal {
    @Override
    public void makeSound() {
       doCommonStuff();
       //do something specific to a dog
    }
}

Note that in Java 8, default interfaces rely you from defining the abstract class that only defines common methods :

public interface Animal {
    void makeSound();
    default void doCommonStuff() {
            //any common logic that can be shared between concrete  implementation goes here    
}

Note also that exposing doCommonStuff() in the API of the abstract class is not necessarily fine. Is client should be able to call it ? If that is an implementation detail, you could extract it into a support class (AnimalDelegate) and favor composition over inheritance by composing an AnimalDelegate into the Animal subclasses.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related