views:

59

answers:

2

Hello to everyone! I have some problem with organizing classes properly.

Suppose, I have some class ABase. When I want to create some different (more particular) abstraction of this class (denote it AParticular), I can use inheritance or just composition. Then it is easy to treat AParticular as ABase: in case of inheritance it is made automatically, in case of composition I can create some const ABase& AParticular::GetABasePart() method. By this I avoid code duplication and get polymorphic features.

But, when I have two or more classes that interact with each other, I have some problems to create analogues of these classes in a more particular abstraction. For example, suppose, I have two classes Car and Station. Station has some public methods to maintain cars:

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

Now, I want to create Truck and TruckStation classes: they are pretty similar to Car and Station classes and have minor changes. To understand problem it is sufficient to think as they do absolutely the same as Car and Station classes, but their methods have a bit other name (i.e. TruckStation::AddTruckToPlaceNumberN instead of Station::AddCarToPlaceNumberN)

How to organize the code of new classes to provide these features?

  1. No code duplication, I want to use the already created Car and Station class methods.
  2. Fast conversion Truck& -> Car&, TruckStation& -> Station& (Not necessary inheritance, composition is suitable also), since I want sometimes to treat Truck as Car and TruckStation as Station.
  3. All interaction methods in level Car-Station should be realized in a new level Truck-TruckStation.

The main problem is the 3d item. Let's consider two interaction methods:

1) It is ok with this method:

// 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) But I don't know how to implement the analogue of 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*.
}

Repeat the question: how can I implement these classes to provide:

  1. no code duplication.
  2. Possibility to treat new classes as their higher level abstractions.
  3. Implementation methods such as TruckStation::GetMutableTruckPointer() that correspond to Station::GetMutableCarPointer().

Tnx!

+1  A: 

Getting specific to your code. I would do it this way.

  1. Base class Vehicle extended by specific classes for Car and Truck.
  2. Class Station with methods
    • void Station::AddVehicleToPlaceNumberN(const Vehicle& vehicle, int placeNumber)
    • Vehicle* Station::GetMutableVehiclePointer()

Reasons behind the design/class organization. 1. Use inheritance only when there is a need of different implementations. If different implementations of an inherited method do the same thing (like in case of AddVehicleToPlaceNumberN) then there is no need for separating the implementations. 2. Use of generic method name always helps in simplifying the code. The methods can be overloaded (passed with different number and type of parameters) to get a specific thing done.

Tushar Tarkas
@Tushkar Tarkas, in your notations. When I work only with cars, I don't even want to mention Vehicle. I want to use some CarStation, and to use CarStation::GetMutableCarPointer(), not Station::GetMutableVehiclePointer(), and not even CarStation::GetMutableVehiclePointer().
Max
@Max: why not? This prevents the code duplication you're so worried about and allows for an abstract use of the whole thing.
rubenvb
@rubenvb, I think, that these are different patterns we are speaking about. I agree, that Tushar's solution is convenient for some cases. But consider, that as opposed to vehicle, car can be left-side-control and right-side-control, And I want some station, that can work with vehicles, and some station that can work with cars (so has some method like: Car* GetFirstLeftSideControlCarMutablePointer()). Then problem I wrote about arises.
Max
@Max: then you are saying that you want to "duplicate" code, by replacing *Car* by *Truck* in each function/member/classname, but these functions will still be doing (almost) the exact same thing.
rubenvb
@rubenvb, no, I am not saying to duplicate. Have you read Nubsis's solution? As for me it works great without duplicating and can treat with what I said.
Max
@max: You can get some lengths by using templated T instead of Car or Truck, but I can't see how you're going to implement the GetMutableTruckPointer() from within your template. You can't have it return T, because that would allow it to return a Car. Blame me for not understanding this, trying to learn, that's all ;)
rubenvb
@rubenvb, I am also just understanding ),Ok, I created Station<Truck> station_truck; I want to add some trucks to it. I say station_truck.StatAddCarToPlaceNumberN(truck, place) several times. It adds lots objects of class Truck that is inhereted from Car. Station<Truck> itself is inherited from CarStation, that has container with Car pointers, so, due to c++ inheritance process, my trucks will be accepted by container that contains Cars. When I want to take some pointer to trunk,I write station_truck.GetMutableTruckPointer().This method looks for appropriate Car* object and dynamic casts it.
Max
+1  A: 

The lazy way is to make the station use a generic type T so that; (Bare with me, I'm not completely clear with the C++ inheritance syntax, but the principle applies)

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

And have an abstract superclass Station that implements a function to return a base pointer, say

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

Now, if you want, you can create a new class TruckStation (This I am uncertain of in C++, again, I call principle)

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

or just go with Station<Truck>

Nubsis
Heck! Somehow I read C#. I'll just refactor to C++ code. Sorry
Nubsis
@Nubsis, should it be class Station<T>: CarStation (misprint?)
Max
Yes, I just corrected it ^^
Nubsis
@Nubsis, looks suitable.. Still under the process of understanding :), Your code should be a little corrected in Station<T>::GetMutableCarPointer(), there must dynamic_cast be used. not (T) (variable), but dynamic_cast<T*>(variable)
Max
Yes, I'm not mainly a C++ programmer. But I believe i got my thoughts through at least ^^
Nubsis