design-patternssolid-principlessingle-responsibility-principle

Does an additional service class violates Single Responsibility Principle?


Let's say I have implemented an Action Design Pattern, and I have these actions:

  1. MakeJuice
  2. MakeCoffee
  3. MakeBreakfast
  4. MakeDessert

now, for every actions, as it should, have their own set of instructions. But let's say, for MakeJuice and MakeCoffee, we have a common method, let's call it prepareWater, and have the same set of instructions for both actions.

My question is, is it anti-pattern to create a trait where I can put the prepareWater method instead of defining it separately to each actions?

I'm thinking of just creating a trait, to address DRY principle, but I also think it's contradictory to the Actions Design Pattern.

Now, I'm torn between creating a trait or creating a contract/interface for this.

What do you guys think?


Solution

  • Assuming water-preparation is an independent action (e.g. "take bottle of water and put it on table"), prepareWater should be implemented once (DRY).

    In the context of parent actions (MakeJuice and MakeCoffee), however, water-preparation might be integrated in somewhat different ways. For example, when making coffee we should also boil water. This is when duplication (across parent actions) is considered to be perfectly fine. Moreover, it is even useful since it promotes decoupling.

    static class SubActions {
      static void PrepareWater() {
        TakeBottleOfWater(); // TODO: implement
        PutBottleOfWaterOnTable(); // TODO: implement
      }
    }
    
    class MakeJuice {
      void Execute() {
        this.PrepareWater();
        // TODO: set other sub-actions
      }
      void PrepareWater() {
        SubActions.PrepareWater();
      }
    }
    
    class MakeCoffee {
      void Execute() {
        this.PrepareWater();
        // TODO: set other sub-actions
      }
      void PrepareWater() {
        SubActions.PrepareWater();
        this.BoilWater(); // TODO: implement
      }
    }