Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OO Design: inheritance vs type (enum) variable

Is there a general best practice when to use inheritance:

public class Information {
  private String text;
}

public class Message extends Information {
}

public class Faq extends Information {
}

public class News extends Information {
}

and when to use enum as a member variable to distinguish:

public enum InformationType {
  MESSAGE, FAQ, NEWS
}

public class Information {
  private String text;
  private InformationType type;
}

In the code that I work with I have the second - a type field and what bothers me is the code

public void displayInformation(final Information information) {
  if (information.getType == InformationType.MESSAGE) {
     ....
  } else if (information.getType == InformationType.FAQ) {
     ....
  } else if (information.getType == InformationType.NEWS) {
     ....
  } else {
     // some error handling
  }
}

This is definitely a code smell and I don't see how you can have the enum as member variable and not have these if-elses!? If that is true, then you should never have enums as types for the objects, but I see this "pattern" so often!

like image 677
user1414745 Avatar asked Dec 06 '16 18:12

user1414745


People also ask

Is enum inheritance?

Inheritance Is Not Allowed for Enums.

What can I use instead of enum?

Alternatives to enums: string unions and object literals.

Why enums are better than constants Java?

Enums limit you to the required set of inputs whereas even if you use constant strings you still can use other String not part of your logic. This helps you to not make a mistake, to enter something out of the domain, while entering data and also improves the program readability.

Why enum is Typesafe?

The enums are type-safe means that an enum has its own namespace, we can't assign any other value other than specified in enum constants. Typesafe enums are introduced in Java 1.5 Version. Additionally, an enum is a reference type, which means that it behaves more like a class or an interface.


2 Answers

Different tools for different jobs.

1. Use an enum class to declare a set of fixed constants known at compile time.

Good examples of things that are conveniently and cleanly described by enum classes are: suits and ranks from a deck of playing cards, and planets in the solar system. Both example have in common the fact that the objects are constant (or reasonably constant; there is no law against evolving an enum class) and can all be instantiated at the start of the execution of your program.

Enum classes are otherwise very similar to ordinary Java classes. You can add constant-specific data to each constant by defining instance fields and a constructor in the enum class, and you can define constant-specific behavior by writing constant-specific methods.

It is easy to write rich enum types in Java and they can be quite powerful. Used correctly, they will make your code more streamlined and easier to maintain. Enum classes are often the best choice WHEN you are dealing with a fixed set of constant objects.

Enums with constant-specific methods often make your program code much easier to read and robust to maintain. For example, instead of:

public void displayInformation(final Information information) {
    if (information.getType == InformationType.MESSAGE) {
        ....
    } else if (information.getType == InformationType.FAQ) {
        ....
    } else if (information.getType == InformationType.NEWS) {
        ....
    } else {
        // some error handling
}

You could define an abstract method in the enum class InformationType called, for example, displayInfo() and then write constant-specific methods like this:

public enum InformationType {
    MESSAGE {
        @Override 
        public String displayInfo() {
            ....
        }
    },
    FAQ {
        @Override 
        public String displayInfo() {
            ....
        }
    },
    :
    :
    LAST_TYPE {
        @Override
        public String displayInfo() {
            ....
        }
    };

    abstract public String displayInfo();
}

Using constant-specific methods in this way in rich enum types is a very good alternative to using switch-case blocks in your code which can become quite ugly very quickly. Once you have implemented this rich enum type, you could replace your original method with the much, much cleaner and easier to read:

public void displayInformation(final Information information) {
    information.getType().displayInfo();
}

2. Use a subclass when you wish to use an arbitrary number of objects that have an "is a" relationship with a superclass.

A subclass is an ordinary Java class that extends a superclass. The right time to use a hierarchy like this is when the instances of your class all have an "is a" relationship with a superclass. The prototype example would be superclass Bicycle and subclass MountainBike. All mountain bikes are bicycles.

If there is no well-defined "is a" relationship between the subclass and superclass, you are likely much better off not using inheritance at all (favor composition instead of inheritance).

3. Avoid the trap of using enum constants to describe a hierarchy.

For example, you have a class Shape with an instance field that holds an enum constant of type ShapeType (and you write the enum class to have constants CIRCLE, RECTANGLE, TRIANGE).

When such Tagged Classes are simple, sometimes using an instance field as a tag is the right thing to do, but avoid the pitfall of using tags as a thinly veiled disguise for a class hierachy. It could certainly be true that the best and cleanest way to represent Shape is as an abstract superclass with subclasses Circle, Rectangle, and Triangle (all of these -are- shapes).

like image 154
scottb Avatar answered Oct 16 '22 22:10

scottb


"Strategy pattern" usually solves problems like this.

"Strategies eliminate conditional statements. The Strategy pattern offers an alternative to conditional statements for selecting desired behavior. When different behaviors are lumped into one class, it's hard to avoid using conditional statements to select the right behavior. Encapsulating the behavior in separate Strategy classes eliminates these conditional statements." http://java.boot.by/scea5-guide/ch07s03.html

like image 36
Gedion D Avatar answered Oct 16 '22 22:10

Gedion D