我最近遇到过一些我认为可以用不同设计清理的情况,但我不知道任何适合的模式。
在所有这些情况下,我都有一些部分共享API的类。例如,记录器类:
struct ILogger { virtual void log(string msg) = 0; };
struct StdOutLogger : public ILogger {
void log(string msg) override; // Log to stdout
};
struct FileLogger : public ILogger {
void log(string msg) override; // Log to file
};
struct GuiLogger : public ILogger {
void log(string msg) override; // Log to GUI
void draw();
void clear();
};
或者也许:
struct Graphic {
virtual void draw();
virtual void setPosition();
// etc.
};
struct AnimatedGraphic : public Graphic {
void draw() override;
void start();
void stop();
void setLooping(bool loop);
};
现在,根据谁拥有这些对象,我可能有一个容器的引用/指向公共接口的指针:
class LogManager {
std::vector<std::unique_ptr<ILogger>> _loggers;
// ...
};
或者我可以保持类型分开,并在运行时选择使用哪一种:
// This is already starting to get messy
class SomethingWithGraphic {
std::unique_ptr<Graphic> _graphic;
std::unique_ptr<AnimatedGraphic> _animatedGraphic;
// ...
};
第一个解决方案很好,直到我需要开始使用不属于通用接口的功能。第二个解决方案允许我选择我需要的解决方案,但它容易出错,并且需要在任何地方使用丑陋的分支。
我已经提出了几种替代解决方案,但我还没有找到真的感觉正确的解决方案。
保留一个拥有容器,并创建指向拥有对象的其他容器,但是通过不同的接口。 (要求容器保持同步)
将所有功能添加到界面,但是对于不需要额外功能的对象,请将实现留空。 (这些功能并不属于该界面的一部分)
存储所有潜在类型的变体。 (感觉像黑客,需要到处都是访客)
使用记录器示例:
//// 1 ////
struct IDrawable {
virtual void draw() = 0;
virtual void clear() = 0;
};
std::vector<std::unique_ptr<ILogger>> _loggers;
std::vector<IDrawable*> _drawableLoggers;
//// 2 ////
struct ILogger {
virtual void log(string msg) = 0;
virtual void draw() {};
virtual void clear() {};
};
struct StdOutLogger : public ILogger {
void log(string msg) override; // Log to stdout
};
struct FileLogger : public ILogger {
void log(string msg) override; // Log to file
};
struct GuiLogger : public ILogger {
void log(string msg) override; // Log to GUI
void draw() override;
void clear() override;
};
//// 3 ////
std::vector<std::variant<StdOutLogger, FileLogger, GuiLogger>> _loggers;
#1似乎是我认为最正确的,但仍然不是最好的。
有没有人知道任何可以清理它的模式或结构?
答案 0 :(得分:1)
一种可行的方法:您可以使用指针向量或对接口的引用,并为您希望从一个实例中获取其实际类型和调用的所有情况实现访问者模式一种不属于公共接口的方法。
这是一个最小的工作示例:
#include<iostream>
#include<memory>
#include<vector>
struct Visitor;
struct Interface {
virtual void method() = 0;
virtual void accept(Visitor &) = 0;
};
struct A: Interface {
void method() override { std::cout << "A::method" << std::endl; }
void f() { std::cout << "A::f" << std::endl; }
void accept(Visitor &) override;
};
struct B: Interface {
void method() override { std::cout << "B::method" << std::endl; }
void g() { std::cout << "B::g" << std::endl; }
void accept(Visitor &) override;
};
struct Visitor {
void visit(A &a) { a.f(); }
void visit(B &b) { b.g(); }
};
void A::accept(Visitor &v) { v.visit(*this); }
void B::accept(Visitor &v) { v.visit(*this); }
int main() {
std::vector<std::unique_ptr<Interface>> vec;
vec.push_back(std::make_unique<A>());
vec.push_back(std::make_unique<B>());
Visitor visitor;
for(auto &&i: vec) {
i->method();
i->accept(visitor);
}
}