问题描述
我正在尝试实现我的第一个工厂设计模式,我不确定如何在将工厂制造的对象添加到列表时避免使用实例.这就是我要做的:
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); } }
根据我在堆栈溢出上阅读的内容,使用"实例"是代码气味.有没有更好的方法来检查工厂在不使用"实例"的情况下创建的车辆类型?
我欢迎有关实施的任何反馈/建议,因为我什至不确定我是否以正确的方式进行此操作.
下面的完整示例:
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; } }
推荐答案
您可以实现访客模式.
详细答案
这个想法是使用多肌形态进行类型检查.每个子类覆盖accept(Visitor)方法,应在超类中声明.当我们有这样的情况时:
void add(Vehicle vehicle) { //what type is vehicle?? }
我们可以将对象传递到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 } }
此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关键字来传递.由于我们在类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方法.如果不需要时,这将减少"访问"的开销,例如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(); } }
其他推荐答案
您可以将方法添加到车辆类中以打印文本.然后覆盖每个专业汽车类中的方法.然后只需将所有汽车添加到车辆列表中即可.并循环列表以打印文本.
其他推荐答案
对您的代码进行了一些重组.希望对您有用.检查一下:
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; } }
使用上述代码,该类将必须了解必须添加的集合.这可以被视为良好设计的缺点,并且可以使用访问者的设计模式来克服它,如接受答案所示(在实施工厂设计模式时如何避免'实例'?).
).问题描述
I am attempting to implement my first Factory Design Pattern, and I'm not sure how to avoid using instanceof when adding the factory-made objects to lists. This is what I'm trying to do:
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); } }
From what I've read on Stack Overflow, using 'instanceof' is a code smell. Is there a better way to check the type of vehicle that was created by the factory without using 'instanceof'?
I welcome any feedback/suggestions on my implementation as I'm not even sure if I'm going about this the right way.
Full example below:
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; } }
推荐答案
You could implement the Visitor pattern.
Detailed Answer
The idea is to use polymorphism to perform the type-checking. Each subclass overrides the accept(Visitor) method, which should be declared in the superclass. When we have a situation like:
void add(Vehicle vehicle) { //what type is vehicle?? }
We can pass an object into a method declared in Vehicle. If vehicle is of type Car, and class Car overrode the method we passed the object into, that object would now be processed within the method declared in the Car class. We use this to our advantage: creating a Visitor object and pass it to an overriden method:
abstract class Vehicle { public abstract void accept(AddToListVisitor visitor); } class Car extends Vehicle { public void accept(AddToListVisitor visitor) { //gets handled in this class } }
This Visitor should be prepared to visit type Car. Any type that you want to avoid using instanceof to find the actual type of must be specified in the 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... } }
Here's where the type checking happens!
When the Car receives the visitor, it should pass itself in using the this keyword. Since we are in class Car, the method visit(Car) will be invoked. Inside of our visitor, we can perform the action we want, now that we know the type of the object.
So, from the top:
You create a Visitor, which performs the actions you want. A visitor should consist of a visit method for each type of object you want to perform an action on. In this case, we are creating a visitor for vehicles:
interface VehicleVisitor { void visit(Car car); void visit(Plane plane); void visit(Boat boat); }
The action we want to perform is adding the vehicle to something. We would create an AddTransportVisitor; a visitor that manages adding transportations:
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 } }
Every vehicle should be able to accept vehicle visitors:
abstract class Vehicle { public abstract void accept(VehicleVisitor visitor); }
When a visitor is passed to a vehicle, the vehicle should invoke it's visit method, passing itself into the arguments:
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); } }
That's where the type-checking happens. The correct visit method is called, which contains the correct code to execute based on the method's parameters.
The last problem is having the VehicleVisitor interact with the lists. This is where your VehicleManager comes in: it encapsulates the lists, allowing you to add vehicles through a VehicleManager#add(Vehicle) method.
When we create the visitor, we can pass the manager to it (possibly through it's constructor), so we can perform the action we want, now that we know the object's type. The VehicleManager should contain the visitor and intercept VehicleManager#add(Vehicle) calls:
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; } }
We can now write implementations for the AddTransportVisitor#visit methods:
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); } }
I highly suggest removing the getter methods and declaring overloaded add methods for each type of vehicle. This will reduce overhead from "visiting" when it's not needed, for example, 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(); } }
其他推荐答案
You can add method to vehicle class to print the text. Then override the method in each specialized Car class. Then just add all the cars to the vehicle list. And loop the list to print the text.
其他推荐答案
Done some restructuring of your code. Hope that works for you. Check this:
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; } }
With the above code, the class will have to know about the collection to which it has to be added. This can be considered as a downside to a good design and it can be overcome using the visitor design pattern as demonstrated in the accepted answer (How to avoid 'instanceof' when implementing factory design pattern?).