3

I am curious about it because I talked with a friend about the strategy pattern, and we diverge about when we have to use it.

Our scenario is that I have to build a view component with 3 variants. All variants have particular colors and ways to resolve the string content, and it is supposed never to change. We don't know when the designers will create new views to this feature.

To do that, I decided to implement a simple strategy pattern using interfaces to create configurations. For example, I have IViewContentStrategy and IViewStyle as obligatory for any variant, and then I have two more optional interfaces, IViewAccessibility and IViewTouchable. Our view has to receive it and set up itself using these parameters. For example, if my strategy extends IViewTouchable, it will accept touch.

So I have two views that should accept Touch and Accessibility and one that doesn't need it.

My question is if I should start it using Enums with some conditions instead of strategy pattern?

ViTUu
  • 139
  • 2
  • So, are you using these objects to read configuration data from them (which is not exactly strategy pattern), or are you delegating certain method calls to them (which is the strategy pattern)? – Filip Milovanović Aug 02 '21 at 00:33
  • Your "set up itself using these parameters" makes it seem like it's perhaps the first one; if that's the case, then "using Enums with some conditions instead" will not make much difference, because you're already doing something similar, but more structured (in fact, under these circumstances enums might make it worse). – Filip Milovanović Aug 02 '21 at 00:35

4 Answers4

3

If you have already designed for the strategy, and managed to find a suitable way to encapsulate it, the hard part is done. Moreover, you now think of 3 variants, but who knows how many variants and what blending you may have in 2 years? So just go on with the strategy.

If you would instead go for the enum, it would certainly work, but the variants are not so well encapsulated: you'd have switch/case in a lot of places. If later you'd need to add only a fourth variant, you'll have to painfully cross-check a lot more code.

Now, if the strategy is just about a minimal, and very localized change of behavior, then the enum could be a reasonable alternative. But this does not seem to be your case, since there are already different content management strategies and apparently there may also be different interaction behaviors (accessibility and touch based).

Christophe
  • 74,672
  • 10
  • 115
  • 187
  • In that case, it is not like a simple backgroundColor. It should handle accessibility and touch events if necessary. The most important is that it should build the content in a particular way. Thank you for answering it with your point of view. It is very similar to my way of thinking about that. – ViTUu Jul 27 '21 at 22:03
  • "*Moreover, you now think of 3 variants, but who knows how many variants you'll have in 2 years?*" [**YAGNI**](https://martinfowler.com/bliki/Yagni.html). Design for what's needed now, not what might be in the future. So only use the strategy pattern if it is of benefit now. – David Arno Jul 28 '21 at 08:50
  • 3
    @DavidArno The need for the strategy is already here now, and implementing it is already justified. The view for easier future is just the cherry on the cake here. – Christophe Jul 28 '21 at 09:20
3

Counting the number of variants is the wrong metrics when deciding for or against usage of the strategy pattern.

In fact, even if there are just two variants of the component in stake, introducing a strategy object may be perfectly justified. On the other hand, a component may allow to provide several dozen variants of itself, just by parametrization, and still not require the strategy pattern.

I can imagine the example requirement of "different colors" in a view solved that way - by providing something like a "colorscheme" object, with no behaviour, no inheritance, only a set of color values or some color descriptions.

"Ok, if it is not the number of variants, what is the right metrics?"

In my experience, there are two major criteria here for making such a decision:

  1. Does your team need the reusable part of the component physically separated from the code which implements the differences?

    This can be the case when you want to allow different responsibilities in your team for different parts, so work in parallel gets easier, changes may be better isolated, for example to minimize the risk to change the behaviour of one variant when working on another.

    Even if that is not your situation, reason #2 might apply:

  2. Will the code without the strategy pattern be more convoluted than code which introduces the pattern?

    If you only need two or three extra tests in your code, the boilerplate code required for the strategy pattern may not be worth the hassle, and can probably overcomplicate things. But if you need more, the strategy pattern may help to keep the code cleaner and more readable.

Of course, this is only a rough guideline, but maybe it helps you to make your decision.

Doc Brown
  • 199,015
  • 33
  • 367
  • 565
2

As Christophe already mentioned, enums start to get really ugly if you use them for switch/case, especially if you have multiple places where you do this.

So, stick to the strategy.

BUT, when I see that you use 4 different interfaces, I smell overengineering and YAGNI violations at its best. So, keep it small. Use one interface, which may contain more than one method. This is OK. Split, when you have an indication that it is necessary, not from the start. Think about the effort and the number of compilation units you have to create when you implement another variant. Keep the work contained.

And do all of us a favor and don't start interface names with "I". ;-)

mtj
  • 1,914
  • 1
  • 9
  • 12
  • I usually don't use I as the prefix. I only wrote in that way to make the text understandable. I thought these 4 different interfaces are an Interface Segregation Principle (ISP). In my case, I have a view that did not need to be touchable or has accessibility capability, so I created it separated to avoid the famous empty function. Do you believe it is still an over-engineering? Thank you for your contribution. – ViTUu Jul 28 '21 at 10:55
  • 1
    Yes. Don't only think of principles, think of the work you are creating for your colleagues as well. This may help in understanding why you face some scepticism. Key is always finding the balance between a clean and an acceptable solution. – mtj Jul 28 '21 at 12:59
1

Implementing the strategy pattern for one variant is just implementing an interface that something uses. The danger here isn't "overuse". It's getting the abstraction wrong and finding out the variants really cut in a different direction than you designed for.

Group together things that change together. Separate things that don't.

So if you have IViewContentStrategy, IViewStyle, IViewAccessibility, and IViewTouchable all changing implementations together every time then I'd say you have an overdesign. All of these could be under one interface. Even the ones that, in some cases, do nothing. Doing nothing is fine when nothing is what needs to happen. Read up on the null object pattern.

But if you're breaking them up then they should be changing independently. Which is hard to see if you only have one variant. Be glad you have three.

candied_orange
  • 102,279
  • 24
  • 197
  • 315