Exposing member objects of complex classes?

Hey all,

I have been working on a project lately and in this project, I've decided to split up the application's functionality into separate classes which handle a portion of the functionality. I've created classes such as a Graphics, Input and Logging class. These classes are all members of an Application class, which is the main class that handles the application's functionality.

An example with public members:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Graphics {
	public:
		void enableVSync();
		void disableVSync();
		// etc...
};

class Input {
	public:
		void pressKey(int key);
		void releaseKey(int key);
		// etc...
};

class Logger {
	public:
		void log(std::string message);
		// etc...
};

class Application {
	public:
		Graphics graphics;
		Input input;
		Logger logger;
};

int main() {
	Application app;
	app.graphics.enableVSync();
}


Now my question is, how do I provide access to these member objects of the Application class? I've come to the following conclusions, none of which seem perfect to me:

Make them public - This will expose the member classes and makes access and modifications easy, but goes against the OO encapsulation principle.

Make them private with a public getter which returns a reference - This will make access and modifications almost as easy as the previous method, and it will make any validation easier to add in the future, but it still goes against encapsulation. (this is the method I'm currently using)

Make them private with a public getter which returns a const reference and a public setter which takes an instance of the class - This will make access difficult, since I would have to copy the object to modify its values and then use the setter to apply those changes. This would also add too much overhead since the classes can be quite complex, and I'd like to avoid having to copy them.

Provide proxy functions - Add functions to the Application class which modify the internal private objects of the other classes. This seems like a lot of work, since I would basically have to replicate all of the interfaces of the member objects and it would make maintenance a pain. The other classes can also be quite complex, containing many functions which should all be accessible.

How do other frameworks / applications handle such issues? Do they make exceptions for complex member classes being part of the public interface?
Any help is appreciated!
Last edited on
How much of Graphics really needs to be exposed? For example with VSync, that could be handled by some call to a configuration function inside of Application.

I would think creating a "run" function inside of Application could help. Just call "app.run()" inside of main. Then you can use all of the private application variables that you want.
It just so happens that I have a run function in the Application class :)

However, things get more complicated since there is a tree structure of classes. The Graphics class for example, contains an OpenGL class, and the Input class contains a Keyboard and a Mouse class.

The project I'm working on is intended to be used as a library as well. So future projects that use the library will create an Application object and call the run method. However, those projects will also need access to the Graphics class since they need to be able to configure the Application to work as needed for that project.

Effectively, what I'm trying to do is compartmentalize the Application class in subclasses that each have their own functionality, but are inherently tied to the Application class and are meaningless outside of it. I could pack all of Graphic's functionality inside the Application class but that would cause a lot of clutter.

A similar metaphor would be a Car class, which has 4 members of the type Wheel, a member of the type Engine and so forth. Objects manipulating the Car class would have to be able to manipulate its Engine and Wheels as well, ideally without cluttering Car's public interface and without breaking encapsulation.
Last edited on
Effectively, what I'm trying to do is compartmentalize the Application class in subclasses that each have their own functionality, but are inherently tied to the Application class and are meaningless outside of it. I could pack all of Graphic's functionality inside the Application class but that would cause a lot of clutter.

Sounds to me like you want some sort of plugin system. I took a game engine class once, that used the PIMPL idiom heavily when writing a sort of plugin system. http://en.cppreference.com/w/cpp/language/pimpl

A similar metaphor would be a Car class, which has 4 members of the type Wheel, a member of the type Engine and so forth. Objects manipulating the Car class would have to be able to manipulate its Engine and Wheels as well, ideally without cluttering Car's public interface and without breaking encapsulation.

Using PIMPL would allow many files to include Engine.h or Wheel.h, so you could return Pointers to these objects from Car getter methods. It may be possible to pass by reference with the PIMPL idiom in more modern C++. I do not know; it was years ago when I used the PIMPL idiom.
Another idea would be to use abstract classes, for example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Graphics 
{
public:
   virtual void enableVSync() = 0;
   virtual void disableVSync() = 0;
   // etc...
};

class Application 
{
private:
   Graphics* graphics;
public:
   Graphics* getGraphics();
};

In this case the calling code does not know anything about the concrete implementation of Graphics. Also classes inheriting from Application can have different implementations of Graphics.

"Program to an 'interface', not an 'implementation'." (Gang of Four 1995:18)


For this type of code it might be worth to study the Design Patterns.
https://en.wikipedia.org/wiki/Design_Patterns
Keep it simple. I would start by just making the members public. As you get further into the development process, you may find a reason to change this, but you might not. And if you make them private now, you may find that you have to jump through hoops later on.
closed account (48T7M4Gy)
Bizarre! Can't we conclude the OP's idea doesn't work.
@Kemort
Bizarre! Can't we conclude the OP's idea doesn't work.

I don't understand what you mean. Can you a bit more specific ?
closed account (48T7M4Gy)
Bizarre in the sense that the OP is turning functions into classes that aren't really classes, but are really a library of reuseable functions but then again they aren't, and then the problem solution involves data hiding and now it is better that it doesn't. Sounds like fudge OO. Worth pursuing but eventually goes nowhere.

