我最近开始自己练习和学习 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 ());
}
}
如何通过将条件替换为多态性来改进以下代码?
首先,永远不要用==
来比较String
s.总是使用.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 时执行逻辑。Human
abstract
.不可能有一个不是Boy
或Girl
(或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
在分配口味时,您应该通过二传手检查通过的类是否是品味(带有实例)。