The clone method is widely used for user-defined renderers in Nana C++ library.
Motivation
A widget is a mini framewark providing a general method for users defining their own renderer. A common way looks like this.
class button_renderer
{
public:
virtual ~button_renderer() = 0;
virtual void render() = 0;
};class button
{
public:
button()
: renderer_(new def_renderer) //class def_renderer: public button_renderer for default
{}~button()
{
delete renderer_;
}void set_user_renderer(button_renderer * r)
{
delete renderer_;
renderer_ = r;
}
private:
void _m_render()
{
renderer_->render();
}
private:
button_renderer * renderer_;
};
This is a typecial usage of strategy pattern that user can write a subclass from button_renderer. In this piece of code, programmers have to be faced with the hidden rule, the renderer object must to be allocated by new operator, it is invisible, not clear. for example.
user_def_renderer renderer;
button.set_user_renderer(&renderer);
button refers to a renderer which will be destroyed immediately. and the renderer object is not deleteable. there are two serious error.
and
user_def_renderer * r = new user_def_renderer;
button.set_user_renderer(r);
This will confuse programmers whether should delete pointee r after button is destroyed, if programmers don’t know what the button’s destructor to do.
A clone method can avoid these problems.
class button_renderer
{
public:
virtual ~button_renderer() = 0;
virtual void render() = 0;
virtual button_renderer * clone() const = 0;
};class button
{
public:
void set_user_renderer(const button_renderer& r)
{
delete renderer_;
renderer_ = r.clone();
}
private:
button_renderer * renderer_;
};
the difference between two solutions is that button::renderer_ refers to duplicate renderer, not directly to user passed object. The clone method is used to avoid getting involved with the renderer object given by user, and const-reference of the parameter type of set_user_renderer is clear to programmer.
The Clone Method in Nana C++ Library
When a programmer wants to define an own renderer of slider, he have to write a subclass inherited from nana::gui::slider::renderer.
class my_slider_renderer: public nana::gui::slider::renderer
{
virtual void background(nana::gui::window, graph_reference, bool isglass);
virtual void adorn(nana::gui::window, graph_reference, const adorn_t&);
virtual void adorn_textbox(nana::gui::window, graph_reference, const nana::string&, const nana::rectangle&);
virtual void bar(nana::gui::window, graph_reference, const bar_t&);
virtual void slider(nana::gui::window, graph_reference, const slider_t&);
};
Then, set the renderer object for slider.
nana::gui::slider::renderer_cloneable<my_slider_renderer> rnd;
slider.ext_renderer(rnd);
or in one statement;
slider.ext_renderer(nana::gui::slider::renderer_cloneable<my_slider_renderer>());
When a programmer wants to extend the renderer that slider is in use, he should,
class my_extend_renderer: public nana::gui::slider::renderer
{
public:
my_extend_renderer(nana::pat::cloneable_interface<renderer>& reuse)
: reuse_(reuse.clone())
{
}my_extend_renderer(const my_extend_renderer& rhs)
: reuse_(rhs.reuse_->clone())
{}~my_extend_renderer()
{
reuse_->self_delete();
}private:
virtual void background(nana::gui::window wd, graph_reference graph, bool isglass);
{
reuse_->refer().background(wd, graph, isglass);
//draw what i want.
}
virtual void adorn(nana::gui::window wd, graph_reference graph, const adorn_t& ad)
{
reuse_->refer().adorn(wd, graph, ad);
//draw what i want.
}virtual void adorn_textbox(nana::gui::window, graph_reference, const nana::string&, const nana::rectangle&);
virtual void bar(nana::gui::window, graph_reference, const bar_t&);
virtual void slider(nana::gui::window, graph_reference, const slider_t&);
private:
nana::pat::cloneable_interface<renderer> * reuse_;};
nana::gui::slider::renderer_cloneable<my_extend_renderer> rnd(slider.ext_renderer());
slider.ext_renderer(rnd);
In this piece of code, there is not a interface named clone in the interface class renderer, it avoid programmers getting involved with implementing for clone.