我在这里滥用继承权吗?什么是最佳实践替代方案/模式?

Am I Abusing Inheritance Here? What's A Best-Practice Alternative/Pattern?

本文关键字:模式 方案 最佳 在这里 继承权 什么      更新时间:2023-10-16

大编辑

因此,在收集了你们所有人的一些反馈,并按照Zack的建议思考了XY问题后,我决定添加另一个代码示例,它确切地说明了我正在努力实现的目标(即"X"),而不是询问我的"Y"。


所以现在我们正在处理汽车,我添加了5个抽象类:ICarICarFeaturesICarPartsICarMakerICarFixer。所有这些接口都将封装或使用第三方库提供的技术特定的复杂对象,这取决于接口后面的派生类。这些接口将智能地管理复杂库对象的生命周期。

我在这里的用例是FordCar类。在本例中,我使用Ford库访问类FordFeatureImplFordPartsImplFordCarImpl。这是代码:

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需要FordCarFordPartsImpl对象)。这需要我使用动态强制转换将指针从基向下转换到其各自的派生类,这样我就可以访问这些特定的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级别的大多数对象都是福特特有的。