问题
I have a class hierarchy that I designed for a project of mine, but I am not sure how to go about implement part of it.
Here is the class hierarchy:
class Shape { };
class Colored { // Only pure virtual functions
};
class Square : public Shape { };
class Circle : public Shape { };
class ColoredSquare : public Square, public Colored { };
class ColoredCircle : public Circle, public Colored { };
In part of my project, I have a std::vector of different type shapes. In order to run an algorithm though, I need to put them in a std::vector of colored objects (all of which are derived types of different concrete shapes, so I need a method to cast a Square into a ColoredSquare and a Circle into a ColoredCircle at runtime. The tricky thing is that the 'shape' classes are in a different library than the 'colored' classes. What is the best method to acomplish this? I have thought about doing a dynamic_cast check, but if there is a better way, I would rather go with that.
Edit 1:
Here's a bit better of an Example:
class Traceable {
public:
// All virtual functions
virtual bool intersect(const Ray& r) = 0;
// ...
};
class TraceableSphere : public Sphere, public Traceable {
};
class IO {
public:
// Reads shapes from a file, constructs new concrete shapes, and returns them to
// whatever class needs them.
std::vector<Shape*> shape_reader(std::string file_name);
};
class RayTracer {
public:
void init(const std::vector<Shape*>& shapes);
void run();
private:
std::vector<Traceable*> traceable_shapes;
};
void RayTracer::init(const std::vector<Shape*>& shapes) {
// ??? traceable_shapes <- shapes
}
void RayTracer::run() {
// Do algorithm
}
回答1:
You could use the decorator pattern:
class ColorDecorator public Colored
{
ColorDecorator(Shape* shape): m_shape(shape) {}
... //forward/implement whatever you want
};
If you want to store a Square in a Colored vector, wrap it in such a decorator.
Whether this makes sense is questionable though, it depends on your design and the alternatives. Just in case, also check out the visitor pattern (aka double dispatch) which you could use to just visit a subset of objects in a container or treat them differently depending on their type.
回答2:
Looks like you are going to design the class library in a "is-a" style, welcome to the Inheritance-Hell. Can you elaborate a bit about your "algorithm" ? Typically it is bad design if you need to "type-test" on objects, since that is what you want to avoid with polymorphism. So the object should provide the proper implementation the algorithm uses (design-pattern: "strategy"), advanced concepts utilize "policy-based class design".
回答3:
With careful design, you can avoid casting
. In particular, care for SRP. Implement methods carefully so that they use a single Interface
to achieve a single goal/fulfill a single responsibility. You have not posted anything about the algorithms or how the objects will be used. Below is a hypothetical sample design:
class A {
public:
void doSomeThing();
};
class B{
public:
void doSomeOtherThing();
};
class C:public A,public B{};
void f1( A* a){
//some operation
a->doSomeThing();
//more operation
}
void f2(B* b){
//some operation
b->doSomeOtherThing();
//more operation
}
int main(int argc, char* argv[])
{
C c;
f1(&c);
f2(&c);
return 0;
}
Note using the object c
in different context. The idea is to use only the interface
of C
that is relevant for a specific purpose. This example can have classes instead of the functions f
or f2
. For example, you have some Algorithm
s classes that do some operation using the objects in the inheritance hierarchy, you should create the classes such that they perform a single responsibility, which most of the time requires a single interface to use, and then you can create/pass objects as instance
of that interface only.
回答4:
Object-oriented programming only makes sense if all implementations of an interface implement the same operations in a different way. Object-orientation is all about operations. You have not shown us any operations, so we cannot tell you if object-orientation even makes sense for your problem at all. You do not have to use object-oriented programming if it doesn't make sense, especially in C++, which offers a few other ways to manage code.
As for dynamic_cast
-- in well-designed object-oriented code, it should be rare. If you really need to know the concrete type in some situation (and there are such situations in real-life software engineering, especially when you maintain legacy code), then it's the best tool for the job, and much cleaner than trying to reimplement the wheel by putting something like virtual Concrete* ToConcrete()
in the base class.
回答5:
I think the simplest & cleanest solution for you would be something like the following similar to what Chris suggests at the end.
class Shape {
virtual Colored *getColored() {
return NULL;
}
};
class Colored { // Only pure virtual functions
};
class Square : public Shape { };
class Circle : public Shape { };
class ColoredSquare : public Square, public Colored {
virtual Colored *getColored() {
return this;
}
};
class ColoredCircle : public Circle, public Colored {
virtual Colored *getColored() {
return this;
}
};
I do not completely understand this statement though " The tricky thing is that the 'shape' classes are in a different library than the 'colored' classes."
How does this not allow you to do what's being suggested here (but still allow you to create a class ColoredSquare) ?
来源:https://stackoverflow.com/questions/23569140/better-solution-than-dynamic-cast-in-c