我正在编写一个实用程序,它使用我无法控制的第三方库中定义的某些类。
我想知道处理如下所述情况的好方法: 第三方图书馆有一个基础抽象类' Food'由“开胃菜”,“主菜”,“饮料'饮料'和'甜点'。 (第三方图书馆的所有部分)
我正在写一个' WaiterUtility'有各种食物的方法。 我想避免无休止的 instanceof 检查链。
`
Class WaiterUtility{
public serveItems(Food[] items)
{
for(Food aFood : items){
//how do i call the sub-class specific methods i wrote below?
}
}
private void serve(Appetizer aFood){//somecode}
private void serve(Entree aFood){//somecode}
private void serve(Beverage aFood){//somecode}
private void serve(Dessert aFood){//somecode}
}
`
答案 0 :(得分:1)
如果可能的话,我会恳求你不要使用反射,因为TBotV63在他的回答中做了(他甚至说要避免它)。来自Oracle documentation:
如果可以在不使用反射的情况下执行操作,则最好避免使用它。
所以,显然我们倾向于说可以提供所有Food
,并且任何Waiter
都可以提供任何类型的Food
。因此,理想情况下,一个好的API会暴露出足以使serve(Food)
方法完成工作的方法,而不知道它是什么类型的食物。似乎你的问题暗示事实并非如此,因此需要做更多的事情
如果第三方库接受社区输入,那么您应该尝试打开问题或拉取请求以添加功能
显然,这并不总是可行的,所以接下来最好的做法是创建一个接口(类似Serveable
)来定义您需要的方法,然后在实现该接口时子类化不同类型的食物。然后你会Waiter.serve(Serveable)
。
这比instanceof
的反射或许多用途更多的工作,但它是更好的OO设计。
反思文档指出了反思的3个缺点
虽然你可能不关心2或3,但1尤其糟糕。
...使用反射可以...渲染代码功能失常,可能会破坏可移植性。反射代码打破了抽象,因此可能会通过升级平台来改变行为。
instanceof
不好(在这种情况下) serveItems(Food[])
向调用者暗示,如果您将多个Food
项传递给它,它将为每个项目提供服务。然而事实并非如此。我们只能提供Food
的某些子类,如果我们尝试其他任何操作,我们将遇到运行时错误。 Java是一种很好的类型安全语言,我们比运行时错误更喜欢编译时错误
另一个缺点是,每次添加或更改Waiter
的新子类时,都需要将额外的代码添加到Food
。这成为一个贯穿各领域的问题,从发展的角度来看,代码不可扩展
这绝不是唯一的缺点/问题,只是几个例子。
答案 1 :(得分:0)
尝试类似以下的内容:
public serveItems(Food[] items)
{
for(Food aFood : items){
Class<?> foodClass = aFood.getClass(); // Get the food's class
Method serve = WaiterUtility.class.getMethod("serve", foodClass); // Get the method by name and argument types
try {
serve.invoke(this, aFood);
} catch (IllegalArgumentException e) { // Should never occur, we're matching it up.
} catch (IllegalAccessException e) { // Shouldn't occur, we're in the same class.
} catch (InvocationTargetException e) {
// Handle errors possibly thrown by the serve method.
}
}
没有测试过这个。
请注意,你应该避免这种情况,这是一种糟糕的设计。
答案 2 :(得分:0)
您可以尝试以下代码:
Class WaiterUtility{
private Map<Class<? extends Food>, Waiter> waiters = new HashMap<>();
WaiterUtility() {
waiters.put(Appetizer.class, new AppetizerWaiter());
waiters.put(Entree.class, new EntreeWaiter());
waiters.put(Beverage.class, new BeverageWaiter());
waiters.put(Dessert.class, new DessertWaiter());
}
public serveItems(Food[] items)
{
for(Food aFood : items){
waiter.get(aFood.getClass()).serve(aFood);
}
}
private static abstract interface Waiter {
private void serve(Food aFood);
}
private static class AppetizerWaiter implements Waiter {
private void serve(Food aFood){
Appetizer appetizer = (Appetizer) aFood;
//somecode
}
}
private static class EntreeWaiter implements Waiter {
private void serve(Food aFood){//somecode}
}
private static class BeverageWaiter implements Waiter {
private void serve(Food aFood){//somecode}
}
private static class DessertWaiter implements Waiter {
private void serve(Food aFood){//somecode}
}
}