Sitemap

Abstract SuperClass

5 min readJan 16, 2023

--

[[Design Patterns — 3]]

Concept of Abstract SuperClass:

An Abstract SuperClass is an abstract class which encapsulates the fields and methods that its sub types might need. It can also be used to provide default implementations of certain methods that can be overridden by sub types. It can also contain abstract instance methods which must be overridden by the sub types. Hence Abstract SuperClass can be used to write high level abstract logic needed by sub types.

Characteristics of Abstract SuperClass:

  • Ensures that logic common to its related classes are implemented consistently in one place.
  • Helps maintain the code reused in several classes in one place.
  • Helps sub types by implementing most of the required functionalities. Sub types only need to implement the abstract methods. Of course the sub types are always allowed to improve the implementations of non-private instance methods through method overriding.

Abstract SuperClass Design:

Press enter or click to view image in full size

AbstractSuperClass: Class in this role contain the code of the reusable logic of some of the operations that are common across all its subclasses. Class in this role may either declare abstract methods for the subtype specific implementations or provide a default implementation for these variable operations. Class in this role can help define instance fields that the type should contain when subclassing it. Class in this role can also help define static fields and static methods that could be shared by the sub types.

Get Prince Jha’s stories in your inbox

Join Medium for free to get updates from this writer.

ConcreteClassA: Class in this role need to provide the implementations of the variable operations according to the purpose of the class. They inherit common functionalities from Abstract Super Class.

ConcreteClassB: Class in this role need to provide the implementation of the variable operations according to the purpose of the class. They inherit common functionalities from Abstract Super Class.

Example: Let us assume that the AbstractShape class contains a logic which mentions the name of the shape and prints the area of the shape. This logic is common to all the shapes and hence can become a part of the common logic in Abstract SuperClass.
On the other hand the dimensions of the shape and how the area of the shape is calculated is only known to the respective shape which forms the variable logic.

Press enter or click to view image in full size
abstract class AbstractShape {
private String nameOfShape;

public static final String Square = "Square";
public static final String Rectangle = "Rectangle";
public static final String Circle = "Circle";

protected AbstractShape(String name) {
nameOfShape = name;
}

abstract public double calculateArea();

public void printArea(){
double area = this.calculateArea(); // dynamic binding
System.out.println("The area of the " + nameOfShape + " = " + area);
}
}

class Rectangle extends AbstractShape {
private double length;
private double breadth;

public Rectangle(String name, double length, double breadth) {
super(name);
this.length = length;
this.breadth = breadth;
}

public double calculateArea() {
return length * breadth;
}
}

class Circle extends AbstractShape {
private double radius;

public Circle(String name, double radius) {
super(name);
this.radius = radius;
}

public double calculateArea() {
return Math.PI * radius * radius;
}
}

public class MyClass {
public static void main(String [] args) {
AbstractShape rect = new Rectangle(AbstractShape.Rectangle, 42.1, 13.7);
AbstractShape circ = new Circle(AbstractShape.Circle, 12.5);

rect.printArea();
circ.printArea();
}
}

The AbstractIterator class in Java is an abstract super class that contains common logic used by the Iterator corresponding to various collections.
The abstract class throws an UnsupportedOperationException as a default implementation of the remove method.

Interface and Abstract SuperClass Design:

The idea of interface and abstract superclass design is to allow the users to access the objects only through an interface thus achieving independence between the client and the implementation classes. Clients can only access a service using an interface. The fields that are needed in each of the subtype objects and the common logic can be encapsulated in the abstract super class. The variable logic can be provided by each of the concrete classes.

Press enter or click to view image in full size

Interface vs Abstract SuperClass:

Since Java interfaces now support default implementations of instance methods, private instance methods, static public methods, static private methods, static fields to be defined on them. They also support inheriting from multiple other interfaces. Can these features be used to replace Abstract SuperClass Design altogether?

The reason for this line of thinking is that a class can only inherit from one super class so the Abstract SuperClass Design is a compromise. Can we avoid inheriting from a class but instead inherit from an interface which still provides us the functionality and we can continue to inherit from other interfaces, one other class.

The answer is “Not Always”. Remember that a class is allowed to define instance fields on the object and write code logic that operates on these fields, interfaces cannot do that. Thus Abstract SuperClass do provide extra convenience in terms of defining the objects and encapsulating common logic.

Although there are scenarios where we do not need an Abstract Super Class but an interface would be sufficient. The compose method in Function functional interface only needs the feature like dynamic binding of apply method but does not need any fields added on the object. It only needs common logic to be encapsulated in a default method. An interface is sufficient to provide the required flexibility.

package java.util.function;
import java.util.Objects;

@FunctionalInterface
public interface Function<T, R> {

R apply(T t); // single abstract method

default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> this.apply(before.apply(v));
}

default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(this.apply(t));
}

static <T> Function<T, T> identity() {
return t -> t;
}
}

--

--

Prince Jha
Prince Jha

Written by Prince Jha

Software Developer, Computer Science Engineer, Programmer, Gamer.