2

Consider a super class Animal, inherited by multiple subclasses(Dog / Bird). On their instantiation, objects have their local state & behavior:

enter image description here


wiki says:

The strategy pattern

  • defines a family of algorithms,
  • encapsulates each algorithm, and
  • makes the algorithms interchangeable within that family.

If a requirement demands to add a new behavior(fly()) in super class Animal, then use Strategy pattern, to create new family of algorithm(CantFly/ ItFly) and encapsulate these algorithms in setFlyingAbility()/tryToFly(), to attain minimal code changes in existing class hierarchy, with below changes:

enter image description here

where ItFyls and CantFly are family of algorithms for new behavior(fly) that are used by subclasses(Dog / Bird) interchangeably, with below code changes

enter image description here


Association of type Composition, at crux, is helping to add new behavior in super class by minimizing code changes in super/sub class relationship, shown below,

class Animal{
    ....
    Flys flyingType;
    ....
    public String tryToFly(){
       return flyingType.fly();
    }
    public void setFlyingAbility(Flys newFlyType){
      flyingType = newFlyType;
    }
}

Question:

To add a new behavior(fly()) in super class(Animal), Does Strategy pattern involves modifying every subclass(Dog/Bird) constructor? Can modification of subclass get avoided?

2
  • Here are changes after inputs from, Brandon Arnold Sep 23, 2017 at 23:19
  • @FrankHileman Please provide your comments here, as well Sep 24, 2017 at 17:47

2 Answers 2

1

Your implementation of the Strategy Pattern is fine, but I do not think your example is a good illustration of the problem Strategy Pattern is solving.

Consider sorting. Given an array of comparable items, we choose among many different sorting algorithms (or, "strategies"). One common use case of the strategy pattern is used to run the algorithms in parallel, for example, until the first one terminates. Databases have a similar approach to finding the most efficient method of performing a query, during the optimization phase.

Your OO analogy with Dog/Bird inheriting from Animal doesn't quite fit this, in my view, because the fly() strategy that you choose depends on whether the input is a dog or a bird.

A better fit for your situation is the idea of a virtual function. In such a case, the base class Animal declares the function signature, and defers the implementation to inheriting classes.

14
  • Are you referring to this api? Sep 23, 2017 at 20:06
  • @user1787812 No, more like this one: docs.oracle.com/cd/B10501_01/server.920/a96533/ex_plan.htm Sep 23, 2017 at 20:08
  • Brandon, I wrote some code here, after reading the answer. Please acknowledge on my understanding Sep 23, 2017 at 21:26
  • @user1787812 the line you highlight with a red square in your constructors is practically identical to the purpose of a virtual function in a derived class Sep 23, 2017 at 21:29
  • After reading your answer, Using strategy pattern, I wrote sorting code here which is different example. Sep 23, 2017 at 21:33
0

The "strategy pattern" is a red herring here!

There is an existing class hierarchy (of animals), and one tries to make the code more SOLID (in this case specifically SRP and OCP conform). Then the refactoring causes the necessity to touch every constructor in the hierarchy - but that is quite normal when making a non-solid class hierarchy more SOLID, and it has absolutely nothing to do the specific approach here is "Strategy". The real reason is, refactoring a more complex codebase involves more effort than refactoring a simpler one - that is IMHO not very surprising!

The "Strategy" pattern requires always one or more places in the program where the specific strategy subclass has to be picked, or changed (maybe it changes at runtime, maybe not). This is part of the business logic, and the "correct" place is case-dependent. When it is introduced into a simple context (like a test class without a class hierarchy), there are typically less things to be changed than when it gets introduced into a more complex context like a class hierarchy. However, the reason for this is the complexity of the context, not the pattern.

In this specific example, it makes sense to make the decision about the strategy in the constructor of the different animals - the association Dog - CantFly, Bird - ItFlys must be somewhere, and since the correct strategy can be picked by the kind of animal, by general knowledge, and it won't change during the lifetime of each object, the constructor is not the worst place to implement it.

So yes, making this code more SOLID involves changing the constructors at the time you introduce the pattern into the code, no less, no more (in real code, you will probably have to refactor more things in the Animal class hierarchy for introducing the pattern). Note the point such refactorings is not to avoid changes to the code before it got introduced, but after.

5
  • Wrt your first point, strategy is picked in my github code at client place, test package. Unlike, in my query, where strategy is picked in subclass constructor. So, isn't github code better fit for strategy pattern and virtual function for above query. Sep 24, 2017 at 14:42
  • @user1787812: see my edit.
    – Doc Brown
    Sep 24, 2017 at 16:45
  • Touching constructor in hierarchy doesn't look normal to me, despite strategy pattern allows me to that or not. Because it takes time to at least to know that I did not miss any subclass after finding some 40+ odd subclass/subsubclass(say some GUI component), where the constructor need to get updated. Sep 25, 2017 at 17:40
  • @user1787812: I don't know how I can make this clearer without repeating myself: when you refactor a non-solid code base containing a class hierarchy with 40 subclasses, you might have to touch all those 40 classes. That has absolutely nothing to with the strategy pattern, it would be the same if you, for example, change the naming scheme of the classes, or just move duplicate code from the 40 subclasses to the base class, or introduce a constructor parameter into all the 40 classes.
    – Doc Brown
    Sep 25, 2017 at 18:13
  • .. and for your specific example: if you had 40 specific animals, you need to tell the program somewhere which of them can fly and which not. This is something you know, but the program cannot know beforehand. Encoding this information into the specific animal subclass is IMHO the natural, right thing. And if the information was not coded there beforhand, you have to add it afterwards, no chance to avoid changes to the 40 subclasses.
    – Doc Brown
    Sep 25, 2017 at 18:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.