I know this is an old question, but I think I have another interesting example that I implemented recently.
This is a very practical example of the strategy pattern being used in a document delivery system.
I had a PDF delivery system which received an archive containing lots of documents and some metadata. Based on the metadata, it decided where to put the document in; say, depending on the data, I could store the document in A
, B
, or C
storage systems, or a mix of the three.
Different customers used this system, and they had different rollback / error handling requirements in case of errors: one wanted the delivery system to stop on the first error, leave all documents already delivered in their storages, but stop the process and not deliver anything else; another one wanted it to rollback from B
in case of errors when storing in C
, but leave whatever was already delivered to A
. It's easy to imagine that a third or fourth one will also have different needs.
To solve the problem, I have created a basic delivery class that contains the delivery logic, plus methods for rolling back stuff from all storages. Those methods are not actually called by the delivery system directly in case of errors. Instead, the class uses Dependency Injection to receive a "Rollback / Error Handling Strategy" class (based on the customer using the system), which is called in case of errors, which in turn calls the rollback methods if it's appropriate for that strategy.
The delivery class itself reports what's going on to the strategy class (what documents were delivered to what storages, and what failures happened), and whenever an error occurs, it asks the strategy whether to continue or not. If the strategy says "stop it", the class calls the strategy's "cleanUp" method, which uses the information previously reported to decide which rollback methods to call from the delivery class, or simply do nothing.
rollbackStrategy.reportSuccessA(...);
rollbackStrategy.reportFailureB(...);
if (rollbackStrategy.mustAbort()) {
rollbackStrategy.rollback(); // rollback whatever is needed based on reports
return false;
}
So I now have two different strategies: one is the QuitterStrategy
(which quits on the first error and cleans up nothing) and the other one is the MaximizeDeliveryToAStrategy
(which tries as much as possible not to abort the process and never rollback stuff delivered to storage A
, but rollbacks stuff from B
if delivery to C
fails).
From my understanding, this is one example of the strategy pattern. If you (yes, you reading) think I'm wrong, please comment below and let me know. I'm curious as to what would constitute a "pure" use of the strategy pattern, and what aspects of my implementation violate the definition. I think it looks a bit funny because the strategy interface is a bit fat. All examples I've seen so far use only one method, but I still think this encapsulates an algorithm (if a piece of business logic can be considered an algorithm, which I think it does).
Since the strategy is also notified about events during the delivery execution, it can also be considered an Observer, but that's another story.
From doing a little research, it seems like this is a "composite pattern" (like MVC, a pattern that uses multiple design patterns underneath in a particular way) called the Advisor. It's an advisor on whether the delivery should continue or not, but it's also an active error handler since it can rollback stuff when asked to.
Anyways, this is a quite complex example that might make that feeling of yours that usages of the strategy pattern are all too simple / silly. It can be really complex and even more applicable when used together with other patterns.