# Strategy The second most popular design pattern is a strategy. If I need to bet how many of you used this pattern at least once I will say all of you. Strategy is the most common pattern used by the STL library and makes it so flexible. That's why we should use it often (maybe not everywhere) but in most cases, strategy will be an answer. Why we should use strategy? As David Thomas and Andrew Hunt said in the book: *Inheritance is Rarely the Answer*. ___ ## Example of bad inheritance Before we talk about strategy patterns, let's look at the bad design of the application. We want to create three types of ammunition: *Round*, *Chain* and *Grape*. Each type of ammunition needs to be displayed as an icon (for instance when we check, the inventory, or buy it from the store) and also needs to be animated when a ship `fires` this ammunition during combat. We decided to solve this problem by create inheritance. We inherit from class `Ammunition` and create three classes of `Shots`, then depending on which graphic library we used we `draw` it using `metal` or `OpenGL`. Amunnition UML ___ ## Next iteration After we successfully implemented the `draw` method, we decided to add `SDL` and `WindowsAnimationManager` libraries for displaying animation during combat. Because we already use `OpenGL` and `metal` libraries, we still need to support them. So we need to create another layer of inheritance. Amunnition UML ___ I hope all of you saw that this is not a good example of a design application. Each time we add support for a new library, we need to create another layer of inheritance. How we can make it more flexible? The answer is the `Strategy` design pattern. Amunnition UML ___ ## Simplify First of all, we don't want to break 2 of the SOLID letters: **L**iskov substitution and **S**ingle responsibility. That's why we should inherit from class which all methods make sense for us. We create two strategies, one for `fire` and one for `draw`. We also want to avoid an oversized hierarchy of class. Amunnition UML ___ ## Implementation The ammunition class will be a base class with the interface. It also contains info about the amount of ammunition. ```C++ class Ammunition { public: Ammunition(size_t amount) : _amount(amount) {} virtual ~Ammunition() = default; virtual void fire() = 0; virtual void draw() = 0; virtual int getDamage() const = 0; size_t amount() const { return _amount; } private: size_t _amount = 0; }; ``` ___ ```C++ class FireStrategy { public: virtual void fire(const Ammunition& ammunition) = 0; }; class SDLFireStrategy : public FireStrategy { public: void fire(const Ammunition& ammunition) override { std::cout << "SDL Deal: " << ammunition.getDamage() << " damage!" << '\n'; } }; class WAMFireStrategy : public FireStrategy { public: void fire(const Ammunition& ammunition) override { std::cout << "WAM Deal: " << ammunition.getDamage() << " damage!" << '\n'; } }; ``` ```C++ class DrawStrategy { public: virtual void draw(const Ammunition& ammunition) = 0; }; class OpenGLDrawStrategy : public DrawStrategy { public: void draw(const Ammunition& ammunition) override { std::cout << "OpenGL Ammunition: " << ammunition.amount() << '\n'; } }; class MetalDrawStrategy : public DrawStrategy { public: void draw(const Ammunition& ammunition) override { std::cout << "Metal Ammunition: " << ammunition.amount() << '\n'; } }; ``` ___ ```C++ class RoundShot : public Ammunition { public: RoundShot(size_t amount, std::unique_ptr&& fireStrategy, std::unique_ptr&& drawStrategy) : Ammunition(amount), _fireStrategy(std::move(fireStrategy)), _drawStrategy(std::move(drawStrategy)) {} void fire() override { _fireStrategy->fire(*this /* other args if needed*/); } void draw() override { _drawStrategy->draw(*this /* other args if needed*/); } int getDamage() const override { return 10; } private: std::unique_ptr _fireStrategy; std::unique_ptr _drawStrategy; }; ``` ```C++ class ChainShot : public Ammunition { public: ChainShot(size_t amount, std::unique_ptr&& fireStrategy, std::unique_ptr&& drawStrategy) : Ammunition(amount), _fireStrategy(std::move(fireStrategy)), _drawStrategy(std::move(drawStrategy)) {} void fire() override { _fireStrategy->fire(*this /* other args if needed*/); } void draw() override { _drawStrategy->draw(*this /* other args if needed*/); } int getDamage() const override { return 15; } private: std::unique_ptr _fireStrategy; std::unique_ptr _drawStrategy; }; ``` ```C++ class GrapeShot : public Ammunition { public: GrapeShot(size_t amount, std::unique_ptr&& fireStrategy, std::unique_ptr&& drawStrategy) : Ammunition(amount), _fireStrategy(std::move(fireStrategy)), _drawStrategy(std::move(drawStrategy)) {} void fire() override { _fireStrategy->fire(*this /* other args if needed*/); } void draw() override { _drawStrategy->draw(*this /* other args if needed*/); } int getDamage() const override { return 20; } private: std::unique_ptr _fireStrategy; std::unique_ptr _drawStrategy; }; ``` ___ ## Usage We can easily inject any strategy into an ammunition class. This will help us keep another solid rule **D**ependency inversion and also dependency injection. ```C++ int main() { std::unique_ptr grapeShot = std::make_unique( 100, std::make_unique(), std::make_unique()); grapeShot->fire(); grapeShot->draw(); } ``` ```bash SDL Deal: 20 damage! OpenGL Ammunition: 100 ``` ___ ## Drawback The main drawback of the strategy pattern is visible when we try to add a new type. In such case, we need to implement a new behavior for each of these classes: * DrawOpenGLStrategy * DrawMetalStrategy * WamFireStrategy * SDLFireStrategy This will be not such big deal, when we have access to all of the source code, but in case, when we can't modify this it will be problematic ___ ## Improvement For now, `DrawStrategy` and `FireStrategy`, have one `virtual` function which probably needs to distinguish which type of ammunition should be drawn or animate. That's why we can create a more flexible interface, which takes a specific type and we don't need to use any casting. ```C++ class FireStrategy { public: virtual void fire(const ChainShot& shot) = 0; virtual void fire(const RoundShot& shot) = 0; virtual void fire(const GrapeShot& shot) = 0; }; ``` ___ ```C++ class SDLFireStrategy : public FireStrategy { public: void fire(const ChainShot& shot) override { std::cout << "SDL Chain Shot Deal: " << shot.getDamage() << " damage!" << '\n'; } void fire(const RoundShot& shot) override { std::cout << "SDL Round Shot Deal: " << shot.getDamage() << " damage!" << '\n'; } void fire(const GrapeShot& shot) override { std::cout << "SDL Grape Shot Deal: " << shot.getDamage() << " damage!" << '\n'; } }; ``` ```C++ class WAMFireStrategy : public FireStrategy { public: void fire(const ChainShot& shot) override { std::cout << "WAM Chain Shot Deal: " << shot.getDamage() << " damage!" << '\n'; } void fire(const RoundShot& shot) override { std::cout << "WAM Round Shot Deal: " << shot.getDamage() << " damage!" << '\n'; } void fire(const GrapeShot& shot) override { std::cout << "WAM Grape Shot Deal: " << shot.getDamage() << " damage!" << '\n'; } }; ``` ___ ```C++ class DrawStrategy { public: virtual void draw(const ChainShot& shot) = 0; virtual void draw(const RoundShot& shot) = 0; virtual void draw(const GrapeShot& shot) = 0; }; ``` ```C++ class OpenGLDrawStrategy : public DrawStrategy { public: void draw(const ChainShot& shot) override { std::cout << "OpenGL Chain Shot: " << shot.amount() << '\n'; } void draw(const RoundShot& shot) override { std::cout << "OpenGL Round Shot: " << shot.amount() << '\n'; } void draw(const GrapeShot& shot) override { std::cout << "OpenGL Grape Shot: " << shot.amount() << '\n'; } }; ``` ```C++ class MetalDrawStrategy : public DrawStrategy { public: void draw(const ChainShot& shot) override { std::cout << "Metal Chain Shot: " << shot.amount() << '\n'; } void draw(const RoundShot& shot) override { std::cout << "Metal Round Shot: " << shot.amount() << '\n'; } void draw(const GrapeShot& shot) override { std::cout << "Metal Grape Shot: " << shot.amount() << '\n'; } }; ``` ___ ## Usage Now the function exactly knows, which type is handling, and can animate it or draw it correctly, without any type checking. ```C++ int main() { std::unique_ptr grapeShot = std::make_unique( 100, std::make_unique(), std::make_unique()); grapeShot->fire(); grapeShot->draw(); } ``` ```bash SDL Grape Shot Deal: 20 damage! OpenGL Grape Shot: 100 ``` ___ ## Separate implementation Strategy can distinguish type, but still depends on all types, let's rewrite the code once more and use another letter form SOLID: **I**nterface segregation. Each implementation will be a separate strategy class, so when we add a new type, other strategies will not know about it. ```C++ class FireChainShotStrategy { public: virtual void fire(const ChainShot& shot) = 0; }; class FireRoundShotStrategy { virtual void fire(const RoundShot& shot) = 0; }; class FireGrapeShotStrategy { virtual void fire(const GrapeShot& shot) = 0; }; class SDLFireChainShotStrategy : public FireChainShotStrategy { public: void fire(const ChainShot& shot) override; }; class SDLFireRoundShotStrategy : public FireRoundShotStrategy { public: void fire(const RoundShot& shot) override; }; class SDLFireGrapeShotStrategy : public FireGrapeShotStrategy { public: void fire(const GrapeShot& shot) override; }; ``` ___ ```C++ class DrawChainShotStrategy { public: virtual void draw(const ChainShot& shot) = 0; }; class DrawRoundShotStrategy { virtual void draw(const RoundShot& shot) = 0; }; class DrawGrapeShotStrategy { virtual void draw(const GrapeShot& shot) = 0; }; ``` ```C++ class OpnGLDrawChainShotStrategy : public DrawChainShotStrategy { public: void draw(const ChainShot& shot) override; }; class OpenGLDrawRoundShotStrategy : public DrawRoundShotStrategy { public: void draw(const RoundShot& shot) override; }; class OpenGLDrawGrapeShotStrategy : public DrawGrapeShotStrategy { public: void draw(const GrapeShot& shot) override; }; ``` ___ ## Single resposibility In the beginning, we may think that we will need to create too many classes because each `Ammunition` type needs 4 classes (2 for draw, and 2 for fire), but when we take a look at the first letter of SOLID - **S**ingle responsibility, we understand that now we follow this rule. Previously `OpenGLDrawStrategy` and `MEtalDrawStrategy` needed to know about 3 different types of ammunition, and we broke all rules: * Single resposinility -> each class need to know about all types * Open close -> code was not open for extension, when we add a new type, we need to implement behavior in all 4 classes, even if we don't want to, which may cause to break another letter: * Liskov substitution -> we may inherit from an interface which is not fully suitable for us, that's why we should: * Interface segregation -> separate too big interface into a smaller one. * Dependency inversion -> High level class DrawStrategy nad FireStrategy depends on low level classes RoundShot, ChainShot and GrapeShot. As you can see, the following implementation of strategy, with many small classes is the correct one. ___ ## Broken Dependency inversion Dependency inversion -> High level class DrawStrategy nad FireStrategy depends on low level classes RoundShot, ChainShot and GrapeShot. Broken architecture ___ ## Modern approach Following the rule of SOLID, we almost reached a perfect architecture, but the one thing left. Class `DrawChainStrategy`, `DrawRoundStrategy`, and `DrawGrapeStrategy` is connected with a specific type like: `RoundShot` or `ChainShot`. We follow all SOLID rules, but still there is one thing to improve. **Do you know how we can improve it**? Good architecture ___ ## Template strategy By creating a template for `DrawStrategy` we remove all dependencies between specific types of ammunition and strategy: ```C++ template class DrawStrategy { virtual ~DrawStrategy() = default; virtual void draw(const T&) const = 0; }; ``` Perfect architecture ___ ## Full implementation ```C++ template class FireStrategy { public: virtual void fire(const T& shot) = 0; }; class SDLFireRoundShotStrategy : public FireStrategy { public: void fire(const RoundShot& shot) override; }; class SDLFireChainShotStrategy : public FireStrategy { public: void fire(const ChainShot& shot) override; }; class SDLFireGrapeShotStrategy : public FireStrategy { public: void fire(const GrapeShot& shot) override; }; ``` ```C++ class WAMFireRoundShotStrategy : public FireStrategy { public: void fire(const RoundShot& shot) override; }; class WAMFirChainShotStrategy : public FireStrategy { public: void fire(const ChainShot& shot) override; }; class WAMFireGrapeShotStrategy : public FireStrategy { public: void fire(const GrapeShot& shot) override; }; ``` ___ ```C++ template class DrawStrategy { public: virtual void draw(const T& shot) = 0; }; class OpenGLDrawRoundShotStrategy : public DrawStrategy { public: void draw(const RoundShot& shot) override; }; class OpenGLDrawChainShotStrategy : public DrawStrategy { public: void draw(const ChainShot& shot) override; }; class OpenGLDrawGrapeShotStrategy : public DrawStrategy { public: void draw(const GrapeShot& shot) override; }; ``` ```C++ class MetalDrawRoundShotStrategy : public DrawStrategy { public: void draw(const RoundShot& shot) override; }; class MetalDrawChainShotStrategy : public DrawStrategy { public: void draw(const ChainShot& shot) override; }; class MetalDrawGrapeShotStrategy : public DrawStrategy { public: void draw(const GrapeShot& shot) override; }; ``` ___ ```C++ class RoundShot : public Ammunition { public: RoundShot(size_t amount, std::unique_ptr>&& fireStrategy, std::unique_ptr>&& drawStrategy) : Ammunition(amount), _fireStrategy(std::move(fireStrategy)), _drawStrategy(std::move(drawStrategy)) {} void fire() override { _fireStrategy->fire(*this /* other args if needed*/); } void draw() override { _drawStrategy->draw(*this /* other args if needed*/); } int getDamage() const override { return 10; } private: std::unique_ptr> _fireStrategy; std::unique_ptr> _drawStrategy; }; ``` ```C++ class ChainShot : public Ammunition { public: ChainShot(size_t amount, std::unique_ptr>&& fireStrategy, std::unique_ptr>&& drawStrategy) : Ammunition(amount), _fireStrategy(std::move(fireStrategy)), _drawStrategy(std::move(drawStrategy)) {} void fire() override { _fireStrategy->fire(*this /* other args if needed*/); } void draw() override { _drawStrategy->draw(*this /* other args if needed*/); } int getDamage() const override { return 15; } private: std::unique_ptr> _fireStrategy; std::unique_ptr> _drawStrategy; }; ``` ___ ```C++ class GrapeShot : public Ammunition { public: GrapeShot(size_t amount, std::unique_ptr>&& fireStrategy, std::unique_ptr>&& drawStrategy) : Ammunition(amount), _fireStrategy(std::move(fireStrategy)), _drawStrategy(std::move(drawStrategy)) {} void fire() override { _fireStrategy->fire(*this /* other args if needed*/); } void draw() override { _drawStrategy->draw(*this /* other args if needed*/); } int getDamage() const override { return 20; } private: std::unique_ptr> _fireStrategy; std::unique_ptr> _drawStrategy; }; ``` ```C++ int main() { std::unique_ptr grapeShot = std::make_unique( 100, std::make_unique(), std::make_unique()); grapeShot->fire(); grapeShot->draw(); } ``` ```bash SDL Grape Shot Deal: 20 damage! OpenGL Grape Shot: 100 ``` ___ ## Exercise 1 * Go into directory Core and implement ShipStrategy as a template class * Strategy should have one pure virtual function: Res handle(T&) * Implement in directory Ship class ShipEasyDifficultLvlStrategy * Each cargo supplies 2 sailors, so if you have 40 crew, you need 20 banas and 20 rum * Implement NextDay by using strategy class * Run code, and verify result ___ ## Exercise 2 * Implement in directory *Player* class *PalyerEasyDifficultLvlStrategy*     * Strategy should roll a die (1-20):     * If result is lower than 5 -> 0 DMG     * If result is higher than 19 -> multiple damage by 2     * Damage should be rolled from (25 to 50)     * deal damage to the ship and return how much damage was deal. Print it in console * Add all necessary implementation to the *Enemy* file * Test it ___ ## Exercise 3 * Implement new classes *EnemyHardDifficultLvlStrategy* and *ShipHardDifficultLvlStrategy* * Strategy for Ship, should subtract cargo equal crew size     * Strategy for Enemy should roll a die (1-20):     * If result is lower than 3 -> 0 DMG * If result is higher than 15 but not equal 20 -> multiple damage by 3     * If result is higher than 19 -> multiple damage by 3     * Damage should be rolled from (30 to 60) * You shouldn't modify any existing file, except the main.cpp, where you should use a new strategy * Run code and verify the result ___ ## Strategy is everywhere Before we go on to the next design pattern. I want to show you that `Strategy` is almost everywhere. Let's closely look at some STL algorithms: ```C++ template< class RandomIt > void sort( RandomIt first, RandomIt last ); ``` ```C++ template< class RandomIt, class Compare > void nth_element( RandomIt first, RandomIt nth, RandomIt last, Compare comp ); ``` ```C++ template< class InputIt1, class InputIt2, class OutputIt, class Compare > OutputIt set_difference( InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, OutputIt d_first, Compare comp ); ``` **What is common for all of these functions?** ___ STL library was designed by using design patterns, which gives this library a lot of flexibility. Users who want to use one of the STL algorithms, need to provide a strategy, mostly it will be a lambda object, which tells the algorithm how to treat a specific object. What's more, it uses modern by-value semantics, because we don't have any polymorphisms and virtual functions. The first version of STL was designed in 1994-1998, so this modern approach has 30 years :) I think we should learn a lot from this design, because not always need to solve everything by creating virtual classes. ___ ## Rewrite Difficult Strategy ```C++ template class Ship : public TimeObserver { public: Ship(Time* time, DifficultLvlStrategy strategy, const std::string& name, int capacity, int crew); // ohter public methods private: // other members DifficultLvlStrategy _strategy; }; ``` ```C++ int main() { auto strategy = [](Ship& ship[]){ Ship::StatusCode status; ship.unload("Rum", ship.crew() / 2, status); Ship::StatusCode status2; ship.unload("Banana", ship.crew() / 2, status2); return status == Ship::StatusCode::OK && status2 == Ship::StatusCode::OK; }; Ship ship(&time, strategy, "Black Widow", 1000, 40); } ``` ___ ## If we don't like templates ```C++ class Ship : public TimeObserver { public: Ship(Time* time, std::function strategy, const std::string& name, int capacity, int crew); // ohter public methods private: // other members std::function _strategy; }; ``` ```C++ int main() { auto strategy = [](Ship& ship[]){ Ship::StatusCode status; ship.unload("Rum", ship.crew() / 2, status); Ship::StatusCode status2; ship.unload("Banana", ship.crew() / 2, status2); return status == Ship::StatusCode::OK && status2 == Ship::StatusCode::OK; }; Ship ship(&time,strategy, "Black Widow", 1000, 40); } ```