Dejell
Dejell

Reputation: 14317

recommended design pattern java dynamic invocation

I have the following db table:

id method_id
1   1
1   2
1   3

and 2 classes:

EmailController and Smscontroller

in my code, I need to iterate over the table and according to the method_id (1 or 2) to invoke the send method of either EmailController or Smscontroller.

What is the recommended design pattern for it?

EDITED

There could be 100 methods! I put only 3. This is why I do not prefer the if else.

As well, the object that I send to EmailController send method is different than the one that I send to SmsController send method.

In EmailController I need to send User object. In SmsController I need to send Manager object

Upvotes: 1

Views: 538

Answers (3)

Behrang Saeedzadeh
Behrang Saeedzadeh

Reputation: 47913

I can't think of a design pattern. But for ultimate flexibility you can have a design similar to this:

public interface Sendable /* or Sender, SendingManager, etc. */ {
  public int getId();
  public void send();
}

public class EmailController implements Sendable {
}

public class SmsController implements Sendable {
}

public class Sendables {

 private Map<Integer, Sendable> sendables = new HashMap<Integer, Sendable>();

 public void addSendable(Sendable s) {
   this.sendables.put(s.getId(), s);
 }

 public void sendById(Integer id) {
   this.sendables.get(id).send();
 }

}

Then you can use it like this:

Sendables sendables = new Sendables();
sendables.add(new EmailController());
sendables.add(new SmsController());
sendables.add(new ChatController());
// etc.


Row row = table.getRow(...); // let's assume this gets a row from your table
sendables.send(row.getId());

Another solution could be to have an extra table like this:

TABLE: CLASS_NAMES
method_id class_name
1         "com.foo.SmsController"
2         "com.foo.EmailController"

And then pass class_name to Class.forName and let it instantiate the appropriate controller for you to use.

EDIT: A reflection-based version of the code as suggested by Luis. Note that for production use you should ensure that the passed parameters are valid (not null, etc.) and also handle exceptions with rigor.

TABLE: CLASS_NAMES

method_id class_name                 param_class_name
1         "com.foo.SmsController"    "com.foo.Manager"
2         "com.foo.EmailController"  "com.foo.User"

SendManager

public class SendManager {

    private static final String SEND_METHOD_NAME = "send";

    /* DAO for the CLASS_NAMES tables */
    private ClassNameDAO classNameDao;

    /**
     * Gets the row corresponding to methodId, for example
     * (1, "com.foo.SmsController", "com.foo.Manager") then using reflection
     * instantiates an instance of SmsController and invokes its send method
     * with <code>param</code> passed to it.
     */
    public void send(int methodId, Object param) throws Exception {
        ClassNameRow classNameRow = classNameDao.findByMethodId(methodId);

        String senderParameterClassName = className.senderParameterClassName();
        Class paramClass = Class.forName(senderParameterClassName);

        if (!paramClass.isInstance(param)) {
            throw new IllegalArgumentException("methodId and param are not compatible");
        }

        String senderClassName = classNameRow.getSenderClassName();
        Class senderClass = Class.forName(senderClassName);     

        /* Your sender classes must be JavaBeans and have no-arg constructors */
        Object sender = senderClass.newInstance();

        Class paramClass = Class.forName(senderParameterClassName);

        Method send = senderClass.getMethod(SEND_METHOD_NAME, paramClass);

        send.invoke(sender, param);
    }

}

Sample Usage

SendManager sendManager = new SendManager();

Manager m = ...;
sendManager.send(1, m);

User u = ...;
sendManager.send(2, u);

Upvotes: 2

&#211;scar L&#243;pez
&#211;scar L&#243;pez

Reputation: 236004

How about this:

abstract class Controller {
    public static Controller getInstance(int methodId) {
        switch (methodId) {
            case 1:
                return new EmailController();
            case 2:
                return new SmsController();
            default:
                return null;
        }
    }
    public abstract void send();
}

class EmailController extends Controller {
    @Override
    public void send() {
        System.out.println("sending email");
    }
}

class SmsController extends Controller {
    @Override
    public void send() {
        System.out.println("sending sms");
    }
}

And use it like this:

Controller.getInstance(methodId).send();

I'm using the Strategy pattern and the Factory Method pattern in my solution.

Upvotes: 2

Related Questions