实现工厂设计模式时如何避免“instanceof"? [英] How to avoid 'instanceof' when implementing factory design pattern?

查看:26
本文介绍了实现工厂设计模式时如何避免“instanceof"?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试实现我的第一个工厂设计模式,但我不确定在将工厂制造的对象添加到列表时如何避免使用 instanceof.这就是我想要做的:

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);
    }
}

从我在 Stack Overflow 上读到的内容来看,使用instanceof"是一种代码异味.有没有更好的方法来检查工厂创建的车辆类型而不使用instanceof"?

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.

完整示例如下:

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) 方法,该方法应该在超类中声明.当我们遇到以下情况时:

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??
}

我们可以将对象传递给Vehicle 中声明的方法.如果 vehicleCar 类型,并且 class Car 覆盖了我们传递对象的方法,那么现在将在声明的方法中处理该对象在 Car 类中.我们利用这个优势:创建一个 Visitor 对象并将其传递给一个覆盖方法:

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
    }
}

这个Visitor应该准备访问类型Car.您想避免使用 instanceof 查找实际类型的任何类型都必须在 Visitor 中指定.

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...
    }
}

这里是进行类型检查的地方!

Car 接收到访问者时,它应该使用this 关键字传入自己.由于我们在 Car 类中,方法 visit(Car) 将被调用.在访问者内部,我们可以执行我们想要的操作,因为我们知道对象的类型.

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.

所以,从顶部:

您创建一个 Visitor,它执行您想要的操作.访问者应该包含一个 visit 方法,用于您要对其执行操作的每种类型的对象.在本例中,我们为车辆创建了一个访问者:

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);
}

我们想要执行的操作是将车辆添加到某物上.我们将创建一个 AddTransportVisitor;管理添加交通工具的访问者:

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);
}

当访问者被传递给车辆时,车辆应该调用它的 visit 方法,将自身传递给参数:

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);
    }
}

这就是类型检查发生的地方.调用正确的 visit 方法,该方法包含根据方法参数执行的正确代码.

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.

最后一个问题是让 VehicleVisitor 与列表交互.这就是 VehicleManager 的用武之地:它封装了列表,允许您通过 VehicleManager#add(Vehicle) 方法添加车辆.

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.

当我们创建访问者时,我们可以将管理器传递给它(可能通过它的构造函数),这样我们就可以执行我们想要的操作,现在我们知道对象的类型.VehicleManager 应该包含访问者并拦截 VehicleManager#add(Vehicle) 调用:

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;
    }
}

我们现在可以为 AddTransportVisitor#visit 方法编写实现:

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);
    }
}

我强烈建议删除 getter 方法并为每种类型的车辆声明重载的 add 方法.这将减少不需要时访问"的开销,例如,manager.add(new Car()):

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();
    }
}

这篇关于实现工厂设计模式时如何避免“instanceof"?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