我遇到了Java OOP问题。我想出了一些玩具代码来解释这个问题。这是我的课程 -
第1类 - Car.java
public class Car {
public void reportProblem(String problem){
ReportUtil.reportVehicleInfo("Car", 4, problem); //4 is number of wheels
}
//bunch of other methods
}
第2类 - Truck.java
public class Truck {
public void reportProblem(String problem){
ReportUtil.reportVehicleInfo("Truck", 6, problem);
}
//bunch of other methods
}
第3类 - ReportUtil.java
public class ReportUtil {
public static void reportVehicleInfo(String name, int wheels, String problem){
System.out.println(String.format("%s %s %s", name, wheels, problem));
}
}
第4类 - Test.java
public class Test {
public static void main(String[] args) {
Car c = new Car();
c.reportProblem("puncture");
Truck t = new Truck();
t.reportProblem("engine missing");
}
}
我想将“Car”和“Truck”中的“reportProblem”方法实现抽象为父类。这就是我做的 -
第1类 - Vehicle.java
public abstract class Vehicle {
public String mName;
public int mNumWheels;
public void reportProblem(String problem){
ReportUtil.reportVehicleInfo(mName, mNumWheels, problem);
}
public void setName(String name){
mName = name;
}
public void setNumWheels(int numWheels){
mNumWheels=numWheels;
}
}
第2类 - Car.java
public class Car extends Vehicle {
//bunch of other methods
}
第3类 - Truck.java
public class Truck extends Vehicle {
//bunch of other methods
}
第4类 - ReportUtil.java(对此类没有任何更改)。
public class ReportUtil {
public static void reportVehicleInfo(String name, int wheels, String problem){
System.out.println(String.format("%s %s %s", name, wheels, problem));
}
}
第5类 - Test.java
public class Test {
public static void main(String[] args) {
Car c = new Car();
c.setName("Car"); //NOTE : Can be missed!
c.setNumWheels(4); //NOTE : Can be missed!
c.reportProblem("puncture");
Truck t = new Truck();
t.setName("Truck"); //NOTE : Can be missed!
t.setNumWheels(6); //NOTE : Can be missed!
t.reportProblem("engine missing");
}
}
这实现了我想要的东西(我已经抽象了“reportProblem”的实现)。但我知道这不是最好的方法。一个原因是,如果不调用“setName”和“setNumWheels”方法,则不应调用“reportProblem”方法。否则将传递'null'。有没有一种方法可以使用一些OOP技术在调用reportProblem之前调用两个方法调用(setName和setNumWheels)?
我希望我已经说清楚了。如果我不是,请告诉我你将如何做到这一点,以便我可以从中学习。
答案 0 :(得分:6)
是的,将name
和numWheels
设为final,然后在构造函数中指定。所以......
第1类 - Vehicle.java
public abstract class Vehicle {
public final String mName;
public final int mNumWheels;
protected Vehicle(String name, int numWheels){
this.mName = name;
this.mNumWheels = numWheels;
}
public void reportProblem(String problem){
ReportUtil.reportVehicleInfo(mName, mNumWheels, problem);
}
...
}
第2类 - Car.java
public class Car extends Vehicle {
public Car(){
super("Car", 4);
}
//bunch of other methods
}
第3类 - Truck.java
public class Truck extends Vehicle {
public Truck(){
super("Truck", 6);
}
//bunch of other methods
}
此外,public
字段不是良好的OO实践,因为它们公开了类的实现的细节,可以由类的用户修改。这些字段应为private
。如果类的客户需要知道它们(或更改它们),那么你应该允许公共getter(或setter)方法。
答案 1 :(得分:1)
如果要设置“required”字段,可以在Truck / Car构造函数中将它们设置为参数,而不是为这些类提供默认构造函数。
答案 2 :(得分:0)
如果成员是对象状态/功能的基本要素,则将它们作为构造函数的一部分,因此无法为这些成员提供适当的值而无法创建对象(并调用关注的方法)。
但是你不应该提供一个no-args构造函数
如果需要太多参数,请考虑查看Builder idion
答案 3 :(得分:0)
除了@Tony的答案(+1),如果你必须使用bean表示法(默认构造函数和setter),并且仍然不希望在初始化对象之前允许使用任何业务方法,你可以执行以下操作。 p>
在checkInitalized()
课程中定义抽象方法Vehicle
。为您的Car
和Truck
实施此方法。顺便说一句,这个方法可能会在Vehicle
中有默认实现。在这种情况下,不要忘记从被覆盖的版本调用super。
checkInitalized()
应抛出异常(例如IllegalStateException
)。
现在在每个业务方法的开头调用此方法。这将阻止您使用尚未初始化的对象。
这种技术有点冗长。可能在这里使用包装器模式或AOP(例如AspectJ)可能是有用的。