【大話QT之四】ctkPlugin插件系統實現項目插件式開發

插件式開發體會:

        自開始寫【大話QT】系列就開始接觸渲染客戶端的開發,說是開發不如更多的說是維護以及重構,在接手這塊的東西之前自己還有點猶豫,因爲之前我一直認爲客戶端嘛,沒什麼技術含量,總是想做比較有挑戰性的,爲了這周總還專門找我談了談,算是“安撫”民心吧。正式談話過後,我才決定接手渲染客戶端的開發。

        渲染客戶端的所有構成均是採用開源框架拼湊起來的整體,細分它的組成大致包含以下開源模塊,簡單描述:

        1> CTKPlugin插件系統框架。負責整個項目的架構,決定了項目採用插件形式開發維護。

        2> Google protocol buffer。負責定義項目的通信協議,它是google內部使用的協議架構,最大的優點是:實現高效,向下兼容的通信協議。

        3> Zeromq框架:負責項目中的網絡通信,用於高性能網絡編程。

        4> 日誌系統。負責項目中所有日誌的輸出。

        其中,最爲關鍵的就是CTKPlugin插件系統,它決定了項目的整體架構——採用插件式開發。經過這麼多天的維護開發也深深的感受到這種插件式開發的方式帶來的好處。以前,總是從課本上讀到所謂的理想的“熱插拔”式的插件開發,而我總是不以爲然,我的意識裏一個項目的開發多多少少都是臃腫的,在使用了這種插件式的開發方式後,突然感覺軟件的開發、維護、升級變得很容易,下面說一下我體會到的幾點好處:

        1. 開發工作由之前的人等人變爲並行開發。項目中插件系統分爲兩大部分:基礎插件與應用插件,基礎插件即通用插件,在其它插件系統中都要使用到的,比如:日誌插件在每個其它插件中都會被使用;而應用插件之間則是相互獨立的,比如:登錄插件、文件管理插件等。基礎插件一般是一些開源庫,只需要我們編譯出來使用即可,基本不需要我們自行開發;而應用插件功能的獨立性決定了它們之間不會相互調用(業務整合插件除外),這樣多個人員就可以獨立開發,每個人負責一個獨立的插件,項目進度會大大加快、週期縮短。

        2. 測試案例容易編寫,插件功能很方便得到驗證。在一個插件的初始版本完成後,可以很方便的編寫測試用例,來驗證插件提供的功能性。由於插件系統最終提供的是動態鏈接庫dll(windows下),而測試用例則可以建立爲應用工程或界面工程,提供程序入口,加載調用插件中提供的方法。而且,測試用例可以保存在項目中(不會最終發佈,最終發佈的是插件的dll),如果將來插件使用出現問題,或者需要添加其它功能,或者升級均可以利用測試案例重新快速測試驗證。

       3. 系統業務邏輯變得異常清晰。如果項目不採用插件方式開發,每個功能均會雜糅在一起,無論是開發人員或者將來加入到項目開發的人都無法很快的瞭解業務流程,在分析這個功能的時候又涉及到那個功能。而採用插件式開發方式則每個業務邏輯很清晰明瞭,如果將來要調試3dmax的渲染模塊,那麼只需要閱讀3dmax渲染插件就可以了,而且結合測試案例,很容易就可以上手。

        上面就是這段時間以來針對項目採用插件系統開發的幾點體會。

CTKPlugin插件系統介紹:

                                            

        在CTKPlugin插件系統中要清晰地理解一個概念:插件是以服務的方式提供功能。每一個插件都有它的生命週期,在插件初始化的時候它會將自己的唯一實例註冊到插件系統中作爲服務提供,即上圖中的register階段。而當另一個插件需要使用到該插件提供的服務的時候就需要通過getService的方式獲取。下面通過代碼簡單說明一下一個插件是如何想CTKPlugin系統中註冊服務以及其它插件是如何使用該服務的:

        1.  插件服務註冊

              每一個插件的實現都必須實現一個插件的聲明週期類,它繼承自CTKPlugin中的ctkPluginActivator,在ctkPluginActivator中定義了start與stop虛函數,插件的聲明週期類必須要實現start與stop,實現服務的註冊。        

class LHAuthPlugin : public QObject, public ctkPluginActivator
{
    Q_OBJECT
    Q_INTERFACES(ctkPluginActivator)

public:
    void start(ctkPluginContext *Context);
    void stop(ctkPluginContext *Context);

private:
    LHAuth *m_Auth;
};
      實現類:

void LHAuthPlugin::start(ctkPluginContext *Context)
{
    m_Auth = new LHAuth();
    Context->registerService(QStringList("LHAuthInterface"), m_Auth);
}

void LHAuthPlugin::stop(ctkPluginContext *Context)
{
    Q_UNUSED(Context)
    if (m_Auth)
    {
        delete m_Auth;
        m_Auth = 0;
    }
}
        其中:registerService即向CTKPlugin插件系統中註冊該插件的唯一實例,而stop則是插件聲明週期的終止。

        2. 使用其它插件提供的服務

        在其它模塊中如果想使用登錄認證插件,則在其它模塊的Init階段完成登錄認證模塊的加載,並完成初始化的功能:

        //! 初始化登錄模塊
        ctkServiceReference refAuth= d->m_PluginContext->getServiceReference("LHAuthInterface");
        d->m_AuthInterface = (qobject_cast<LHAuthInterface *>(d->m_PluginContext->getService(refAuth)));
        if (!d->m_AuthInterface ||
                (d->m_AuthInterface->Init(d->m_Parameters) != LH_SUCCESS) ||
                (d->m_AuthInterface->CreateInstance(varInstance, d->m_Parameters) != LH_SUCCESS))
        {
            qDebug()<<QObject::tr("Module %1 is invalid").arg("com.lht.auth");
            return LH_FAILURE;
        }
        else
            d->m_nAuthInstance = varInstance.toInt();
        getServiceReference()即在CTKPlugin插件系統中獲取LHAuthInterface服務。在初始化完成之後,就可以利用m_AuthInterface->Login()來使用登錄認證插件提供的功能了。

項目如何使用插件式開發:

       

       如上圖所示,只是我這個項目本身實現插件系統功能的一個基本架構,相信不同的人使用會探索出更加有效,更加方便的使用方式。

       每個項目都會有它的入口,我們不妨稱之爲portal,在portal中實現的功能很簡單,最主要的就是完成CTKPlugin系統的初始化工作,待ctkplugin初始化完成之後首先加載lht_controller插件,lht_controller插件是很重要的一個插件,它主要負責完成其它所有應用插件的加載工作,如上圖所示,它加載了lht_login登錄插件、lht_mayaMaya渲染插件、lht_log日誌插件、lht_goldenfarm渲染客戶端插件(業務邏輯插件),然後執行業務邏輯插件,即lht_goldenfarm,而在lht_goldenfarm中根據業務邏輯實現不同的功能,調用不同的插件。

        看上面的架構,很清晰明瞭,對於系統的維護很方便、容易。

本章總結:

        好了,以上就是我這段時間開發收穫到的東西,很多東西都是我以前開發中不注意的,現在慢慢當成規則嚴格要求自己,爭取讓自己的開發更加規範。

        只有不斷總結才能不斷進步,還是驗證了周總的那句話:“還是太年輕啊!!”。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章