在 java 中用多态替换条件



我最近开始自己练习和学习 Java,我很难理解如何使用多态性来更好地进行编码练习。 如何使用多态重构来摆脱以下代码中的 if 和 else 条件?

我有一个父类水果,它有子类猕猴桃和苹果以及一堆其他水果。 例如:苹果是甜的,猕猴桃是酸的。

我有一个家长班人类,有男孩和女孩的孩子班。 男孩喜欢甜味,女孩喜欢酸味。 我创建了一个水果对象数组,并使用 if 语句根据每种水果的味道来检查谁喜欢每种水果。

Fruit[] fruit = new Fruit[2];
fruit[0] = new Apple ();
fruit[1] = new Kiwi ();
Boy boy1 = new Boy ();
String boyTaste = boy1.taste;
for (int i = 0; i < fruit.length; i++){
if (fruit[i].flavor.equals (boyTaste)){
System.out.println ("Boy likes " + fruit[i].name + " because " + fruit[i].taste ());
} else {
System.out.println ("Girl likes " + fruit[i].name +  " because " + fruit[i].taste ());
}
}

如何通过将条件替换为多态性来改进以下代码?

首先,永远不要==来比较Strings.总是使用.equals(),例如str.equals("hello").

目前尚不清楚使用特定的多态性/继承可以改进什么。可以提出一个论点,即Boy boy1 = new Boy();应该替换为Human boy1 = new Boy();,但在这种情况下,由于它是一个只使用一次的局部变量,并且在方法中实例化,所以它不会产生很大的差异。

您可能想要做的一件事是在名为likesFruit()Human上创建方法,getMessage()如下所示:

public abstract class Human {
...
public boolean likesFruit(Fruit f) {
return f.getFlavor().equals(getTaste());
}
public abstract String getMessage();
...
}

getMessage()将在Boy中定义为:

public String getMessage() {
return "Boy likes %s because ...";  // truncated for brevity
}

以及用于Girl的类似实现。

然后在代码中,可以调用:

Human human = new Boy();
Fruit[] fruits = ...
for (Fruit fruit : fruits) {
if (human.likesFruit(fruit)) System.out.println(String.format(human.getMessage(), fruit.getName(), fruit.getFlavor()));
}

