【问题标题】:Interacting classes - organizing code交互类 - 组织代码
【发布时间】:2010-07-22 08:59:44
【问题描述】:

我在正确组织课程方面遇到了一些问题。

假设,我有一些 ABase 类。当我想为这个类创建一些不同的(更具体的)抽象时(表示为 AParticular),我可以使用继承或组合。然后很容易将 AParticular 视为 ABase:在继承的情况下它是自动生成的,在组合的情况下我可以创建一些 const ABase& AParticular::GetABasePart() 方法。这样我可以避免代码重复并获得多态特性。

但是,当我有两个或多个相互交互的类时,在更具体的抽象中创建这些类的类似物时会遇到一些问题。 例如,假设我有两个类 Car 和 Station。 Station有一些公共的汽车保养方法:

class Car {
  ...
}

class Station {
  ...
  void AddCarToPlaceNumberN(const Car& car, int place_number) {
    // adds car to some_container<Car>.
  }
  Car* GetMutableCarPointer(int place_number) {
    // gets mutable pointer from some_container<Car>.
  }
  ...
  some_container<Car> cars;
}

现在,我想创建 Truck 和 TruckStation 类:它们与 Car 和 Station 类非常相似,并且有细微的变化。要理解问题,认为它们与 Car 和 Station 类完全一样就足够了,但它们的方法有一点不同的名称(即 TruckStation::AddTruckToPlaceNumberN 而不是 Station::AddCarToPlaceNumberN)

如何组织新类的代码来提供这些功能?

  1. 没有代码重复,我想使用已经创建的 Car 和 Station 类方法。
  2. 快速转换 Truck& -> Car&, TruckStation& -> Station&(不需要继承,组合也适合),因为我有时想将 Truck 视为 Car,将 TruckStation 视为 Station。
  3. 关卡 Car-Station 中的所有交互方法都应在新关卡 Truck-TruckStation 中实现。

主要问题是 3d 项目。让我们考虑两种交互方式:

1) 这个方法没问题:

// If we use inheritance in creating Truck and TruckStation, then we just run
void TruckStation::AddTruckToPlaceNumberN(const Truck& car, int place_number) {
   AddCarToPlaceNumberN(car, place_number)
}
// If we use composition, then it is appropriate to run sth like that:
void TruckStation::AddTruckToPlaceNumberN(const Truck& car, int place_number) {
   station_.AddCarToPlaceNumberN(car.GetCarPart(), place_number);
}

2) 但我不知道如何实现 Station::GetMutableCarPointer(): 的类比:

// For example, if TruckStation was inherited from Station, then suppose:
Truck* TruckStation::GetMutableTruckPointer() {
   Car* car = GetMutableCarPointer();
   // Ups! I need to return Truck*, not Car*.
}

重复问题:我怎样才能实现这些类来提供:

  1. 没有代码重复。
  2. 可以将新类视为其更高级别的抽象。
  3. 实现方法如 TruckStation::GetMutableTruckPointer() 对应于 Station::GetMutableCarPointer()。

Tnx!

【问题讨论】:

    标签: c++ design-patterns inheritance polymorphism


    【解决方案1】:

    具体到您的代码。我会这样做。

    1. 基类 Vehicle 由 Car 和 Truck 的特定类扩展。
    2. 类站与方法
      • void Station::AddVehicleToPlaceNumberN(const Vehicle&amp; vehicle, int placeNumber)
      • Vehicle* Station::GetMutableVehiclePointer()

    设计/班级组织背后的原因。 1. 仅在需要不同实现时才使用继承。如果继承方法的不同实现执行相同的操作(例如 AddVehicleToPlaceNumberN),则无需分离实现。 2. 使用泛型方法名总是有助于简化代码。这些方法可以被重载(传递不同数量和类型的参数)以完成特定的事情。

    【讨论】:

    • @Tushkar Tarkas,在你的符号中。当我只使用汽车时,我什至不想提及车辆。我想使用一些 CarStation,并使用 CarStation::GetMutableCarPointer(),而不是 Station::GetMutableVehiclePointer(),甚至不是 CarStation::GetMutableVehiclePointer()。
    • @Max:为什么不呢?这可以防止您担心的代码重复,并允许对整个事物进行抽象使用。
    • @rubenvb,我认为,这些是我们正在谈论的不同模式。我同意,Tushar 的解决方案在某些情况下很方便。但是考虑一下,与车辆相反,汽车可以是左侧控制和右侧控制,而且我想要一些可以与车辆一起使用的站,以及一些可以与汽车一起使用的站(所以有一些方法,例如:汽车* GetFirstLeftSideControlCarMutablePointer())。然后我写的问题就出现了。
    • @Max:那么你是说你想“复制”代码,通过在每个函数/成员/类名中将 Car 替换为 Truck ,但这些函数仍然(几乎)做同样的事情。
    • @rubenvb,不,我不是说要复制。你读过 Nubsis 的解决方案吗?就我而言,它无需重复就可以很好地工作,并且可以按照我所说的来对待。
    【解决方案2】:

    懒惰的方法是让站使用泛型类型 T 以便; (请耐心等待,我对 C++ 继承语法并不完全清楚,但原则适用)

    template<typename T>
    class Station<T>:CarStation
    {
        void Station<T>::StatAddCarToPlaceNumberN(const T& car, int place_number)
        {
        // adds car to some_container<T>.
        }
    
        T * Station<T>::GetMutableCarPointer(int place_number) 
        {
            // gets mutable pointer from some_container<Car>.
            return dynamic_cast<T>(car);
        }
    }
    

    并且有一个抽象超类 Station,它实现了一个返回基指针的函数,比如

    class CarStation
    {
        //...
        some_container<Car> cars;
        virtual Car * CarStation::GetMutableCarBasePointer(int place_number) = 0;
    
    }
    
    Car * CarStation::GetMutableCarBasePointer(int place_number)
    {
           //gets mutable pointer to base class from some_container<T>
    }
    

    现在,如果你愿意,你可以创建一个新的类 TruckStation(这在 C++ 中我不确定,我再次称之为原理)

    class TruckStation : Station<Truck>
    {
       //...
    }
    

    或者直接选择Station&lt;Truck&gt;

    【讨论】:

    • 见鬼!不知何故,我读了 C#。我将重构为 C++ 代码。对不起
    • 应该是 class Station: CarStation (misprint?)
    • 看起来很合适。。还在理解过程中:),你的代码应该在Station::GetMutableCarPointer()中稍作修正,必须使用dynamic_cast。不是 (T) (变量),而是 dynamic_cast(变量)
    • 是的,我主要不是 C++ 程序员。但我相信我至少明白了我的想法^^
    猜你喜欢
    • 1970-01-01
    • 2018-10-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-08
    • 2020-10-15
    • 1970-01-01
    • 2011-06-04
    相关资源
    最近更新 更多