博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Symbian中所体现的软件编程艺术
阅读量:4190 次
发布时间:2019-05-26

本文共 5399 字,大约阅读时间需要 17 分钟。

Author:孙东风 2007-04-08

MVC架构 

        我们知道,在软件编写过程中一直提倡"数据"和"界面"的高度分离,Symbian中也是这么做的。

        首先,基于"传统EIKON框架"的应用程序会产生App、Document、AppUi、Container四个类,其中App是应用程序的"启动类",Document基础上没什么用处,而Symbian中大量的处理工作都放在了AppUi和Container类中。AppUi就象是一个交通枢纽负责南来北往的数据,一般来说,在Symbian的程序中都会新建一个Engine的"引擎类"来负责程序的逻辑处理,而AppUi就是负责把"引擎类"中数据的处理结果、数据的变化及时更新到Container中。

        下面是我写的一个Symbian游戏引擎中AppUi二阶段构造函数中的代码:

void CMegajoyAppUi::ConstructL()

    {
    BaseConstructL();

    iAppContainer = new (ELeave) CMegajoyContainer;

   iAppContainer->SetMopParent( this );
   iAppContainer->ConstructL( ClientRect() );
   AddToStackL( iAppContainer );

iMainEngine = new (ELeave) CMegajoyMain(this);

iLancher = CIdle::NewL( CActive::EPriorityIdle );

iLancher->Start(TCallBack(Start,this));

}

        从中可以看到,上面AppUi的二阶段构造函数中同时产生了iAppContainer和iMainEngine实例,并且我们把一个AppUi的this指针传递给了"引擎类"。我们知道,做为一种GUI程序,无非就是用户界面的交互(包括键盘、鼠标等)和引擎处理数据。而Symbian中提供给用户界面交互接口的正是AppUi类,它里面的HandleCommandL()负责处理用户的菜单操作,HandleKeyEventL()负责处理用户的键盘操作,DoExit()负责用户的退出操作等。那么,一切数据的处理和界面的显示都经过AppUi这个中枢就显得很有必要了!

        试想一下,用户按下某个键,这个键从传递到AppUi的HandleKeyEventL()函数里(当然也有可能是其上某个控件的消息响应函数中,这里忽略控件栈的讨论),而AppUi调用iMainEngine来处理这个按键数据,从而进行必要的逻辑转变,比如从一个界面跳转到另一个界面,那么iMainEngine里就会把一个全局的界面ID从一种状态转换到另一种状态,但是这种状态的切换会伴随着界面的变化,界面上也需要体现出这种变化,而界面的绘制是在iAppContainer中完成的,iAppContainer又是在AppUi中构造并初始化的。

        就是说我们的iAppContainer和iMainEngine需要一种类似通信的机制,让iAppContainer能及时的知道iMainEngine中某个全局变量状态的变化并及时做出界面上的更新。

        问题到了这里已经很明显,iAppContainer和iMainEngine都在AppUi类里,而这两个实例对象之间需要一种类似通信的机制。

        解决这种两个对象实例之间通信问题的非我们的"Observer模式"莫属!下面我们就来看看Symbian中的"Observer模式"。

Observer模式

        Observer模式提供一种类与类之间传递消息的机制,当某个事件发生或状态改变时,拥有观察者的类可以向另一个类发送某个消息,这样另一个类可以根据变化做出相应的处理。呵呵,是不是觉得简直是为了我们Symbian量身定做的一个模式?

       在我们的Symbian架构中iMainEngine中会有事件发生(因为AppUi类把事件传递给它了)或状态改变(例如全局界面ID的变化),那么我们的iMainEngine中就需要有一个"观察者"的实例以便向iAppContainer发送消息,iAppContainer可以根据变化做出相应的处理。

        这样我们就可以定义一个消息接口的类,这个类是抽象的,内部的函数也都是纯虚函数(只提供接口),下面是我写的Symbian游戏引擎中观察者接口(或者说消息接口)的定义:

class MMegajoyAction{
public:
 // Graphic functions
 virtual void FlipBackBuffer(void)=0;
 virtual void BitBlt(TInt iBltType, CExtendedBitmap &iBitmap, const TPoint &aPosition)=0;
 virtual void DoExit(void)=0;
 virtual TBool ReadIniFile(TUid iInfo, void *ptr, TUint &size)=0;
 virtual TBool CheckIniFile(TUid iInfo)=0;
 virtual void WriteIniFile(TUid iInfo, void *ptr, TUint size)=0;
 virtual void RemoveIniData(TUid iInfo)=0;
};

        因为iMainEngine和iAppContainer都在AppUi中,那么AppUi就成为它们之间消息中转的最佳选择了,让AppUi类实现这个接口MMegajoyAction,并传递AppUi的this指针到iMainEngine,而iMainEngine中也有个MMegajoyAction的实例对象 MMegajoyAction *Actions;从而当iMainEngine中有事件发生(因为AppUi类把事件传递给它了)或状态改变(例如全局界面ID的变化)时直接调用Actions传递消息到AppUi,AppUi中通过实现的具体接口再调用iAppContainer中的方法,实现了iMainEngine和iAppContainer之间的通信机制。

         如下是我写的Symbian游戏引擎中AppUi对观察者接口的实现

void CMegajoyAppUi::FlipBackBuffer(void){

 iAppContainer->FlipBackBuffer();
}