What happened to protected? What happened to inheritance? Once I start asking those questions, and even though there is nothing to say they have to be used, all of a sudden I reached the conclusion I have. I.e. what's the point of it all when a pile of library functions dotted with namespaces, if we must, could do the same.
I don't see what the problem is. Why do Apps components need to be accessed outside of App?

Bizarre! Can't we conclude the OP's idea doesn't work.

Isn't the OP's idea just doing the same thing that other GIU libraries do, such as QT?

Keep it simple. I would start by just making the members public. As you get further into the development process, you may find a reason to change this, but you might not. And if you make them private now, you may find that you have to jump through hoops later on.

This argument is usually the other way around isn't it? If you expose everything and subsequently end up spreading around and mixing up state, then you may end up with spaghetti code and need to rewrite the whole thing.
Last edited on
closed account (48T7M4Gy)
Isn't the OP's idea just doing the same thing that other GIU libraries do, such as QT?

I've got my doubts about that because Qt is completely object oriented, ie object, methods, messages -signals and slots, etc whereas OP is focused on functions alone. Qt classes are generally complete widgets. OP has a graphics function, not a graphics object by the look of it.

I don't have a problem with that except to find the spinning off into class 'warfare' and force-fitting the OP's function concepts seems to end up as macaroni AND spaghetti when the orthodox and well tested approaches, including that for libraries, seem to me to be sufficient.

Maybe my perception of 'bizarre' is simply due to the names of the classes. They're all processes, and not objects (real or otherwise). :)
> Now my question is, how do I provide access to these member objects of the Application class?

>> Keep it simple. I would start by just making the members public. ..
>> if you make them private now, you may find that you have to jump through hoops later on.

>>> This argument is usually the other way around isn't it?

Yes, it really is the other way around.
Identifying invariants (in this particular instance, class-invariants) and localising the code that is responsible for their preservation is the essence of writing robust maintainable code.

Stroustrup in 'The C++ Programming Language'
There are several benefits to be obtained from restricting access to a data structure to an explicitly declared list of functions. For example, any error causing a Date to take on an illegal value (for example, December 36, 2016) must be caused by code in a member function. This implies that the first stage of debugging – localization – is completed before the program is even run. This is a special case of the general observation that any change to the behavior of the type Date can and must be effected by changes to its members. In particular, if we change the representation of a class, we need only change the member functions to take advantage of the new representation. User code directly depends only on the public interface and need not be rewritten (although it may need to be recompiled).


Sutter and Alexandrescu in 'C++ Coding Standards: 101 Rules, Guidelines, and Best Practices'
41. Make data members private, except in behaviorless aggregates (C-style structs)

Summary
They're none of your caller's business: Keep data members private. Only in the case of simple C-style struct types that aggregate a bunch of values but don't pretend to encapsulate or provide behavior, make all data members public. Avoid mixes of public and nonpublic data, which almost always signal a muddled design.

Discussion
Information hiding is key to good software engineering. Prefer making all data members private; private data is the best means that a class can use to preserve its invariants now, and to keep preserving them in the face of future changes.

Public data is bad if a class models an abstraction and must therefore maintain invariants. Having public data means that part of your class's state can vary uncontrollably, unpredictably, and asynchronously with the rest of its state. It means that an abstraction is sharing responsibility for maintaining one or more invariants with the unbounded set of all code that uses the abstraction, and that is obviously, fundamentally, and indefensibly flawed. Reject such designs outright.
...
Nonprivate data members are almost always inferior to even simple passthrough get/set functions, which allow for robust versioning.

Examples
Example 1: Proper encapsulation.
Most classes (e.g., Matrix, File, Date, BankAccount, Security) should have all private data members and expose adequate interfaces. Allowing calling code to manipulate their internals directly would directly work against the abstraction they provide and the invariants they must sustain.

Example 2: ...

Example 3: Getters and setters.
If there is no better domain abstraction available, public and protected data members (e.g., color) can at least be made private and hidden behind get and set functions (e.g., GetColor, SetColor); these provide a minimal abstraction and robust versioning.

Using functions raises the level of discourse about "color" from that of a concrete state to that of an abstract state that we are free to implement as we want: We can change to an internal color encoding other than int, add code to update the display when changing color, add instrumentation, and make many other changes without breaking calling code. ...
Last edited on
I've decided to split up the application's functionality into separate classes which handle a portion of the functionality.

If I understand you right, your subclasses are just a way of organizing things. Some of the public things needed in Application are in Graphics, and some are in Input and some are in Logger. Ditto for the private things. But to put this another way, everything that's public in Logger, Input and Graphics needs to be publicly accessible in Application. That's why it seems easier in this case to start by making them public members.

Another option is to derive Application from the other three classes.
I've noticed that your objects don't represent real objects -- they're just there for organization.

As your code is organized now, your caller is going to have to know absolutely everything about the contents of each class. That's terribly inflexible.

You have a graphics class: when you instantiate it, it should represent a specific "Graphics".

Of course, that doesn't make any sense, and that's a good indication that you're misusing objects --- if it's not a specific, independent "graphics", then you should get rid of the class.

If you simply want a name hierarchy (i.e., to avoid name collisions) then you should replace them with namespaces, which don't add the complexity associated with OO.
Topic archived. No new replies allowed.