2

Let's say I am doing a 2D application with the following design:

There is the Level object that manages the world, and there are world objects which are entities inside the Level object.

A world object has a location and velocity, as well as size and a texture. However, a world object only exposes get properties. The set properties are private (or protected) and are only available to inherited classes.

But of course, Level is responsible for these world objects, and must somehow be able to manipulate at least some of its private setters. But as of now, Level has no access, meaning world objects must change its private setters to public (violating encapsulation).

How to tackle this problem? Should I just make everything public?

Currently what I'm doing is having a inner class inside game object that does the set work. So when Level needs to update an objects location it goes something like this:

void ChangeObject(GameObject targetObject, int newX, int newY){
 // targetObject.SetX and targetObject.SetY cannot be set directly
 var setter = new GameObject.Setter(targetObject);
 setter.SetX(newX); 
 setter.SetY(newY); 
}

This code feels like overkill, but it doesn't feel right to have everything public so that anything can change an objects location for example.

2

5 Answers 5

2

How about using interfaces?

public interface IImmutableWorld
{
    // only get methods
}

public interface IMutableWorld : IImmutableWorld
{
    // add set methods
}

public class World : IMutableWorld
{
    // implementation for both get and set methods.
}

You don't need to directly talk with World class, you talk with the interfaces.

For example, Level will talk with IMutableWorld and can use its set methods. Other classes will talk to IImmutableWorld, so they don't have access to set methods.

If you are still worry about that someone can create a World instance and directly manipulate it, you can use Factory.

public class WorldFactory
{
    IImmutableWorld createImmutableWorld()
    {
        // return World instance
    }

    IMutableWorld createMutableWorld()
    {
        // return World instance
    }
}

Make World as an inner class of WorldFacotry, now, other classes don't know the existing of World. It forces them to deal with the interfaces.

0

The idea is to restrict what objects can modify another object. There are two broad ways to achieve this.

  1. Forget it and let anyone modify the objects. Just do not call the mutating functions from outside Level and move on.

  2. Use a language feature to achieve this. For example, in C++ you could use the friend keyword to allow Level more access. In Java, you could use package-private visibility and put those objects (and only those objects) that need access in the same package.

Personally I think a clear, clean design is really nice to have. However, the real world pokes it head in sometimes and makes it necessary to pound that square peg into the round hole. While it can be good OO practice to restrict who can mutate or use an object, perhaps option one is the quick and easy way. Maybe option 2 is better.

If this code is not going to be used by others (e.g. plugins) then maybe option one is quicker and easier. If it will be used and extended by others' code, then option two would be better.

0

A simple solution would be to make them immutable and produce new world objects for the next world state. This gives the Level complete control over the world's state without allowing objects to interfere with each other directly.

Another benefit of this approach is that it still works even if your world objects stop being pure data and have some internal AI. The objects can "update" their state by returning a copy of itself with modified position/velocity, and the world is still free to take that copy and "alter" it further to resolve collisions or apply global forces (e.g. gravity, wind.) Since the objects are immutable, you can also divvy up the work of calculating the new positions across multiple threads.

Finally, if you ever need to implement some sort of "save state" or "rewind time" mechanic you get it for free, because you're not destroying the old states. You'll need to do this if you want to have deterministic behavior and smooth animation.

Deterministic behavior requires that you to update the world in fixed time increments (as opposed to however long it's been since the last update.) In order to keep the gameplay in sync with real time you'll need to keep track of the excess time as well (e.g. you update in 0.05s intervals, but it's been 0.07 secs - you have an excess of 0.02 you can't throw away.) Eventually that excess time accumulates enough to produce a second world update in the same iteration of the game loop, which causes the animation to stutter periodically. The solution is to keep the previous world state and interpolate the positions between the previous and current state to smooth out the animation again. See Fix Your Timestep! for more info.

0

This code feels like overkill, but it doesn't feel right to have everything public so that anything can change an objects location for example.

Encapsulation is part of the problem, but I'd argue that you should design objects with services to clients (responsibilities) which are more abstract than simple methods (sets and gets).

setLocation(x,y) is an example of a service. Reveal that service only to clients who should be able to do it. If x and y are things you might change (and thus want to hide), abstract them into a Location object, e.g., setLocation(new Location(x,y)). Location's implementation could actually be an angle and a distance from the origin. You could have two ways to access locations, theta + distance or x,y (it's just an example).

Another reason to do think of services is that you can avoid objects being in an inconsistent state between setters. For example, if a client does set(x) then fails to call set(Y), the object's location could be inconsistent. Putting the operation into a service (setLocation) allows you to enforce consistency rules on the object's state.

Finally, one important purpose of hiding things from the outside with encapsulation is so that you'll be allowed to change them later. If you're 100% sure nothing will change, make everything public. This is generally a bad idea, because in reality things do change. Whatever you make public will possibly be a headache later if it changes (your client classes will potentially break).

TL;DR Designers have to design two aspects of each class - the public part, and the private part. Make the public part be useful services to the clients (classes that will use the class being designed).

Read more in Hiding the implementation chapter of Thinking in Java by Bruce Eckel.

0

This has clear answer: Interfaces (or variations of them in languages than don't support them).

Objects can implement various interfaces, that allow level to manipulate them. For example, there could be IMovable interface, that allows object to change it's position. Then, if you used is and as operators, you could cherry-pick objects that are movable and operate on them. This has multiple advantages. First is that it is not necessary to objects themselves to implement those interfaces. You could have wrappers around those objects, which them implement level interfaces, achieving higher decoupling. You could also use C#'s explicit interface implementation to "hide" those interfaces, so their methods are not called "accidentally".

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.