Command Design Pattern – Reducing Class Dependencies

design-patternsdomain-driven-designobject-oriented-design

I recently realized that I have way too many dependencies in many of my classes. Now I am trying to solve that for the most important class. I think I have an idea how but I'm not sure if it is the best idea.

The most important class is a Service class. What it does is make one "thing" in the app go through different states. There is a specific flow it follows, triggered by users taking certain actions in the app. (Submit, Confirm, Reject, Close, …)

At this moment, there is a public method for each action that you can do to change the status of that object, about 10 methods. This feels wrong although the responsibility of this class is "changing the state".

When the state changes however, other actions need to happen as well like sending an e-mail, closing certain tasks, looking up documents etc. Many of those tasks have other different dependencies and because all of the methods are in the same class the main service now has 12 dependencies.

My main goal is reducing the number of dependencies in the main service while still containing some kind of "overview" of all the actions you can take.

I was looking through a list of common design patterns and noticed the "Command" pattern. I think I can create a series of command classes like "SubmitThing", "CloseThing". Each with their own specific dependencies. This way I move the dependencies to other classes and each action has its own dependencies.

The information I can find about the Command pattern however describe its main benefits being that you can keep track of which commands where executed, revert them, add logging. This is not really the problem I wanted to solve, even though the pattern description sounds appealing.

So, is the Command design pattern the way to go here or are there better patterns for splitting up a class that has several actions into smaller pieces?

A small example:

    public class ThingService {

        private IDocumentService documentService;
        private IMailService mailService;
        private IAnotherService anotherService;
        private IActionsOnClosingService actionsOnClosingService;

        public ThingService(IDocumentService documentService,
                            IMailService mailService,
                            IAnotherService anotherService,
                            ...
                            ) {
            this.documentService = documentService;
            this.mailService = mailService;
            this.anotherService = anotherService;
            ...
        }

        public void submitThing(Thing thing, SubmitData data) {
            thing.setStatus(SUBMITTED);
            thing.setData(data);
            documentService.doSomething();
            mailService.doSomething();
        }

        public void closeThing(Thing thing, CloseData data) {
            thing.setStatus(CLOSED);
            thing.setData(data);
            documentService.doSomething();
            mailService.doSomething();
            actionsOnClosingService.doActionsOnClosing();
        }
    }

Best Answer

This could be solved by using event driven programming and a few observers. How many things does this method do?

public void submitThing(Thing thing, SubmitData data) {
    thing.setStatus(SUBMITTED);
    thing.setData(data);
    documentService.doSomething();
    mailService.doSomething();
}

Three. It does three things.

  1. Submits the thing.
  2. Does something with a document service.
  3. Sends a mail notification that the thing was submitted.

What happens when we need yet another something to happen when a thing is submitted? I know! We'll just modify this class again!

This design violates both the Single Responsibility and the Open-Closed principles. Compare to this event driven design. (Please excuse my C#...)

public event EventHandler<ThingSubmittedEventArgs> ThingSubmitted;

public void submitThing(Thing thing, SubmitData data) {
    thing.setStatus(SUBMITTED);
    thing.setData(data);

    var handler = ThingSubmitted;
    if (handler != null) {
        handler(this, data);
    }
}

Now when we need to do yet another something else, we don't have to modify this class. We just listen for the ThingSubmitted event and take whatever action is appropriate for the listener.

As a beneficial side effect of this design, this class no longer has dependencies on MailService or DocumentService.