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
是一个接口。现在您将拥有 Building
和 Dockyard
之类的对象,它们将实现 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 的最佳方法是对游戏有清晰的理解和术语。如果您阅读任何 现代 棋盘游戏手册,您很快就会发现他们会用一个词来表示一个概念,例如 Turn
、Round
、Building
, 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,Building
和 StarHotel
,StarHotel
是 Building
的一种特定类型。拥有一个抽象 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);
}
}
这可能有很多需要理解的地方,但希望它能为您提供进一步研究的好方向。
我正在创建一个 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
是一个接口。现在您将拥有 Building
和 Dockyard
之类的对象,它们将实现 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 的最佳方法是对游戏有清晰的理解和术语。如果您阅读任何 现代 棋盘游戏手册,您很快就会发现他们会用一个词来表示一个概念,例如 Turn
、Round
、Building
, 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,Building
和 StarHotel
,StarHotel
是 Building
的一种特定类型。拥有一个抽象 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);
}
}
这可能有很多需要理解的地方,但希望它能为您提供进一步研究的好方向。