【问题标题】:How to use strategy pattern with additional methods in derived classes如何在派生类中使用带有附加方法的策略模式
【发布时间】:2019-03-07 09:22:00
【问题描述】:

我正在编写一个使用策略的项目,但是,我的派生类具有基类不应该具有的其他附加功能。

class Base {
        public:
            virtual void execute() = 0;
            virtual void print() const noexcept = 0;
            virtual ~Base() {}
        };


class DerivedA : public virtual Base {
        public:
            void execute() override;
            void print() const noexcept override;
            void doSomething();

        private:
            int x;
            double y;
};

class DerivedB final : public Base {
        public:
            void execute() override;
            void print() const noexcept override;
            std::string getZ() const noexcept;

        private:
            std::string z;
        };


在 main() 中,我尝试使用 dynamic_cast 来使用这样的附加功能:

int main() {

    DerivedA da;
    Base* base = &da;
    DerivedA* derivedA = dynamic_cast<DerivedA*>(base);
    derivedA.doSomething();

    return 0;
}


但是当我尝试运行代码时,出现以下错误:

“DerivedA *”与“DerivedA”的间接级别不同


我的问题是,我应该为此使用策略,还是应该使用另一种设计模式?如果我应该使用策略,我该如何解决这个错误?


更新

我正在考虑改用装饰器,因为 DerivedA 可以是 DerivedB 的基类,但是,我担心强制转换,因为每个类都有不同的类成员。


更新

好像我写错了。但是,我在这里发布时写对了!它现在有效,但是,我同意有更好的方法来实现它。

【问题讨论】:

  • 派生A->doSomething()
  • 我也是这么想的。当我尝试这样做时,我收到警告 表达式必须具有指针类型
  • 关于您的更新:在您当前的示例中,DerivedA 不是DerivedB 的超类。如果它是DerivedB,将继承它所有基类的成员,包括DerivedA
  • 并不是因为一开始我认为执行策略会更好。但是,我正在审查我的设计,并认为我应该更改它并实现装饰器。

标签: c++ algorithm design-patterns


【解决方案1】:

如果这正是您要编译的代码,那么解决方案很简单。而不是

derivedA.doSomething();

你应该写

derivedA->doSomething();

因为 derived 是一个指针,而不是一个引用或实例。

但是,对于考虑dynamic_cast,我确实有一些其他严重的担忧。我的观点是,如果你使用dynamic_cast,你的设计可能有问题。您有一个隐藏实现的接口,但有依赖于实现而不是在接口中的代码。

需要考虑的一些替代方案:

  • 尝试将特定于实现的方法转换为对接口有意义的方法。也许您可以找到所有实现的一些共同概念。
  • 将特定于实现的方法提取到“功能”接口中,并在接口中提供功能以获得“功能”接口。例如。如果您的基类是IAnimal,并且您的一些动物可以游泳,则创建一个接口ISwimmable 并向IAnimal 添加一个方法,该方法返回一个指向ISwimmable (IAnimal::getSwimmable) 的指针。可以游泳的动物实现,可以从ISwimmable 继承并实现getSwimmable 并返回指向ISwimmable 接口的指针(实际上,返回自身的向上转换,这只是隐含的自身)。不会游泳的动物可以返回一个nullptr(如果您想保持接口为纯接口,则可以是IAnimal 或一些继承自IAnimalAnimalBase 类的默认实现)。

Microsoft 的 COM 系统也使用了第二种方法,其中每个 COM 接口都实现了IUnknown 接口,您可以调用IUnknown 来为某些“功能”获取特定的不同接口。

【讨论】:

  • 功能接口对于这种情况来说似乎是个好主意。但是,每个派生类都有不同的类成员和不同数量的成员。我不确定如何在不强制转换的情况下解决这个问题?
  • 哦,对了,我可以使用非纯接口。这是一个很好的建议!谢谢。
【解决方案2】:

iw 工作文件下面的代码,带有 g++ 和编译: g++ main.cc -o main -std=c++14

#include <string>

class Base {
        public:
            virtual void execute() = 0;
            virtual void print() const noexcept = 0;
            virtual ~Base() {}
        };


class DerivedA : public virtual Base {
        public:
            void execute() override {}
            void print() const noexcept override {}
            void doSomething(){}

        private:
            int x;
            double y;
};

class DerivedB final : public Base {
        public:
            void execute() override {}
            void print() const noexcept override {}
            std::string getZ() const noexcept {}

        private:
            std::string z;
        };
int main() {

    DerivedA da;
    Base* base = &da;
    DerivedA* derivedA = dynamic_cast<DerivedA*>(base);
    derivedA->doSomething();

    return 0;
}

【讨论】:

    【解决方案3】:

    您所描述的是软件中的一个常见问题,并且在各种设计模式中出现了很多。您想多态地使用DerivedADerivedB,因此它们必须共享一个公共接口。没关系。但是DerivedA 有一个有用的方法doSomething 你也想使用。您的选择是:

    1. 不要使用doSomething 方法,这会使您的代码保持整洁;
    2. 进行运行时类型检查和强制转换,以便您可以访问 doSomething,但您的代码不可避免地看起来有点 hacky;
    3. Base 中实现doSomething 以不执行任何操作,那么您的代码看起来不错,但是当您在Base 引用上调用doSomething 时,如果接收对象实际上属于DerivedB 类,它将什么也不做.

    Decorator 在这里帮不上忙。装饰器有利于扩充现有方法。你不能使用它(在静态类型语言中)添加新方法,因为我学到了很难的方法:-)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多