4
Crost
4y

I just read Robert Martin's chapter on the Single responsibility principle in Clean Architecture.

In it he explains that stakeholders, or actors, that require their own functionality that may be similar to others should have separated code. This is because 2 actors == 2 potential reasons for change.

But this seems to run counter to DRY. Am I mistaken?

Comments
  • 8
    "It depends"

    If you are copy/pasting the code and changing something an if statement in the method would solve, I'd stick to DRY. However if there's obvious separation between the two actors as to what this one method should be doing, split it, or split it 3 ways and have the common (where possible) code in its own method and mutate said data in their own methods.

    There's no perfect methodology, they all have pitfalls along the way.
  • 3
    I had 2 classes that looked almost identical. I chose when I created the first class that I wanted static map for holding common data. When creating the second class it had the same everything except I could not share the static variable and one of the functions acted very differently. They also had very different purposes and use cases. I thought about reworking this to use inherentance, but I chose to keep them separate due to the use cases. From quick glance they operate almost identically. I kept them in the private implementation so that the interface to the objects that use these classes is never seen outside the compilation unit. So if I choose I can implement without affected code outside this unit. The use case differences is what sealed my decision to repeat myself. So I think there are cases where there may not be a good alternate, or you don't see an alternate until later.
  • 7
    If the requirements can drift apart, they are only coincidentally the same at a point in time. If they drift and they become incompatible from an abstraction/interface standpoint, you have a breaking change.

    DRY is designed to discouraging copy pasting the same code when you can link a single piece of functionality to multiple things. This assumes a certain level of interface agnosticity. It is generally considered bad form to violate SRP in the pursuit of being dry.
  • 3
    Robert also says that there must be a balance. You will never in real life find a textbook case with all the requirements satisfied 100%. If you maintain SRP there will be duplications, be it more or less.

    Not only you have to maintain the balance - you have to consider various levels of your components. Units, if you will. Entities, repositories, interactors, modules, services, services' groups, etc, etc.

    You've got to play the scale at all those layers at once rather than focus on maintaining the DRY principle.

    IMO DRY is one of the least important principles. If you really want independent deployability then get rid of the common ground completely. But if you do that - maintaining all the components of the same project will be no different than maintaining separate projects.
  • 4
    I think it is important to differentiate between copy pasting logic and boiler plate.

    Boiler plate code often needs to be identical, for example, if multiple classes are implementing the same interface and in the end its only a few methods and values that differ, you can either make almost identical classes with small differences in implementation, or break out a base class so you only have the unique parts in every implementation class.

    Getting this right is not always easy, and I can see many cases when you start out one way and then refactor into another.

    Code should not be subject to nostalgia or considered written in stone, it should be questioned and reworked if the selected solution proves inferior, just not to often ;)

    And keeping implementations separate is usually a good thing.

    At least start that way, and once you have two or more, see how much you can make the implementations identical. Then look at if you can pull parts out in a base class you inherit from.

    Do mot start with inheritance unless you already have done something almost identical so you know what you need.

    Do not be afraid to go down the wrong path with structure as long as you also are prepared to back track once you realize the mistake.

    You might end up tossing away more code than you end up with, but it can still be faster than trying to find the perfect solution in one go.
  • 2
    @Voxera Boiler plate makes me appreciate templates.
  • 0
    This is how functions end up with like 20 randomly ordered arguments.
  • 2
    @HiFiWiFiSciFi nah, that's just Pasta Driven Development
  • 0
    @electrineer Well, I am a card carrying pastafarian...

    I mean... it's just a card I printed off the internet and keep in my wallet... but it's legit.
  • 0
    Mixins or traits can solve code reuse without semantically coupling the classes using the same code.
  • 1
    You can safely implement your behavioral traits separately in most cases. If they're completely dissimilar in their intent and just happen to mostly match at *some* point in time, you're still DRY, and as a bonus, not misleading yourself and anyone who will read this after you.
  • 0
    @0x5d0 this is what I'm leaning to, and as @SortOfTested also says, even of the code is practically or maybe even identical, it's temporal.

    Splitting will express intent clearly for the next person working on it and reduce risk, however that doesn't mean I wouldn't use a template pattern to escape boilerplate, or perhaps a facade to keeps things separate but make usage easier.

    I think this is what Robert Martin means.
Add Comment