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

时间:2014-02-26 19:54:43

标签: c++ inheritance polymorphism

BIG EDIT

所以在收集了你们所有人的反馈意见,并按照Zack建议的那样冥想XY problem之后,我决定添加另一个代码示例来说明我正在努力完成的事情(即“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)(即{{1需要一个FordCarFixerFordCar对象)。这需要我使用动态转换来将指针从基础向下转换到其各自的派生类,以便我可以访问这些特定的Ford对象。

5 个答案:

答案 0 :(得分:4)

我的问题是:我在这里滥用继承吗?我试图在工人和对象之间建立多对多的关系。我觉得我做错了,因为拥有一个Object类的类,除了保存数据并使ObjectWorker类必须使用dynamic_cast对象访问内部之外什么都不做。

这不是滥用继承......这是滥用继承

class CSNode:public CNode, public IMvcSubject, public CBaseLink,
         public CBaseVarObserver,public CBaseDataExchange, public CBaseVarOwner

其中具有C前缀的人具有巨大的实现

不仅如此......标题超过300行声明。

所以不......你现在没有滥用继承权。

但是我刚给你看的这门课是侵蚀的产物。我确信Node开始时它是光和多态的闪亮灯塔,能够在行为和节点之间巧妙地切换。

现在它已经变成了一个海妖,一个巨人,Cthulu本身试图用它的视野来咀嚼我的内心。

听从这个自由的人,听从我的忠告,谨防你的多态性可能成为什么。

否则它很好,很好地利用了我认为是纸尿裤架构的继承。

如果我只想使用一个work()方法,我还有哪些其他选择?

单一工作方法......您可以尝试:

  • 基于策略的设计,其中策略具有模型的实现
  • 功能&#34;工作&#34;它被每一个类使用
  • 一个Functor!在每个类中实例化它将被使用

但是你的继承似乎是正确的,每个人都会使用一种方法。

还有一件事....... 我要离开这个维基链接right here

或者只是复制粘贴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对象?

您当前的实施无法处理

有了政策,就有可能。

有哪些可能的对象?

帮助您定义是否需要此类行为

请记住,不要用霰弹枪杀死一只鸡

也许你的模特永远不会真正改变加班。

答案 1 :(得分:1)

您提交了设计错误,但它不是“滥用继承”。您的错误是您试图过于通用。默想You Aren't Gonna Need It的原则。然后,想想你实际拥有的东西。你没有物体,你有狗,猫和马。或者你可能有正方形,多边形和直线。或者TextInEnglish和TextInArabic。或者......重点是,您可能只有相对较少的具体事物,它们可能都在同一个superordinate category。同样,你没有工人。假设你拥有的是狗,猫和马,那么你可能还有一个练习器,一个美容师和一个兽医。

具体地考虑你的具体问题。只实现类,只实现您实际需要的关系。

答案 2 :(得分:1)

重点是您没有通过接口访问特定功能。使用接口的全部原因是你想要制作,修复和推荐所有的汽车......如果你不打算以这种方式使用它们,不要使用接口(和继承),而只需检查在用户输入时选择汽车并实例化正确的专用物体。

我已经改变了你的代码,所以只有在“汽车制造”时才会有一个向上的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);
    }
};

答案 3 :(得分:0)

人们可以做得更好(对于某些“更好”的值),复杂性会增加。

这里到底做了什么?让我们一点一点地看:

  • 有一些对象类型,静态未知,在运行时从字符串
  • 确定
  • 有一些工作者类型,也是静态未知的,在运行时从另一个字符串确定
  • 希望对象类型和工作者类型匹配

我们可以尝试使用一些模板代码“肯定地”变成“肯定”。

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类,但我们可以有例如StringObject的几个子类型,都与各种StringWorker兼容。

我们有一个抽象的Worker<ObjectType>类模板和具体的MyStringWorker : public Worker<StringObject>OtherStringWorker : public Worker<StringObject> ...类。

这两种工厂都是无继承的。不同类型的工厂完全分开(在不同的调度员中)并且从不混合。

还有一些空白需要填补,但希望这一切都应该或多或少地清楚。

在制作此设计时不使用任何演员表。你决定这个属性是否值得这样增加复杂性。

答案 4 :(得分:0)

我认为您可以根据自己的需求找到合适的解决方案。我看到可以改进的一件事是从处理基类级别对象的函数中删除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级别的大多数对象都是特定于福特的。