如何在减少代码重复的情况下为游戏添加Levels?

nho*_*er9 5 java oop design-patterns code-duplication

我正在设计一个多层次的游戏.我有一个安装类,它根据收到的参数设置电路板,它指示应该设置的电平.这是班级:

public class BoardState {
    public BoardState(InitialState state) {
        switch (state) {
            case EMPTY:
                setupEmptyState();
                break;
            case INTEGRATIONTEST:
                setupIntegrationTestState();
                break;
            case LEVEL_1:
                setupLevelOne();
                break;
            case LEVEL_2:
                setupLevelTwo();
                break;
            default:
                throw new Error("Invalid level selection");
        }
    }

    private void setupEmptyState() { }

    private void setupIntegrationTestState() { }

    private void setupLevelOne() { }

    private void setupLevelTwo() { }
}
Run Code Online (Sandbox Code Playgroud)

这工作正常,但每次我添加一个新的级别我必须在三个地方添加代码:InitialState enum它定义接受状态的列表,switch构造函数中的语句,以及类的主体,我必须添加一个方法设置有问题的级别.

我想要保留的一件好事是,我的GUI会根据enum定义级别列表自动填充我添加的每个级别的新按钮.

如何重构此代码,以便减少与添加新级别相关的开销?

Spo*_*ted 3

通常,当您需要减少代码重复时,就会出现接口。这次(根据您在OP中的评论)似乎您需要根据您的级别向板上添加不同的对象:

import java.util.List;

public interface LevelSettings {
    List<GameObject> startingObjects();
}
Run Code Online (Sandbox Code Playgroud)

现在,BoardState看起来像这样(没有更多的setupX()方法)

import java.util.List;

public class BoardState {
    private final List<GameObject> gameObjects;

    public BoardState(LevelSettings settings) {
        this.gameObjects = settings.startingObjects();
    }
}
Run Code Online (Sandbox Code Playgroud)

由于您还指定了enum在 GUI 上动态创建按钮是件好事,因此可以通过在枚举中实现接口来结合两个世界(界面和枚举)的优点...

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public enum InitialState implements LevelSettings {
    EMPTY {
        @Override
        public List<GameObject> startingObjects() {
            return Collections.emptyList();
        }
    },
    INTEGRATIONTEST {
        @Override
        public List<GameObject> startingObjects() {
            GameObject g1 = new GameObject("dummy 1");
            GameObject g2 = new GameObject("dummy 2");
            return Arrays.asList(g1, g2);
        }
    },
    LEVEL_1 {
        @Override
        public List<GameObject> startingObjects() {
            //read a config file to get the starting objects informations
            //or also hardcoded (not preferred)
        }
    },
    LEVEL_2 {
        @Override
        public List<GameObject> startingObjects() {
            //read a config file to get the starting objects
            //or also hardcoded (not preferred)
        }
    };
}
Run Code Online (Sandbox Code Playgroud)

基本上就是这样。如果您需要添加,LEVEL_3那么InitialState一切都会随之而来。

更进一步

从这里开始,它超出了您的要求,如果您不相信,请随意忽略这部分。

作为一个好的实践,我将仅将这些配置存储在配置文件中,以减少更多的代码重复并获得灵活性:

import java.util.List;

public enum InitialState implements LevelSettings {
    EMPTY {
        @Override
        public List<GameObject> startingObjects() {
            return readFromFile("empty.level");
        }
    },
    INTEGRATIONTEST {
        @Override
        public List<GameObject> startingObjects() {
            return readFromFile("integration_test.level");
        }
    },
    LEVEL_1 {
        @Override
        public List<GameObject> startingObjects() {
            return readFromFile("1.level");
        }
    },
    LEVEL_2 {
        @Override
        public List<GameObject> startingObjects() {
            return readFromFile("2.level");
        }
    };

    private static List<GameObject> readFromFile(String filename) {
        //Open file
        //Serialize its content in GameObjects
        //return them as a list
    }
}
Run Code Online (Sandbox Code Playgroud)

因此,当您决定添加新关卡时,您实际上只需要知道存储该关卡配置的文件名。

更进一步

您将看到的内容确实很棘手,我不建议您在生产代码中使用它(但它减少了代码重复)!

import java.util.List;

public enum InitialState implements LevelSettings {
    EMPTY, INTEGRATIONTEST, LEVEL_1, LEVEL_2;

    @Override
    public List<GameObject> startingObjects() {
        return readFromFile(this.name() + ".level");
    }

    private static List<GameObject> readFromFile(String filename) {
        //Open file
        //Serialize its content in GameObjects
        //return them as a list
    }
}
Run Code Online (Sandbox Code Playgroud)

这里我们依靠枚举名称本身来找到相应的正确文件。该代码之所以有效,是因为它基于以下约定:文件根据带有“.level”扩展名的枚举名称进行命名。当您需要添加新级别时,只需将其添加到枚举中即可......