I find that the encapsulation required by OO has me frequently passing paramenters down the line from parent to child to great grandchild to second grand nephew once removed (not actually that bad). But inevitably in the process, and especially if graphics are involved there will be garbling the by the third iteration of someConstructor(int minX, int minY, int maxX, int maxY).

I found at least one question here suggesting that parameter objects are the solution. These seem to me more like a kludge designed to satisfy a low parameter count fetish than genuinely useful. Besides, in these scenarios, I am generally cutting the numbers into smaller pieces (of screen) with each step.

It occurs to me if these constructor calls are laid out line by line higher up the chain, I'd have easy comparisons. But this seems to lose all semblance of OOPiness. In my most recent adventure with find-the-transposed-measurements, I was drawing a music keyboard. I don't want the main view to know or care what or exactly where the G# key is.

So, best practices? Please don't say, go slower, be less sloppy.

  • 10
    If 2D graphics is involved, my best practice is to use some kind of Point and Rect types everywhere coordinates are mentioned. Beside other advantages, it helps you separate coordinates from other numbers on type level.
    – 9000
    Jan 2 '13 at 6:11
  • Try to get rid of the chaining. By terminating the chain in some way frequently.
    – tp1
    Jan 2 '13 at 8:02
  • @anthropomo: I don't know about other languages, but you can always call someConstructor(minX = 10, minY = 9, maxX = 15, maxY = 54) Jan 2 '13 at 8:30
  • Could you expand on this bit? "Besides, in these scenarios, I am generally cutting the numbers into smaller pieces (of screen) with each step." It sounds like you have a view hierarchy here. If so, doing what you're doing might be the only approach that makes any kind of sense. Jan 2 '13 at 9:33
  • @HoàngLong: You can, but shouldn't. It is very unlikely that a call of someConstructor(7, 800, 14) intended to use the default maxY = 54. In GFX development, you usually don't pass one and a half point.
    – MSalters
    Jan 2 '13 at 16:50

To expand on @9000's comment, I would use objects that provide more information and possibly error checking about your arguments. For example, in the following:

drawLine(100, 20, 300, 30);
drawLine(new Point(100, 20), new Point(300, 30));

The second is generally more readable. It can check errors (can coordinates be negative? can a rectangle have a negative width or height?) and provides an easier way to access helper methods (such as converting coordinate systems or affine transformations). Also, if there is a lot of data being passed around (such as a list of points for a polyline), you end up passing object references rather than the whole data structure.

  • 3
    Object Callisthenics (xpteam.com/jeff/writings/objectcalisthenics.rtf) suggests wrapping EVERY primitive in a class, for exactly this reason, taking it to the point where you can't accidentally pass an X coordinate into a Y argument -- drawLine(new Point(new X(100), new Y(20)) new Point(new X(300), new Y(30))). (Note, object callisthenics is not strictly a guide to programming, it's more "try these strict rules in a small personal project and they'll make you think about where the common-sense line is for a professional project.)
    – pdr
    Jan 2 '13 at 8:56
  • 1
    Note that when working with graphics performance may be a prime feature, thus checking parameters or instantiating new objects in intensive loops may cause significant performance loss.
    – Kemoda
    Jan 2 '13 at 10:54
  • @Kemoda: depends critically on the language and implementation. For instance, a modern C++ compiler will likely use 4 registers to pass the arguments in drawLine(Point(100, 20), Point(300, 30)). Sure, theoretically speaking that constructs 2 objects, but there's not a single CPU instruction associated with that.
    – MSalters
    Jan 2 '13 at 16:47
  • @Kemoda - along these lines, F# has a handy feature called unit of measure. Basically, you end up annotating things, and the checking exists solely at compile time - the raw values are used at runtime. Using that for a 'dimension x' object would work; although I can't speak for how well F# could do graphics... Jan 2 '13 at 19:10
  • Ok, thanks for the info. But my concern was primarilly checking values in intensive loops (if x < 0 then what).
    – Kemoda
    Jan 3 '13 at 7:46

I'm not entirely sure what your class hierarchy looks like, but I will give you my thoughts on object construction and how I have started designing my classes in projects I work on.

  • Inheritance or Composition: It sounds like you are assuming that because you are programming in an OO style, you must always use inheritance; this is just not true. It is up to you, the designer, to determine what solution best solves you problem. Below I'll give you an example of composition instead of inheritance.

  • Parameter Objects (as you put it): When you design a class, you are modeling your data by how it is seen in your head; you are giving it a name and structure. A simple example would be a class called Point2D that would have an X and a Y property associated with that class. The good thing about creating this "parameter object" is now you can create functions or methods that operate on that object. I don't create Point2D with a constructor, I make a second class called Point2DFactory with a static Create(x, y) method. This does 2 things for me, it keeps my Point2D class light weight and puts all the logic for creating a Point2D in one place. I can now compose a Point2D in anyway I wish and the Point2D is no longer responsible for both being a point and knowing how a point is created. It is not tied to some inheritance chain, it is the Point2DFactory's job to worry about that.

  • Inversion of Control: If you need to encapsulate some over arching functionality into a single object that can be broken down into little bits of functionality, you can use an inversion of control framework to help you out. What it does is take all those "parameter objects" you have created (hopefully with abstract interfaces) and figures out how to build the object you require by injecting the functionality into it's constructor.

If you are creating constructors that rely on some math to generate their parameters, consider creating a parameter object and a factory that creates it. Then you can make another factory that creates your desired object based on any input you wish. Can you tell I use the factory pattern a lot :P? I hope what I have said shows you that creating more objects isn't a bad thing (OOP requires it :/) and if you can get each class/object to only have one responsibility, your overall code base will be extremely flexible and a pleasure to maintain.

EDIT in response to comment: Maybe I am not understanding exactly what you are asking. I thought your problem what nested constructors that have similar input names. What I was trying to get at is that, in general, a parent shouldn't need to know how to instantiate its child objects. That makes your parent responsible for doing what ever it is the parent does AND for creating the child objects. If the parent's soul purpose to is create the child objects, that is called a factory in design pattern terms. Instead of constructor chaining, pull that logic that instantiates child objects outside of the constructors. This giving you more flexible usability of the logic that creates child objects and better adherence to SOLID principles.

  • I think maybe my parent/child comment was mis-worded. I meant parent as in the object that calls the constructor to instantiate the child object. Which isn't quite right. So yes, I am aboard the interface train. But I am still working my way through patterns.
    – anthropomo
    Jan 2 '13 at 11:39
  • The edit makes sense. I was responding specifically to the idea of the first bullet point, inheritance vs. composition.
    – anthropomo
    Jan 2 '13 at 14:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.