C++ 使用部分共享接口组织对象
我最近遇到了一些情况,我认为可以用不同的设计来解决,但我不知道有什么模式适合 在所有这些情况下,我有几个类部分共享一个API。例如,记录器类:C++ 使用部分共享接口组织对象,c++,design-patterns,interface,polymorphism,C++,Design Patterns,Interface,Polymorphism,我最近遇到了一些情况,我认为可以用不同的设计来解决,但我不知道有什么模式适合 在所有这些情况下,我有几个类部分共享一个API。例如,记录器类: struct ILogger { virtual void log(string msg) = 0; }; struct StdOutLogger : public ILogger { void log(string msg) override; // Log to stdout }; struct FileLogger : public ILo
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;
// ...
};
//这已经开始变得一团糟了
用图形分类{
std::唯一的ptr图形;
std::唯一的_ptr _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////
结构IDrawable{
虚空绘制()=0;
虚空清除()=0;
};
标准::矢量记录器;
std::矢量可绘图记录器;
//// 2 ////
结构ILogger{
虚拟无效日志(字符串消息)=0;
虚空绘制(){};
虚空清除(){};
};
结构StdOutLogger:公共ILogger{
void log(字符串msg)override;//记录到标准输出
};
结构文件记录器:公共ILogger{
无效日志(字符串msg)覆盖;//记录到文件
};
结构GuiLogger:公共ILogger{
无效日志(字符串msg)覆盖;//记录到GUI
void draw()覆盖;
void clear()覆盖;
};
//// 3 ////
标准::矢量记录器;
#我认为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);
}
}
#包括
#包括
#包括
结构访问者;
结构接口{
虚空方法()=0;
虚拟无效接受(访问者&)=0;
};
结构A:接口{
void method()重写{std::cout,这样保存所有ILogger
实例的LogManager
将拥有所有接口?比如draw()
,clear()
来自GuiLogger
和flush()
,open()
,close()
来自文件记录器
?当您添加越来越多的记录器
时,日志管理器
将变得越来越复杂…接口被发明来隐藏实现,所以通常您不需要打开其他详细信息。日志管理器
应该只知道日志
方法。这是OO键。不要强制使用int如果某个实现不适合,请访问该实现。在这种情况下,如果您想以多种方式记录某个内容,请查看责任链。有3个处理程序,将具体的记录器提供给每个处理程序,并构建一个请求,以便沿该链传递。然后,链中的每个日志处理程序都可以执行任何它想执行的操作。当然,这意味着对于每个记录器实现,您都需要一个新的处理程序,更不用说大量的锅炉板来设置链。