8

In most examples of clean architecture (Android projects mostly, though) I've noticed that the use-case/interactor classes (units that encapsulate a feature) often share base class/interface, like here or here. Others do not (like here or here), instead allowing interactors to accept some parameters that are then used to execute some logic.

Is either of these approach better than the other? I'm particularly interested in how parameterless approach handles use cases for something that requires user input for example - say we want to let user edit an entity and pass it to the server. We could inject the same entity into use case and whomever invokes it, but then it would have to be mutable, so that changes are reflected in both places. But then we can't create immutable models, which we may not want (because of threading issues etc). How to handle such case?

Pardon me if I use the terms usecase/interactor incorrectly, but this is how they're used in Android land, which admittedly may be a bit behind on design patterns

6
+25

Let me reiterate what you're saying to be sure we're on the same page.

In clean architecture

enter image description here

Case A

use-case/interactor classes often share base class/interface, like here

  public abstract class UseCase {

  private final ThreadExecutor threadExecutor;
  private final PostExecutionThread postExecutionThread;

  private Subscription subscription = Subscriptions.empty();

  protected UseCase(ThreadExecutor threadExecutor,
      PostExecutionThread postExecutionThread) {
    this.threadExecutor = threadExecutor;
    this.postExecutionThread = postExecutionThread;
  }

  /**
   * Builds an {@link rx.Observable} which will be used when executing the current {@link UseCase}.
   */
  protected abstract Observable buildUseCaseObservable();

  /**
   * Executes the current use case.
   *
   * @param UseCaseSubscriber The guy who will be listen to the observable build
   * with {@link #buildUseCaseObservable()}.
   */
  @SuppressWarnings("unchecked")
  public void execute(Subscriber UseCaseSubscriber) {
    this.subscription = this.buildUseCaseObservable()
        .subscribeOn(Schedulers.from(threadExecutor))
        .observeOn(postExecutionThread.getScheduler())
        .subscribe(UseCaseSubscriber);
  }

  /**
   * Unsubscribes from current {@link rx.Subscription}.
   */
  public void unsubscribe() {
    if (!subscription.isUnsubscribed()) {
      subscription.unsubscribe();
    }
  }
}

and here

package cat.ppicas.framework.task;

public interface Task<R, E extends Exception> {

    TaskResult<R, E> execute();

}

Case B

Others do not, and instead allow interactors to accept some parameters that are then used to execute some logic.

like here

package pl.charmas.shoppinglist.domain.usecase;

public interface UseCase<Result, Argument> {
  Result execute(Argument arg) throws Exception;
}

or here

AbstractInteractor.java
GetMarvelCharactersLimit.java
GetMarvelCharactersLimitImp.java
GetMarvelCharactersPaginated.java
GetMarvelCharactersPaginatedImp.java

Stop

They're both right.

Sharing an interface or base class is part of inheritance.

Accepting parameters so you can execute logic through them is part of composition.

Is either of these approach better than the other?

Both are better at what they're good at. Though a popular design principle states, "favor composition over inheritance".

If I'm understanding, you're seeing how composition and inheritance can both allow for polymorphism and now that you've seen it, you're struggling to choose between them. Saying this in pattern language: you can use the template pattern or the strategy pattern to get polymorphism.

Inheritance gives you polymorphism on it's own. Thus, less typing at the keyboard. Composition requires that you add delegation to expose and connect the interface to the parameter. That means more typing. But composition is not statically bound so it's very flexible and testable.

Should use case/interactor execute method accept parameters?

Remember that this method x.y() and this function y(x) are essentially the same. An execute() method is always getting at least one parameter.

I'm particularly interested in how parameterless approach handles use cases for something that requires user input for example - say we want to let user edit an entity and pass it to the server. We could inject the same entity into use case and whomever invokes it, but then it would have to be mutable, so that changes are reflected in both places. But then we can't create immutable models, which we may not want (because of threading issues etc). How to handle such case?

Well, now we're talking about entities, not use cases, nor polymorphism.

An entity has an ID. It's very good to make an entity immutable. Why do you want a user editing something immutable? Why do you want it's information existing in two places? If it does, which place knows best?

No, better to let the user build something new. If it must have an ID then it gets a new unique ID. If not, it's a value object. If there is something else with a preexisting ID that this information must be associated with then build a new association. Don't go poking around in immutable entities.

It's entirely possible to model a changing world without ever updating your entities, so long as you have the space to keep creating things that represent what's new.

  • I still have some doubts, however. Aren't both approaches, A and B, examples of inheritance? And having separate class to execute some logic is composition. I fail to see that A is inheritance, and B is composition - could you elaborate? As for method is always getting at least one parameter - that's a technicality."Should execute method accept more than one parameter", if you wish. And finally the part about immutability doesn't address the problem either, which is "how do I pass usecase a new immutable entity (that reflects user's changes) if its execute method doesn't accept parameters? – wasyl Jun 4 '16 at 21:54

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.