HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaMinor

Take These Buttons Back

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
takethesebackbuttons

Problem

I created a side bar of buttons for my game, the idea being that if you click on the "main" button of the group, the rest of the buttons in the group would pop out from the side of the screen.

Later, I wanted to make it so that if you open one of the side menus the other ones would close. I thought about implementing some booleans and methods that would iterate through the tables that comprised the side bar, closing them if they were open and were not the table associated with the button that the player clicked. Instead I made something a little more object oriented.

One complication here was that I needed some special logic to be called when only one of these buttons was clicked, specifically the Overlay button. When closed, I wanted any open color overlays to be automatically switched off, so that the player doesn't need to open up the menu again to switch it off.

Here are the buttons in action:

SideButtonSlider.java

```
public class SideButtonSlider {

private Table table;
//private float stageWidth;
private float stageHeight;
private GameScreen screen;
private boolean isOpen;

public SideButtonSlider(ArrayListbuttons, GameScreen screen, Skin skin, String name, Color color, float stageWidth, float stageHeight) {
this.table = new Table(skin);
//this.stageWidth = stageWidth;
this.stageHeight = stageHeight;
this.screen = screen;

Table buttonTable = new Table();
buttonTable.defaults().pad(5).width(stageHeight/9).height(stageHeight/18).fill();
buttonTable.setFillParent(true);
for (Button button : buttons) {
buttonTable.add(button);
buttonTable.row();
}
this.table.add(buttonTable);

TextButton toggleButton = new TextButton(name, skin);
toggleButton.setColor(color);
toggleButton.addListener(new ClickListener() {
@Override
public void clicked(InputEvent event, float x, float y) {

Solution

You can literally use the builder pattern for your Builder game here. :)

See, you have group of buttons that must be assigned a same color. These buttons have a corresponding pair of button label and a ClickListener implementation. Therefore, it's not hard to imagine a Builder implementation that lets you chain these operations:

public final class SideButtonSliderBuilder {

    private final GameScreen screen;
    private final Skin skin;
    private final String groupLabel;
    private final Color color;
    private final List labels = new ArrayList();
    private final List listeners = new ArrayList();

    private SideButtonSliderBuilder(GameScreen screen, Skin skin, 
                    String groupLabel, Color color) {
        this.screen = screen;
        this.skin = skin;
        this.groupLabel = groupLabel;
        this.color = color;
    }

    public SideButtonSliderBuilder addButton(String label, ClickListener listener) {
        // remember null checks too, just in case
        labels.add(label);
        listeners.add(listener);
        return this;
    }

    private TextButton toButton(String label, ClickListener listener) {
        // since this is a private method, null checks are probably optional
        TextButton button = new TextButton(label, skin);
        button.setColor(color);
        button.addListener(listener);
        return button;
    }

    private List toList() {
        List list = new ArrayList(labels.size());
        for (Iterator labelIterator = labels.iterator(),
                    Iterator listenerIterator = listeners.iterator();
                labelIterator.hasNext() && listenerIterator().hasNext();) {
            list.add(toButton(labelIterator.next(), listenrIterator.next()));
        }
        return list;
    }

    public SideButtonSlider build() {
        // take note of List vs ArrayList, see below
        return new SideButtonSlider(toList(), screen, skin, groupLabel, color, 
                        LibGDXGame.STAGE_WIDTH, LibGDXGame.STAGE_HEIGHT);
    }

    public static SideButtonSliderBuilder of(GameScreen screen, Skin skin,
                    String groupLabel, Color color) {
        return new SideButtonSliderBuilder(screen, skin, groupLabel, color);
    }
}


In my opinion, the benefits of the builder pattern is that it lets you specify any common parameters once to minimize code repetition (and therefore bugs), and enforces the requirements for creating the necessary instances consistently too. In the long run, it also lets you introduce new features or fix bugs all in one go. Furthermore, builder classes tend to adopt the 'fluent' programming approach by using more descriptive sounding method names and return-ing itself, thereby allowing for daisy-chaining.

An example usage will be:

// inside your GameScene class
SideButtonSliderBuilder cameraBuilder = SideButtonSliderBuilder.of(this, this.skin, 
                                                            "Camera", Color.YELLOW);
cameraBuilder.addButton("Clip", /* clip listener */)
                .addButton("Zoom", /* zoom listener */);
this.cameraSlider = cameraBuilder.build();


You can even opt to daisy-chain the entire thing, from of() to calling build().

Just two more things to highlight from my code above:


// since this is a private method, null checks are probably optional

My personal opinion is that private methods can be less... particular about null inputs, as you should be trusting enough of yourself/your team that you aren't introducing them in the first place. This is more true, I hope, when the method calling the private method is right next to it, such that there is even lesser concern that nulls will be introduced.


// take note of List vs ArrayList, see below

Declarations should be done with the interfaces, rather than the concrete implementation. This lets users of your variables simply use them as they are defined by the interface methods, rather than the exact type of implementing class. Therefore, I will suggest List list = new ArrayList().

Code Snippets

public final class SideButtonSliderBuilder {

    private final GameScreen screen;
    private final Skin skin;
    private final String groupLabel;
    private final Color color;
    private final List<String> labels = new ArrayList<String>();
    private final List<ClickListener> listeners = new ArrayList<String>();

    private SideButtonSliderBuilder(GameScreen screen, Skin skin, 
                    String groupLabel, Color color) {
        this.screen = screen;
        this.skin = skin;
        this.groupLabel = groupLabel;
        this.color = color;
    }

    public SideButtonSliderBuilder addButton(String label, ClickListener listener) {
        // remember null checks too, just in case
        labels.add(label);
        listeners.add(listener);
        return this;
    }

    private TextButton toButton(String label, ClickListener listener) {
        // since this is a private method, null checks are probably optional
        TextButton button = new TextButton(label, skin);
        button.setColor(color);
        button.addListener(listener);
        return button;
    }

    private List<TextButton> toList() {
        List<TextButton> list = new ArrayList<TextButton>(labels.size());
        for (Iterator<String> labelIterator = labels.iterator(),
                    Iterator<ClickListener> listenerIterator = listeners.iterator();
                labelIterator.hasNext() && listenerIterator().hasNext();) {
            list.add(toButton(labelIterator.next(), listenrIterator.next()));
        }
        return list;
    }

    public SideButtonSlider build() {
        // take note of List vs ArrayList, see below
        return new SideButtonSlider(toList(), screen, skin, groupLabel, color, 
                        LibGDXGame.STAGE_WIDTH, LibGDXGame.STAGE_HEIGHT);
    }

    public static SideButtonSliderBuilder of(GameScreen screen, Skin skin,
                    String groupLabel, Color color) {
        return new SideButtonSliderBuilder(screen, skin, groupLabel, color);
    }
}
// inside your GameScene class
SideButtonSliderBuilder cameraBuilder = SideButtonSliderBuilder.of(this, this.skin, 
                                                            "Camera", Color.YELLOW);
cameraBuilder.addButton("Clip", /* clip listener */)
                .addButton("Zoom", /* zoom listener */);
this.cameraSlider = cameraBuilder.build();

Context

StackExchange Code Review Q#96495, answer score: 5

Revisions (0)

No revisions yet.