1

After looking at many open source projects i have noticed that many of them use the same idea for initialization and deinitialization of the whole system.

Many of them have one specific class (likely it's a singletone) which loads resources, instantiates all other classes, subsystems, connects them to each other and prepares the objects structure which is used during run-time. Why not to do that in main()?

Which idea is behind this approach? Is it some high-level programming pattern which has a lot of advantages?

Jared Forth
  • 1,577
  • 6
  • 17
  • 32
Leonid P.
  • 33
  • 4
  • 1
    Can you name a few projects that do this? – drescherjm Jul 01 '19 at 13:17
  • 3
    Sounds like the "everything must be a class, even things that have no business being a class" design pattern. – Quentin Jul 01 '19 at 13:18
  • From the description it seems like a *manager object*. Which is closely related to the [*mediator pattern*](https://en.wikipedia.org/wiki/Mediator_pattern). – Some programmer dude Jul 01 '19 at 13:19
  • Because doing it in `main` is "object-oriented". – Pete Becker Jul 01 '19 at 13:25
  • 1
    As an example you can take a look at OpenSCADA project. it's [main.cpp](http://oscada.org/svn/trunk/OpenSCADA/src_call/main.cpp) instantiates the class TSYS which performs all other initialization acitvities. As far as i undertand Qt's documentation also recommends such style – Leonid P. Jul 01 '19 at 13:40
  • 1
    Any code in main() has to be manually put there by the programmer who is using the library. That means more work for that programmer, and more importantly it introduces an opportunity for that programmer to forget to add that code or otherwise do it wrong, potentially leading to a support issue for the library developers. Therefore anything the library developers can do to make everything “just work” without requiring an explicit action by the calling code is a win for usability. – Jeremy Friesner Jul 01 '19 at 13:53
  • @JeremyFriesner but using the dedicated class doesn't solve the problem of adding the code manually. We just add that to this class implementation instead of main() I think the main idea of making such class (let's call it Initializer) is to reuse that in subsequent projects and encapsulate all initialization activity in one place and keep main.cpp clean – Leonid P. Jul 01 '19 at 14:03
  • 1
    @LeonidP it's true that the *library implementers* still have to add the code, just to another library location. But they only have to add that code once, and since they wrote the library, they presumably know what they are doing. For the 10,000 third-party developers who want to *use* the library (and probably don't understand its internals very well, since they didn't write it), it makes life much easier if they don't have to know detailed information about how to set everything up. – Jeremy Friesner Jul 01 '19 at 14:10
  • @JeremyFriesner yes, i absolutely agree with you if we talk about libraries and their initialization. But i didn't mean libraries in the main question. I meant the software application and its initialization procedure which is clear for developers of this application. – Leonid P. Jul 01 '19 at 14:18
  • Designing a software application with the anticipation that one day you (or someone else) will want to use the code as a library is never a bad idea. – Jeremy Friesner Jul 01 '19 at 14:21

4 Answers4

2

There are several reasons to have a manager class:

  • It makes things a bit more consistent. If the rest of your project is written in OOP-style code, having the main class be like that too is just a bit nicer.
  • It allows for easier testing in many cases. main cannot be called in C++; if everything is in a class's constructor, your tests can call that quite freely.
  • It allows you to have multiple "global" systems at the same time. Obviously, the utility of this depends on the project, but it's not remotely unreasonable to, say, run two systems in parallel to ensure consistency.
  • It can present a simpler API to interact with the library, so that functions which affect multiple subsystems can be a method on the manager class instead of a free function taking a parameter for each subsystem. This is especially valuable in something like a GUI library or game engine, where a simple action (say, adding a button) might need to be registered with multiple subsystems to work properly (rendering, mouse-input handling, keyboard-input handling).
  • It can make it easier to swap out subsystems with other backends. For example, if you have one backend that renders with OpenGL and another that renders with DirectX, you can just make them both implement the same "rendering subsystem" interface and change the entire system once, at compile time, at the level of the entire program, without having to change every mention or use typedefs and count on people including the correct headers.

Which ones apply depend on the project and its goals, of course, as well as other pieces of its architecture. If you're curious about why any particular library did it like it did, you should ask that library's maintainers.

Nic
  • 6,211
  • 10
  • 46
  • 69
  • It's rather _'main() is not supposed to be called'_ than _'main() cannot be called'_. – nada Jul 01 '19 at 14:31
  • 2
    @nada It can be called in the same way NULL can be dereferenced. – Nic Jul 01 '19 at 14:31
  • Well there's a difference in theory in practice. In practice there is at least 1 (common!) compiler, which allows calling main, even if not standard conforming. But what compiler is 100% standard conforming? With dereferencing nullptrs it's different - it does neither 'work' in theory nor in practice. – nada Jul 01 '19 at 14:41
  • @nada In practice there are quite a few situations where dereferencing NULL is not only valid, but extremely useful code (e.g. embedded systems where `0` is a real address). So... yes. You can call `main` in the same way you can dereference NULL: If you want your code to be standard-compliant, you can't, but if you're willing to lock yourself to a specific implementation and possibly even target platform, then it's possible, and may even be useful. – Nic Jul 01 '19 at 14:59
  • 1
    If you feel the need to manually call `main()`, you might as well avoid undefined behavior by renaming `main()` to `main_aux()` (or similar), and calling that instead. (and, of course, add a new `main()` that simply calls through to `main_aux()`, so that your program can still compile and run). And once you've done that, you may find it useful to go one step further and upgrade `main_aux()` to be a class-object so that it can have methods and member variables and so on... and then you've arrived at the design the questioner asked about :) – Jeremy Friesner Jul 01 '19 at 15:51
