设计模式本来就是一些成熟的使用方法,它的方法性和思想超越了具体的语言,在编码中,不是刻意而为的,而是先领会了才能使用,特别是一些模式的混合使用。
比如下面的代码就用到了观察者模式和访问者模式。
mdl的新型API中用了很多的设计模式,比如mdl中C++中的回调基本上多采用观察者模式将用户自定义事件插入到系统的回调函数列表里,比如视图监视器等。。。
#include "stdafx.h"
#include <vector>
#include <string>
#include <list>
#include <stdexcept>
#include <iostream>
#include "temp.h"
#include <boost/smart_ptr.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
class Element;
class Visitor
{
public:
virtual void visit (Element* el) = 0;
};
class Element
{
public:
virtual void accept (Visitor* visit) = 0;
};
class employee : public Element
{
public:
employee(std::string name):_name(name){}
virtual void accept (Visitor* vis)
{
vis->visit (this);
}
std::string getname()
{
return _name;
}
private:
std::string _name;
};
class VisitorA : public Visitor
{
public:
virtual void visit (Element* el)
{
employee* ep = dynamic_cast<employee*>(el);
if(ep)
{
std::cout << "VisitorA : " << ep->getname ( ) << std::endl;
}
}
};
class VisitorB : public Visitor
{
public:
virtual void visit (Element* el)
{
employee* ep = dynamic_cast<employee*>(el);
if (ep)
{
std::cout << "VisitorB : " << ep->getname ( ) << std::endl;
}
}
};
class employees
{
std::list<employee*> l_emp;
public:
void Attach(employee* emp)
{
l_emp.push_back (emp);
}
void Detach(const employee* emp)
{
l_emp.remove (const_cast<employee*>(emp));
}
void accept(Visitor* vis)
{
using namespace boost::lambda;
std::for_each (l_emp.begin ( ), l_emp.end ( ), bind (&Visitor::visit,vis,_1));
}
};
int _tmain(int argc, _TCHAR* argv[])
{
employees emps;
typedef boost::shared_ptr<employee> employeePtr;
typedef boost::shared_ptr<Visitor> visitorPtr;
employeePtr emp1 = boost::make_shared<employee>("kangshifu");
employeePtr emp2 = boost::make_shared<employee>("lishifu");
emps.Attach (emp1.get());
emps.Attach (emp2.get());
visitorPtr va = boost::make_shared<VisitorA> ( );
visitorPtr vb = boost::make_shared<VisitorB> ( );
emps.accept (va.get());
emps.accept (vb.get());
std::cout << "-----------------------------" << std::endl;
emps.Detach (emp1.get ( ));
emps.accept (va.get ( ));
emps.accept (vb.get ( ));
return 0;
}
现在,我们看一个mdl中的一个实例,这个实例用到了观察者模式和单例模式。
(为了让代码更紧凑,使用了几个Boost组件,但可能增加了阅读的难度)
在mdl中可以用mdlLocate_setFunction挂一个用户函数来响应当选择集的变化event。但可惜的只能使用一个,如果以后想增加更多的事件或者改成另外的事件呢,就的重写这个函数,造成扩展性差,耦合度高。用观察者模式就解决了这个问题.
#include "SelectTool.h"
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <MicroStationAPI.h>
#include <list>
#include <algorithm>
#include <boost/tuple/tuple.hpp>
using namespace Bentley::Ustn;
using namespace Bentley::Ustn::Element;
class eventOnSelectChg
{
public:
virtual void _OnChanged ( ) = 0;
};
class selecttool
{
selecttool ( ) {};
~selecttool ( ) {};
public:
static selecttool Intance;
static StatusInt select_callbk
(
SelectionSetAction* action,
UInt32 filePos,
DgnModelRefP modelRef
)
{
if(*action == SELECT_SS_CHANGED)
{
using namespace boost::lambda;
std::for_each (Intance.m_evlist.begin ( ),
Intance.m_evlist.end ( ),
bind (&eventOnSelectChg::_OnChanged, _1));
}
return SUCCESS;
}
void install()
{
mdlLocate_setFunction (LOCATE_SELECT_CMD, selecttool::select_callbk);
}
void Attach(eventOnSelectChg* op)
{
m_evlist.push_back (op);
}
void Detach(eventOnSelectChg* op)
{
m_evlist.remove (op);
}
private:
typedef std::list<eventOnSelectChg*> EventList;
EventList m_evlist;
};
selecttool selecttool::Intance;
boost::tuple<ElementId,int> GetFirstEle()
{
ISelectToolR tool = ISelectTool::GetTool ( );
if (ISelectTool::GetTool ( ).IsActive ( ))
{
ElementAgenda agd;
ISelectTool::GetTool ( ).BuildAgenda (agd);
ElemAgendaEntry* entry = agd.GetFirstP ( );
ElementId id = entry->GetElemRef ( )->GetElemID ( );
int type = entry->GetElemRef ( )->GetElemType ( );
return boost::make_tuple (id, type);
}
return boost::make_tuple (0, 0);;
}
class setwintitle : public eventOnSelectChg
{
public:
virtual void _OnChanged ( )
{
ElementId id;
int type;
boost::tie (id, type) = GetFirstEle ( );
char title[128];
sprintf_s (title, 128, "Element ID : %lld", id);
MSWindow* winp = mdlWindow_viewWindowGet (tcb->lstvw);
mdlWindow_titleSet (winp, title);
}
};
boost::shared_ptr<eventOnSelectChg> action0;
extern"C" DLLEXPORT int MdlMain(int argc,char** argv)
{
action0 = boost::make_shared<setwintitle> ( );
selecttool::Intance.Attach (action0.get ( ));
selecttool::Intance.install ( );
return EXIT_SUCCESS;
}
程序实现了一个功能,当用户选择一个元素的时候,取得这个函数的id,并显示在当前视图的title上。如果想增加一个功能,当选择元素的时候,同时还要输出一条消息,来显示它的类型呢。这仅仅需要重新写一个类,从基类继承并将目标代码写到那个唯一的虚函数里来实现它,然后将这个类实例化,再attach即可。如下:
class outputmsg : public eventOnSelectChg
{
public:
virtual void _OnChanged ( )
{
ElementId id;
int type;
boost::tie (id, type) = GetFirstEle ( );
char title[128];
sprintf_s (title, 128, "Element type : %ld", type);
mdlDialog_dmsgsPrint (title);
}
};
boost::shared_ptr<eventOnSelectChg> action0;
boost::shared_ptr<eventOnSelectChg> action1;
extern"C" DLLEXPORT int MdlMain(int argc,char** argv)
{
action0 = boost::make_shared<setwintitle> ( );
selecttool::Intance.Attach (action0.get ( ));
action1 = boost::make_shared<outputmsg> ( );
selecttool::Intance.Attach (action1.get ( ));
selecttool::Intance.install ( );
return EXIT_SUCCESS;
}
如果想在运行的时候,决定使用什么样的功能,可以用mdl的命令号给这两个类Attach或者Detach,即可获得这样的灵活性。
完美的解决方案!
理解了以上的方法,再看MDL API中的那些回调的方法为啥要你继承一个类,实现虚方法,再加入监视器。
下面的代码为简单重构以后的版本,取消的全局变量和个别的函数名:
#include "SelectTool.h"
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/foreach.hpp>
#include <boost/tuple/tuple.hpp>
#include <MicroStationAPI.h>
#include <list>
#include <algorithm>
using namespace Bentley::Ustn;
using namespace Bentley::Ustn::Element;
class ActionOnSelectChg
{
public:
virtual void _OnChanged ( ) = 0;
};
class selecttool
{
selecttool ( ) {};
~selecttool ( ) {};
public:
typedef boost::shared_ptr<ActionOnSelectChg> ActionType;
static selecttool Instance;
static StatusInt select_callbk
(
SelectionSetAction* action,
UInt32 filePos,
DgnModelRefP modelRef
)
{
if (*action == SELECT_SS_CHANGED)
{
using namespace boost::lambda;
BOOST_FOREACH (EventList::value_type& v, Instance.m_evlist)
{
v->_OnChanged ( );
}
}
return SUCCESS;
}
void install()
{
mdlLocate_setFunction (LOCATE_SELECT_CMD, selecttool::select_callbk);
}
void Attach(ActionType op)
{
Detach (op);
m_evlist.push_back (op);
}
void Detach(ActionType op)
{
m_evlist.remove (op);
}
private:
typedef std::list<ActionType> EventList;
EventList m_evlist;
};
selecttool selecttool::Instance;
boost::tuple<ElementId,int> GetFirstEle()
{
ISelectToolR tool = ISelectTool::GetTool ( );
if (ISelectTool::GetTool ( ).IsActive ( ))
{
ElementAgenda agd;
ISelectTool::GetTool ( ).BuildAgenda (agd);
ElemAgendaEntry* entry = agd.GetFirstP ( );
ElementId id = entry->GetElemRef ( )->GetElemID ( );
int type = entry->GetElemRef ( )->GetElemType ( );
return boost::make_tuple (id, type);
}
return boost::make_tuple (0, 0);;
}
class setwintitle : public ActionOnSelectChg
{
public:
virtual void _OnChanged ( )
{
ElementId id;
boost::tie (id, boost::tuples::ignore) = GetFirstEle ( );
char title[128];
sprintf_s (title, 128, "Element ID : %lld", id);
MSWindow* winp = mdlWindow_viewWindowGet (tcb->lstvw);
mdlWindow_titleSet (winp, title);
}
};
class outputmsg : public ActionOnSelectChg
{
public:
virtual void _OnChanged ( )
{
int type;
boost::tie (boost::tuples::ignore, type) = GetFirstEle ( );
char title[128];
sprintf_s (title, 128, "Element type : %ld", type);
mdlDialog_dmsgsPrint (title);
}
};
extern"C" DLLEXPORT int MdlMain(int argc,char** argv)
{
selecttool::ActionType action0 = boost::make_shared<setwintitle> ( );
selecttool::Instance.Attach (action0);
selecttool::ActionType action1 = boost::make_shared<outputmsg> ( );
selecttool::Instance.Attach (action1);
selecttool::Instance.install ( );
return EXIT_SUCCESS;
}