Single Responsibility Principle in Clean Architecture, Aggregating UseCases in one UseCaseManager which can provide UseCase based on In & Out Object
M

3

6

I want to implement Single Responsibility principle in my projects Domain layer (Clean MVVM).

I've approximately 200 different use-cases which are being very hectic to manage. Now I'm thinking to create one UseCaseManager which can provide me required UseCase based on Input & Output Object.

I've tried an approach but that's not looking very good.I'm mentioning some sample code, Please help me how can I aggregate all the UseCases to one UseCaseManager.

UseCase1:

public class ActualUseCase1 extends AsyncUseCase<Object3,Object4> {

    public ActualUseCase1(SchedulerProvider schedulerProvider) {
        super(schedulerProvider);
    }

    @Override
    public Flowable<Object4> buildUseCaseFlowable(Object3 input) {
        return Flowable.just(new Object4());
    }
}

UseCase2:

public class ActualUseCase2 extends AsyncUseCase<Object1, Object2> {

    public ActualUseCase2(SchedulerProvider schedulerProvider) {
        super(schedulerProvider);
    }

    @Override
    public Flowable<Object2> buildUseCaseFlowable(Object1 input) {
        return Flowable.just(new Object2());
    }
}

UseCaseManager:

public interface UseCaseManager<In, Out> {
    <T> T getUseCase(In input, Out output);
}

T can be different UseCase with different In & Out Object.

UseCaseManagerImpl:

public class UseCaseManagerImpl  implements UseCaseManager {

    @Override
    public Object getUseCase(Object object1, Object object2) {
        return null;
    }
}

Now this is the main problem, I'm not able to understand. How can i implement getUseCase method.

Malvasia answered 23/6, 2018 at 6:39 Comment(3)
this would be the biggest switch case of instanceofs you've ever seen... but is it a good idea?Creolized
It's not a good idea. Can you please suggest? Anything else. I've tried to create different interfaces for different use cases. And tried to extend those use cases on use cases manager. By this way, I'm able to achieve things but you can understand an Interface with 200 supper interfaces.Malvasia
UseCase1 and UseCase2 have the same signature. Trying to achieve SRP does not mean it's OK to violate DRY. I think this might be an XY Problem.Scurlock
T
0

What you are trying to do is NOT the single responsibility, it's the opposite.

Single responsibility means

There should be one reason to change

See The Single Responsibility Principle

The UseCaseManager you try to implement will handle all of your 200 use cases. Thus it will change whenever a use case change." - That's is mixing of concerns and not separating them.

Usually use cases are invoked by controllers and usually a controller also has a single responsibility. Thus the controller knows which use case it must invoke. Thus I don't see the need for a UseCaseManager.

I guess there is another problem in your design that leads to the problem you have. But since I don't have your full source code I can't give you any further advice.

Teaser answered 12/5, 2021 at 6:50 Comment(0)
S
3

I think you're re-inventing the abstract factory pattern. Google will provide you with lots of content on that subject...

The tricky bit is how you decide which subtype to instantiate and return; that can be as simple as a switch statement, or involve lookup tables, etc. The key point is that you isolate that logic into a single place, where you can unit test it.

A bigger question is - how do you end up with 200 subclasses?

Somme answered 25/6, 2018 at 13:34 Comment(1)
Thanks, @Neville, I was pretty close to your answers but It's not such easy. I've tried many things.Malvasia
R
1

Okay I am getting an idea here that you want sort of a system wherein for a given input you get some output. And you can have 200 such inputs for which 200 corresponding outputs are possible. And you want to make all that manageable.

I will try to explain the solution I have in mind. I am a beginner in Java and hence won’t be able to produce much code.

You can implement this using the Chain of Responsibility pattern. In this design pattern, you have a job runner (UseCaseManagaer in your case) and several jobs (UseCases) to run, which will be run in sequence until one of them returns an output.

You can create a RequestPipeline class which will be a job runner. In the UseCaseManager, you instantiate the pipeline once and all the use cases you want to add using a Builder Pattern like so:

RequestPipeline.add(new UseCase1())
RequestPipeline.add(new UseCase2())...

When an input comes in, you trigger the RequestPipeline which will run all the jobs added to it, one after the other in sequence. If a UseCase returns null, the job runner will call the next UseCase in line until it finds a UseCase which can manage the input and produce an answer.

The advantages of this design pattern are:

  1. Abstraction: The RequestPipeline is responsible for running the jobs in line but does not know anything about the jobs it is running. The UseCase on the other hand only knows about processing it’s own use-case. It’s a unit in itself. Hence Single Responsibility Principle is satisfied as both are independent of each other and are re-usable whenever we have a similar design requirement later.
  2. Easily extensible: If you have to add 10 other use-cases, you can easily add them to the RequestPipeline and you are done.
  3. No switch case and if-else. This in itself is a big achievement. I love Chain of Responsibility for this very reason.
  4. Declarative Programming: We simple declare what we need to do and leave the details how to do it to the separate units. The design of the code is easily understandable by a new developer.
  5. More control: RequestPipeline has the ability to dynamically decide the job to run at run-time.

Reference: https://www.google.co.in/amp/s/www.geeksforgeeks.org/chain-responsibility-design-pattern/amp/

Some Java Code is provided here for you to check, if it satisfies your use-case.

Hope this helps. Please let me know if you have any doubts in the comment section.

Rhee answered 25/6, 2018 at 18:39 Comment(2)
Thanks for you answer @Yeshashah But this will not solve my problem. I just want to create a scoped UseCase factory. By which I can inject any UseCase anywhere.Malvasia
In that case, you can still use Chain of Responsibility pattern. Your job runner will return you the UseCase you want to inject depending on the input given. Same method but applied on a higher level. Your getUseCase will run a series of jobs it gets the UseCase it wants to return.Rhee
T
0

What you are trying to do is NOT the single responsibility, it's the opposite.

Single responsibility means

There should be one reason to change

See The Single Responsibility Principle

The UseCaseManager you try to implement will handle all of your 200 use cases. Thus it will change whenever a use case change." - That's is mixing of concerns and not separating them.

Usually use cases are invoked by controllers and usually a controller also has a single responsibility. Thus the controller knows which use case it must invoke. Thus I don't see the need for a UseCaseManager.

I guess there is another problem in your design that leads to the problem you have. But since I don't have your full source code I can't give you any further advice.

Teaser answered 12/5, 2021 at 6:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.