实现接口的缺点

时间:2015-10-16 14:21:33

标签: oop interface superclass object-oriented-analysis

以下是我正在处理的示例图:enter image description here

  • 汽车和公共汽车是车辆
  • 司机有0辆或以上的汽车/公共汽车
  • 每个驱动程序都有默认的汽车或公共汽车
  • 汽车/公共汽车最多可以有1名司机

现在我需要在相同的上下文中使用Driver and Vehicle,每当我通过Driver时,给我默认的Car / Bus,每当我通过Vehicle时我都会this。在这种情况下,在两者之上引入接口是一个坏主意吗?如果是的话,原因是什么?

修改:

  • 这是遗留代码,我只是编写了类(duh)
  • 我们无法修改当前结构
  • 我觉得在它们之上实现一个界面是不对的,只需要对此进行解释。不需要实施。

2 个答案:

答案 0 :(得分:1)

也许我误解了这个问题,但这是我的想法:

  1. 驱动程序应该拥有Vehicle个,而不是Car / Bus个对象
      拥有接口的关键在于您可以以相同的方式处理两种类型的对象,并减少重复的代码。
  2. Vehicle应该需要驱动程序的getter
      使用Vehicle::getDriver()BusCar个对象包含对Driver的引用。
  3. 无需在DriverVehicle之上引入接口。

    C ++中快速未经测试的代码存根(包含错误~~),了解其中一些方法的实现方式:

    class Driver;
    
    // Vehicle interface
    class Vehicle {
    public:
        virtual void setDriver(Driver* driver);
    
        virtual Driver* getDriver() const;
    
        /**
        * Some exciting driving function!! :D
        */
        virtual void drive();
    };
    
    class Car : public Vehicle {
        Driver* m_driver = nullptr;
    
    public:
        void setDriver(Driver* driver) override { m_driver = driver; }
        Driver* getDriver() const override { return m_driver; }
        void drive() override { /* do something! */ }
    };
    
    class Bus : public Vehicle {
        Driver* m_driver = nullptr;
    
    public:
        void setDriver(Driver* driver) override { m_driver = driver; }
        Driver* getDriver() const override { return m_driver; }
        void drive() override { /* do something! */ }
    };
    
    // Driver
    class Driver {
        int m_defIdx;
        std::vector<Vehicle*> m_vehicles;
    public:
        void addVehicle(Vehicle* vehicle, bool setDefault = true) {
            m_vehicles.push_back(vehicle);
            vehicle->setDriver(this);
            if (setDefault) m_defIdx = m_vehicles.length() - 1;
        }
        Vehicle* getDefaultVehicle() { return m_vehicles[m_defIdx]; }
        Vehicle* getVehicle(int index) { return m_vehicles[index]; }
    }
    

    请注意,如果您使用的是C ++,那么智能指针可能是一个更好的主意,而不是接口,抽象类可能更有意义。

    现在,我们来看看你如何与它进行交互。

    void robBank(Driver& driver) {
        /* do some totally criminal stuff! */
    
        // run away!! Note that we can access the vehicle here.
        driver.getDefaultVehicle()->drive();
    }
    
    int main() {
        Driver sally;
        Car* car = new Car();
        sally.addVehicle(car, true);
        robBank(sally);
        delete car; // clean up after ourselves :)
    }
    

    请注意,即使我只传递DriverrobBank(),因为Driver包含对Vehicle的引用,Driver对象公开这样,我们就可以访问Vehicle

    DriverVehicle

    之间共享界面的情况
    • 你可以拥有一个他们都继承的界面,例如Movable,它可能具有Movable::move()功能,因此驾驶员和车辆都可以四处移动。这是一个具有共同功能的概念,是composition over inheritance概念背后的关键。

答案 1 :(得分:1)

我希望我能正确理解你的问题。

我不会在它们上面引入一个界面,因为它似乎并不反映您的域名。似乎很难想出一个名字。这不是一个好兆头。

您可以尝试策略模式,它可以帮助封装为您的上下文获取该Vehicle实例的过程。

在这种特定情况下,它可能是一种过度杀伤,但遗留代码可能会受益。

例如(C#)。

class Context {
    public Vehicle Drive(DrivingStrategy strategy) {
        //...
        return strategy.GetDrivable();
    }
}

abstract class DrivingStrategy {
    public static DrivingStrategy For(Driver driver) {
        //...
        return new DriverDrivingStrategy(driver);
    }

    public static DrivingStrategy For(Vehicle vehicle) {
        //...
        return new VehicleDrivingStrategy(vehicle);
    }

    abstract public Vehicle GetDrivable();
}

class DriverDrivingStrategy : DrivingStrategy {
    private readonly Driver driver;

    public DriverDrivingStrategy(Driver driver) {
        //...
        this.driver = driver;
    }

    public override Vehicle GetDrivable() {
        //Default vehicle logic can be potentially moved from Driver to this class.
        return driver.GetDefaultVehicle();
    }
}

class VehicleDrivingStrategy : DrivingStrategy {
    private readonly Vehicle vehicle;

    public VehicleDrivingStrategy(Vehicle vehicle) {
        this.vehicle = vehicle;
    }

    public override Vehicle GetDrivable() {
        return vehicle;
    }
}

class Driver {
    public Vehicle GetDefaultVehicle() {
        //...
    }
}

class Vehicle {
    //...
}

客户端:

class Client {
    public void Do() {
        Context context = new Context();
        Driver driver = new Driver();
        Vehicle vehicle = new Vehicle();

        context.Drive(DrivingStrategy.For(driver));
        context.Drive(DrivingStrategy.For(vehicle));
    }
}

当然,VehicleDrivingStrategy类没有做太多,你可以失去一个抽象的DrivingStrategy,并使VehicleDrivingStrategy成为其他类的基类。

此外,您可能希望扩展策略,不仅仅检索Vehicle实例,还要求他们为您的上下文做更多事情。它更适合用于该模式。与驾驶员策略一样:driver.GetDefaultVehicle().Drive() / this.GetDefaultVehicle().Drive()和车辆:vehicle.Drive();