【问题标题】:What's the best way to DRY Java code ? Creating private method with different Objects for parameters?DRY Java 代码的最佳方法是什么?为参数创建具有不同对象的私有方法?
【发布时间】:2021-03-04 13:00:17
【问题描述】:

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

构建方法如下所示:

  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);
      }

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

所有这些都发生在 Base 类的静态帮助器类中。

你会如何重构这段代码? 提前谢谢你!

【问题讨论】:

  • 给你一个起点:为什么需要提取所需的资源来分别传递给验证器?当验证者可以直接处理建筑物时,不是更容易吗?当验证器只检查base 中是否有所需的资源时,为什么验证器要自己检查呢?无法要求给定的建筑物判断可用资源是否足够?

标签: java dry code-cleanup


【解决方案1】:

您的问题始于对所有事情都使用静态方法。在面向对象的世界中,理想情况下您有一个对象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);

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

【讨论】:

  • 谢谢!我忘了提到所有这些都发生在 Base 类的辅助类中,因此所有方法都与 Validator 静态相同(正在进行大量验证:D)
  • @Ili 这不是将方法设为静态的好理由。并不是说它们不一定是必要的,但给出的理由本身并不是做出这种设计决定的好理由。 Tarmo 提出的观点不仅有效,而且可能与您的问题直接相关。您应该考虑重新检查您的决定,并可能听从他的建议,特别是,如果这些“帮助”类被用于存储数据。
【解决方案2】:

在制作游戏时使用 Java 进行 DRY 的最佳方法是对您的游戏有一个清晰的理解和术语。如果您阅读任何现代棋盘游戏手册,您很快就会发现他们将只使用一个词来表示一个概念,例如TurnRoundBuildingPlayerResource .这允许形成一个粗略的结构:Building 花费一定数量的Resource。如果玩家没有足够的Resource,请告诉他“我们需要更多的 vespine >

参数

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

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

那么这是一个强烈的暗示,也许这两个函数都应该是Base 类的一部分。

枚举

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

public enum Resource {
    POWER, STARDUST, POPULATION
}

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

多态性

假设我们有两个类,BuildingStarHotelStarHotelBuilding 的一种特定类型。拥有一个抽象类Building 允许我们以特定方式处理一些通用机制,如下所示:

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

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

}

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

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

接口

Java 接口允许您定义定义方法的接口。通俗地说:这很有用,因为实现接口的每个类都必须实现方法,除非接口提供default 实现。

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

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

把所有东西放在一起

将枚举、接口和多态性放在一起,我们现在可以创建一个扩展 Building 并实现 ResourceProviderStarHotel 类,为我们的 Base 提供 8 个食物单元和 2 个幸福单元。

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

这可能会让你大吃一惊,但希望它能给你一个好的方向,让你进一步了解。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-12-28
    • 1970-01-01
    • 1970-01-01
    • 2020-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-01
    相关资源
    最近更新 更多