实现工厂设计模式时如何避免'instanceof'?
How to avoid 'instanceof' when implementing factory design pattern?
我正在尝试实现我的第一个工厂设计模式,但我不确定在将工厂制造的对象添加到列表时如何避免使用 instanceof。这就是我想要做的:
for (Blueprint bp : blueprints) {
Vehicle v = VehicleFactory.buildVehicle(bp);
allVehicles.add(v);
// Can I accomplish this without using 'instanceof'?
if (v instanceof Car) {
cars.add((Car) v);
} else if (v instanceof Boat) {
boats.add((Boat) v);
} else if (v instanceof Plane) {
planes.add((Plane) v);
}
}
根据我在 Stack Overflow 上阅读的内容,使用 'instanceof' 是一种代码味道。有没有更好的方法来检查工厂制造的车辆类型而不使用 'instanceof'?
我欢迎 feedback/suggestions 对我的实施提出任何意见,因为我什至不确定我是否以正确的方式进行。
完整示例如下:
import java.util.ArrayList;
class VehicleManager {
public static void main(String[] args) {
ArrayList<Blueprint> blueprints = new ArrayList<Blueprint>();
ArrayList<Vehicle> allVehicles = new ArrayList<Vehicle>();
ArrayList<Car> cars = new ArrayList<Car>();
ArrayList<Boat> boats = new ArrayList<Boat>();
ArrayList<Plane> planes = new ArrayList<Plane>();
/*
* In my application I have to access the blueprints through an API
* b/c they have already been created and stored in a data file.
* I'm creating them here just for example.
*/
Blueprint bp0 = new Blueprint(0);
Blueprint bp1 = new Blueprint(1);
Blueprint bp2 = new Blueprint(2);
blueprints.add(bp0);
blueprints.add(bp1);
blueprints.add(bp2);
for (Blueprint bp : blueprints) {
Vehicle v = VehicleFactory.buildVehicle(bp);
allVehicles.add(v);
// Can I accomplish this without using 'instanceof'?
if (v instanceof Car) {
cars.add((Car) v);
} else if (v instanceof Boat) {
boats.add((Boat) v);
} else if (v instanceof Plane) {
planes.add((Plane) v);
}
}
System.out.println("All Vehicles:");
for (Vehicle v : allVehicles) {
System.out.println("Vehicle: " + v + ", maxSpeed: " + v.maxSpeed);
}
System.out.println("Cars:");
for (Car c : cars) {
System.out.println("Car: " + c + ", numCylinders: " + c.numCylinders);
}
System.out.println("Boats:");
for (Boat b : boats) {
System.out.println("Boat: " + b + ", numRudders: " + b.numRudders);
}
System.out.println("Planes:");
for (Plane p : planes) {
System.out.println("Plane: " + p + ", numPropellers: " + p.numPropellers);
}
}
}
class Vehicle {
double maxSpeed;
Vehicle(double maxSpeed) {
this.maxSpeed = maxSpeed;
}
}
class Car extends Vehicle {
int numCylinders;
Car(double maxSpeed, int numCylinders) {
super(maxSpeed);
this.numCylinders = numCylinders;
}
}
class Boat extends Vehicle {
int numRudders;
Boat(double maxSpeed, int numRudders) {
super(maxSpeed);
this.numRudders = numRudders;
}
}
class Plane extends Vehicle {
int numPropellers;
Plane(double maxSpeed, int numPropellers) {
super(maxSpeed);
this.numPropellers = numPropellers;
}
}
class VehicleFactory {
public static Vehicle buildVehicle(Blueprint blueprint) {
switch (blueprint.type) {
case 0:
return new Car(100.0, 4);
case 1:
return new Boat(65.0, 1);
case 2:
return new Plane(600.0, 2);
default:
return new Vehicle(0.0);
}
}
}
class Blueprint {
int type; // 0 = car; // 1 = boat; // 2 = plane;
Blueprint(int type) {
this.type = type;
}
}
您可以向车辆添加方法 class 以打印文本。然后覆盖每个专用 Car class 中的方法。然后只需将所有汽车添加到车辆列表中即可。并循环列表以打印文本。
对您的代码进行了一些重构。希望对你有用。检查这个:
import java.util.ArrayList;
class VehicleManager {
public static void main(String[] args) {
ArrayList<ABluePrint> bluePrints = new ArrayList<ABluePrint>();
ArrayList<AVehicle> allVehicles = new ArrayList<AVehicle>();
ArrayList<ACar> cars = null;
ArrayList<ABoat> boats = null;
ArrayList<APlane> planes = null;
/*
* In my application I have to access the blueprints through an API
* b/c they have already been created and stored in a data file.
* I'm creating them here just for example.
*/
ABluePrint bp0 = new ABluePrint(0);
ABluePrint bp1 = new ABluePrint(1);
ABluePrint bp2 = new ABluePrint(2);
bluePrints.add(bp0);
bluePrints.add(bp1);
bluePrints.add(bp2);
for (ABluePrint bp : bluePrints) {
AVehicle v = AVehicleFactory.buildVehicle(bp);
allVehicles.add(v);
// Can I accomplish this without using 'instanceof'?
// dont add objects to list here, do it from constructor or in factory
/*if (v instanceof ACar) {
cars.add((ACar) v);
} else if (v instanceof ABoat) {
boats.add((ABoat) v);
} else if (v instanceof APlane) {
planes.add((APlane) v);
}*/
}
cars = ACar.getCars();
boats = ABoat.getBoats();
planes = APlane.getPlanes();
System.out.println("All Vehicles:");
for (AVehicle v : allVehicles) {
System.out.println("Vehicle: " + v + ", maxSpeed: " + v.maxSpeed);
}
System.out.println("Cars:");
for (ACar c : cars) {
System.out.println("Car: " + c + ", numCylinders: " + c.numCylinders);
}
System.out.println("Boats:");
for (ABoat b : boats) {
System.out.println("Boat: " + b + ", numRudders: " + b.numRudders);
}
System.out.println("Planes:");
for (APlane p : planes) {
System.out.println("Plane: " + p + ", numPropellers: " + p.numPropellers);
}
}
}
class AVehicle {
double maxSpeed;
AVehicle(double maxSpeed) {
this.maxSpeed = maxSpeed;
}
void add(){}
}
class ACar extends AVehicle {
static ArrayList<ACar> cars = new ArrayList<ACar>();
int numCylinders;
ACar(double maxSpeed, int numCylinders) {
super(maxSpeed);
this.numCylinders = numCylinders;
}
void add(){
cars.add(this);
}
public static ArrayList<ACar> getCars(){
return cars;
}
}
class ABoat extends AVehicle {
static ArrayList<ABoat> boats = new ArrayList<ABoat>();
int numRudders;
ABoat(double maxSpeed, int numRudders) {
super(maxSpeed);
this.numRudders = numRudders;
}
void add(){
boats.add(this);
}
public static ArrayList<ABoat> getBoats(){
return boats;
}
}
class APlane extends AVehicle {
static ArrayList<APlane> planes = new ArrayList<APlane>();
int numPropellers;
APlane(double maxSpeed, int numPropellers) {
super(maxSpeed);
this.numPropellers = numPropellers;
}
void add(){
planes.add(this);
}
public static ArrayList<APlane> getPlanes(){
return planes;
}
}
class AVehicleFactory {
public static AVehicle buildVehicle(ABluePrint blueprint) {
AVehicle vehicle;
switch (blueprint.type) {
case 0:
vehicle = new ACar(100.0, 4);
break;
case 1:
vehicle = new ABoat(65.0, 1);
break;
case 2:
vehicle = new APlane(600.0, 2);
break;
default:
vehicle = new AVehicle(0.0);
}
vehicle.add();
return vehicle;
}
}
class ABluePrint {
int type; // 0 = car; // 1 = boat; // 2 = plane;
ABluePrint(int type) {
this.type = type;
}
}
使用上面的代码,class 必须知道要将其添加到的集合。这可以被认为是良好设计的一个缺点,可以使用访问者设计模式来克服它,如已接受的答案 () 中所示。
您可以实施 Visitor pattern。
详细解答
想法是使用polymorphism 来执行类型检查。每个 subclass 都会覆盖 accept(Visitor)
方法,该方法应该在 superclass 中声明。当我们遇到这样的情况时:
void add(Vehicle vehicle) {
//what type is vehicle??
}
我们可以将对象传递给 Vehicle
中声明的方法。如果 vehicle
是 Car
类型,并且 class Car
覆盖了我们将对象传入的方法,那么该对象现在将在 Car
[=91] 中声明的方法中处理=].我们利用它来发挥我们的优势:创建一个 Visitor
对象并将其传递给覆盖的方法:
abstract class Vehicle {
public abstract void accept(AddToListVisitor visitor);
}
class Car extends Vehicle {
public void accept(AddToListVisitor visitor) {
//gets handled in this class
}
}
这Visitor
应该准备访问类型Car
。任何您想要避免使用 instanceof
查找实际类型的类型都必须在 Visitor
中指定。
class AddToListVisitor {
public void visit(Car car) {
//now we know the type! do something...
}
public void visit(Plane plane) {
//now we know the type! do something...
}
}
这是进行类型检查的地方!
当Car
接收访问者时,它应该使用this
关键字传入自己。由于我们在 class Car
中,因此将调用方法 visit(Car)
。在我们的访问者内部,我们可以执行我们想要的操作,现在我们知道了对象的类型。
因此,从顶部开始:
您创建一个 Visitor
,它执行您想要的操作。访问者应包含一个 visit
方法,用于您要对其执行操作的每种类型的对象。在这种情况下,我们正在为车辆创建访问者:
interface VehicleVisitor {
void visit(Car car);
void visit(Plane plane);
void visit(Boat boat);
}
我们要执行的操作是将车辆添加到某物中。我们将创建一个 AddTransportVisitor
;管理添加交通工具的访客:
class AddTransportVisitor implements VehicleVisitor {
public void visit(Car car) {
//add to car list
}
public void visit(Plane plane) {
//add to plane list
}
public void visit(Boat boat) {
//add to boat list
}
}
每辆车应能接待车辆访客:
abstract class Vehicle {
public abstract void accept(VehicleVisitor visitor);
}
当访客被传递给车辆时,车辆应调用它的 visit
方法,将自身传递到参数中:
class Car extends Vehicle {
public void accept(VehicleVisitor visitor) {
visitor.visit(this);
}
}
class Boat extends Vehicle {
public void accept(VehicleVisitor visitor) {
visitor.visit(this);
}
}
class Plane extends Vehicle {
public void accept(VehicleVisitor visitor) {
visitor.visit(this);
}
}
这就是类型检查发生的地方。调用了正确的 visit
方法,其中包含根据方法的参数执行的正确代码。
最后一个问题是 VehicleVisitor
与列表交互。这就是您的 VehicleManager
发挥作用的地方:它封装了列表,允许您通过 VehicleManager#add(Vehicle)
方法添加车辆。
当我们创建访问者时,我们可以将管理器传递给它(可能通过它的构造函数),这样我们就可以执行我们想要的操作,因为我们知道了对象的类型。 VehicleManager
应包含访问者并拦截 VehicleManager#add(Vehicle)
调用:
class VehicleManager {
private List<Car> carList = new ArrayList<>();
private List<Boat> boatList = new ArrayList<>();
private List<Plane> planeList = new ArrayList<>();
private AddTransportVisitor addVisitor = new AddTransportVisitor(this);
public void add(Vehicle vehicle) {
vehicle.accept(addVisitor);
}
public List<Car> getCarList() {
return carList;
}
public List<Boat> getBoatList() {
return boatList;
}
public List<Plane> getPlaneList() {
return planeList;
}
}
我们现在可以为 AddTransportVisitor#visit
方法编写实现:
class AddTransportVisitor implements VehicleVisitor {
private VehicleManager manager;
public AddTransportVisitor(VehicleManager manager) {
this.manager = manager;
}
public void visit(Car car) {
manager.getCarList().add(car);
}
public void visit(Plane plane) {
manager.getPlaneList().add(plane);
}
public void visit(Boat boat) {
manager.getBoatList().add(boat);
}
}
我强烈建议删除 getter 方法并为每种类型的车辆声明重载的 add
方法。这将在不需要时减少 "visiting" 的开销,例如 manager.add(new Car())
:
class VehicleManager {
private List<Car> carList = new ArrayList<>();
private List<Boat> boatList = new ArrayList<>();
private List<Plane> planeList = new ArrayList<>();
private AddTransportVisitor addVisitor = new AddTransportVisitor(this);
public void add(Vehicle vehicle) {
vehicle.accept(addVisitor);
}
public void add(Car car) {
carList.add(car);
}
public void add(Boat boat) {
boatList.add(boat);
}
public void add(Plane plane) {
planeList.add(plane);
}
public void printAllVehicles() {
//loop through vehicles, print
}
}
class AddTransportVisitor implements VehicleVisitor {
private VehicleManager manager;
public AddTransportVisitor(VehicleManager manager) {
this.manager = manager;
}
public void visit(Car car) {
manager.add(car);
}
public void visit(Plane plane) {
manager.add(plane);
}
public void visit(Boat boat) {
manager.add(boat);
}
}
public class Main {
public static void main(String[] args) {
Vehicle[] vehicles = {
new Plane(),
new Car(),
new Car(),
new Car(),
new Boat(),
new Boat()
};
VehicleManager manager = new VehicleManager();
for(Vehicle vehicle : vehicles) {
manager.add(vehicle);
}
manager.printAllVehicles();
}
}
起初我对车船飞机的清单不太满意。您有多个现实示例,但该列表本质上并不是包罗万象的——当您的工厂开始制造潜艇或火箭时会发生什么?
相反,使用汽车、船和飞机类型的枚举怎么样。您有一系列车辆列表。
通用车辆有一个抽象的 属性 CatalogAs,各种车辆实际上实现了这个和 return 正确的值。
有一个类似的问题,所以我使用了这个模式,为了更好地理解它,我创建了一个简单的 UML 绘图,显示注释中的事物顺序(遵循数字)。我在上面使用了 Vince Emighs 解决方案。模式解决方案更优雅,但可能需要一些时间才能真正理解。它需要一个接口,比原来多了一个class,但是很简单。
我知道已经很久没有人问这个问题了。我发现 http://www.nurkiewicz.com/2013/09/instanceof-operator-and-visitor-pattern.html 看起来很有用。在这里分享它以防有人感兴趣。
如果 AVehicle classes 不在您的控制范围内怎么办?例如。你从一些第 3 方库中获得它?所以你没有办法添加 Visitor 模式的 accept() 方法。此外,您可能不喜欢每个 AVehicle 子 class 中的样板代码,而更喜欢将所有内容放在一个特殊的 class 中以保持 classes 干净。
对于某些情况,只使用 HashMap 可能会更好。
在您的示例中只需使用:
Map<Class<? extends AVehicle>, List<? extends AVehicle>> lists = new HashMap<>();
lists.put(ACar.class, new ArrayList<ACar>());
lists.put(ABoat.class, new ArrayList<ABoat>());
lists.put(APlane.class, new ArrayList<APlane>());
for (ABluePrint bp : bluePrints) {
AVehicle v = AVehicleFactory.buildVehicle(bp);
allVehicles.add(v);
lists.get(v.getClass()).add(v);
}
这种 HashMap 方法的问题是您必须注册所有可能的 classes,包括所有已知的子classes。虽然如果你有庞大的层次结构并且不需要所有 classes 来完成你的任务,你可以节省大量的工作,在 Map 中注册只需要的。
我正在尝试实现我的第一个工厂设计模式,但我不确定在将工厂制造的对象添加到列表时如何避免使用 instanceof。这就是我想要做的:
for (Blueprint bp : blueprints) {
Vehicle v = VehicleFactory.buildVehicle(bp);
allVehicles.add(v);
// Can I accomplish this without using 'instanceof'?
if (v instanceof Car) {
cars.add((Car) v);
} else if (v instanceof Boat) {
boats.add((Boat) v);
} else if (v instanceof Plane) {
planes.add((Plane) v);
}
}
根据我在 Stack Overflow 上阅读的内容,使用 'instanceof' 是一种代码味道。有没有更好的方法来检查工厂制造的车辆类型而不使用 'instanceof'?
我欢迎 feedback/suggestions 对我的实施提出任何意见,因为我什至不确定我是否以正确的方式进行。
完整示例如下:
import java.util.ArrayList;
class VehicleManager {
public static void main(String[] args) {
ArrayList<Blueprint> blueprints = new ArrayList<Blueprint>();
ArrayList<Vehicle> allVehicles = new ArrayList<Vehicle>();
ArrayList<Car> cars = new ArrayList<Car>();
ArrayList<Boat> boats = new ArrayList<Boat>();
ArrayList<Plane> planes = new ArrayList<Plane>();
/*
* In my application I have to access the blueprints through an API
* b/c they have already been created and stored in a data file.
* I'm creating them here just for example.
*/
Blueprint bp0 = new Blueprint(0);
Blueprint bp1 = new Blueprint(1);
Blueprint bp2 = new Blueprint(2);
blueprints.add(bp0);
blueprints.add(bp1);
blueprints.add(bp2);
for (Blueprint bp : blueprints) {
Vehicle v = VehicleFactory.buildVehicle(bp);
allVehicles.add(v);
// Can I accomplish this without using 'instanceof'?
if (v instanceof Car) {
cars.add((Car) v);
} else if (v instanceof Boat) {
boats.add((Boat) v);
} else if (v instanceof Plane) {
planes.add((Plane) v);
}
}
System.out.println("All Vehicles:");
for (Vehicle v : allVehicles) {
System.out.println("Vehicle: " + v + ", maxSpeed: " + v.maxSpeed);
}
System.out.println("Cars:");
for (Car c : cars) {
System.out.println("Car: " + c + ", numCylinders: " + c.numCylinders);
}
System.out.println("Boats:");
for (Boat b : boats) {
System.out.println("Boat: " + b + ", numRudders: " + b.numRudders);
}
System.out.println("Planes:");
for (Plane p : planes) {
System.out.println("Plane: " + p + ", numPropellers: " + p.numPropellers);
}
}
}
class Vehicle {
double maxSpeed;
Vehicle(double maxSpeed) {
this.maxSpeed = maxSpeed;
}
}
class Car extends Vehicle {
int numCylinders;
Car(double maxSpeed, int numCylinders) {
super(maxSpeed);
this.numCylinders = numCylinders;
}
}
class Boat extends Vehicle {
int numRudders;
Boat(double maxSpeed, int numRudders) {
super(maxSpeed);
this.numRudders = numRudders;
}
}
class Plane extends Vehicle {
int numPropellers;
Plane(double maxSpeed, int numPropellers) {
super(maxSpeed);
this.numPropellers = numPropellers;
}
}
class VehicleFactory {
public static Vehicle buildVehicle(Blueprint blueprint) {
switch (blueprint.type) {
case 0:
return new Car(100.0, 4);
case 1:
return new Boat(65.0, 1);
case 2:
return new Plane(600.0, 2);
default:
return new Vehicle(0.0);
}
}
}
class Blueprint {
int type; // 0 = car; // 1 = boat; // 2 = plane;
Blueprint(int type) {
this.type = type;
}
}
您可以向车辆添加方法 class 以打印文本。然后覆盖每个专用 Car class 中的方法。然后只需将所有汽车添加到车辆列表中即可。并循环列表以打印文本。
对您的代码进行了一些重构。希望对你有用。检查这个:
import java.util.ArrayList;
class VehicleManager {
public static void main(String[] args) {
ArrayList<ABluePrint> bluePrints = new ArrayList<ABluePrint>();
ArrayList<AVehicle> allVehicles = new ArrayList<AVehicle>();
ArrayList<ACar> cars = null;
ArrayList<ABoat> boats = null;
ArrayList<APlane> planes = null;
/*
* In my application I have to access the blueprints through an API
* b/c they have already been created and stored in a data file.
* I'm creating them here just for example.
*/
ABluePrint bp0 = new ABluePrint(0);
ABluePrint bp1 = new ABluePrint(1);
ABluePrint bp2 = new ABluePrint(2);
bluePrints.add(bp0);
bluePrints.add(bp1);
bluePrints.add(bp2);
for (ABluePrint bp : bluePrints) {
AVehicle v = AVehicleFactory.buildVehicle(bp);
allVehicles.add(v);
// Can I accomplish this without using 'instanceof'?
// dont add objects to list here, do it from constructor or in factory
/*if (v instanceof ACar) {
cars.add((ACar) v);
} else if (v instanceof ABoat) {
boats.add((ABoat) v);
} else if (v instanceof APlane) {
planes.add((APlane) v);
}*/
}
cars = ACar.getCars();
boats = ABoat.getBoats();
planes = APlane.getPlanes();
System.out.println("All Vehicles:");
for (AVehicle v : allVehicles) {
System.out.println("Vehicle: " + v + ", maxSpeed: " + v.maxSpeed);
}
System.out.println("Cars:");
for (ACar c : cars) {
System.out.println("Car: " + c + ", numCylinders: " + c.numCylinders);
}
System.out.println("Boats:");
for (ABoat b : boats) {
System.out.println("Boat: " + b + ", numRudders: " + b.numRudders);
}
System.out.println("Planes:");
for (APlane p : planes) {
System.out.println("Plane: " + p + ", numPropellers: " + p.numPropellers);
}
}
}
class AVehicle {
double maxSpeed;
AVehicle(double maxSpeed) {
this.maxSpeed = maxSpeed;
}
void add(){}
}
class ACar extends AVehicle {
static ArrayList<ACar> cars = new ArrayList<ACar>();
int numCylinders;
ACar(double maxSpeed, int numCylinders) {
super(maxSpeed);
this.numCylinders = numCylinders;
}
void add(){
cars.add(this);
}
public static ArrayList<ACar> getCars(){
return cars;
}
}
class ABoat extends AVehicle {
static ArrayList<ABoat> boats = new ArrayList<ABoat>();
int numRudders;
ABoat(double maxSpeed, int numRudders) {
super(maxSpeed);
this.numRudders = numRudders;
}
void add(){
boats.add(this);
}
public static ArrayList<ABoat> getBoats(){
return boats;
}
}
class APlane extends AVehicle {
static ArrayList<APlane> planes = new ArrayList<APlane>();
int numPropellers;
APlane(double maxSpeed, int numPropellers) {
super(maxSpeed);
this.numPropellers = numPropellers;
}
void add(){
planes.add(this);
}
public static ArrayList<APlane> getPlanes(){
return planes;
}
}
class AVehicleFactory {
public static AVehicle buildVehicle(ABluePrint blueprint) {
AVehicle vehicle;
switch (blueprint.type) {
case 0:
vehicle = new ACar(100.0, 4);
break;
case 1:
vehicle = new ABoat(65.0, 1);
break;
case 2:
vehicle = new APlane(600.0, 2);
break;
default:
vehicle = new AVehicle(0.0);
}
vehicle.add();
return vehicle;
}
}
class ABluePrint {
int type; // 0 = car; // 1 = boat; // 2 = plane;
ABluePrint(int type) {
this.type = type;
}
}
使用上面的代码,class 必须知道要将其添加到的集合。这可以被认为是良好设计的一个缺点,可以使用访问者设计模式来克服它,如已接受的答案 (
您可以实施 Visitor pattern。
详细解答
想法是使用polymorphism 来执行类型检查。每个 subclass 都会覆盖 accept(Visitor)
方法,该方法应该在 superclass 中声明。当我们遇到这样的情况时:
void add(Vehicle vehicle) {
//what type is vehicle??
}
我们可以将对象传递给 Vehicle
中声明的方法。如果 vehicle
是 Car
类型,并且 class Car
覆盖了我们将对象传入的方法,那么该对象现在将在 Car
[=91] 中声明的方法中处理=].我们利用它来发挥我们的优势:创建一个 Visitor
对象并将其传递给覆盖的方法:
abstract class Vehicle {
public abstract void accept(AddToListVisitor visitor);
}
class Car extends Vehicle {
public void accept(AddToListVisitor visitor) {
//gets handled in this class
}
}
这Visitor
应该准备访问类型Car
。任何您想要避免使用 instanceof
查找实际类型的类型都必须在 Visitor
中指定。
class AddToListVisitor {
public void visit(Car car) {
//now we know the type! do something...
}
public void visit(Plane plane) {
//now we know the type! do something...
}
}
这是进行类型检查的地方!
当Car
接收访问者时,它应该使用this
关键字传入自己。由于我们在 class Car
中,因此将调用方法 visit(Car)
。在我们的访问者内部,我们可以执行我们想要的操作,现在我们知道了对象的类型。
因此,从顶部开始:
您创建一个 Visitor
,它执行您想要的操作。访问者应包含一个 visit
方法,用于您要对其执行操作的每种类型的对象。在这种情况下,我们正在为车辆创建访问者:
interface VehicleVisitor {
void visit(Car car);
void visit(Plane plane);
void visit(Boat boat);
}
我们要执行的操作是将车辆添加到某物中。我们将创建一个 AddTransportVisitor
;管理添加交通工具的访客:
class AddTransportVisitor implements VehicleVisitor {
public void visit(Car car) {
//add to car list
}
public void visit(Plane plane) {
//add to plane list
}
public void visit(Boat boat) {
//add to boat list
}
}
每辆车应能接待车辆访客:
abstract class Vehicle {
public abstract void accept(VehicleVisitor visitor);
}
当访客被传递给车辆时,车辆应调用它的 visit
方法,将自身传递到参数中:
class Car extends Vehicle {
public void accept(VehicleVisitor visitor) {
visitor.visit(this);
}
}
class Boat extends Vehicle {
public void accept(VehicleVisitor visitor) {
visitor.visit(this);
}
}
class Plane extends Vehicle {
public void accept(VehicleVisitor visitor) {
visitor.visit(this);
}
}
这就是类型检查发生的地方。调用了正确的 visit
方法,其中包含根据方法的参数执行的正确代码。
最后一个问题是 VehicleVisitor
与列表交互。这就是您的 VehicleManager
发挥作用的地方:它封装了列表,允许您通过 VehicleManager#add(Vehicle)
方法添加车辆。
当我们创建访问者时,我们可以将管理器传递给它(可能通过它的构造函数),这样我们就可以执行我们想要的操作,因为我们知道了对象的类型。 VehicleManager
应包含访问者并拦截 VehicleManager#add(Vehicle)
调用:
class VehicleManager {
private List<Car> carList = new ArrayList<>();
private List<Boat> boatList = new ArrayList<>();
private List<Plane> planeList = new ArrayList<>();
private AddTransportVisitor addVisitor = new AddTransportVisitor(this);
public void add(Vehicle vehicle) {
vehicle.accept(addVisitor);
}
public List<Car> getCarList() {
return carList;
}
public List<Boat> getBoatList() {
return boatList;
}
public List<Plane> getPlaneList() {
return planeList;
}
}
我们现在可以为 AddTransportVisitor#visit
方法编写实现:
class AddTransportVisitor implements VehicleVisitor {
private VehicleManager manager;
public AddTransportVisitor(VehicleManager manager) {
this.manager = manager;
}
public void visit(Car car) {
manager.getCarList().add(car);
}
public void visit(Plane plane) {
manager.getPlaneList().add(plane);
}
public void visit(Boat boat) {
manager.getBoatList().add(boat);
}
}
我强烈建议删除 getter 方法并为每种类型的车辆声明重载的 add
方法。这将在不需要时减少 "visiting" 的开销,例如 manager.add(new Car())
:
class VehicleManager {
private List<Car> carList = new ArrayList<>();
private List<Boat> boatList = new ArrayList<>();
private List<Plane> planeList = new ArrayList<>();
private AddTransportVisitor addVisitor = new AddTransportVisitor(this);
public void add(Vehicle vehicle) {
vehicle.accept(addVisitor);
}
public void add(Car car) {
carList.add(car);
}
public void add(Boat boat) {
boatList.add(boat);
}
public void add(Plane plane) {
planeList.add(plane);
}
public void printAllVehicles() {
//loop through vehicles, print
}
}
class AddTransportVisitor implements VehicleVisitor {
private VehicleManager manager;
public AddTransportVisitor(VehicleManager manager) {
this.manager = manager;
}
public void visit(Car car) {
manager.add(car);
}
public void visit(Plane plane) {
manager.add(plane);
}
public void visit(Boat boat) {
manager.add(boat);
}
}
public class Main {
public static void main(String[] args) {
Vehicle[] vehicles = {
new Plane(),
new Car(),
new Car(),
new Car(),
new Boat(),
new Boat()
};
VehicleManager manager = new VehicleManager();
for(Vehicle vehicle : vehicles) {
manager.add(vehicle);
}
manager.printAllVehicles();
}
}
起初我对车船飞机的清单不太满意。您有多个现实示例,但该列表本质上并不是包罗万象的——当您的工厂开始制造潜艇或火箭时会发生什么?
相反,使用汽车、船和飞机类型的枚举怎么样。您有一系列车辆列表。
通用车辆有一个抽象的 属性 CatalogAs,各种车辆实际上实现了这个和 return 正确的值。
有一个类似的问题,所以我使用了这个模式,为了更好地理解它,我创建了一个简单的 UML 绘图,显示注释中的事物顺序(遵循数字)。我在上面使用了 Vince Emighs 解决方案。模式解决方案更优雅,但可能需要一些时间才能真正理解。它需要一个接口,比原来多了一个class,但是很简单。
我知道已经很久没有人问这个问题了。我发现 http://www.nurkiewicz.com/2013/09/instanceof-operator-and-visitor-pattern.html 看起来很有用。在这里分享它以防有人感兴趣。
如果 AVehicle classes 不在您的控制范围内怎么办?例如。你从一些第 3 方库中获得它?所以你没有办法添加 Visitor 模式的 accept() 方法。此外,您可能不喜欢每个 AVehicle 子 class 中的样板代码,而更喜欢将所有内容放在一个特殊的 class 中以保持 classes 干净。 对于某些情况,只使用 HashMap 可能会更好。
在您的示例中只需使用:
Map<Class<? extends AVehicle>, List<? extends AVehicle>> lists = new HashMap<>();
lists.put(ACar.class, new ArrayList<ACar>());
lists.put(ABoat.class, new ArrayList<ABoat>());
lists.put(APlane.class, new ArrayList<APlane>());
for (ABluePrint bp : bluePrints) {
AVehicle v = AVehicleFactory.buildVehicle(bp);
allVehicles.add(v);
lists.get(v.getClass()).add(v);
}
这种 HashMap 方法的问题是您必须注册所有可能的 classes,包括所有已知的子classes。虽然如果你有庞大的层次结构并且不需要所有 classes 来完成你的任务,你可以节省大量的工作,在 Map 中注册只需要的。