我有以下问题: 我希望有多个Windows都显示某种功能图。假设一个窗口应显示坐标轴并具有一些交互功能,另一个窗口也应显示坐标轴并具有设置对话框。
我是如何直观地实现这一点的,是将Window
实现为使用我的框架(在我的案例中为VTK)执行基本窗口初始化的一些类,一些虚拟继承类WindowWithAxis
, WindowInteracting
,WindowWithSettingsDialog
,然后
A
继承WindowInteracting
和WindowCoordinateAxis
B
继承WindowWithSettingsDialog
和WindowCoordinateAxis
因此,通过互联网阅读我现在到处都可以看到(例如Google风格指南),我应该Window
,WindowInteracting
,WindowCoordinateAxis
,{{1}都是纯抽象类/接口。
这是我不明白的。这是否意味着我不允许实际实现这些类,并且我必须在类WindowWithSettingsDialog
和类{{1}中实现Window
和WindowCoordinateAxis
的所有功能(以及使用这些的所有其他类)?这对我来说似乎不是一个干净的解决方案。
这种“菱形继承”应该是一个经常出现的问题,那么我可以通过什么方式来解决这个问题呢?
答案 0 :(得分:0)
一般原则是避免重复代码/功能并提高可重用性。在您的情况下,WindowInteracting
和WindowCoordinateAxis
类都具有相同的功能(来自Window)。相反,要么定义一个名为IInteracting
和IHasTheCoordinateAxis
的接口(具有纯虚函数的类)并提供它们的具体实现,或者根据传递的参数使您的Window
类行为不同
答案 1 :(得分:0)
我想我会采用以下方式:
定义一个多态Window基类,它是处理UI事件的接口。
从中导出一个WindowImpl,它是使用Feature类列表进行模板化的。每个功能都可以选择是否响应事件。
WindowImpl通过枚举一组特征来调用每个Feature对象上的事件代码。功能可能会保持自己的状态。
e.g:
#include <utility>
#include <tuple>
#include <iostream>
/// Basic concept of a window that handles events
struct Window
{
Window(std::string title)
: title_(std::move(title)) {}
// all windows handle clicks, but defer to a private polymorphic implementation
void onClickOrWhatever()
{
handleOnClickOrWhatever();
}
// all windows have a title
auto title() const -> const std::string&
{
return title_;
}
private:
// make ploymorphism an implementation detail
virtual void handleOnClickOrWhatever() = 0;
std::string title_;
};
// All concrete windows are a WindowImpl customised with a feature list
template<class...Features>
struct WindowImpl
: Window
{
static constexpr auto FeatureCount = sizeof...(Features);
using Window::Window;
private:
// the onClick handler defers to actions specified by each enabled feature
void handleOnClickOrWhatever() override
{
std::cout << title() << " detects a click (or whatever)\n";
implHandleOnClickOrWhatever(features_,
std::make_index_sequence<FeatureCount>());
std::cout << std::endl;
}
template<class FeatureTuple, std::size_t...Is>
void implHandleOnClickOrWhatever(FeatureTuple&& features, std::index_sequence<Is...>)
{
using expand = int[];
void(expand{0,
(std::get<Is>(features).handleClick(*this), 0)...
});
}
using FeatureTuple = std::tuple<Features...>;
FeatureTuple features_;
};
// a base feature that takes no action
struct BaseFeature
{
template<class Window>
static void handleClick(Window& window)
{
// don't respond
}
};
//
// some features
//
struct HasAxis
: BaseFeature
{
template<class Window>
static void handleClick(Window& window)
{
std::cout << "testing touch on axis" << std::endl;
}
};
struct HasSettingsDialog
: BaseFeature
{
template<class Window>
static void handleClick(Window& window)
{
std::cout << "testing touch on settings button" << std::endl;
}
};
struct HasAlphaBackground
: BaseFeature
{
// note : does not handle clicks
};
// some customised windows, mixing anf matching features
using WindowWithAxis = WindowImpl<HasAxis>;
using WindowWithAxisAndSettings = WindowImpl<HasAxis, HasSettingsDialog>;
using WindowWithAxisAndAlpha = WindowImpl<HasAxis, HasAlphaBackground>;
int main()
{
WindowWithAxis w{"window 1"};
w.onClickOrWhatever();
WindowWithAxisAndSettings w2{"window 2"};
w2.onClickOrWhatever();
WindowWithAxisAndAlpha w3{"window 3"};
w3.onClickOrWhatever();
}
预期产出:
window 1 detects a click (or whatever)
testing touch on axis
window 2 detects a click (or whatever)
testing touch on axis
testing touch on settings button
window 3 detects a click (or whatever)
testing touch on axis