Chinaunix首页 | 论坛 | 博客
  • 博客访问: 130431
  • 博文数量: 38
  • 博客积分: 2050
  • 博客等级: 大尉
  • 技术积分: 471
  • 用 户 组: 普通用户
  • 注册时间: 2007-11-19 15:49
文章分类

全部博文(38)

文章存档

2009年(1)

2008年(37)

我的朋友

分类: WINDOWS

2009-04-10 11:40:36


按照MVC模式重构Table模块的设想

MVC简介

MVC模式在用户交互式应用程序中有许多成功的应用,其基本思想是通过分离Model/View/Controller来构建用户界面。Model表示应用的数据,Viewmodel在屏幕上的表示,而Controller负责处理对用户操作的反应。通过分离这三部分内容可以提高软件的灵活性和复用性,通过合理分配类的责任也有助于降低WMCCBO指标。

 

Table模块现状分析

MVC的角度分析目前的table模块,可以将现有的类先做一个粗略的划分,然后再逐步重构:

Model部分:主要是描述Table的数据结构部分:包括CTable, CCellList, CCell

这一部分不应向外部暴露过多底层实现的细节, 而应考虑抽象出一些基本操作的接口供外部调用。总的来看,目前的实现还是不错的。不过个人以为按照页面的大小切分table, 似乎应该是放到视图层去处理。

Controller部分:负责响应用户操作(如选择菜单、拖拽点击鼠标等), 并修改model部分的数据结构。

这部分的入口函数主要存在于CTableEngine中, CTableEngine的成员变量保存了当前操作的对象。CTableProc是一个静态的工具类,提供一些公用的服务。HCUndoOP是一个全局的Undo/Redo引擎, 负责创建和存储undo对象, 并调用undo/redo方法。

每种操作都对应着一个undo类,例如拆分表格对应CUndoDivideTable, 合并单元格对应的是CUndoUnionCell等。这些类继承自CObject,且只存储了一些操作的中间结果,而没有成员函数。

这种设计使得CTableEngine规模过于庞大(WMCCBO指标过高)。此外,HCUndoOpAPI过于繁杂,没有做很好的抽象处理。同时各种UndoObject只作为数据容器,没有与相关操作很好的结合起来。

此处可以考虑把CTableEngine的功能按操作抽取出来, 并与利用现有的这些Undo类结合。 考虑应用Command模式,抽象出一个Command基类,各个具体的操作继承Command基类,并在execute()方法中描述如何进行操作。Engine中与各个操作相关的临时变量也可以分离到这些Command子类中去,带有全局性的变量 (model对象的指针保留在engine中或分离到Context类中)

但是目前Engine定义的许多public接口无法进行修改,所以这方面还需进行更细致的考虑。

 

View部分:通过绘制以graphics的形式反映当前数据结构的状况。这方面的代码主要存在于CTableDraw中。以下部分重点谈一下CTableDraw的重构。

 

关于CTableDraw重构的考虑

分为如下几个步骤

1) 分析外部对CTableDraw的调用依赖

目前CTableDraw中所有的成员函数都是public static的,public的函数应该体现该类对外所提供的服务,而分析显示这些函数绝大部分是通过本类的其他成员函数来调用的。所以第一步应考虑甄别出该类真正的对外接口,隐藏实现的细节。

方法: 将CTableDraw这个类中所有的成员函数设置为private, 然后编译、链接。查找出编译不过的部分即为该类的对外接口,需要定义为public,而其他均可以定义为private。下表描述了最后筛选出的CTableDrawpublic接口

函数名

外部调用

用途

static BOOL drawTable(CDC *pDC, HCCoord &dUnit, HCFrame *pFrame, int nHeadingPos = 0);

Drawhdoc.obj

Table的视图是整个doc视图的一部分,在进行doc绘制时调用table绘制的方法

static void drawEdge(CDC *pDC, int nX, int nY, int nLen, int nType);

DlgCellAttr.obj

‘单元格属性’对话框效果预览

static void drawTableStyleLine(CDC *pDC, HCCoord *pUnit, int nSX, int nSY, int nEX, int nEY, CCellLine *cellLine);

DlgCellAttr.obj

‘单元格属性’对话框效果预览

static void drawTableShape(CDC *pDC, int nRowNum, int nColNum, int sx, int sy, int ex, int ey);

DlgTableNew.obj

‘新建表格’对话框效果预览

 

D_bSplit.obj

‘拆分表格’效果预览

从中可以看出,最主要的对外接口就只有一个方法static BOOL drawTable(),应作为主要的关注点。

2) 改造静态方法为成员函数

如上所述CTableDraw的主要功能是通过绘制以graphics的形式反映当前数据结构的状况,目前是一些静态绘制方法的集合。这是一种过程式程序设计风格的体现,不是很好的OO风格,也不利于进一步的重构。但是目前又要保持静态的API不变,否则编译不过。可以考虑应用Adapter模式,把该接口改造成一个外壳, 并适配到相应的动态方法上。

例如:

static BOOL drawTable(CDC *pDC, HCCoord &dUnit, HCFrame *pFrame, int nHeadingPos = 0)

    pFrame -> paint(pDC, dUnit);

HCFrame中增加paint()方法

void HCFrame::paint(CDC *pDC, HCCoord &dUnit)

在理想情况下,HCFrame应该能够根据内容类型的不同,绑定到不同的绘制方法上去。

目前HCFrame里装的EDStextimagetablecell等,它们被定义为Frame的成员变量 CObject  *m_pEds; 并通过另一个成员变量BYTE  m_bClass来区别具体的EDS类型。这也不是很好的OO风格,在理想的情况下,应该为HCFrame所包含的EDS抽象出一个接口来, 比如说HCEDS,然后由具体的class来实现这些接口。

但是目前只有table这种EDS的代码,所以只能这样写

static BOOL drawTable(CDC *pDC, HCCoord &dUnit, HCFrame *pFrame, int nHeadingPos = 0)

  if (NULL == pDC || NULL == pFrame || !pFrame->IsTable())

              return false;

    CTable *pTable = (CTable *)pFrame->GetEDS();

    pTable -> paint(pDC, dUnit);

 

3)分离modelview部分

为了避免CTable这个类承担太多的责任,降低wmc值,考虑将绘制这方面的功能由一个view类来负责。CTablepaint()方法的具体实现委托CTableView这个类来实现。而 CTableView在绘制时需要访问CTable 中的数据结构来完成绘制任务。

class CTable()

{

   CTableView * m_view;

   void installView()

{                  

         m_view = new CTableView(this);

}

   void paint(CDC *pDC, HCCoord &dUnit)

   {

       m_view->paint(pDC, dUnit);  

}

}

class CTableView()

{

       CTableView(CTable *model)

    {

           m_model = model;

    }

    void paint(CDC *pDC, HCCoord &dUnit);

}

4) 建立composite view, 降低CBOWMC

CTableViewpaint()方法主要做两件事情, 一是绘制各单元格的内容,二是绘制表格的边框。这就涉及到访问单元格EDS, 斜线CDiagonal, 边框CCellLine等细节, 从而形成对这些类的依赖。为了降低WMCCBO指标,可以考虑将整个表格视图拆分为内容、边框等子视图, 每个子视图由各自的类绘制具体绘制。CTableView的责任简化为遍历表格的结构, 并调用相应的绘制器来完成绘制工作。 通过合理分配类之间的责任,应该可以降低相关指标值, 调整后的结构如下图所示。

 


阅读(1035) | 评论(0) | 转发(0) |
0

上一篇:fscanf/fopen/fstat

下一篇:没有了

给主人留下些什么吧!~~