Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is decorator pattern suitable here

Tags:

java

decorator

So I have a couple of classes, A, B, C, ... N. And a couple of possible properties, setP(), setQ(), setR(), ... setZ(). Now, each class can have a different combination of properties:

A          B          C          N
- setX     - setR     - setP     - setY
- setY     - setZ     - setQ     - setZ
- setZ                - setR
                      - setS
                      - setZ

all of the setters return an instance of the object itself so that chaining is possible.

im trying to find an elegant way to solve this problem. i dont want to redefine the tens of properties in each class (code duplication) and i dont wanna use inheritance since the there will be ugly, pointless intermediate classes (BaseABCD -> setZ) and cuz the base class setter will return an instance of type BaseABCD which will not allow full chaining of all properties.

here are some possible things im trying to look into:

  1. somehow define each property as a decorator in an elegant fashion, and compose A something like Base.decorators().addX().addY().addZ().finalize().

  2. define all possible properties in Base and hide non-required properties in derived classes. I dont think this is possible.

Is any of these things possible? or is there a better idea to solve a problem like this one?


More details

The classes are basically different message types used by the application to communicate with an external system. Different messages contain different fields of different types. For instance:

ECHO message:
- Timestamp (DateTime)
- SourceAddress (String)

CHECK RESOURCE message:
- Timestamp (DateTime)
- Resource Identifier (Integer)

MANIPULATE RESOURCE
- Resource Identifier (Integer)
- Operation Type (Enum)

The messages are serialized as a string when transmitted and type information is not retained. So I chose to go with a HashMap<String, String>. Each property corresponds to a key within that class's hashmap. When serializing, I just iterate over all key/value entries in that class's hashmap and produce a string message representation to be sent.

However, I want to enforce types towards the caller code. So I dont want to expose a method like A.set('RESOURCEIDENTIFIER', '123456'). Instead I want to expose methods like A.setResourceIdentifier(123456) and C.setOperation(OperationType.DELETE).

Internally, these setter functions are simply putting a relevant key in the hashmap and assigning a string value to it.

public A setOperation(OperationType operation) {
    this.hashmap.put('OPERATION', operation.name());
    return this;
}

There are about 40 unique field types and all messages employ a unique subset of these fields. Like A and B contain setTimestamp(). B and C contain setResourceIdentifier(). Only C contains setOperationType(). And so on.

I don't want to redefine these tens of properties in each class over and over again. That's why I want to explore the following two options:

Option 1 Define a Base class with ALL possible properties and in derived A class, override only the required properties to be public. This is do-able. But I want to see if it is possible to implement something like described in option #2.

Option 2 Somehow define decorators and a factory class such that

public ? getA() {
    return Base.startDecorating()
        .addTimestamp()
        .addResourceIdentifier()
        .finalize();
}

? objA = Factory.getA()
    .setTimestamp(DateTime.now())
    .setResourceIdentifier(123456);

Can this be possible? While writing out this question, I realized that Option 1 should be the way to go. It's simple and less error prone. But just out of curiosity I want to know if decorator pattern can be used here. After all, what I have here is a complete set of independent modules (properties) and different ways of assembling them together (the classes).

like image 657
AweSIM Avatar asked Feb 16 '26 08:02

AweSIM


2 Answers

A decorator is made for something that you can keep adding to. Like a xmas tree. But once the xmas tree is decorated, then you use common methods like turnOn(). Its not made for adding new methods to an existing API. I think you can use regular old inheritance for this. I would also use examples like List. Your Base class can provide the common methods that all will share, and then each other sub-class will add new methods.

To help keep things clean you can add a Factory or a Builder to make things easier.

Here are some examples. Assuming base class is called Base, and sub-classes are one letter named classes, A, B, etc.

  1. Factory example 1.

    Base x = factory.getInstance(A.class);

  2. Factory example 2.

    Map props = ...; Base x = factory.getInstance(A.class, props);

  3. Builder example 1.

    Base x = new ABuilder().setX(x).setY(y).setZ(z).create();

This involves creating a builder class for each individual sub-class. The sub-class builder classes may or may not inherit from an abstract BaseBuilder class that defines a method with signature public Base create();.

like image 136
Jose Martinez Avatar answered Feb 18 '26 22:02

Jose Martinez


If the setters of the properties have some logic in them which is shared across multiple main classes, a decorator pattern can fit and be a good design approach. If not, e.g. you are sure that all they will ever do is this.x=x or that each class sets a property of some type diffrently then no.

You can define a class for each of the properties, and have your main classes have variables of types of the properties classes. this way when a property is being set on the main class it delegates the job to the setter of the appropriate property class, This way you define the setters of your properties once and use them everywhere. Again, in case you have more logic then this.x=x; in your setters this may be the best idea.

Oh and you can still return this for chainning after delegating the set job to the property class.

Also, if you are lazy and care less for real time performance, you can use reflections to ease the coding of your main classes.

like image 41
Ofek Ron Avatar answered Feb 18 '26 22:02

Ofek Ron



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!