void CMegajoyAppUi::BitBlt(TInt iBltType, CExtendedBitmap &iBitmap, const TPoint &aPosition){

 switch(iBltType){
 case BLTTYPE_NORMAL:
  iAppContainer->BlitToBackBuffer(iBitmap, aPosition);
  break;
 case BLTTYPE_MASKED:
  iAppContainer->BlitToBackBufferMasked(iBitmap, aPosition);
  break;
 }
}

void CMegajoyAppUi::DoExit(void){

 Exit();
}

TBool CMegajoyAppUi::ReadIniFile(TUid iInfo, void *ptr, TUint &size){

 TInt r;
 TBool result = EFalse;
 RFs fs;
 fs.Connect();
 CleanupClosePushL( fs );
 RDictionaryReadStream rdsIniFile;
 CDictionaryStore *cdIniFile = Application()->OpenIniFileLC(fs);
 if (cdIniFile->IsPresentL(iInfo)){
  rdsIniFile.OpenLC(*cdIniFile, iInfo);
  TPtr8 buf((TUint8*)ptr, size);
  TRAP(r, rdsIniFile.ReadL(buf));
  CleanupStack::PopAndDestroy(); // rdsIniFile
  result = ETrue;
 }
 CleanupStack::PopAndDestroy( 2 ); // fs, cdIniFile
 return result;
}

TBool CMegajoyAppUi::CheckIniFile(TUid iInfo){

 TBool result = EFalse;
 RFs fs;
 fs.Connect();
 CleanupClosePushL( fs );
 RDictionaryReadStream rdsIniFile;
 CDictionaryStore *cdIniFile = Application()->OpenIniFileLC(fs);
 result = cdIniFile->IsPresentL(iInfo);
 CleanupStack::PopAndDestroy( 2 ); // fs, cdIniFile
 return result;
}

void CMegajoyAppUi::WriteIniFile(TUid iInfo, void *ptr, TUint size){

 TInt r;
 RFs fs;
 fs.Connect();
 CleanupClosePushL( fs );
 RDictionaryWriteStream rdsIniFile;
 CDictionaryStore *cdIniFile = Application()->OpenIniFileLC(fs);
 rdsIniFile.AssignLC(*cdIniFile, iInfo);
 TPtr8 buf((TUint8*)ptr, size, size);
 TRAP(r, rdsIniFile.WriteL(buf));
 rdsIniFile.CommitL();
 CleanupStack::PopAndDestroy();
 cdIniFile->CommitL();
 CleanupStack::PopAndDestroy( 2 );
}

void CMegajoyAppUi::RemoveIniData(TUid iInfo){

 RFs fs;
 fs.Connect();
 CleanupClosePushL( fs );
 RDictionaryWriteStream rdsIniFile;
 CDictionaryStore *cdIniFile = Application()->OpenIniFileLC(fs);
 cdIniFile->Remove(iInfo);
 cdIniFile->CommitL();
 CleanupStack::PopAndDestroy( 2 );
}

         可见,接口的实现也分为两部分

virtual void FlipBackBuffer(void)=0;

virtual void BitBlt(TInt iBltType, CExtendedBitmap &iBitmap, const TPoint &aPosition)=0;

        这两个接口属于界面的更新,AppUi直接调用iAppContainer中的函数进行消息的传递,而其它几个数据保存、读取、删除的操作都是在AppUi本地完成。

        而下面的用户接口消息(按键、菜单等操作)则直接传递消息给iMainEngine进行处理:

TKeyResponse CMegajoyAppUi::HandleKeyEventL(const TKeyEvent& aKeyEvent,TEventCode aType) {

  if(iMainEngine)
   return iMainEngine->DoKeyEvent(aKeyEvent, aType);
  return EKeyWasNotConsumed;
}
void CMegajoyAppUi::HandleCommandL(TInt aCommand) {
 switch ( aCommand ) {
 case EAknSoftkeyOk:
  iMainEngine->ExternalEvent(EVT_SELECT);
  break;
 case EAknSoftkeyBack:
  iMainEngine->ExternalEvent(EVT_ESCAPE);
  break;
 case EEikCmdExit:
  iMainEngine->ForceQuit();
  Exit();
  break;
 default:
  break;     
 }
}

Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1556704

你可能感兴趣的文章
项目开发经验谈(一)
查看>>
浅谈项目感觉
查看>>
用积木搭出的埃菲尔铁塔
查看>>
IT项目经理是否需要技术能力
查看>>
饮水者才能自知冷暖
查看>>
产品和样品
查看>>
测试Windows Sockets协议
查看>>
浅谈流媒体测试
查看>>
在接口后面能不能使用new操作符
查看>>
Windows API一日一练(37)MoveWindow函数
查看>>
C/C++内存问题检查利器—Purify (三)
查看>>
C/C++内存问题检查利器—Purify (二)
查看>>
让自定义的类型可以和任意的类型之间转换
查看>>
你讨厌 C++中的“
查看>>
STL的L细细品
查看>>
实实在在说多态(相同函数名 依据上下文 实现却不同)
查看>>
仿基因编程原理导论(里面的几个重要的概念正在做修正,所以这只能算是一个原理的初稿。)
查看>>
node的 http-server 启动以后 浏览器访问有问题
查看>>
解决VS+QT无法自动生成moc文件的问题
查看>>
wpf image资源释放问题
查看>>