Sign in to follow this  

Design Discussion: The Application Object

This topic is 3479 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hey guys. I wound up taking a look at some code I wrote some years back. It was a basic MUD I wrote in C++. Nothing too fancy, I think around 30k lines of code or so. Recently, I got the inclination to play with it again. At the time, I thought singletons were great and so I had an application object that was a singleton and could return references to the other objects in the system. I think the majority agrees that singletons have drawbacks with little benefit. On the same token, I don't want a global either. As I started to refactor, it became apparent that there was the potential for too much coupling if I allowed the other components to be aware of the application object, since the application object can become a dumping ground for any new components that get added. I started to break things apart, so instead each component gets passed to the other components upon construction; thus, no globalness. However, this leads to issues when there's circular references (component A uses component B and component B uses component A) that there's no safe order to create them in. This leads to my question: what's the best way to approach this? Is passing 1 .. n components to each component the best way? Or is the application object the best way? If I do use an application object, I am thinking of a base interface that allows registration of components from somewhere else, as I still don't want the other components to have implicit knowledge of every component in the system. I hope I explained this well. Thoughts?

Share this post


Link to post
Share on other sites
Both a global 'application' instance and passing around all components to all other components are merely implementations of the same (fundamentally procedural) design. In itself, a procedural design has no fatal flaws as long as you keep it modular.

For a correct object-oriented design, however, you wish to avoid the dependencies themselves (regardless of whether they are implemented as members of a global instance or by passing them to constructors). If your design contains circular dependencies (not at the individual module scale, such as container-iterator dependencies) then there's a high probability that the design is wrong. Of course, this would need analysis on a per-component basis, so can you give some examples of circular dependencies?

Share this post


Link to post
Share on other sites
Usually, one component does not need the entire other component, just some services of it. You can avoid circular dependency that way, of course that would take an example as ToohrVyk says to demonstrate.

An other solution is to use message pipes to connect components. Every component does not get neither a global Application object nor references to other components. It merely get a message pipe where it can put messages. The messages will be processed by whoever holds the other end of the pipe. I don't see anything fundamentally wrong with that idea.

Share this post


Link to post
Share on other sites
Thanks for the comments. Well, this is certainly some older code that I hadn't touched in about six years or so. I've certainly learned a lot since then.

Having said that, an example is something like the following: There are managers for the different components in the system, such as RoomManager, ItemManager, PlayerManager and FileManager (I may rework this later; I'm not sure I'm fond of the "managers"). As it turns out, ItemManager and PlayerManager both use FileManager.

FileManager uses ItemManager, PlayerManager, RoomManager, etc... The purpose of FileManager was to load/save to/from configuration files. I'm not currently fond of having all of that logic in one class, but that was what I had done at the time.

ItemManager uses FileManager to dynamically load items, as it appears I had some sort of a delayed loading concept so I would only load an item the first time it was needed. PlayerManager was using FileManager for a similar reason; not loading a player account unless it's requested.

I definitely agree that the design as-is is not as object-oriented as I would like. It sounds like both of you are in agreement to avoid the idea of an application object. I'm fine with this, as it's what I normally tend to do in my professional work.

As for the pipes, I like the pipes concept Mikeman. We successfully employed pipes in a multithreaded app at my previous job. Things were kept very separated and pipes used for communication as you said. This worked quite well. At present, the MUD I have is single-threaded. I don't think I fully understood multi-threading at the time, so I recall being more comfortable with single threaded code. As I refactor parts of this, I would like to make the app multithreaded.

Share this post


Link to post
Share on other sites
Quote:
Original post by Rydinare
FileManager uses ItemManager, PlayerManager, RoomManager, etc... The purpose of FileManager was to load/save to/from configuration files. I'm not currently fond of having all of that logic in one class, but that was what I had done at the time.
Wait, so why does it use those other classes?

It sounds like you could use the Dependency Inversion Principle. Your managers should not have direct, concrete references to one another; instead they should have references to abstractions (interfaces/base classes) that you populate with concrete classes.

