c++ - 真正允许在C++中将实现与用户隔离的数据抽象

标签 c++ abstraction

我犹豫要问这个问题,因为它很简单。除了我看不到解决方案。

我最近尝试编写一个简单的程序,该程序在某种程度上忽略了引擎呈现其UI的方式。

一切在纸面上看起来都很棒,但是实际上,理论并没有使我走得太远。

假设我的工具关心使用IWindowIContainer托管一个ILabelIButton。这是4个UI元素。放弃其中的每一项都是一项琐碎的任务。我可以使用Qt,Gtk和主题创建这些元素中的每一个-命名。

我知道,为了使实现(例如带有QtWindowQtContainer)起作用,抽象(IWindowIContainer)也必须起作用:IWindow需要能够接受IContainer作为其子元素:

  • 我可以将任何UI元素添加到容器或
  • 所有UI元素均从单个父
  • 继承

    那是完全解决抽象问题的理论。实践(或实施)是另外一回事。为了使实现与抽象一起工作-以我的方式看,我可以
  • 用丑陋的调用污染了抽象,暴露了实现(或给出了提示)-杀死了抽象概念或
  • 将强制转换从抽象添加到实现可以理解的内容(dynamic_cast<>())。
  • 向用户界面特定元素(ISomething)添加一个map<IElement*, QtElement*>()实例的全局映射池,这有点像强制转换,但由我自己完成。

  • 所有这些看起来都很丑陋。我在这里看不到其他选择-这是数据抽象概念实际上失败的地方吗?铸造是这里唯一的选择吗?

    编辑

    我花了一些时间尝试提出最佳解决方案,而且似乎这是用C++不能简单完成的事情。不是没有转换,也没有模板。

    我最终想出的解决方案(在弄乱了很多接口(interface)以及如何定义接口(interface)之后)如下所示:

    1.需要一个参数化的基本接口(interface)来定义调用

    基本接口(interface)(将其称为Container的TContainerBase和元素的TElementBase)指定了预期由容器或元素实现的方法。这部分很简单。

    该定义需要遵循以下内容:
    template <typename Parent>
    class TElementBase : public Parent {
        virtual void DoSomething() = 0;
    };
    
    template <typename Parent>
    class TContainerBase : public Parent {
        virtual void AddElement(TElementBase<Parent>* element) = 0;
    };
    

    2.必须有一个指定继承的模板。

    这就是分离的第一阶段(引擎与用户界面)。在这一点上,什么类型的后端驱动渲染都无关紧要。这是有趣的部分:就我而言,成功实现此功能的唯一语言是Java。模板必须遵循以下原则:

    一般:
    template<typename Engine>
    class TContainer : public TContainerBase<Parent> {
        void AddElement(TElementBase<Parent>* element) {
            // ...
        }
    };
    
    template<typename Engine>
    class TElement : public TElementBase<Parent> {
        void DoSomething() {
            // ...
        }
    };
    

    3. UI需要能够仅接受TContainersTElements
    也就是说,将不得不忽略这些元素的来源。那是分离的第二阶段。毕竟,它关心的只是TElementBaseTContainerBase接口(interface)。在Java中已通过引入问号解决了。就我而言,我可以在UI中简单使用:
    TContainer<?> some_container;
    TElement<?> some_element;
    container.AddElement(&element);
    

    vtable中的虚拟函数调用没有问题,因为它们正是编译器期望的位置。唯一的问题是在这两种情况下确保模板参数相同。假设后端是单个库-可以正常工作。

    以上三个步骤使我可以完全(和安全地)不考虑后端地编写代码,而后端可以实现几乎任何需要的东西。

    我尝试了这种方法,结果变得非常理智。唯一的限制是编译器。实例化类并在此处来回转换它们是违反直觉的,但不幸的是,这是必需的,主要是因为使用模板继承,您不能仅提取基类本身,也就是说,您不能说以下任何一种:
    class IContainerBase {};
    
    template <typename Parent>
    class TContainerBase : public (IContainerBase : public Parent) {}
    

    也不
    class IContainerBase {};
    template <typename Parent>
    typedef class IContainerBase : public Parent TContainerBase;
    

    (请注意,在上述所有解决方案中,仅依靠TElementBaseTContainerBase感觉非常自然和理智-如果将TElementBase<Foo>转换为TElementBase<Bar>,则生成的代码可以很好地工作-因此,这只是语言限制)。

    无论如何,这些最终语句(从B继承的A类的typedef和从B继承的基类A的X类的typedef)只是C++中的垃圾(并且会使该语言变得比原来更难),因此,唯一的出路是遵循我非常感谢提供的解决方案之一。

    多谢您的协助。

    最佳答案

    您正在尝试在此处使用“对象定向”。 OO有一种实现通用代码的特殊方法:按类型擦除。 IWindow基类接口(interface)删除确切的类型,在您的示例中为QtWindow。在C++中,您可以通过RTTI获取一些已删除的类型信息,即dynamic_cast

    但是,在C++中,您也可以使用模板。不实现IWindowQtWindow,但实现Window<Qt>。这使您可以声明Container<Foo>接受任何可能的Window<Foo>窗口库的Foo。编译器将强制执行此操作。

    关于c++ - 真正允许在C++中将实现与用户隔离的数据抽象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33250148/

    相关文章:

    java - 抽象如何帮助隐藏 Java 中的实现细节?

    c++ - C/C++ - 从封闭类继承的嵌套类

    c++ - 不同平台/系统中限定符大小的变化

    c++ - 从 Ignite 读取 Cassandra 的 Blob

    PHP:我应该传入并返回这些变量吗?

    Python 列表抽象

    c# - 如何编写具有相同方法和继承的 2 个类?

    c++ - 在 STL 容器上向前迭代然后向后迭代

    c# - Windows Mobile 6.0 蓝牙串口掉电/挂起后保活

    javascript - CommonJS 中的 'promise' 抽象有什么好处?