1

I have a problem keeping my code maintainable. In my problem I have defined a 'RenderableShape' that is the parent interface of both 2D and 3D shapes (those two are interfaces).

I have a Renderer class that 'asks' a RenderableShape for the data to render.

A 3D shape should return some mathematical formula or object wrapped around that and a 2D shape returns all its 2D shapes it consists of (triangles, circles etc.).

Obviously 'RenderableShape' cannot return both types two the renderer.

I could make two different methods, but that would force all implementations to implement a useless method.

The renderer can also ask for the shape what kind it is, but you would need a cast afterwards, which should not only be unnecessary, but would also be too slow to use in rendering.

Furthermore, the rendering code should not be in shapes themselves, since I would like to have all rendering code in a renderer, to allow for different renderer types (Z-Buffer, raytracer etc.)

What would be a maintainable and preverably efficient approach to this problem?

GhostCat
  • 137,827
  • 25
  • 176
  • 248
RabbitBones22
  • 302
  • 4
  • 16
  • `but would also be too slow to use in rendering.` How do you know it would be "too slow"? I will spare you from coming up with an answer: you don't. You have no idea. The slowness of an instanceof and a cast would actually be imperceptible. I am not suggesting that you do that, but slowness is not a reason for not doing that. – Mike Nakis Jul 02 '17 at 12:10
  • I might have been misinformed about that. I figured they came with some overhead and seeing that I might have thousands of objects, it did not seem to be a good idea. And do understand the rule of premature optimization is evil, but this is a renderer. It needs to be fast. – RabbitBones22 Jul 02 '17 at 12:20

3 Answers3

2

What would be a maintainable and preverably efficient approach to this problem?

One of the OOP principles which should be applied here is tell, don't ask!

Pass the renderer to the RenderableShape and have it draw itself to the renderer.

Timothy Truckle
  • 15,071
  • 2
  • 27
  • 51
  • Well the I based mine on does this, but it is tricky to have different types of renderers then. Of course the object could go back and forth with the renderer, but I don't know if that kind of tight coupling would be good and it certainly does not make it faster. I will edit my question a bit. – RabbitBones22 Jul 02 '17 at 12:09
  • @RabbitBones22 This is a good approach. Have a look at java swing, method involving `Graphics` are all based on a polygon/shape object which takes the renderer as argument and render itself. – BackSlash Jul 02 '17 at 12:11
  • Okay, but how would I go about and support different rendering types? I think it would be very difficult to make objects raytrace themselves, or maybe worse, scanline-render themselves. – RabbitBones22 Jul 02 '17 at 12:14
  • @RabbitBones22 The renderers should have a common interface that declares methods used by the shapes. – Timothy Truckle Jul 02 '17 at 12:20
  • My renderers are already defined through interfaces. The thing is, taking the scanline-renderer for example, it needs to know all the shapes before it can render. If objects would render themselves, this would be very messy. – RabbitBones22 Jul 02 '17 at 12:23
  • *"taking the scanline-renderer for example, it needs to know all the shapes before it can render."* - I thing it does not need the shapes, but the positions where to put a colored dot. So I would do this in two phases: first create the image by iterating the list of shapes then drawing the image to the actual device line by line... – Timothy Truckle Jul 02 '17 at 12:29
1

First of all, you should not even think about "removing" a method from an interface. If you can call foo() on the super class, the Liskov substitution principle states that you need that foo() must exist on the derived classes, too. Anything else is a clear violation of how OOP is supposed to work.

So, you should follow the answer that was already given. Don't ask a shape for something that tells the renderer what to do - but instead enable each shape to render itself (by passing the renderer to the shape).

GhostCat
  • 137,827
  • 25
  • 176
  • 248
1

Try to decompose your task further, what is shapes? it is set of curves and lines in certain positions of display. How about to make something like simple api to do that? Make render to write lines, curves, dots, etc.

interface Render { 

    renderLine(Formula formula);

    renderCurve(Formula formula);

    renderDot(Formula formula);

}

after that create Shape that get Render instance and write himself through this api. Shape knows all about what amount of dots, lines etc. need to write himself. Formula is interface that present mathematic formula. When you need more complex objects further make Render to render simple triangles, squares, etc.

fxrbfg
  • 1,756
  • 1
  • 11
  • 17
  • 1
    Very interesting. I will give this some thought. Well I currently thought of the following 3D shapes: Sphere, Cylinder, Ellipsoid, Torus and Cone. 2D: Triangle, Polygon, Circle and Ellipse. But dividing the 2D might do the trick... – RabbitBones22 Jul 02 '17 at 13:41
  • I might actually handle it like OpenGL or others and split all geometry to triangles and make every renderer able to render the triangles (with the exception of Ray Tracing). But I will see how basic shapes can turn out first. – RabbitBones22 Jul 03 '17 at 07:57