In the particular case you've described, there's also the question of why the FileManager would have what I assume is class-specific logic for each of items, players, managers, etc. That sounds like way too big a responsibility. If each class's logic was put into a separate class - ItemLoader, PlayerLoader, RoomLoader, etc - and all those loaders could be accessed through a common abstraction, then you could write a FileManager object that just sucked in data and passed it to a loader without even having to know about which loader it was going to use (i.e. the loader could be passed in by the ItemManager/PlayerManager/etc).

Share this post


Link to post
Share on other sites
Quote:
Original post by superpig
Quote:
Original post by Rydinare
FileManager uses ItemManager, PlayerManager, RoomManager, etc... The purpose of FileManager was to load/save to/from configuration files. I'm not currently fond of having all of that logic in one class, but that was what I had done at the time.
Wait, so why does it use those other classes?


FileManager was notifying the other appropriate manager that it had added a new object. For example, ItemManager would get notified of a new item being loaded.

Quote:
Original post by superpig
It sounds like you could use the Dependency Inversion Principle. Your managers should not have direct, concrete references to one another; instead they should have references to abstractions (interfaces/base classes) that you populate with concrete classes.

In the particular case you've described, there's also the question of why the FileManager would have what I assume is class-specific logic for each of items, players, managers, etc. That sounds like way too big a responsibility. If each class's logic was put into a separate class - ItemLoader, PlayerLoader, RoomLoader, etc - and all those loaders could be accessed through a common abstraction, then you could write a FileManager object that just sucked in data and passed it to a loader without even having to know about which loader it was going to use (i.e. the loader could be passed in by the ItemManager/PlayerManager/etc).


I like where you're going with it that. I think that would work out a lot nicer. I'm going to give that a try later when I refactor more of it. [smile]

Share this post


Link to post
Share on other sites
Quote:
Original post by Rydinare
Quote:
Original post by superpig
Quote:
Original post by Rydinare
FileManager uses ItemManager, PlayerManager, RoomManager, etc... The purpose of FileManager was to load/save to/from configuration files. I'm not currently fond of having all of that logic in one class, but that was what I had done at the time.
Wait, so why does it use those other classes?


FileManager was notifying the other appropriate manager that it had added a new object. For example, ItemManager would get notified of a new item being loaded.
I would say you need a new manager - although I would call it a 'NotificationCentre', where objects can register to receive certain notifications, and other objects can dispatch them.

Share this post


Link to post
Share on other sites
Quote:
Original post by swiftcoder
I would say you need a new manager - although I would call it a 'NotificationCentre', where objects can register to receive certain notifications, and other objects can dispatch them.


Strongly disagreed. There's no need to centralize such a thing.

All you need is something like:

class IResourceLoadNotifiable
{
public: virtual void NotifyResourceLoaded(std::string resourceName) = 0;
};

that each manager class can implement. Then your load function simply takes a new argument:

void LoadResource(std::string resourceName, IResourceLoadNotifiable* notifyOnLoad)

Share this post


Link to post
Share on other sites
Quote:
Original post by superpig
Quote:
Original post by swiftcoder
I would say you need a new manager - although I would call it a 'NotificationCentre', where objects can register to receive certain notifications, and other objects can dispatch them.


Strongly disagreed. There's no need to centralize such a thing.

All you need is something like:

class IResourceLoadNotifiable
{
public: virtual void NotifyResourceLoaded(std::string resourceName) = 0;
};

that each manager class can implement. Then your load function simply takes a new argument:

void LoadResource(std::string resourceName, IResourceLoadNotifiable* notifyOnLoad)


Fair enough, I can see the desire not to centralise it, (though it wont reduce, and may increase the tightness of the coupling to bind them directly) - but for heavens sake, use a functor. That isn't java, where you can declare anonymous, inline sub-classes, and making everyone subclass from INotifiable(s) in C++ is a right pain.

Share this post


Link to post
Share on other sites

This topic is 3479 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this