简体   繁体   English

如何根据标识符实例化某个子类,还是有更好的方法?

[英]How to instantiate a certain subclass depending on an identifier, or is there a better way?

Background: 背景:
I am currently working on a game, in which there will be several types of furniture such as chairs, tables or paintings for the player to interact with. 我目前正在开发一款游戏,其中将有几种类型的家具,如椅子,桌子或绘画供玩家互动。 For this I have created an abstract class GameObject that handles everything all these objects will have in common (such as drawing, holding position variables etc.). 为此我创建了一个抽象类GameObject ,它处理所有这些对象共有的所有东西(例如绘图,保持位置变量等)。 The furniture pieces will have a few parameters that differs, such as width and height (integers) and also what happens when the user choose to interact with it (for example: chairs -> if player is close, sit down, tables -> nothing, paintings -> examine independent of distance). 家具件将有一些不同的参数,例如宽度和高度(整数),以及当用户选择与之交互时会发生什么(例如:椅子 - >如果玩家靠近,坐下,桌子 - >什么都没有,绘画 - >独立于距离检查。 Furniture can be created from a menu, that returns some kind of identifier for the selected piece (currently an integer). 可以从菜单创建家具,该菜单返回所选片段的某种标识符(当前为整数)。

The Question: 问题:
How should I implement the different pieces of furniture? 我该如何实施不同的家具?
One way would be to create a subclass for each piece of furniture. 一种方法是为每件家具创建一个子类。 But this has two drawbacks; 但这有两个缺点; First it will be hard to manage that many classes, and secondly when the furniture is created, the correct subclass of GameObject will somehow have to be created based on the identifier (see the followup question). 首先,很难管理那么多的类,其次,当创建家具时,GameObject的正确子类将以某种方式基于标识符创建(参见后续问题)。

The most logical option to me seems to be to create a general class that takes the width and height parameters in the constructor as well as a runnable/other interface that can be run when the user clicks on the object. 对我来说,最合乎逻辑的选择似乎是创建一个通用类,它接受构造函数中的width和height参数以及可在用户单击对象时运行的runnable / other接口。 The different values could possibly be stored in a database and retrieved based on the identifier, but the problem still arises with the different actions, since the Runnables cannot be stored externally, and that leads back to the same problem as solution 1; 不同的值可能存储在数据库中并根据标识符进行检索,但问题仍然出现在不同的操作中,因为Runnables无法存储在外部,这导致了与解决方案1相同的问题; Many different classes where one has to be chosen for the current object. 许多不同的类,其中必须为当前对象选择一个。

Followup Question: Depending on the types of furniture and actions it seems possible that more advanced subclasses with additional methods might be required. 后续问题:根据家具和动作的类型,似乎可能需要使用其他方法的更高级的子类。 If so, the problem of choosing which one to implement depending on an identifier remains regardless of the solution to the previous question. 如果是这样的话,无论前一个问题的解决方案如何,根据标识符选择实现哪一个的问题仍然存在。 I don't want to use a gigantic switch statement to instantiate different classes, so is there any way around this? 我不想使用巨大的switch语句来实例化不同的类,所以有什么方法可以解决这个问题吗?

The game is for the Android platform, but there might be future IOS port, so java-specific answers is not preferable, but acceptable. 该游戏适用于Android平台,但可能会有未来的IOS端口,因此特定于Java的答案不是优选的,但可以接受。

Forget about the generic class; 忘记泛型类; you should make subclasses, as you presented in your first option. 您应该创建子类,如您在第一个选项中所示。 About your drawbacks: 关于你的缺点:

it will be hard to manage that many classes 很难管理那么多课程

Why? 为什么? If you create subclasses you would do something like this: 如果你创建子类,你会做这样的事情:

GameObject chair = new Chair();

and if you follow the generic mode, you would do something like this: 如果你遵循通用模式,你会做这样的事情:

GameObject chair = new GameObject(Type.CHAIR);

It is pretty much the same effort to create both, but you have much more benefits using the first one. 创建两者的努力几乎相同,但使用第一个功能可以获得更多好处。 The difference is that you can't handle different actions with the second approach without making a lot of conditional verifications. 不同之处在于,如果不进行大量条件验证,则无法使用第二种方法处理不同的操作。 Also, your core will be highly coupled with your existing objects, which means that you will need to change the core if you change one of the classes and vice-versa. 此外,您的核心将与您现有的对象高度耦合,这意味着如果您更改其中一个类,则需要更改核心,反之亦然。 With the first option you can do something like this: 使用第一个选项,您可以执行以下操作:

selectedObject.makeAction();

and it will call the correct action based on the instance that it keeps. 它将根据它保留的实例调用正确的操作。 If you follow the generic approach, it will look like this: 如果您遵循通用方法,它将如下所示:

if(selectedObject.getType() == Type.CHAIR) {
    //do char action. Notice that this ins't even a method provided by GameObject, you will need to get this from another place, since the GameObject is generic.
}     if(selectedObject.getType() == Type.TABLE) {
     //do table action
} if....

when the furniture is created, the correct subclass of GameObject will somehow have to be created based on the identifier 在创建家具时,必须根据标识符以某种方式创建GameObject的正确子类

I didn't get your point. 我没有明白你的观点。 You don't need to create based on an id, you just need to create the appropriated class. 您不需要基于id创建,您只需要创建适当的类。 When you are creating the obejcts, you already know the object types, you just need to instanciate the correct class. 在创建obejcts时,您已经知道了对象类型,您只需要实例化正确的类。

I'd definitely go with having subclasses for the differente types of furniture. 我肯定会选择不同类型的家具的子类。

Instead of the menu returning an identifier, you may think on the menu returning the class name you need to instantiate (I suppose when displaying the menu you know which class will represent each of the entries). 而不是返回标识符的菜单,您可能会在菜单上想到返回您需要实例化的类名(我想在显示菜单时您知道哪个类将代表每个条目)。 This way you avoid the identifier issue you are describing. 这样就可以避免描述的标识符问题。

Regarding the actions that can be done on each piece, you can use interfaces : 关于可以对每个部分执行的操作,您可以使用接口:

Each furniture class will implement a set of interfaces depending on their posible actions : 每个家具类将根据其可行的动作实现一组接口:

Interface IExaminable {
  public <result> examine();
}

Interface IBuyable {
  public <result> buy();
}

and then you can easily check if an object is examinable with the isntanceof operator (if needed). 然后,您可以使用isntanceof运算符(如果需要)轻松检查对象是否可以检查。

Hope it helps 希望能帮助到你

Its really tough to come up with a perfect design solution for the problem without considering all the if's and but's. 在没有考虑所有if和but的情况下,很难为问题提出完美的设计解决方案。

However, one crucial part for your game is the relationship between the items (Furniture, painting, etc) and the actions that can be performed on each item. 但是,游戏的一个关键部分是项目(家具,绘画等)与可以对每个项目执行的操作之间的关系。 This is how you can model this relationship (Using composition ) : 这是你如何建模这种关系(使用组合):

Action : Interface representing a game action. 动作:表示游戏动作的界面。 Contains aa single method called performAction. 包含一个名为performAction的方法。

SittingAction : A concrete class that implements Action. SittingAction:实现Action的具体类。 Represents a game action for sitting. 代表坐着的游戏动作。 Implements the performAction method to define the code for performing the sitting action. 实现performAction方法以定义执行坐标操作的代码。

ExamineAction : A concrete class that implements Action. ExamineAction:实现Action的具体类。 Represents a game action for examining an item. 表示用于检查项目的游戏操作。 Implements the performAction method to define the code for performing the examine action. 实现performAction方法以定义用于执行检查操作的代码。

Similarly, you can define one action per type of action that can be performed in your game. 同样,您可以为游戏中可执行的每种操作定义一个操作。

GameItem : A class representing an item in the game. GameItem:表示游戏中项目的类。 Contains properties common to all items such as item name, breadth, width, depth, etc and getters and setters for these properties. 包含所有项目共有的属性,例如项目名称,宽度,宽度,深度等,以及这些属性的getter和setter。 GameItem 'has-a' instance variable of type Action and a constructor that takes an Action parameter to initialize the Action. GameItem'具有Action类型的实例变量'和一个构造函数,它使用Action参数初始化Action。 GameItem also defines a method called onInteraction that you can call when the user interacts with a GameItem. GameItem还定义了一个名为onInteraction的方法,您可以在用户与GameItem交互时调用该方法。 This method will delegate the task of performing the Action associated with a GameItem by calling the performAction method on the Action instance variable. 此方法将通过调用Action实例变量上的performAction方法来委派执行与GameItem关联的Action的任务。 Therefore, any class that instantiate a GameItem must pass the appropriate Action to the GameItem constructor that represents the Action associated with the GameItem. 因此,任何实例化GameItem的类都必须将相应的Action传递给GameItem构造函数,该构造函数表示与GameItem关联的Action。 When a user interacts with an item, just call the onIneraction method. 当用户与项目交互时,只需调用onIneraction方法即可。

GameController : A class that listens to user events and calls the onIneraction method on the GameItem that was interacted with. GameController:一个侦听用户事件并调用与之交互的GameItem上的onIneraction方法的类。

That's it. 而已。 Clean and simple. 干净简单。 No need for any if else statements to check what action to perform on what item. 不需要任何if else语句来检查要对哪个项执行什么操作。 Simpy instantiate all your GameItem instances with the appropriate Action instances and have your GameController that listen for user inputs call the onIneraction method on the GameItem that was interacted with. Simpy使用适当的Action实例实例化所有GameItem实例,并让您的GameController监听用户输入,调用与之交互的GameItem上的onIneraction方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM