零零碎碎 -- 继承与代理
2013-03-06 15:34 robturtle 阅读(264) 评论(0) 收藏 举报继上次尝试用 boost::any 容纳窗口部件失败后,使用了类的继承特性实现,这是昨天初始版本的一个快照:
ifndef WINDOWCOMPONENT_HPP
#define WINDOWCOMPONENT_HPP
#include <string>
#include <memory>
#include <list>
using std::string;
using std::shared_ptr;
using std::list;
#include "cppcommon.hpp"
using namespace cliout;
namespace cvoo { // OpenCV with OO window classes
// A abstract of Window's components (e.g., trackbar, scrollbar)
// Window's handler will show it or not according to needShowed()
class WindowComponent {
public:
// notify window's handler create its resources
virtual void Create(int flag = 0) = 0;
// Visibility operations (implemented already)
void Visible(bool stat)
{ showed = stat; }
bool needShowed() const
{ return showed; }
void FlipVisible()
{ showed = !showed; }
WindowComponent() {}
virtual ~WindowComponent() {}
protected:
// It's weired if you create a component to a window
// and it doesn't show up by default
bool showed = true;
};
// Named Window is identified by its name
class NamedWindowComponent: public WindowComponent {
public:
// if window with this name existed, create the component on it
// Otherwise, it may create a new window (at OpenCV2/highgui)
// or DO NOTHING (other name-based GUI)
virtual void Create(const string & windowName, int flag = 0) = 0;
const string & GetName() const {}
NamedWindowComponent() {}
virtual ~NamedWindowComponent() {}
};
} // namespace
#endif /*WINDOWCOMPONENT_HPP*/
然后窗口就可以用一个容器储存和管理其上的部件。这个时候问题来了,目前的设计没有办法支持对组件的分组操作。比如在播放器加入高斯滤波功能,并提供3个滚动条调整滤波参数,那么很自然地,用户一定会希望打开/关闭滤波功能时,这三个滚动条也同时显示或消失。为了实现这个功能,我想到了《C++沉思录》里的“代理”模式:用被代理类实现“节点”语义,用代理类实现“边”语义。大致构思如下:
class ComponentHandler;
class WindowComponent {
public:
WindowComponent() {}
virtual ~WindowComponent() {}
void foo()
{
foo_self();
}
protected:
virtual void foo_self() = 0;
};
class ComponentGroup: public WindowComponent {
public:
ComponentGroup()
// intialize sub_components_
{}
virtual ~ComponentGroup() {}
void foo()
{
foo_self();
sub_components_->foo();
}
protected:
shared_ptr<ComponentHandler>
sub_components_;
};
class ComponentHandler {
public:
using item_t = shared_ptr<WindowComponent>;
using container_t = std::container<item_t>;
// for Example: std::container = std::map<string, item_t>;
void foo()
{
for (auto sp : components_)
sp->foo(); // if container == map, than it will be sp.second->foo()
}
protected:
container_t components_;
}
如此这般,只要在窗口内声明一个 ComponentHandler, 就可以创建出无限复杂的部件包含关系了。
第一次尝试失败了,因为我想把没有 ID 参数的 WindowComponent 和以字符串名字为 ID 的 NamedWindowComponent 统一在一起。这在写它们的句柄类的时候遇到了麻烦,我无法用统一的容器装载它们。然后我又一次尝试使用 boost::any ,很可惜的,std::map<boost::any, content_t> 却无法通过编译,似乎any不是一个可以比较的类。
第二次尝试化繁为简,因为这个类是用在 cv::namedWindow 的包装上的,所以干脆只考虑以字符串为ID这一种情况。然后,很快就写出了如下的内容:
WindowComponent.hpp
#ifndef WINDOWCOMPONENT_HPP
#define WINDOWCOMPONENT_HPP
#include <string>
#include <memory>
#include <map>
using std::string;
using std::shared_ptr;
using std::make_shared;
namespace cvoo {
class NamedWindowComponent {
protected:
// You created it, you showed it.
bool showed = true;
public:
NamedWindowComponent() {}
virtual ~NamedWindowComponent() {}
virtual void Create(const string & windowName, int flag = 0)
{ CreateSelf(windowName, flag); }
virtual const string & GetName() const = 0;
// Visibility control
void Visible(bool stat)
{ showed = stat; }
void FlipVisibility()
{ showed = !showed; }
bool needShowed()
{ return showed; }
protected:
virtual void CreateSelf(const string & windowName,
int flag = 0) = 0;
};
class NamedWindowHandler {
public:
using item_t = shared_ptr<NamedWindowComponent>;
using container_t = std::map<string, item_t>;
protected:
container_t components_;
public:
void AddComponent(item_t comp)
{ components_[comp->GetName()] = comp; }
virtual void Create(const string & windowName, int flag = 0)
{
if (components_.size() == 0) return;
for (auto pr : components_)
pr.second->Create(windowName, flag);
}
};
class NamedComponentGroup: public NamedWindowComponent {
public:
using item_t = shared_ptr<NamedWindowComponent>;
protected:
shared_ptr<NamedWindowHandler> sub_components_;
public:
NamedComponentGroup()
: sub_components_(make_shared<NamedWindowHandler>())
{}
virtual ~NamedComponentGroup() {}
virtual void Create(const string & windowName, int flag = 0)
{
CreateSelf(windowName, flag);
sub_components_->Create(windowName, flag);
}
void AddComponent(item_t comp)
{ sub_components_->AddComponent(comp); }
};
} // namespace
#endif /*WINDOWCOMPONENT_HPP*/
然后用上面的声明包装 cv::namedWindow 和 cv::trackbar:
NamedWindow.hpp
#ifndef NAMEDWINDOW_HPP
#define NAMEDWINDOW_HPP
#include "opencv2/highgui/highgui.hpp"
#include "WindowComponent.hpp"
namespace cvoo {
class NamedWindow: public NamedComponentGroup {
protected:
string win_name_;
int flag_;
public:
NamedWindow(const string & windowName = "",
int flag = 0)
: win_name_(windowName)
, flag_(flag)
{}
virtual ~NamedWindow()
{ cv::destroyWindow(win_name_); }
const string & GetName() const
{ return win_name_; }
// Special methods
inline void Show()
{
Visible(true);
Create(win_name_, flag_);
}
inline void Redraw()
{ Create(win_name_, flag_); }
inline void Hide()
{
Visible(false);
cv::destroyWindow(win_name_);
}
void Rename(const string & newName)
{
if (newName != win_name_) {
cv::destroyWindow(win_name_);
win_name_ = newName;
Create(win_name_, flag_);
}
}
void SetFlags(int flag)
{ flag_ = flag; }
// Image Displayer
void Display(const cv::Mat & img)
{ cv::imshow(win_name_, img); }
protected:
void CreateSelf(const string & windowName, int flag = 0)
{
cv::destroyWindow(win_name_);
win_name_ = windowName;
flag_ = flag;
if (needShowed()) {
cv::namedWindow(windowName, flag);
}
}
};
} // namespace
#endif /*NAMEDWINDOW_HPP*/
Trackbar.hpp
#ifndef TRACKBAR_HPP
#define TRACKBAR_HPP
#include "WindowComponent.hpp"
#include "opencv2/highgui/highgui.hpp"
namespace cvoo {
class Trackbar: public NamedWindowComponent {
public:
using callback_t = void (*)(int, void*);
using callback_data_t = void *;
private:
string name_;
string win_name_cache_ = "";
int * ptr_pos_;
int max_pos_;
callback_t callback_;
callback_data_t data_;
public:
Trackbar(const string & trackbarName,
int * pPos,
int maxPos,
callback_t callback = 0,
callback_data_t callback_data = 0)
: name_(trackbarName)
, ptr_pos_(pPos)
, max_pos_(maxPos)
, callback_(callback)
, data_(callback_data)
{}
~Trackbar() {} // no derived
const string & GetName() const
{ return name_; }
// Special methods
void SetCallback(callback_t callback, callback_data_t data)
{
callback_ = callback;
data_ = data;
if (!win_name_cache_.empty()) {
// because flag can only influence sub components
// so it's no need to store or pass flags
CreateSelf(win_name_cache_);
}
}
// Other Accessors
const string & GetWindowName() const
{ return win_name_cache_; }
int GetPos() const
{ return *ptr_pos_; }
int GetMaxPos() const
{ return max_pos_; }
callback_t GetCallback() const
{ return callback_; }
callback_data_t GetCallbackData() const
{ return data_; }
protected:
void CreateSelf(const string & windowName, int flag = 0)
{
win_name_cache_ = windowName;
if (needShowed()) {
cv::createTrackbar(name_, windowName,
ptr_pos_,
max_pos_,
callback_,
data_);
}
if (callback_)
callback_(GetPos(), data_);
}
};
} // namespace
#endif /*TRACKBAR_HPP*/
最后赞美一下 Boost 的 unit test framework, 上面有好些缺陷都是在做单元测试时找到的,比如在重新创建滚动条后立刻调用回调函数等等。
浙公网安备 33010602011771号