DirectUI笔记(二)模块化

说到软件,“模块”无法绕过去的一个概念一个名词。我们在很多时候不同的场合会经常听到或说到这个词。在目前的软件开发中,软件模块的形式多种多样,比如一个MFC程序里,可能一个对话框或一个View就是可以定义为一个模块,C++中的一个类也可以是一个模块。

软件模块不外乎几个作用,一个是完成一定的功能,这也是模块出现和存在最根本原因;一个是应对变化,软件做好以后一般来说是不可避免的药变化的,这个变化可能是需求推动的,也可能是内部业务升级推动的,总之,你需要拥抱变化。当变化来临的时候,需要做到修改尽量的少,影响范围尽可能的窄,这就是划分软件模块的目标;最后一个我认为是团队协作、维护沟通的需要,试想,将软件切成一块一块的,不同的人专注特定的块,效率很高,进行维护的时候,同样不会写入无穷的代码海洋中。

说了这么多,回到DirectUI来,先说说界面描述如何模块化:
之前说到,DirectUI一般来说使用xml文件来描述文件(当然,你也可以使用任何的其它格式描述,主要你能解析它就可以了),xml使用标签来组织,相对来说,阅读性还好。但功能稍微丰富一点的软件,其界面元素也肯定较多,xml组织起来可能也是长篇大论了;另一方面,任何的界面,其实都有其“扎堆”的特性,像写文章以段落组织一样,DirectUI解决Java SWT的思想,支持以Layout和Container的思想组织界面。这样,一个xml文件就可以是若干个Layout或Container组成。我们进一步的改进这个机制,不同的Layout或Container放在不同的xml文件中,最后一个主框架的xml来组织,这样,我们就可以多个人来做这个界面描述,只要遵循一定的约定。

DirectUI影响力最大的莫过于viksoe,正是他的一片文章和一个示例代码促成了多个DirectUI库的出现。在其示例代码中,我们也可以看到类xml格式的界面描述,其界面是多个tab页面,他的做法是每个tab一个描述,但有个确定,每次都重新生成,切换tab后,其内容不能保存,看来,框架xml描述是必须的。

下面用一张图片来描述如何进行界面描述的模块划分:
DirectUI笔记(二)模块化 - sogoodlife - sogoodlife的博客
 



说完了界面描述的模块划分,再说说如何进行界面处理的模块划分。

DirectUI使用Notify机制(可以理解为观察者模式),渲染引擎以及系统发送消息,我们的代码使用AddNotify来注册是否关注这些消息。很多时候,我们是通过消息源头的控件名称来判断该如何处理的,这所有的控件,虽然描述上是分了模块,但其实都是建立在同一个WindowFrame中,在WindowFrame中调用AddNotify注册,那么,所有控件的消息反馈都会在WindowFrame里出现。最原始的做法是在WindowFrame的Notify处理中处理所有的控件消息。代码如下:

    LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
    {
        LONG styleValue = ::GetWindowLong(*this, GWL_STYLE);
        styleValue &= ~WS_CAPTION;
        ::SetWindowLong(*this, GWL_STYLE, styleValue | WS_CLIPSIBLINGS | WS_CLIPCHILDREN);
        m_pm.Init(m_hWnd);
        CDialogBuilder builder;
        CDialogBuilderCallbackEx cb;
        CControlUI* pRoot = builder.Create(_T("main.xml"), (UINT)0, &cb, &m_pm);
        ASSERT(pRoot && "Failed to parse XML");
        m_pm.AttachDialog(pRoot);
        m_pm.AddNotifier(this);//注册
        Init();
        return 0;
    }

然后是对消息的处理:

    void Notify(TNotifyUI& msg)
    {
        if( msg.sType == _T("windowinit") ) 
{
//.....
}
        else if( msg.sType == _T("click") )
 {
}
else if 
{
//.....
}
else if
{
//.....
}
}

但,这明显是与我们的模块化思想相背离的,也不符合软件可维护性、可阅读性等方面的要求。

其实,只要稍作改进即可,在回调函数生成内嵌模块中加入Notify的处理:

CPaintManagerUI* g_PM = NULL;
class SubUI : public CContainerUI, public INotifyUI
{
public:

SubUI() 
{
CDialogBuilder builder;
CContainerUI* pSub = static_cast<CContainerUI*>(builder.Create(_T("sub.xml"), (UINT)0, NULL, g_PM));
if( pSub ) {
this->Add(pDesk);
g_PM->AddNotifier(this);
}
}

void Notify(TNotifyUI& msg) //处理内嵌模块的消息
{
CStdString name = msg.pSender->GetName();
if( msg.sType == _T("click") ) 
{
if ( name == _T("TestBtn") )
{
MessageBox(NULL, _T(":Sub:您点击了测试按钮"), _T("按钮例子"), MB_OK);
}
}
}
}


class CDialogBuilderCallbackEx : public IDialogBuilderCallback
{
public:
    CControlUI* CreateControl(LPCTSTR pstrClass) 
    {
        if( _tcscmp(pstrClass, _T("SubUI")) == 0 ) 
return new SubUI;

        return NULL;
    }
};

需要在主框架创建(OnCreate)中加入g_PM = &m_pm;赋值语句。


这样,不管我们的界面有多负责,我们都可以做到界面描述和界面处理的模块化了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值