需要注意的几点:

  • 我已经用 getter/setter 替换了字段引用(例如fruit.flavor变得fruit.getFlavor().这是很好的做法,因为它对调用者隐藏了必要的状态,并允许您在调用 getter 时执行逻辑。

  • Humanabstract.不可能有一个不是BoyGirl(或Man/Woman等,政治除外)的Human,因此实例化普通Human应该是非法的。

  • 同样,getMessage()是抽象的,因为实现会根据子类而变化

我会按以下方式更改代码:

  • 如果可能的话,使用枚举作为口味 - 将帮助您避免很多编码错误。

  • 将 find 方法替换为更现代的方法,也许使用 streams API(如果 Java 版本允许)

  • 将代码拆分为功能完善的方法,并将它们分布在类中 以有意义的方式

在这一点上,我是这样写的:

我会创建一个这样的水果类,并添加风味枚举作为子类(因为风味是水果的属性)

public class Fruit {
private FLAVOR flavor;
public Fruit( FLAVOR flavor ) {
this.flavor = flavor;
}
public FLAVOR getFlavor() {
return this.flavor;
}
public static enum FLAVOR {
SWEET, 
SOUR;
}

}

人类阶级看起来像这样

public class Human {
private FLAVOR flavor;
public Human( FLAVOR flavor ) {
this.flavor = flavor;
}
public List<Fruit> findLikedFruit( List<Fruit> availableFruits ) {
List<Fruit> likes = availableFruits.stream().filter( candidate -> Human.this.flavor == candidate.getFlavor() ).collect( Collectors.toList() );
return likes;
}

}

它基本上包含喜欢的味道,以及一种消耗可用水果列表并返回仅包含喜欢的水果列表的方法

最后水果和人类都会像这样扩展(示例仅在水果上,但原理适用)

public static class Apple extends Fruit {
public Apple() {
super(FLAVOR.SOUR);
}
}

主类如下所示:

public static void main( String[] args ) {
List<Fruit> fruits = Arrays.asList( new Fruit[] {new Apple(), new Pear(), new Strawberry()});
List<Fruit> boyLikes = new Boy().findLikedFruit(  fruits );
//if you wanna output it, you could do like so
boyLikes.forEach(fruit -> System.out.println( fruit.getFlavor().name() ) );
}

这使得代码方式更安全,将来更具可扩展性,并符合现代Java编码实践。

希望有帮助

干杯

修复比较后,为了引入多态性,您需要将 main() 方法中的"条件"移动到类定义中。但在某种程度上,你不能摆脱使用 if/else。 但是"多态"主方法中的 for 循环可能如下所示:

Human person = new Boy(FlavorPreferences.SWEET)
for (int i = 0; i < fruit.length; i++) { 
System.out.println(person.likes(fruit[i]);
}

另外 - 仅仅因为男孩不喜欢给定的"水果",并不一定意味着女孩喜欢同样的水果。 也许有些男孩和女孩对水果口味有相同的口味。

你遇到麻烦的地方是,你试图将多态性用于一个简单的属性——水果的味道。 多态性适用于不同的行为,而不仅仅是不同的值。 我试图想出一个水果的例子,但失败了。

相反,考虑银行账户。 假设有一个定义接口的银行账户的抽象概念,包括一个方法 withdrawMoney(双倍金额)。

interface Account {  
boolean withdrawMoney(double amount);
}
class SimpleAccount implements Account {
double cash;   // Assume initialized in constructor, not shown.
boolean withdrawMoney(double amount) {
if (amount <= cash) {
cash -= amount;
return true;
} else {
return false;
}
}
}
class OverdraftProtectedAccount implements Account {
double cash;
Account overdraftAccount;  // Assume both initialized in constructor
boolean withdrawMoney(double amount) {
if (amount <= cash) {
cash -= amount;
return true;
} else if (overdraftAccount.withdrawMoney(amount - cash)) {
cash = 0;
return true;
} else {
return false;
}
}
}

当然,这个例子在计时和只返回布尔值方面有很多问题,但重点是展示多态性是关于行为而不是数据的想法。

让我们编写一个函数来检查是否有人喜欢水果

public boolean likesThisFruit(Human human, Fruit fruit) {}

您想检查human是否喜欢fruit

if ( (human instanceof Boy && fruit.taste.equals("sweet"))
|| (human instanceof Girl && fruit.taste.equals("sour"))
) {
return true;
} else {
return false;
}

相当于

return (human instanceof Boy && fruit.taste.equals("sweet")) 
|| (human instanceof Girl && fruit.taste.equals("sour"));

请注意,您可以使用instanceof来检查对象是否是给定类型的实例。
您的最终功能

public boolean likesThisFruit(Human human, Fruit fruit) {
return (human instanceof Boy && fruit.taste.equals("sweet")) 
|| (human instanceof Girl && fruit.taste.equals("sour"));
}

我建议您使用Interfaces:酸与甜。并在函数中使用instanceof

public boolean likesThisFruit(Human human, Fruit fruit) {
return (human instanceof Boy && fruit instanceof Sweet) 
|| (human instanceof Girl && fruit instanceof Sour);
}

并与

public class Apple extends Fruit implements Sweet {}

编辑以匹配操作请求

interface Taste {}
interface Sour extends Taste {}
interface Sweet extends Taste {}
public abstract class Human {
protected final Class taste;
Human(Class taste) {
this.taste = taste;
}
public boolean likes(Fruit fruit) {
return fruit instanceof taste;
} 
}
public class Boy extends Human {
Boy(Class taste) {
super(taste);
}
}

我想在这里水果实现了酸或甜界面

Boy boy = new Boy(Sweet.class);
boy.likes(new Apple()); //true
boy.likes(new Kiwi()); //false if Kiwi implements Sour

在分配口味时,您应该通过二传手检查通过的类是否是品味(带有实例)。

最新更新