在一个项目中,我有一个服务和一个使用该服务的类。在此示例中,车辆将使用维修服务。维修服务只能维修某种类型的车辆:车库只能维修车辆。我需要车辆中的方法来使用适用的服务repairUsingService(..)
进行修复。
我的目标是拥有一个干净的Vehicle
基类和干净的RepairService
实现。我尝试了两种设计修复服务的repair
方法的方法:
repair(Vehicle<T> vehicle)
:这很丑陋,因为实施需要做repair(Vehicle<Car> car)
,但显然汽车是一种载体。repairSimple(T vehicle)
:对此很好但是如果没有丑陋的演员阵容就无法从Vehicle
级别调用。 有没有办法避免投射,但仍然只使用通用参数类型T
(例如repairSimple(T)
)?
public class Vehicle<T extends Vehicle<T>> {
public void repairUsingService(RepairService<T> obj) {
obj.repair(this);
obj.repairSimple((T) this);
}
}
public class Car extends Vehicle<Car> {
}
public interface RepairService<T extends Vehicle<T>> {
void repair(Vehicle<T> vehicle);
void repairSimple(T vehicle);
}
public class Garage implements RepairService<Car> {
@Override
public void repair(Vehicle<Car> car) {
System.out.println("Car repaired.");
}
@Override
public void repairSimple(Car car) {
System.out.println("Car repaired.");
}
}
答案 0 :(得分:2)
一种方法是询问子类本身:
abstract class Vehicle<T extends Vehicle<T>> {
public void repairUsingService(RepairService<T> obj) {
obj.repair(this);
obj.repairSimple(getThis());
}
abstract T getThis();
}
class Car extends Vehicle<Car> {
@Override
Car getThis(){
return this;
}
}
答案 1 :(得分:2)
你能用这个实现吗?这样,车辆都知道,维修服务可以修理它,并且服务知道它可以修理哪些车辆。
public interface RepairService<T extends Vehicle<?>> {
public void repair(T vehicle);
}
public interface Vehicle<T extends RepairService<?>> {
public void repairUsingService(T service);
}
public class Car implements Vehicle<Garage> {
@Override
public void repairUsingService(Garage service) {
}
}
public class Garage implements RepairService<Car>{
@Override
public void repair(Car vehicle) {
}
}
public class AuthorizedGarage extends Garage {
}
public class Train implements Vehicle<TrainDepot> {
@Override
public void repairUsingService(TrainDepot service) {
}
}
public class TrainDepot implements RepairService<Train> {
@Override
public void repair(Train vehicle) {
}
}
public class Test {
public static void main(String[] args) {
// this works:
new Car().repairUsingService(new Garage());
new Train().repairUsingService(new TrainDepot());
// and this works
new Garage().repair(new Car());
new TrainDepot().repair(new Train());
// but this does not (which is ok)
//new Garage().repair(new Train());
//new Car().repairUsingService(new TrainDepot());
// this also works
List<Car> cars = new ArrayList<>();
cars.add(new Car());
cars.get(0).repairUsingService(new Garage());
// this also works, if you have an expensive car ;)
new Car().repairUsingService(new AuthorizedGarage());
}
}
您甚至可以为所有修复服务提供基类,以避免重复代码:
public abstract class BaseRepairService<T extends Vehicle<?>> implements
RepairService<T> {
@Override
public void repair(T vehicle) {
}
}
然后你的Garage将使用Car类型参数扩展BaseRepairService。
答案 2 :(得分:1)
让我提出两个合理的选择。
第一个是Gafter's Gadget的变体:
public abstract class Vehicle<V extends Vehicle<V>> {
private boolean validate() {
Class<?> cls = getClass();
for(Class<?> sup;
(sup = cls.getSuperclass()) != Vehicle.class;
cls = sup
);
Type sup = cls.getGenericSuperclass();
if(!(sup instanceof ParameterizedType))
return false;
Type arg = ((ParameterizedType)sup).getActualTypeArguments()[0];
if(!(arg instanceof Class<?>))
return false;
return ((Class<?>)arg).isInstance(this);
}
protected Vehicle() {
assert validate() : "somebody messed up";
}
}
由于Vehicle
始终由子类参数化,因此可以使用此惯用法。在开发期间,您将使用断言运行,如果某人错误地扩展了类,构造函数将抛出错误。
现在未经检查的演员表总是安全的。
第二个是RepairService
不再带有类型参数。相反,您会保留Class<? extends Vehicle>
RepairService
可以修复的列表。
public interface RepairService {
boolean canRepair(Vehicle v);
// if v can't be repaired, perhaps repair
// throws an exception or returns boolean instead of void
void repair(Vehicle v);
}
public class ServiceStation implements RepairService {
private final List<Class<? extends Vehicle>> types;
public ServiceStation(Class<? extends Vehicle>... types) {
this.types = Arrays.asList(types);
}
@Override
public boolean canRepair(Vehicle v) {
for(Class<? extends Vehicle> c : types) {
if(c.isInstance(v))
return true;
}
return false;
}
@Override
public void repair(Vehicle v) {
if(!canRepair(v))
throw new IllegalArgumentException();
// impl
}
}
至少对于Vehicle
/ RepairStation
类比,这可能比试图强制泛型设计更有用。 Vehicle
可能不再需要类型参数。
也许你的实际程序不同但你应该始终考虑直接程序逻辑是否在引入参数化设计之前解决了问题。试图强制泛型在一个非理想的解决方案的情况下工作变得非常尴尬。