Question about design (inheritance, polymorphism)

I have a question about a problem I'm struggling with. I hope you can carry me.

Imagine that I have an Object class that represents the base class of a hierarchy of physical objects. Later I inherit it to create the classes Object1D, Object2D and Object3D. Each of these derived classes will have some specific methods and attributes. For example, a three-dimensional object may have functionality for loading a 3d model to be used by the visualizer.

So I would have something like this:

class Object {}; class Object1D : public Object { Point mPos; }; class Object2D : public Object { ... }; class Object3D : public Object { Model mModel; }; 

Now I will have a separate class called Renderer, which simply accepts the object as an argument and well does it :-) In a similar way, I would like to support various types of rendering. For example, I could use the default one that every object could rely on, and then provide other specific visualization tools for some objects:

 class Renderer {}; // Default one class Renderer3D : public Renderer {}; 

And here is my problem. The renderer class must receive the object as an argument, for example, in the constructor, in order to get any data needed to render the object.

So far so good. But Renderer3D must receive the Object3D argument in order to get not only the basic attributes, but also the specific attributes of the 3D object.

Constructors will look like this:

 CRenderer(Object& object); CRenderer3D(Object3D& object); 

Now, how can I indicate this in a general way? Or better yet, is there a better way to develop it?

I know I can rely on RTTI or the like, but I would like to avoid this if possible, as I believe that is probably the best way to handle this.

Thanks in advance!

+4
source share
5 answers

One approach is to allow objects to create their own Renderer, although the getRenderer () method, which returns a Renderer of the appropriate type. This is an example of a Factory method template. Depending on the specifics of the implementation language, the getRenderer method can be an abstract method in the parent class Object, returning an instance of the Renderer parent class / interface, which concrete Renderers extend / implement.

+3
source

One way to solve this problem is to use factories. A general factory creates objects and visualization tools. 2D factory creates 2D objects and compatible 2D renderers; 3D factory creates 3D objects and compatible 3D renderers.

 abstract class Factory { abstract Object createObject(); abstract Renderer createRenderer(); } class 2DFactory { Object createObject() { return new 2DObject(); } Renderer createRenderer() { return new 2DRenderer(); } } // similarly for 3D class Client { Client(Factory factory) { ... } void render() { factory.createRenderer().render(factory.createObject()); } } 

Thus, if you do not mix products from different factories, you can keep the customer code clean. If the client code processes only one family of objects and renderers at a time, it is trivial to pass the correct factory to it, and then let it use objects and visualization tools without knowing their specific type. A variation on this is to allow the object to create compatible rendering using the factory method, as suggested by Jason .

Another option is to use / generics templates, but it depends on the language. The implementation language looks like C ++, so I take risks in this direction. You can use specialized specialization:

 template<class T> class Renderer { void render(T object) { ... } }; template<> class Renderer<Object3D> { void render(Object3D object) { // render the 3D way } }; // similarly for 2D 
+4
source

Is this doable?

 template<typename T> class Renderer {}; class Renderer3D : public Renderer<Object3D> {}; 
+1
source

It looks like you are looking for a double submit pattern. To use this, you add an abstract method to your base class, something like this:

 Dispatch(renderer) 

Each subclass of the object then overrides Dispatch and calls the appropriate method for the provided renderer for its type:

 Object2D.Dispatch(renderer) { renderer.Render2D(self) } 

You can also use interfaces to make this more general. Create an interface for rendering each type of object and ask the submit method to check if the corresponding interface is supported:

 Object2D.Dispatch(renderer) { render2d = render as IRender2D if render2d found render2d.Render(self) } 
+1
source

Here is another way to look at it. Make the render part of your object. You can assign the correct type of visualization tool for each object you create, and on each object you will have

 Render(){ myRenderer.Render(this) } 
0
source

Source: https://habr.com/ru/post/1306519/


All Articles