Cha*_*mes 37 java design-patterns factory instanceof factory-pattern
我正在尝试实现我的第一个工厂设计模式,我不确定在将工厂制造的对象添加到列表时如何避免使用instanceof.这就是我想要做的:
for (ABluePrint bp : bluePrints) {
AVehicle v = AVehicleFactory.buildVehicle(bp);
allVehicles.add(v);
// Can I accomplish this without using 'instanceof'?
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);
}
}
Run Code Online (Sandbox Code Playgroud)
从我在SO上看到的,使用'instanceof'是代码气味.有没有更好的方法来检查工厂创建的车辆类型而不使用'instanceof'?
我欢迎任何关于我的实施的反馈/建议,因为我不确定我是否正确地采用了这种方式.
完整示例如下:
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 = new ArrayList<ACar>();
ArrayList<ABoat> boats = new ArrayList<ABoat>();
ArrayList<APlane> planes = new ArrayList<APlane>();
/*
* 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'?
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);
}
}
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;
}
}
class ACar extends AVehicle {
int numCylinders;
ACar(double maxSpeed, int numCylinders) {
super(maxSpeed);
this.numCylinders = numCylinders;
}
}
class ABoat extends AVehicle {
int numRudders;
ABoat(double maxSpeed, int numRudders) {
super(maxSpeed);
this.numRudders = numRudders;
}
}
class APlane extends AVehicle {
int numPropellers;
APlane(double maxSpeed, int numPropellers) {
super(maxSpeed);
this.numPropellers = numPropellers;
}
}
class AVehicleFactory {
public static AVehicle buildVehicle(ABluePrint blueprint) {
switch (blueprint.type) {
case 0:
return new ACar(100.0, 4);
case 1:
return new ABoat(65.0, 1);
case 2:
return new APlane(600.0, 2);
default:
return new AVehicle(0.0);
}
}
}
class ABluePrint {
int type; // 0 = car; // 1 = boat; // 2 = plane;
ABluePrint(int type) {
this.type = type;
}
}
Run Code Online (Sandbox Code Playgroud)
Vin*_*igh 67
您可以实现访问者模式.
详细解答
我们的想法是使用多态来执行类型检查.每个子类都会覆盖该accept(Visitor)方法,该方法应在超类中声明.当我们遇到这样的情况时:
void add(Vehicle vehicle) {
//what type is vehicle??
}
Run Code Online (Sandbox Code Playgroud)
我们可以将一个对象传递给一个声明的方法Vehicle.如果vehicle是类型Car,并class Car覆盖我们传递对象的方法,那么该对象现在将在Car类中声明的方法内处理.我们使用它有利于我们:创建一个Visitor对象并将其传递给overriden方法:
abstract class Vehicle {
public abstract void accept(AddToListVisitor visitor);
}
class Car extends Vehicle {
public void accept(AddToListVisitor visitor) {
//gets handled in this class
}
}
Run Code Online (Sandbox Code Playgroud)
这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...
}
}
Run Code Online (Sandbox Code Playgroud)
这是类型检查发生的地方!
当Car收到访问者时,它应该使用this关键字传递给自己.由于我们在课堂上Car,因此visit(Car)将调用该方法.在访问者内部,我们可以执行我们想要的操作,现在我们知道了对象的类型.
所以,从顶部:
您创建一个Visitor,执行您想要的操作.访问者应该包含visit要对其执行操作的每种类型对象的方法.在这种情况下,我们正在为车辆创建访客:
interface VehicleVisitor {
void visit(Car car);
void visit(Plane plane);
void visit(Boat boat);
}
Run Code Online (Sandbox Code Playgroud)
我们想要执行的操作是将车辆添加到某物.我们会创造一个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
}
}
Run Code Online (Sandbox Code Playgroud)
每辆车都应该能够接受车辆访客:
abstract class Vehicle {
public abstract void accept(VehicleVisitor visitor);
}
Run Code Online (Sandbox Code Playgroud)
当访客被传递到车辆时,车辆应该调用它的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);
}
}
Run Code Online (Sandbox Code Playgroud)
这就是类型检查发生的地方.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;
}
}
Run Code Online (Sandbox Code Playgroud)
我们现在可以编写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);
}
}
Run Code Online (Sandbox Code Playgroud)
我强烈建议删除getter方法并add为每种类型的车辆声明重载方法.这将减少不需要时"访问"的开销,例如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();
}
}
Run Code Online (Sandbox Code Playgroud)