Interacting classes - organizing code
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?
- No code duplication, I want to use the already created Car and Station class methods.
- 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.
- 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_nu开发者_JAVA百科mber) {
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:
- no code duplication.
- Possibility to treat new classes as their higher level abstractions.
- Implementation methods such as TruckStation::GetMutableTruckPointer() that correspond to Station::GetMutableCarPointer().
Tnx!
Getting specific to your code. I would do it this way.
- Base class Vehicle extended by specific classes for Car and Truck.
- 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.
The lazy way is to make the station use a generic type T so that; (Bear 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>
精彩评论