Refactoring Tightly Coupled Calls to the Strategy Pattern

Zoran Horvat
A free video tutorial from Zoran Horvat
CEO and Principal Consultant at Coding Helmet s.p.r.l.
4.8 instructor rating • 4 courses • 2,649 students

Learn more from the full course

Refactoring to Design Patterns by Example

Improving internal design of an existing application with refactoring techniques and design patterns in C#

04:25:51 of on-demand video • Updated May 2019

  • How to apply design patterns while refactoring an existing application
  • How to contain complexity of the business domain while performing small refactoring and redesign steps
English Let's start with this Cleanup function, as an example I want to pull that function out to a separate class... ... and then use an instance of that class as a concrete cleanup strategy These are the refactorings offered by ReSharper I will select this "Move to another type" command I only want to move the Cleanup method And the class into which it will be moved will be known as LinesTrimmer This refactoring will ask for a bit of manual work I don't want the method to be static The class will not be static, either And I want the class to be defined in the Domain namespace That will be enough for now I have just pulled out a class which will perform one isolated task Back in the Parse method, I need to instantiate the object before using it What you are seeing here is the Strategy pattern in its most rudimentary form It is still rigid, there is not a trace of flexibility built into it yet That is because this LinesTrimmer does not implement any abstract type We can pull an interface, or a base class out of a class using one of these commands... ... Extract interface, or Extract superclass You would normally choose to extract a superclass if there is some state... ... which can be shared among derived classes Otherwise, if there is no shared state, then extracting an interface is a better idea Speaking of that, you should generally avoid sharing state... ... between base and derived classes but that is another story Now I am asked to choose a name for the new interface... ... and also to select the members which will be defined on the interface I might simply select the Cleanup method and be done with it, but I won't do that Here is the problem When you extract an interface, that is a special moment for you You are pulling out an interface because you have seen a higher purpose in a class... ... a purpose more abstract than its current implementation It makes no sense to define an interface named ILinesTrimmer... ... when there is already a class named LinesTrimmer Those two would mean the same There is no point in defining a method Cleanup on an interface... ... when there is already a method called Cleanup on a concrete class That method will obviously clean up the data, whatever those are And it is almost certainly not a good idea... ... to define an interface member which processes an array of strings Array is too concrete, which means that some of the callers of this abstract method... ... might have a hard time adapting to its overly concrete signature I'll make a step back to think some more before trying again to extract the interface First, I'll deal with this method Cleanup is too concrete, even for a concrete class The class was already named after one single operation it will perform It will obviously trim lines Therefore, I might call this method Trim This might sound reasonable On the other hand, I am considering an abstract interface right now... ... and then Trim will not make sense to all its implementers Let's go with a totally abstract function name - Execute I am implementing the Strategy pattern, which puts some pressure on... ... giving more abstract names to elements of the design Execute says nothing about what exactly will happen when we call it Under these circumstances, that is precisely what I need I want callers to not know what will happen when they invoke this method That is the purpose of the Strategy pattern... ... decouple the consumers from any concrete implementation of an algorithm So, Execute it will be The next detail that catches my eye is again this array of strings Let's get rid of it once and for all I don't like to use concrete data structures in method signatures That is putting too much of a burden on method callers This is done, then The last detail that remains is the interface name It should be more abstract than any concrete class implementing it I will select this Execute method Now you can see that problem with interface names, which I have already mentioned Interface and class should not be named the same ILinesTrimmer is too concrete name for an interface... ... which only exposes the Execute method... ... which receives a sequence of strings and returns a sequence of strings Interface name would then remain the only concrete element That would not be appropriate You will often see people using the design pattern name... ... when naming an interface or a class I see no point in this No, even worse than that I see problems lurking on the horizon when word Strategy is added to the interface name What if I added a method to it, and it seized to be a strategy implementation? Well, I would have to rename it again, and remove the misleading word Strategy from it That might sound like a no-brainer, and in many occasions... ... it would really be easy to remove the name On the other day, renaming an interface might not be so easy... ... for example because it was published That is how it comes to be that some interface carries an awkward, historic name... ... which is not aligned with its current purpose To avoid all those dangers, I rarely add a pattern name to a type I find it sufficiently descriptive to just call this interface ITextProcessor Here it is, my first strategy Back at the consuming end, I can finally decouple the call... ... from the concrete implementation The way in which you would get hold of a strategy is to set an interface reference Right now, I still have nowhere to get this reference from... ... so I will just instantiate the concrete strategy Soon enough, this will become a matter of configuration at system startup... ... and I won't need to think about concrete implementations But look at one interesting side-effect here The variable I am setting is named cleanup LinesTrimmer says nothing about cleaning up Text processor interface says nothing about cleaning up either But the variable does! There you can see the purpose of abstract and concrete types Abstract types are there to tell what kind of behavior we plan to use Concrete type is there to tell what specific implementation will be used That is why class names must be concrete We must be able to tell one algorithm from the other just by looking at their names Finally, the glue which binds abstract and concrete types together are variables Variable name should tell what role in this concrete situation... ... will that abstract behavior play That will be the cleanup step in my function Now the variable is called cleanup, and everything else disappears from view When cleanup Execute is invoked, you won't even need to read the line above This line's intention is more than clear - the cleanup will be... Executed... alright That's all we need to know And, when said that way, I expect that this line will do whatever it takes to cleanup the text I don't know what exactly that means, and I don't even want to know That has become somebody else's problem in this implementation That wonderful feeling of a relief, that is what decoupling is in programming This call has just become decoupled