firyice
firyice

Reputation: 527

Pattern for an enum-like class with many different types / constructors

I am working with a communication standard (over which I have no control) that defines data items to be sent/received in various packets.

Each item is defined by a custom type, and type-dependent information. These items will rarely change. I'd like to be able to limit the scope of item constructors to one place, and define all items there once as public static (similar to an enum).

Currently, I have an enum for the types, and a class for the items.

enum Type {TYPE_A, TYPE_B, ...}

public class Item {
    public static Item ITEM_1 = new Item(TYPE_A, someNumber);
    public static Item ITEM_2 = new Item(TYPE_B, someNumber, someMap);
    public static Item ITEM_3 = new Item(TYPE_A, someNumber);
    ...

    // Constructor for type A
    private Item(Type type, int param1) {...}

    // Constructor for type B
    private Item(Type type, int param1, Map param2) {...}

    ...

    // Type-dependent methods
    public String decode(byte[] data) {
        switch(this.type) {
        case TYPE_A:
            ...
        }
        case TYPE_B:
            ...
        }
        ...
    }
}

This has become unmanageable, and I am looking for a better pattern / structure.

I could have an abstract Item class, and subclass for each type. With this setup, I don't know how I could define all the items in one place without making all the constructors public.

Upvotes: 1

Views: 489

Answers (2)

Bohemian
Bohemian

Reputation: 425248

Your instances don't have different types, they are different types.

Step 1: Delete your enum entirely.

Step 2: Create an abstract class:

public abstract class Item {
    public abstract String decode(byte[] data);
    // other common stuff
}

Step: Create subclasses for each type that provide implementations for the decode method:

public class ItemType1 extends Item {
    public String decode(byte[] data) {
        // implementation for this type
    }
}

Any time you find yourself using a case or instanceof to decide which code to run, consider breaking out new classes that override the method. This is all straightforward class hierarchy stuff.


Edit:

If you wanted to use an enum to self-document the range of types available, you could do this:

enum Type {
    TYPE_A () {
        public Item createItem() {
            return new ItemType1();
        }
    },
    TYPE_B () {
        public Item createItem() {
            return new ItemType2();
        }
    },
    ...;
    public abstract Item createItem();
}

Upvotes: 3

Ghostkeeper
Ghostkeeper

Reputation: 3050

I'd try to use the abstract class structure, like you described: An abstract item class, and subclasses for each type.

However, since you have that many different types, I suggest you put them in a separate package, and then make the constructors of the subclasses package-local. That way you can all construct them in the static section of the abstract class, while still protecting external code from trying to construct an item.

Upvotes: 1

Related Questions