我在这里滥用继承权吗?什么是最佳实践替代方案/模式?
Am I Abusing Inheritance Here? What's A Best-Practice Alternative/Pattern?
大编辑
因此,在收集了你们所有人的一些反馈,并按照Zack的建议思考了XY问题后,我决定添加另一个代码示例,它确切地说明了我正在努力实现的目标(即"X"),而不是询问我的"Y"。
所以现在我们正在处理汽车,我添加了5个抽象类:ICar
、ICarFeatures
、ICarParts
、ICarMaker
、ICarFixer
。所有这些接口都将封装或使用第三方库提供的技术特定的复杂对象,这取决于接口后面的派生类。这些接口将智能地管理复杂库对象的生命周期。
我在这里的用例是FordCar类。在本例中,我使用Ford库访问类FordFeatureImpl
、FordPartsImpl
和FordCarImpl
。这是代码:
class ICar {
public:
ICar(void) {}
virtual ~ICar(void) {}
};
class FordCar : public ICar {
public:
ICar(void) {}
~FordCar(void) {}
FordCarImpl* _carImpl;
};
class ICarFeatures {
public:
ICarFeatures(void) {}
virtual ~ICarFeatures(void) {}
virtual void addFeature(UserInput feature) = 0;
};
class FordCarFeatures : public ICarFeatures{
public:
FordCarFeatures(void) {}
virtual ~FordCarFeatures(void) {}
virtual void addFeature(UserInput feature){
//extract useful information out of feature, ie:
std::string name = feature.name;
int value = feature.value;
_fordFeature->specialAddFeatureMethod(name, value);
}
FordFeatureImpl* _fordFeature;
};
class ICarParts {
public:
ICarParts(void) {}
virtual ~ICarParts(void) {}
virtual void addPart(UserInput part) = 0;
};
class FordCarParts :public ICarParts{
public:
FordCarParts(void) {}
virtual ~FordCarParts(void) {}
virtual void addPart(UserInput part) {
//extract useful information out of part, ie:
std::string name = part.name;
std::string dimensions = part.dimensions;
_fordParts->specialAddPartMethod(name, dimensions);
}
FordPartsImpl* _fordParts;
};
class ICarMaker {
public:
ICarMaker(void) {}
virtual ~ICarMaker(void) {}
virtual ICar* makeCar(ICarFeatures* features, ICarParts* parts) = 0;
};
class FordCarMaker {
public:
FordCarMaker(void) {}
virtual ~FordCarMaker(void) {}
virtual ICar* makeCar(ICarFeatures* features, ICarParts* parts){
FordFeatureImpl* fordFeatures = dynamic_cast<FordFeatureImpl*>(features);
FordPartsImpl* fordParts = dynamic_cast<FordPartsImpl*>(parts);
FordCar* fordCar = customFordMakerFunction(fordFeatures, fordParts);
return dynamic_cast<ICar*>(fordCar);
}
FordCar* customFordMakerFunction(FordFeatureImpl* fordFeatures, FordPartsImpl* fordParts) {
FordCar* fordCar = new FordCar;
fordCar->_carImpl->specialFeatureMethod(fordFeatures);
fordCar->_carImpl->specialPartsMethod(fordParts);
return fordCar;
}
};
class ICarFixer {
public:
ICarFixer(void) {}
virtual ~ICarFixer(void) {}
virtual void fixCar(ICar* car, ICarParts* parts) = 0;
};
class FordCarFixer {
public:
FordCarFixer(void) {}
virtual ~FordCarFixer(void) {}
virtual void fixCar(ICar* car, ICarParts* parts) {
FordCar* fordCar = dynamic_cast<FordCar*>(car);
FordPartsImpl* fordParts = dynamic_cast<FordPartsImpl*>(parts);
customFordFixerFunction(fordCar, fordParts);
}
customFordFixerFunction(FordCar* fordCar, FordPartsImpl* fordParts){
fordCar->_carImpl->specialRepairMethod(fordParts);
}
};
请注意,我必须使用动态转换来访问抽象接口中的技术特定对象。这就是为什么我认为我在滥用遗产,并促使我最初提出这个问题。
这是我的终极目标:
UserInput userInput = getUserInput(); //just a configuration file ie XML/YAML
CarType carType = userInput.getCarType();
ICarParts* carParts = CarPartFactory::makeFrom(carType);
carParts->addPart(userInput);
ICarFeatures* carFeatures = CarFeaturesFactory::makeFrom(carType);
carFeatures->addFeature(userInput);
ICarMaker* carMaker = CarMakerFactory::makeFrom(carType);
ICar* car = carMaker->makeCar(carFeatures, carParts);
UserInput repairSpecs = getUserInput();
ICarParts* replacementParts = CarPartFactory::makeFrom(carType);
replacementParts->addPart(repairSpecs);
ICarFixer* carFixer = CarFixerFactory::makeFrom(carType);
carFixer->fixCar(car, replacementParts);
也许现在你们都更好地理解了我在努力做什么,也许我可以在哪里改进。
我试图使用基类的指针来表示派生类(即Ford),但派生类包含其他派生类所需的特定对象(即FordPartsImpl
)(即FordCarFixer
需要FordCar
和FordPartsImpl
对象)。这需要我使用动态强制转换将指针从基向下转换到其各自的派生类,这样我就可以访问这些特定的Ford对象。
我的问题是:我在这里滥用继承吗?我试图在工人和物体之间建立一种多对多的关系。我觉得我做错了什么,因为我有一个Object类族,它只做数据,而让ObjectWorker类必须dynamic_cast对象才能访问内部
这不是滥用遗产。。。这是滥用继承
class CSNode:public CNode, public IMvcSubject, public CBaseLink,
public CBaseVarObserver,public CBaseDataExchange, public CBaseVarOwner
其中那些有C前缀的人有巨大的实现
不仅如此。。。Header包含300多行声明。
所以不…你现在没有滥用遗产。
但我刚刚给你看的这门课是侵蚀的产物。我确信Node一开始就是一个发光和多态性的灯塔,能够在行为和节点之间智能切换。
现在它已经变成了一只海怪,一只巨大的飞蛾,Cthulu自己试图用它的视觉咀嚼我的内脏。
注意这个自由人,注意我的建议,小心你的多态性可能会变成什么样子。
除此之外,这很好,我想这是对尿布中的建筑的继承。
如果我只想有一个单一的work()方法,我还有什么其他选择
单一工作方法。。。你可以试试:
- 基于策略的设计,其中策略实现了您的模型
- 一个函数";工作;每个类都使用它
- Functor!在将要使用的每个类中实例化
但您的继承似乎是正确的,是每个人都会使用的单一方法。
还有一件事我只想把这个wiki链接留在这里
或者只是复制粘贴wiki C++代码。。。它和你的非常相似:
#include <iostream>
#include <string>
template <typename OutputPolicy, typename LanguagePolicy>
class HelloWorld : private OutputPolicy, private LanguagePolicy
{
using OutputPolicy::print;
using LanguagePolicy::message;
public:
// Behaviour method
void run() const
{
// Two policy methods
print(message());
}
};
class OutputPolicyWriteToCout
{
protected:
template<typename MessageType>
void print(MessageType const &message) const
{
std::cout << message << std::endl;
}
};
class LanguagePolicyEnglish
{
protected:
std::string message() const
{
return "Hello, World!";
}
};
class LanguagePolicyGerman
{
protected:
std::string message() const
{
return "Hallo Welt!";
}
};
int main()
{
/* Example 1 */
typedef HelloWorld<OutputPolicyWriteToCout, LanguagePolicyEnglish> HelloWorldEnglish;
HelloWorldEnglish hello_world;
hello_world.run(); // prints "Hello, World!"
/* Example 2
* Does the same, but uses another language policy */
typedef HelloWorld<OutputPolicyWriteToCout, LanguagePolicyGerman> HelloWorldGerman;
HelloWorldGerman hello_world2;
hello_world2.run(); // prints "Hallo Welt!"
}
更重要的问题是
如何在StringWorker中使用Int对象
您当前的实现将无法处理
有了政策,这是可能的。
可能的对象是什么
帮助您确定是否需要这种行为
记住,不要用猎枪杀鸡
也许随着时间的推移,你的模特永远不会真正改变。
您犯了一个设计错误,但这不是"滥用继承"。您的错误是试图使过于通用。沉思一下"你不需要它"的原则。然后,想想你实际上拥有什么。你没有物体,你有狗、猫和马。或者,您可能有"正方形"、"多边形"answers"直线"。或TextInEnglish和TextInArabic。或关键是,你可能有相对较少的具体的东西,它们可能都属于同一个上级类别。同样,您也没有Workers。假设你拥有的是狗、猫和马,那么你可能也有一个健身师、一个美容师和一个兽医。
用具体的语言思考你的具体问题。只实现您实际需要的类和关系。
关键是您没有通过接口访问特定的功能。使用接口的全部原因是,你希望所有的汽车都是制造的、固定的和特色的。。。如果你不打算以这种方式使用它们,那么就不要使用接口(和继承),只需在用户输入时检查选择了哪辆车,并实例化正确的专用对象。
我对您的代码做了一些更改,这样只有在"造车"的时候才会有一个向上的dynamic_cast。我必须知道你想要做的所有事情,才能创建我非常满意的接口。
class ICar {
public:
ICar(void) {}
virtual ~ICar(void) {}
virtual void specialFeatureMethod(ICarFeatures *specialFeatures);
virtual void specialPartsMethod(ICarParts *specialParts);
virtual void specialRepairMethod(ICarParts *specialParts);
};
class FordCar : public ICar {
public:
FordCar(void) {}
~FordCar(void) {}
void specialFeatureMethod(ICarFeatures *specialFeatures) {
//Access the specialFeatures through the interface
//Do your specific Ford stuff
}
void specialPartsMethod(ICarParts *specialParts) {
//Access the specialParts through the interface
//Do your specific Ford stuff
}
void specialRepairMethod(ICarParts *specialParts) {
//Access the specialParts through the interface
//Do your specific Ford stuff
}
};
class ICarFeatures {
public:
ICarFeatures(void) {}
virtual ~ICarFeatures(void) {}
virtual void addFeature(UserInput feature) = 0;
};
class FordCarFeatures : public ICarFeatures{
public:
FordCarFeatures(void) {}
~FordCarFeatures(void) {}
void addFeature(UserInput feature){
//extract useful information out of feature, ie:
std::string name = feature.name;
int value = feature.value;
_fordFeature->specialAddFeatureMethod(name, value);
}
FordFeatureImpl* _fordFeature;
};
class ICarParts {
public:
ICarParts(void) {}
virtual ~ICarParts(void) {}
virtual void addPart(UserInput part) = 0;
};
class FordCarParts :public ICarParts{
public:
FordCarParts(void) {}
~FordCarParts(void) {}
void addPart(UserInput part) {
//extract useful information out of part, ie:
std::string name = part.name;
std::string dimensions = part.dimensions;
_fordParts->specialAddPartMethod(name, dimensions);
}
FordPartsImpl* _fordParts;
};
class ICarMaker {
public:
ICarMaker(void) {}
virtual ~ICarMaker(void) {}
virtual ICar* makeCar(ICarFeatures* features, ICarParts* parts) = 0;
};
class FordCarMaker {
public:
FordCarMaker(void) {}
~FordCarMaker(void) {}
ICar* makeCar(ICarFeatures* features, ICarParts* parts){
return customFordMakerFunction(features, parts);
}
ICar* customFordMakerFunction(ICarFeatures* features, ICarParts* parts) {
FordCar* fordCar = new FordCar;
fordCar->specialFeatureMethod(features);
fordCar->specialPartsMethod(parts);
return dynamic_cast<ICar*>(fordCar);
}
};
class ICarFixer {
public:
ICarFixer(void) {}
virtual ~ICarFixer(void) {}
virtual void fixCar(ICar* car, ICarParts* parts) = 0;
};
class FordCarFixer {
public:
FordCarFixer(void) {}
~FordCarFixer(void) {}
void fixCar(ICar* car, ICarParts* parts) {
customFordFixerFunction(car, parts);
}
void customFordFixerFunction(ICar* fordCar, ICarParts *fordParts){
fordCar->specialRepairMethod(fordParts);
}
};
一个人可以做得更好(对于某些"更好"的值),并增加复杂性。
这里到底在做什么?让我们逐点来看:
- 有一些对象类型,静态未知,在运行时根据字符串确定
- 有一些工作者类型,也是静态未知的,在运行时由另一个字符串确定
- 希望对象类型和工作者类型匹配
我们可以尝试用一些模板代码将"希望"变成"肯定"。
ObjectWorkerDispatcher* owd =
myDispatcherFactory->create("someWorker", "someObject");
owd->dispatch();
显然,对象和工作者都隐藏在调度器中,这是完全通用的:
class ObjectWorkerDispatcher {
ObjectWorkerDispatcher(string objectType, string workerType) { ... }
virtual void dispatch() = 0;
}
template <typename ObjectType>
class ConcreteObjectWorkerDispatcher : public ObjectWorkerDispatcher {
void dispatch () {
ObjectFactory<ObjectType>* of = findObjectFactory(objectTypeString);
WorkerFactory<ObjectType>* wf = findWorkerFactory(workerTypeString);
ObjectType* obj = of->create();
Worker<ObjectType>* wrk = wf->create();
wrk->doWork(obj);
}
map<string, ObjectFactory<ObjectType>*> objectFactories;
map<string, WorkerFactory<ObjectType>*> workerFactories;
ObjectFactory<ObjectType>* findObjectFactory(string) { .. use map }
WorkerFactory<ObjectType>* findWorkerFactory(string) { .. use map }
}
我们有不同的不相关类型的Object。没有通用的Object类,但我们可以有例如StringObject的几个子类型,它们都与各种StringWorker兼容。
我们有一个抽象的Worker<ObjectType>
类模板和具体的MyStringWorker : public Worker<StringObject>
、OtherStringWorker : public Worker<StringObject>
。。。类。
这两种工厂都是免继承的。不同类型的工厂是完全分开的(在不同的调度员中),从不混合。
还有一些空白需要填写,但希望所有内容都或多或少清晰明了。
本设计未使用铸件。您可以决定是否仅此属性就值得增加复杂性。
我认为您有适合您需求的解决方案。我认为可以改进的一点是,从处理基类级别对象的函数中删除carType
的使用。
ICar* FordCarFixer::getFixedCar(UserInput& userInput)
{
FordCarParts* carParts = new FordPartFactory;
carParts->addPart(userInput);
FordCarFeatures* carFeatures = new FordCarFeatures;
carFeatures->addFeature(userInput);
FordCarMaker* carMaker = new FordCarMaker;
FordCar* car = carMaker->makeCar(carFeatures, carParts);
UserInput repairSpecs = getUserInput();
ForCarParts* replacementParts = new ForCarParts;
replacementParts->addPart(repairSpecs);
FordCarFixer* carFixer = new FordCarFixer;
carFixer->fixCar(car, replacementParts);
return car;
}
UserInput userInput = getUserInput();
ICar* car = CarFixerFactory::getFixedCar(userInput);
使用这种方法,FordCarFixer
级别的大多数对象都是福特特有的。
- 运行同一解决方案的另一个项目的项目
- 具有奇怪重复模板模式的派生类中的成员变量已损坏
- Project Euler问题4的错误解决方案
- 为什么在保护模式下继承升级不起作用
- 如何在全屏模式下(在OpenGL中)使背景透明
- 为什么使用__LINE_的代码在发布模式下在MSVC下编译,而不是在调试模式下
- 派生类是否可以在抽象工厂设计模式中具有数据成员
- 计算每个节点的树高,帮助我解释这个代码解决方案
- 此模式的C++RegEx
- C++:Application.cpp中抛出了未解析的外部符号(解决方案在问题的末尾,供未来的读者参考)
- avrogencpp能为模式中的每种类型生成单独的头文件吗
- C++ 单例设计模式替代方案
- 我在这里滥用继承权吗?什么是最佳实践替代方案/模式?
- 用于以下方案的模式
- 发布者订阅模式的现代替代方案
- 寻找字符串模式的更好解决方案
- 使用本机C++、托管 C++ CLI 和 C# 解决方案在混合模式下进行调试
- 用于全局模式匹配的递归解决方案
- 等待条件的非线程替代方案.(编辑:Proactor模式与boost.asio?)
- 无法在调试模式下编译解决方案,因为缺少MSVCR100D.dll