1

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.

5
  • 2
    Why don't you use enums? Enums can have fields, methods and constructors. Commented Oct 5, 2013 at 20:59
  • Making the Item class an Enum is a good suggestion. But this does not fully solve the problem. I'll still have 20+ constructors... one for each enum constant. Commented Oct 5, 2013 at 21:02
  • @firyice Can you use varargs to combine them all? Commented Oct 5, 2013 at 21:03
  • @PeterLawrey It could be done, but I do not think varargs makes sense here. The type-dependent parameters in each constructor vary in nature. Commented Oct 5, 2013 at 21:13
  • @firyice Instead of multiple constructors you can use multiple factory methods. This would make sure the type matches the arguments used. Commented Oct 5, 2013 at 21:14

2 Answers 2

3

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();
}
Sign up to request clarification or add additional context in comments.

2 Comments

"Your instances don't have different types, they are different types." thanks for the 'Aha!' moment. Exactly what I needed to hear. I decided to use this approach along with Ghostkeeper's answer for enforcing scope.
You might consider the Abstract Factory Pattern - see edit to answer for something I think you will like
1

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.