【问题标题】:Cons of Implementing an Interface实现接口的缺点
【发布时间】:2016-01-15 08:14:27
【问题描述】:

这是我正在处理的示例图:

  • 汽车和公共汽车就是交通工具
  • 司机有 0 辆或更多辆汽车/公共汽车
  • 每个司机都有默认的汽车或公共汽车
  • 汽车/公共汽车最多可以有 1 名司机

现在我需要在相同的上下文中使用 Driver 和 Vehicle,每当我通过 Driver,给我默认的 Car/Bus,每当我通过 Vehicle 给我this。在这种情况下,在它们之上引入一个接口是不是一个坏主意?如果有,原因是什么?

编辑:

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

【问题讨论】:

  • 当您说“在它们之上实现接口”时,您的意思是在 DriverVehicle 之上?还是在CarBus 之上?
  • @kotakotakota 抱歉不清楚,我的意思是在司机和车辆之上

标签: oop interface superclass object-oriented-analysis


【解决方案1】:

希望我正确理解了您的问题。

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

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

在这种特定情况下,这可能有点矫枉过正,但遗留代码可能会受益。

例如 (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()

【讨论】:

    【解决方案2】:

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

    1. 驱动程序应该拥有Vehicles,而不是Car/Bus 对象
      拥有接口的全部意义在于您可以以相同的方式处理这两种类型的对象,并减少重复代码。
    2. Vehicle 应该需要驱动程序的吸气剂
      使用Vehicle::getDriver(),并让BusCar 对象包含对Driver 的引用。

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

    请注意,即使我只将Driver 传递给robBank(),因为Driver 包含对Vehicle 的引用并且Driver 对象公开了这一点,我们可以访问Vehicle

    DriverVehicle 之间共享接口是有意义的情况

    • 您可以拥有一个它们都继承自Movable 的接口,它可能具有Movable::move() 函数,以便驾驶员和车辆都可以移动。这是具有通用功能的概念,也是 composition over inheritance 概念背后的关键。

    【讨论】:

      猜你喜欢
      • 2015-08-31
      • 2017-06-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-18
      • 1970-01-01
      相关资源
      最近更新 更多