There is an abstract class (interface) Presentation, which has two descendants of SpritePresentation and ShapePresentation, how best to write a purely virtual method, if possible, that will set the images for each presentation, because the parameters will be different. I would not like to write overloads, because objects of these classes will have extra methods available.
- And you describe what the parameters will be one, and what the other. It’s also not entirely clear why Strategy is here, I only see a problem with inheritance and interface prototype - Unick
- Well, the same composition, which is implemented in entity classes, to increase the flexibility of the code. And the parameters: for one view, for example: (int x, int y, int width, int height), for the second: (string path) - Sergey
- oneSo why should this method be virtual? - Abyx
- @Abyx to be in an abstract interface :-) - Grundy 5:27 pm
- Perhaps it is worth encapsulating parameters into an object or structure? - Shadasviar
1 answer
In general, there are several ways to transfer the right data to different strategies that may require different input data.
First, the arguments themselves can be polymorphic:
class DrawingContext abstract { public: virtual ~DrawingContext() {} }; class SpriteDrawingContext : public DrawingContext { }; class ShapeDrawingContext : public DrawingContext { }; class Presentation abstract { public: virtual ~Presentation() {}; virtual void Draw(const DrawingContext& context) abstract; }; class SpritePresentation : public Presentation { public: virtual void Draw(const DrawingContext& context) override { // Упадем, если context будет другого типа. // Можно кастить к указателю и бросать исключение/ассертить auto spriteContext = dynamic_cast<const SpriteDrawingContext&>(context); // обрабатываем spritecontext } }; class ShapePresentation : public Presentation { public: virtual void Draw(const DrawingContext& context) override { // Упадем, если context будет другого типа. // Можно кастить к указателю и бросать исключение/ассертить auto shapeDrawingContext = dynamic_cast<const ShapeDrawingContext&>(context); // обрабатываем shapeDrawingContext } }; In this case, the arguments of the method are polymorphic and each strategy can assign them to the necessary and perform the necessary work.
However, in this case, we can easily get an error at runtime, because through the interface of the base class it is not at all clear which arguments are valid and which are not.
The alternative is to convey the necessary context when constructing a specific strategy and to enable it to obtain the necessary data that is needed to perform a specific operation.
class SpriteDrawingContext { }; class ShapeDrawingContext { }; class Presentation abstract { public: virtual ~Presentation() {}; virtual void Draw() abstract; }; class SpritePresentation : public Presentation { const SpriteDrawingContext& context_; public: SpritePresentation(const SpriteDrawingContext& context): context_(context) {} virtual void Draw() override { // используем SpriteDrawingContext } }; class ShapePresentation : public Presentation { const ShapeDrawingContext& context_; public: ShapePresentation(const ShapeDrawingContext& context) : context_(context) {} virtual void Draw() override { // используем ShapeDrawingContext } }; In this case, the contexts are not related to each other, and the draw method has ceased to take any arguments. For the implementation of a specific operation, each strategy will refer to an instance of a specific context to get the necessary data from it.
The first case is implemented using the interaction 'push-model', when all the necessary data are forcibly transferred in an operation. The second option is a so-called 'pull model' of interaction, where each type of strategy gets its own context and “pulls” the data it needs to perform a particular operation.
- oneLoved the second option. Thank you very much. - Sergey
- Yes, he is the most sane. - Sergey Teplyakov