SOLID GRASP controller pattern?

1.9k views Asked by At

I have a question about applying the GRASP controller pattern while keeping it SOLID, more specifically, while keeping it with a single responsibility.

Wikipedia's Controller Pattern definition says:

The Controller pattern assigns the responsibility of dealing with system events to a non-UI class that represents the overall system or a use case scenario. A Controller object is a non-user interface object responsible for receiving or handling a system event.

And about SOLID's single responsibility principle:

In object-oriented programming, the single responsibility principle states that every class should have a single responsibility, and that responsibility should be entirely encapsulated by the class. All its services should be narrowly aligned with that responsibility.

Let's go to some example code. Let's say I have the following Java classes:

public class foo {
    public foo(){}
    public int foo1(){/*Some code*/}
    public String foo2(){/*Some code*/}
    public String foo3(int foo31){/*Some code*/}
}

public class bar {
    public bar(){}
    public int bar1(){/*Some code*/}
    public String bar2(){/*Some code*/}
    public String bar3(int bar31){/*Some code*/}
}

What's a good controller implementation while keeping a single responsibility on it? Do I just go through use cases or what? For example:

public class bazController {

    private foo fooInstance;
    private bar barInstance;

    public bazController(){
        this.fooInstance = new foo();
        this.barInstance = new bar();
    }

    public void fooAction1(int arg){
        this.foo.foo3(arg);
    }

    public void barAction1(int arg){
        this.bar.bar3(arg);
    }

    public void fooAction2(){
        this.foo.foo1();
    }

    public void barAction2(){
        this.bar.bar1();
    }

}

Am I keeping one responsibility here? Am I doing this, or understanding this, correctly? Thank you beforehand.

EDIT: What would happen if bazController had this method, making both classes related?

public int bazAction(){
    return this.foo.fooAction1() + this.bar.barAction1();
}
2

There are 2 answers

2
NaveenBhat On BEST ANSWER

I'm not a much experienced developer, but I'll try to explore my ideas on this based on the understanding of the concepts.

Single Responsibility: I believe, it is the answer of the question "Your class is responsible for what?" when you are going to answer this question, you should tell only one responsibility. In your case, the answer is: "My class is responsible to control the baz"(Your implementation should do that of-course).

Since your answer specifies only one responsibility you implemented it properly.

But I think your code doesn't meet the D of SOLID. i.e. Dependency Injection. You could have injected foo and bar via constructor or by other means.

Update: Still your class is fine because the responsibility of your class is to control the baz.

foo and bar are the components of baz and you are controlling their action via your bazCntroller.

I can say you violated Single Responsibility when you add methods that is doing other job than the controlling of your baz. Ex:

public void LogBazExecution() {}
public int GetBazExecutionCount() {}

As you can see, it is not the responsibility of the baz controller to keep track of how many times the baz action fired.

The reason behind the principle is ease of maintenance. When each class is designed to have only one responsibility it is easy for you to find the location of the failure in your system and easy to extend it whenever required without introducing much new bugs.

2
jeuton On

It really depends on the context behind the classes foo and bar and how closely aligned business wise they are with the business context of the controller.

In your example, you have methods that work with foo and methods that work with bar, but zero methods that work with both foo and bar. To me this indicates that foo and bar probably don't have much in common.

This is how I would change your example: (assuming foo and bar don't have anything in common):

public class fooController
{
    private foo fooInstance;

    public fooController() {
        fooInstance = new foo
    }

    public void fooAction1(int arg){
        this.foo.foo3(arg);
    }

    public void fooAction2(){
        this.foo.foo1();
    }
}

public class barController 
{

    private bar barInstance;

    public bazController(){
        this.barInstance = new bar();
    }

    public void barAction1(int arg){
         this.bar.bar3(arg);
    }

    public void barAction2(){
         this.bar.bar1();
    }
}

EDIT
An example of where you would have a controller that delegates to more than one business logic instance might look something like this:

public class UserSettingsController
{
    private UserAddressLogic addressLogic;
    private UserPersonalInfoLogic personalInfoLogic;

    public UserSettingsController() {
        addressLogic = new UserAddressLogic();
        personalInfoLogic = new UserPersonalInfoLogic();
    }

    public User GetUser() {
        User user = new User();
        user.Address = addressLogic.GetUserAddress();
        user.PersonalInfo = personalInfoLogic.GetPersonalInfo();

        return user;
    }
}