1

I recently discovered SOLID principles and i'm trying to learn how to properly apply them. I have an application that had a huge interface:

public interface NotificationService {
  public void sendNewProjectRequestNotificationToDesigners(String projectId);
  public void sendNewProjectAcceptedNotificationToCustomer(String projectId);
  public void sendNewProjectRejectedNotificationToCustomer(String projectId);
  ...
  // and aboud 30 more methods like that
  // specifying the type of notification and who is going to be sent to
}

This clearly violates:

  • SRP: so many different kind of notifications
  • OCP: Every new notification i just modified the class
  • ISP: In every single class that injects a NotificationService, will only want 1 of all the methods.
  • DIP: The clients of this class need to be aware of the exact method to use.

so i began my refactoring and came up with:

public interface NotificationSender {
  public void sendNotification(String projectId);
}

public class NewProjectRequestNotificationSender implements NotificationSender {
  public void sendNotification(String projectId) {
    ...
  }
}

// and many other classes, one for each method in the original interface

Now to apply Dependency Injection, i declared each class that implements NotificationSender as a @Bean with a qualifier name.

And the classes that require a specific notification sender, the dependency is injected with @Qualifier.

Question 1: By having to use @Qualifier in the clases that require a specific NotificationSender, hence these clases are still forced to know details like the qualifier name of the given NotificationSender, am i still violating Dependency Inversion Principle?

Question 2: Is there a better approach?

3 Answers 3

1

No, the qualifiers are just a hint to your dependency injection system. They're more configuration than code. You are still accessing the beans only though the NotificationSender interface, so your code doesn't know any more about the implementation details of the different instances.

Imagine you were unit testing a bean that used a qualified NotificationSender injection. You could pass it any NotificationSender you liked (a mock, perhaps). Outside the DI framework, your code couldn't care less what implementation you give it!

0

Well you could add another layer of indirection by using a factory and injecting the concrete classes into the factory, then injecting the factory where you need the concrete classes. The factory could have a method to which the caller is passed, and then the factory could figure out what to return based on that.

2
  • That sounds like an interesting idea, i'll give it a try and then post my results as soon as i can.
    – saljuama
    Commented Jun 8, 2015 at 0:16
  • 1
    Every issue can be solved by adding another layer of indirection... en.wikipedia.org/wiki/… What's wrong with having the class who needs the sender require the interface of said sender in it's constructor?
    – JDT
    Commented Aug 6, 2015 at 19:53
0

Have you considered refactoring your interface to something like:

public interface NotificationSender {
  public void sendNotification(NotificationEvent event);
}

public final class NotificationEvent {
    private final NotificationKind type;
    private final String projectId;

    ...
}

public enum NotificationKind {
    REQUESTED, ACCEPTED, REJECTED, ...;
}
1
  • Yes i have, and i discarded it because, there were 30+ type of notifications, and having such enum, was potentially leading to really big switch/if-else statements in some cases, the first time i had to write a switch, really noticed the code smell.
    – saljuama
    Commented Oct 6, 2015 at 7:08

Your Answer

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

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