简体   繁体   中英

Does Extern Break Encapsulation

I am new to C++ and I am creating a game. I have a class called main in which I declare

Game * game; //globally
int main() {
    game = new Game();
    game->show();
}

My class game initiates my game etc. Now in other classes (player, enemy, etc.), I access variables from the game such as player health using

#include<game.h>
extern Game * game;
func::func() {
    game->health->resetHealth();
}

Is this breaking encapsulation/ood paradigm? Is it bad practice? The thing is I can see any other way of doing it for a game.

Yes, extern breaks encapsulation. The main concept of encapsulation is data hiding and binding property and behavior of an object in a single entity. Making the variable extern would break the law.
In some more advance OOP language like java , there is no extern . And in Java, it always suggested making property/field private to restrict its access.

I mean, yeah, it's not encapsulated. game is a global pointer that can be accessed and changed from anywhere. Encapsulation is about data hiding, and game is totally exposed. It is also not typical object-oriented design. For proper encapsulation and OOD, you should restrict who uses and "knows about" the Game * game . For example, you can have a GameController object that is composed of a Game * . The scope and lifetime of the Game * could live in GameController , and then GameController can encapsulate its member variable by making it private and deciding who, how and when the pointer is accessed. There are other approaches, like wrapping the pointer in a global singleton class. This is better than your example because the wrapping class can enforce certain invariants (like what should happen when the game is accessed, or how a client should delete a game). Typically, global singletons are not the best approach for reasons outside of the scope of this answer. Another approach would be to use dependency injection. So, whenever a class needs to modify the Game * , it would have the pointer passed into it. These are all objected-oriented techniques for accessing encapsulated data.

Just having a global variable already starts breaking down your encapsulation because it provides access to the object from any code in your program. When you have a global like that any function can generate game-altering side effects, even ones in completely unrelated object instances. Using extern doesn't break encapsulation any further because it's roughly equivalent to just pasting more code into the single source file that declared the global.

Using a global variable itself doesn't break encapsulation -- the object could hide its implementation details as much as it could if it were not global. In most cases, it does, however, conflict with another design principle: flexibility. Say, you want your program to handle more than one game at a time (if such a later change doesn't make sense at all, you may decide to keep it global).

Whenever you're about to declare some global variable, it's perhaps good to ask yourself:

  • Would it be hard to avoid it? (In the case shown, it would be easy by just passing the game object around to functions which need it.)
  • Is it possible that there isn't only one kind of such an object at a later point in time? (For example, you may want to write an editor for levels of the game, where you want to reuse some of the code, and this editor should be capable of editing multiple games in multiple tabs.)
  • Does using a global harden reading the code? (Remember, easy to read is way more important than easy to write!)

In most cases, it's better to avoid global variables, in my opinion; and I think it's better to avoid it for the case shown in the question. A game parameter can be passed around easily.

In short, yes this breaks encapsulation. The fact that you keep referring to the game object reveals that you have dependencies towards this object.

You might consider of having an initialisation sequence in your program, called the composition root, (for simple examples main is the most obvious place to do this), where you pass all dependencies like this towards the objects that need them via the constructor, which is called dependency injection.

If you want to go even a step further you can create base classes and pass abstract base classes instead of implementation objects. That way you are more programming towards interfaces instead of objects.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM