Question

I have an BallEntity class that is part of a game that uses a Entity Component System architecture.

This class have 'components' that are like the attributes of that class.
I create and initialize this components in getComponents method. To build this components I need others objects, like a Sprite (that is the image represents that ball) for SpriteComponent, a Body for BodyComponent, a camera for CameraFollowerComponent.
Those objets come to the class through arguments to the constructor.

I instantiate the BallEntity this way:

        BallEntity ballEntity = new BallEntity(new Sprite(atlas.findRegion("ball")),rubeSceneHelper.getBody("ball"), camera, rubeSceneHelper);

My question is Whether these objects (or which) must be initialized in the constructor of BallEntity or Within the class that instantiates BallEntity?

public class BallEntity extends UserEntity {
    public static final float SCALE = 0.78f;
    private final RubeSceneHelper rubeSceneHelper;
    private ScaledSprite ballSprite;
    private Body ballBody;
    private Camera camera;

    public BallEntity(Sprite sprite, Body ballBody, Camera camera, RubeSceneHelper rubeSceneHelper) {
        this.ballSprite = ScaledSprite.create(sprite, SCALE / sprite.getHeight());
        this.ballBody = ballBody;
        this.camera = camera;
        this.rubeSceneHelper = rubeSceneHelper;
    }

    @Override
    public Array<Component> getComponents() {
        Array<Component> components = new Array<Component>();
        components.add(PositionComponent.newInstance());
        components.add(CameraFollowerComponent.newInstance(camera));
        components.add(SpriteComponent.newInstance(ballSprite.getSprite()));
        components.add(BodyComponent.newInstance(ballBody));
        components.add(BallContextComponent.newInstance());
        return components;
    }

    @Override
    public void init(Entity entity) {
        BodyComponent bodyComponent = entity.getComponent(BodyComponent.class);
        bodyComponent.setPosition(Vector2.Zero);

        Fixture ballFixture = rubeSceneHelper.getFixture(bodyComponent.getBody(), "ball");
        ballFixture.setUserData(new FixtureUserData(FixtureType.BALL, entity));
    }
}
Was it helpful?

Solution

The way you're showing it now ("I instantiate the BallEntity this way"), it is being done by the implementation (code) that calls the constructor to create BallEntity.

To be clear, the point is that the way you're showing it, everywhere that you create a BallEntity (whether in separate classes or multiple places within the same class), you're re-specifying how to create new component objects. In other words, you are saying that it is the caller's job (creator of BallEntity) to identify and create the appropriate components, wherever these BallEntitys are created.

If there is only one place where BallEntity is created, I wouldn't worry about it too much.

If it turns out that there are multiple places in the code where a BallEntity is created, you should look to see if they are each doing it conceptually differently or conceptually the same way.

For as much of the components that are being created conceptually the same way, then you should apply DRY (Don't Repeat Yourself) principles and find a place to put this code.

There are two alternatives that come to mind.

(1) As you suggest, in BallEntity, by putting an additional higher level constructor in BallEntity, that takes the atlas, rubeSceneHelper, and camera as parameters and then calls the other constructor using code as you suggest for the instantiation above. (Maybe make the original constructor private, too, or remove it and just have the one.)

(2) Use the Factory design pattern, which says you put this same common code in some other factory class. Usually you'd use a factory pattern if you also have additional reasons beyond what you're showing here for decoupling the request for a (creation of) BallEntity and what and how is actually created.

For example, with the factory pattern you can return a subclass of BallEntity even, or, you could return a Singleton (the design pattern), or, you could manage a pool of BallEntitys, and hand them out based on any kind of criteria. Whereas with a constructor you can only return a brand new BallEntity each time.

(Lastly, if it turns out that several classes each instantiate BallEntity, and each class instantiates the BallEntity in multiple places, yet each class does it conceptually the same internally to itself, but it is done conceptually differently between the classes, then your other suggestion is appropriate, to gather that code perhaps once in each of those other classes. This is sort of a mix and match alternative, which is just applying DRY more than once as needed.)

Licensed under: CC-BY-SA with attribution
scroll top