6

I am trying to do a design for notification part in the system I have 2 parts inApp notification and email notification so I used strategy pattern where I have interface NotificationSender with one single method send

NotificationSender{
    public void send(A,B,C);
}

then I have 2 implmentations

InAppNotificationSender{
    public void send(A,B,C){}
}
EmailNotificationSender{
    public void send(A,B,C){}
}

later on, I had to increase parameters in the InAppNotificationSender so send should have D,E params as well which may change the design I thought of doing one single parameter for both methods using builder pattern something like

NotificationSender{
        public void send(NotificationTemplateBuilder);
    }

What would be the good design to do such case

  • Not sure the types of D and E, (or A,B,C) but would varargs work? – user949300 Jan 16 at 19:52
  • They refer to different args – YouYou Jan 16 at 22:25
  • 2
    You're most likely overmodularizing your architrcture here. The change in parameters means that the program using those classes probably needs to know if it is an email notification or an inapp notification, so why extend your architecture specifying shared features that are not relevant to your problem? – jacmkno Jan 17 at 4:07
1

The simplest answer is that your notifier interface needs to accept all parameters that any of them need i.e. the superset of parameters. It's OK for an implementation to ignore some parameters of the interface.

Five parameters is starting to get a little unwieldy, though so creating a type for this isn't a terrible idea. The question is whether you want to go through the effort of trying to figure out when you need to pull together all 5 parameters or just the 3.

I would say unless you have a good reason, don't bother trying to limit to 3 parameters. It's going to greatly complicate the design. If it's especially costly to pull in the last 2 parameters (D,E) then it might be something you should do.

I would suggest in that case that instead of a builder, you would use more of a Facade pattern. Your parameters object would not actually hold the values but instead retrieve them on demand i.e. getA() would pull the A parameter from some source that it has knowledge of. The notifier won't need to know how to get A so it stays simple. The advantage is that the email notifier will never call getD() or getE() and the cost of doing that will only be incurred by the in-app notifier. Again, though, this is a lot more complicated and not worth it if D and E are not costly to retrieve.

  • That is actually what I was trying to avoid complicating the design by adding more code to restrict the use of 3 params, So my question actually was to ensure that for a design point of view passing useless params to some implementation class (D,E) is not antipattern or bad design – YouYou Jan 16 at 22:34
  • @YouYou It can be a code smell but when it comes to interfaces, it's pretty typical. It's better than creating logic at the call point to figure out what to do. The only caveat would be as I say in the answer: if there's some significant cost involved. – JimmyJames Jan 16 at 22:41
10

It feels like you are missing an abstraction here: a notification.

A new class called Notification, which could be little more than a bag of data, may have some information relevant to the InAppNotificationSender that isn't relevant to the EmailNotificationSender — and that's OK.

This gives you a consistent parameter to pass to the notification senders, and the senders are free to use (or not use) whatever they see fit. You also get the added benefit of expanding what this Notification class can hold with no additional cost to the existing implementations.

  • Yes this what I did by using a builder pattern object, NotificationTemplate – YouYou Jan 16 at 22:31
  • It sounds like the Private Data Class pattern can maybe do it as well – Tjorriemorrie Jan 24 at 22:07
0

I prefer simple solutions. In this case I would remove all Params from the Send method in the interface. Give each class that implements this interface a constructor with the required properties, save those in private fields and use them in the implementation of the Send method.

  • I think you might be missing the point of the interface here. To do what you describe, the code that was calling the send method now will need to create a new instance of the implementation class on each send. That means it would need to know which class to instantiate. Something it doesn't need to know in order to send notifications now. – JimmyJames Jan 16 at 20:09
  • That depends on where the required information is first available I guess. If you can create the instances in a main or setup and inject them into the client depending on your needs, the client only has to know the interface and call send() on that. – Rik D Jan 16 at 22:34
  • @RikD The interface defines how to send the notification, and the clients typically know what to send. What you're describing is the command pattern, and I'm not sure that's what the OP wants here. – casablanca Jan 17 at 3:05
0

I would perhaps go for the following design (not knowing your use-cases for the parameters), loosely based on the builder pattern, but adding proper behavior:

public interface Notification {
    Notification withRecipient(...); // A
    Notification withSubject(...); // B
    Notification addParagraph(...); // C
    ...
    void send(); // That's what we want from a Notification      
}

You can introduce multiple style/semantic-based things, and the actual implementation can decide how (or whether) to handle that. Like this:

public interface NotificationFactory {
    Notification create();
}

public final class EmailNotificationFactory implements NotificationFactory {
    @Override
    public Notification create() {
        // This is just an example here
        Message message = new MimeMessage(session); // Using javax.mail
        return new Notification() {
            @Override
            public Notification withRecipient(String recipient) {
                message.setRecipients(...);
            }
            ...
            @Override
            public void send() {
                Transport.send(message); // Or whatever
            }
        }
    }
}

Obviously the in-app notification would build the appropriate widget or notification dialog with the send() displaying it on screen.

0

I like (and upvoted) @GregBurghardt answer, but, maybe, you can get away with something simpler. This is borderline "quick and dirty", so not sure I would use it myself - depends on how important this API is, how likely to change, etc... (And how comfortable your users would be with this more "dynamic language" feel.)

If

  1. A,B,and C are always required
  2. D,E are "rare"
  3. D and E are of the same type, say URL

Your signature could be

public void send(A a, B b, C c, URL... extras)

and you would access d and e, after checking the length of extras via extras[0], extras[1].

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.