0

Keeping function modules short helps in clearity and debugging, hence main module is preferable to keep short.

Factory Pattern, registers objects during initialization, which in turn spawns objects later in the program. This pattern is also suitable if new objects are to be integrated into your system, say a new plugin

ark1974
  • 615
  • 5
  • 16
0

That class is called the "Composition Root", and is used with software modules that support dependency injection.

It allows the parts to be independent of each other by pulling the dependencies between them into a single, separate module that defines the application as a whole.

Probably start here: What is a composition root in the context of Dependency Injection

Matt Timmermans
  • 53,709
  • 3
  • 46
  • 87
  • But the dependencies can also be pulled together by a `main` that isn't calling a manager class's constructor. – Nic Jul 01 '19 at 14:11
  • Thank you for mentioning the composition root pattern. I have heard about that during reading articles about Dependency injection but haven't look at this pattern in such way. Yes it can be used as main class which instantiates all other subsystems – Leonid P. Jul 01 '19 at 14:38
0

Which idea is behind this approach? Is it some high-level programming pattern which has a lot of advantages?

The analysis part of this is sometimes referred to as a Use Case (feature) called "Start Up." Craig Larman's book on OOAD calls it the initial domain object idiom (not quite a pattern):

How do Applications Start Up?

The startUp or initialize system operation of a Start Up use case abstractly represents the initialization phase of execution when an application is launched. To understand how to design an interaction diagram for this operation, you must first understand the contexts in which initialization can occur. How an application starts and initializes depends on the programming language and operating system.

In all cases, a common design idiom is to create an initial domain object or a set of peer initial domain objects that are the first software “domain” objects created. This creation may happen explicitly in the starting main method or in a Factory object called from the main method.

Often, the initial domain object (assuming the singular case), once created, is responsible for the creation of its direct child domain objects. For example, a Store chosen as the initial domain object may be responsible for the creation of a Register object.

In a Java application, for example, the main method may create the initial domain object or delegate the work to a Factory object that creates it.

He later gives the following guideline for choosing the domain object:

Choose as an initial domain object a class at or near the root of the containment or aggregation hierarchy of domain objects. This may be a facade controller, such as Register, or some other object considered to contain all or most other objects, such as a Store.

The latter refers to the domain model for a Point of Sale application, where Store and Register are classes.

Community
  • 1
  • 1
Fuhrmanator
  • 11,459
  • 6
  • 62
  • 111
  • Thanks for such a full answer. I have never heard about **initial domain object** but it look like that i'm asking about. – Leonid P. Jul 02 '19 at 11:24
  • @LeonidP. Welcome to stackoverflow. If you like answers (and you have enough reputation), you can vote them up or accept them if they're the answer you think satisfies your question. – Fuhrmanator Jul 02 '19 at 11:38