gstackoverflow
gstackoverflow

Reputation: 36984

How to replace switch with polymorphism?

I have 2 applications: application_1 and applicaion_2

appplication_1 sends messages of different types to application_2

there several types. I can declare enum of these types.

enum MessageType{
   TYPE_1,
   TYPE_2,
   ...
}

In application_2 framework I use suggest me write following API

public void handle(Object o){
    //logic
}

I think about how to build classes to process each message separately.

I understand that I can declare common type for all messages:

abstract class AbstractMessage{
    MessageType type;
    Object o; 
    //...   
}

and in application_2 inside handle I can write smth like this:

MessageType mt = ((AbstractMessage) o).getType();
  switch(mt){
     case TYPE_1: 
        //handle TYPE_1
        break;
     case TYPE_2: 
        //handle TYPE_2
        break;
        ....
  }

But this code looks ugly.

Please, help to find nicer solution.

Upvotes: 3

Views: 1656

Answers (4)

Ray Tayek
Ray Tayek

Reputation: 10003

maybe something like below. it does have a switch, but the code for each type is together in the enum.

public class So43459907 {
    public enum Type {
        m1 {
            @Override Object create(Object o) {
                return o;
            }
            @Override void handle(Object o) {}
        },
        m2 {
            @Override Object create(Object o) {
                return o;
            }
            @Override void handle(Object o) {}
        };
        abstract Object create(Object o);
        abstract void handle(Object o);
        public static Object create(Type type,Object o) {
            switch(type) {
                case m1:
                    return m1.create(o);
                case m2:
                    return m2.create(o);
                default:
                    throw new RuntimeException("oops");
            }
        }
    }
    public static void main(String[] args) {
        // TODO Auto-generated method stub
    }
}

Upvotes: 0

user208608
user208608

Reputation:

You can use the chain-of-responsibility pattern. It differs from the strategy pattern in that your messages are indicating commands that each application performs. This is essentially what switch is doing.

You dynamically load classes that implement an interface with your handle method (Pavlo's class works with some modification to combine it with Loris' abstract message):

public interface MessageHandler
{
  void handle (AbstractMessage msg);
}

Java has the concept of a service provider that is one method for dynamic loading (I'm sure there are other methods that can be used if this doesn't fit your need). You can iterate through the handlers at the time the message is handled, passing each handler the message instance. Each handler decides if it wants to handle the message or not. You could even make handle return a boolean to indicate that the chain can stop calling subsequent handlers if you so wish.

You can implement handlers in each application for the message types you want to handle. There are many ways to go about this (load the handlers and initialize each at startup, load them at time of message handling, etc) so pick the one that fits your need. The linked service providers article has a simple loop demonstrating the loading of the handlers.

No need for a switch that changes as your code is modified, you just reconfigure how your jar is built. This is also a good example of the open-closed principle where your code does not change but is open to extension.

Upvotes: 0

Loris Securo
Loris Securo

Reputation: 7638

If you want to use polymorfism you could define the abstract message class:

abstract class AbstractMessage { 
    public abstract void doStuff();
    //...   
}

instead of using enums, create a class for each message type extending the abstract class and overriding the methods:

class Type1Message extends AbstractMessage {
    @Override
    public void doStuff() {
        //handle TYPE_1
    }
}

class Type2Message extends AbstractMessage {
    @Override
    public void doStuff() {
        //handle TYPE_2
    }
}

then in your handle method:

((AbstractMessage) o).doStuff();

Upvotes: 4

Pavlo Viazovskyy
Pavlo Viazovskyy

Reputation: 937

application_2 anyway will need to know which type of message it has received, so some kind of switch is unavoidable. But the key thing is to have this switch only at one place. For example, you could have method like following:

public MessageHandler getHandlerFor(MessageType messageType) {
    switch (messageType) {
        case TYPE_1: return Type1MessageHandler();
        case TYPE_2: return Type2MessageHandler();
        ............
        default: throw new IllegalArgumentException("No handler found for messageType: " + messageType);
    }
}

Then you will need the hierarchy of MessageHandler's which correspond to strategy pattern:

public interface MessageHandler {
    void handle();
}

Each implementation of MessageHandler interface should provide MessageType-specific handling logic.

Upvotes: 0

Related Questions