DRY Java 代码的最佳方法是什么?为参数创建具有不同对象的私有方法?

What's the best way to DRY Java code ? Creating private method with different Objects for parameters?

我正在创建一个 RTS 游戏,其中一个功能是建造不同类型的建筑物。我发现了很多重复,我想在辅助方法中提取它,但问题是每个建筑都是不同的对象,它继承了主建筑的一些属性 class。

构建方法如下所示:

  public static void buildDockyard(Base base) {
    if (Validator.checkForBuilding(base, "Dockyard")) {
       throw new IllegalStateException("Dockyard is already build");
     }
    Dockyard dockyard = new Dockyard("Dockyard");
    int requiredPower = dockyard.requiredResource("power");
    int requiredStardust = dockyard.requiredResource("stardust");
    int requiredPopulation = dockyard.requiredResource("population");

    Validator.checkResource(base, requiredPower, requiredStardust, requiredPopulation);
    updateResourceAfterBuild(base, requiredPower, requiredStardust, requiredPopulation);
    dockyard.setCompleteTime(dockyard.requiredResource("time"));
    base.getBuildings().add(dockyard);
  }

  public static void buildHotel(Base base) {
    if (Validator.checkForBuilding(base, "Space Hotel")) {
      throw new IllegalStateException("Space Hotel is already build");
    }

    SpaceHotel spaceHotel = new SpaceHotel("Space Hotel");
    int requiredPower = spaceHotel.requiredResource("power");
    int requiredStardust = spaceHotel.requiredResource("stardust");
    int requiredPopulation = spaceHotel.requiredResource("population");

    Validator.checkResource(base, requiredPower, requiredStardust, requiredPopulation);
    updateResourceAfterBuild(base, requiredPower, requiredStardust, requiredPopulation);
    spaceHotel.setCompleteTime(spaceHotel.requiredResource("time"));
    base.getBuildings().add(spaceHotel);

    base.setCapacity(base.getCapacity() + spaceHotel.getCapacity());
  }

我想像这样重构: 辅助方法

private static void construct(Building building, Base base) {
    int requiredPower = building.requiredResource("power");
    int requiredStardust = building.requiredResource("stardust");
    int requiredPopulation = building.requiredResource("population");

    Validator.checkResource(base, requiredPower, requiredStardust, requiredPopulation);
    updateResourceAfterBuild(base, requiredPower, requiredStardust, requiredPopulation);
    building.setCompleteTime(building.requiredResource("time"));
  }

目标结果

public static void buildDockyard(Base base) {
        if (Validator.checkForBuilding(base, "Dockyard")) {
           throw new IllegalStateException("Dockyard is already build");
         }
        Dockyard dockyard = new Dockyard("Dockyard");
        construct(dockyar, base);
        base.getBuildings().add(dockyard);
      }

问题是每个建筑都有独特的属性和资源要求,主建筑 class 不知道它们,所以我不能将它用作辅助方法中的参数。

所有这些都发生在 Base class 的静态助手 class 中。

您将如何重构这段代码? 提前致谢!

您的问题始于对所有内容都使用静态方法。在面向对象的世界中,理想情况下你有一个对象 Base 并且它会有一个非静态方法 addStructure(Struture structure) 例如 Structure 是一个接口。现在您将拥有 BuildingDockyard 之类的对象,它们将实现 Structure.

addStructure 的实现是这样的:

if (getBuildings().contains(structure)) {
   throw new IllegalStateException(structure.name + " is already build");
}
if (validateStillHaveEnoughResourcesFor(structure)) {
   throw new IllegalStateException(structure.name + " can not be added. Not enough resources");
}
getBuildings().add(structure);

验证结构本身不应在基础中。验证结构如何适合底座应该在底座中。

在制作游戏时 Java 中 DRY 的最佳方法是对游戏有清晰的理解和术语。如果您阅读任何 现代 棋盘游戏手册,您很快就会发现他们会用一个词来表示一个概念,例如 TurnRoundBuilding, Player, Resource。这允许形成一个粗略的结构:一个 Building 花费一定数量的 Resource。如果一个玩家没有足够的 Resource 然后告诉他“我们需要更多的胡麻气。”等等。图片越清晰,你的 Java 就越容易创造必要的 Classes 用于您的代码。

参数

如果你最终得到这样的结果:

public static void someFunction(Base base, Object param1, Object param2)
public static void someOtherFunc(Base base, Object paramA, Object paramB)
...

那么这是一个强烈的暗示,也许这两个函数都应该是 Base class.

的一部分

枚举

如果您的值集有限,那么 Java 枚举可以很好地表示它们,例如你的资源系统:

public enum Resource {
    POWER, STARDUST, POPULATION
}

现在你不必记住你是否称它为“星尘”、“星尘”,或者你是否还有像“星尘”这样的资源。相反,您可以使用 int requiredPower = building.requiredResource(Resource.POWER);

多态性

假设我们有两个 class,BuildingStarHotelStarHotelBuilding 的一种特定类型。拥有一个抽象 class Building 允许我们以特定的方式处理一些通用机制,就像这样:

public abstract class Building {
    private ... cost;
    private ... requirements;
    private ... 

    // Std-Getter and Setter methods
    public ... getCost() { return this.cost; }

}

每栋建筑都有成本、要求和其他重要变量。但是我们处理了所有标准的事情,将这些通用变量获取和设置为基础 class,我们现在可以从中扩展其他更具体的建筑物。感谢 extends 关键字,您可以获得 StarHotel 对象的成本,而无需使用重复的 Getters 和 Setters 填充 StarHotel class。

public class StarHotel extends Building {
    // Getter, Setter inherited from Building class
}

接口

Java 接口允许您定义定义方法的接口。用外行的话来说:这很有用,因为每个实现接口的 Class 都必须实现该方法,除非该接口提供 default 实现。

public interface ResourceProvider {
    void provideResourceFor(Base base); // A Resource Provider provides Resource for a base.
}

通过这个接口,我们定义了如果某些 Class 实现 ResourceProvider 它必须指定如何以及为某些 Base 对象提供什么资源。我们的接口不关心哪个 Resource,哪个 Base 甚至 provideResourceFor 可能意味着什么,但只要实现了 ResourceProvider 它就必须提供功能。

综合起来

将枚举、接口和多态结合在一起,我们现在可以创建一个 StarHotel class 扩展 Building 并实现 ResourceProvider,提供 8 个食物单位和 2 个幸福单位到我们的基地。

public class StarHotel extends Building implements ResourceProvider
    public void provideResourceFor(Base base) {
        base.addResource(Resource.FOOD, 8);
        base.addResource(Resource.HAPPINESS, 2);
    }
}

这可能有很多需要理解的地方,但希望它能为您提供进一步研究的好方向。