2

I'm working on a university project, it's subject being the animation of articulated figures. The code is written in C++ and I'm using Qt's UI framework.

The business logic of the application is separated into a wrapper class, let's call this Wrapper. This uses the singleton design pattern, because multiple widgets (windows, tabs etc.) are accessing it and I want to be sure there's only one instance.

Here's the code (simplified) illustrating the class hierarchy of the Wrapper class:

class Wrapper { private: vector<Skeleton> _skeletons; public: static Cor3d& getInstance() { static Cor3d instance; return instance; } // other data members and functions } class Skeleton { private: vector<Joint> _joints; } class Joint { private: DCoordinate3 _rotation_axis; } class DCoordinate3 { private: double _data[3]; } 

The user should be able to build their figures (add a skeleton, add joints change their coordinates etc.). Let's suppose that the user wants to change the x coordinate of the rotation axes. Here's what I'm currently doing:

double DCoordinate3::x() const { return _data[0]; } double Joint::rotation_axes_x() const { return _rotation_axis.x(); } double Skeleton::joint_rotation_axes:x(unsigned int joint_id) const { return _joints[joint_id].rotation_axes_x(); } double Wrapper::skeleton_joint_rotation_axes_x(unsigned int skeleton_id, unsigned int joint_id) const { return _skeletons[skeleton_id].joint_rotation_axes(joint_id); } 

can be called like:

double x = Wrapper::getInstance().skeleton_joint_rotation_axes_x(1, 25); 

The problem with this is that I had to write four get methods to get a single coordinate. The classes contain more data in reality, and writing all the get/set methods will result in bloated, hard to maintain code.

Question: Is there a better way to be able to manipulate the data in this class hierarchy (without violating OOP and encapsulation principles)?

Other solutions I have thought of:

  • Returning the "mid-level" objects (Skeleton, Joint)

    Skeleton Wrapper::get_skeleton(unsigned int skeleton_id) { return _skeletons[skeleton_id]; } // same with Joint and DCoordinate3 

    The problem with this is that the classes might get big and making a copy of them every time might be an issue.

  • Returning references to objects. The same data is accessed from multiple widgets, this might cause references to objects which no longer exist (See: Scott Meyers, Effective C++ 3rd Edition, 2005, Item 21)

  • declaring the Wrapper class friend of the others

Question (reformulated): What is the OOP way to elegantly solve this problem (from the ones that I described or something else)? Or what would be an "accepted" solution?

3
  • Separate access to x, y and z coordinates? Why not to the integral and fractional parts of same? What about separate access to each individual decimal digit? Of course if you have getters and setters you are not really doing OO, but that's another question altogether. Commented Aug 23, 2014 at 12:59
  • OOP is not a silver bullet. If your problem does not require the implementation of abstract operations to be chosen at run-time, then you don't need OOP, and the question becomes moot. And if you think you need a Singleton, think twice. Most Singletons are glorified global variables, sharing all of their design problems. Commented Aug 23, 2014 at 13:03
  • @n.m. You're right, separate access to x, y and z is probably unnecessary but I was just illustrating my point. Commented Aug 23, 2014 at 13:23

1 Answer 1

1

I am cobbling a couple of pieces of information together in the hope of coming up with a coherent answer, I also want to preface that that my Qt specific know how mostly pertains to Qt4, patterns might have changed with Qt5. In terms of OOP and design patterns

Especially with regard to Qt, the object that you are manipulating would be the Model part of the Model-View-Controller (MVC) pattern. Qt usually supports this pattern fairly well, if you do your UI authoring in the QtDesigner you are basically authoring the View part.

In general you want to stay away from singleton use as long as possible. Qt already implements a singleton that is easily accessible that lets you implement most functionality that would require a singleton or singleton like structure with the QApplication, class. Your singleton 'Wrapper' can easily be deposited in a custom subclass of QApplication, that receives calls from the document management like menus in your application i.e. Load, Save, New, etc. and then makes the current skeleton accessible via getCurrent() or whatever you may deem to call it. This automatically gives you a place to manage your model(s) and a place where other pieces of the software can get to them.

Depending on how fancy your application needs to be you can now also deposit a selection state in the QApplication class, i.e. your joints (e.g. setCurrentJoint(), getCurrentJoint() or get/setSelectionState()) so that the disjoint pieces in your UI can query and change the current selection. And then make changes with the current selection in mind i.e. change the position of the rotation axis of a previously selected joint.

In the less fancy version you just give the users a drop down list of the available joint ids for them to select and then do the operation on the model.

In some of the setters of your model you will want to emit signals so that views observing the model will update themselves with regard to the changes done by the users.

In general you don't want to implement your setters and getters to reach down through the hierarchy but rather use the implementation of the contained class for access. E.g rather do skeleton.getJoint(jointId).getAxis()[0] than skeleton.getJointAxisXValue(jointId).

Especially for the axes you don't need to encapsulate cartesian geometry, Points, Vectors, Axis, Angles (Quaternions) are the concepts that you want to expose to the users of your class (As mentioned in multiple comments). If a piece of ui is set up to only change the x component of an axis then, read the axis, change the x-component and set the new value. In most instances get/set will be sufficient moveBy or something equivalent is almost always just a convenience function

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for your answer. I checked out the QApplication class and I prefer this to the singleton myself. I'm new to Qt and I didn't know about it. And for the data manipulation part: am I getting right that you suggest that writing get/set methods for the upper-level classes (eg. Skeleton, Joint) is ok? I mean, apart from the size of the class - obviously you can't see that in the sample code I provided and depends on multiple things.
You usually don't need to provide access through to the bottom of your hierarchy in your top class i.e. rather use skeleton.getJoint(id).getAxis()[0] than skeleton.get_joint_axis_x(id), this will keep your interface much smaller.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.