求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
  
 
 
 
模块化C代码与UML对象模型之间的映射(二)
 

作者:autoca,发布于2012-1-12

 

模块化C代码与UML对象模型之间的映射(3)——UML关系

下图是从StarUML工具界面截下来的,从上往下依次表示UML的关系:关联、单向关联、聚合、组合、泛化、依赖和实现。

图3 UML关系集

3.1 关联、聚合、组合

关联(association)是一种结构关系,它指明一个事物的对象与另一个事物的对象间的联系。聚合和组合是更强的关联,表示整体和部分的关系。

聚合的整体不负责部分的生命期,组合的整体负责部分的生命期。关联关系需根据实际场景来识别,例如军队和士兵的关系一般可理解为聚合,士兵退役了就和军队脱离聚合关系了。但是,如果是打仗时,士兵们必须生死与共,军队没了则士兵命也没了,则可理解为组合。

UML示例:

C代码示例:

//A关联/聚合/组合了B

struct A{

    struct B *b;

    void (*Create)(B *b); //方式1

};

struct A{

    struct B *b;

    void (*SetB)( B *b); //方式2,单B

};

struct A{

    struct B *bset[N];

    void (*RegisterB)(B *b); //方式3,B集

};

3.3 泛化

泛化(generalization)是一种特殊/一般的关系。也可以看作是常说的继承关系。

下面举一个用C语言实现继承的一个经典例子,从Linux内核源码中拷贝过来的。

UML示例:

图3-2 继承(泛化)

C代码示例(删节版):

struct kobject {

    const char *name;

    struct kobject *parent;

};

struct cdev {

    struct kobject kobj; //继承kobject

    const struct file_operations *ops;

    dev_t dev;

    unsigned int count;

};

struct scullc_dev {

    void **data;

    struct scullc_dev *next;

    struct cdev cdev; //继承cdev

};

注意:继承不是定义结构体指针,而是定义结构体。

3.2 依赖

依赖(dependency)是两个事物之间的语义关系,其中一个事物(独立事物)发生变化,会影响到另一个事物(依赖事物)的语义。最常用的依赖关系是一个类的构造函数中用到另一个类的定义。

UML示例:

由于类的客户要依靠接口实现类的操作,所以我们把与接口的交互建模为一种依赖关系。

图 3-3 依赖

C代码示例:

//方式1

void A_Method(struct A *a)

{

    B_Method(); //B是实用类(即全局的实例),这种依赖方式在我们系统中最最多

}

//方式2

void A_Method(struct A *a, const struct B *b); //B作为参数被传递

//方式3

void A_Method(struct A *a)

{

    struct B *b = B_Create(); //B在A的方法中实例化

}

3.4 实现

实现(realization)是类元之间的语义关系,其中的一个类元指定了由另一个类元保证执行的契约。

下面再拿Linux设备驱动做例子。scullc_dev类实现文件操作接口file_operations。

UML示例:

图3-4 实现

C代码示例:

//实现接口函数

int scullc_open (struct inode *inode, struct file *filp)

{

    struct scullc_dev *dev; /* device information */

    /* Find the device */

    dev = container_of(inode->i_cdev, struct scullc_dev, cdev);

    /* ...*/

    /* and use filp->private_data to point to the device data */

    filp->private_data = dev;

    return 0; /* success */

}

ssize_t scullc_read (struct file *filp, char __user *buf, size_t count,

loff_t *f_pos)

{

    struct scullc_dev *dev = filp->private_data; /* the first listitem */

    struct scullc_dev *dptr;

    int quantum = dev->quantum;

    /* ... */

    return retval;

}

//…

//文件操作接口实例

struct file_operations scullc_fops = {

    .owner = THIS_MODULE,

    .llseek = scullc_llseek,

    .read = scullc_read,

    .write = scullc_write,

    .ioctl = scullc_ioctl,

    .open = scullc_open,

    .release = scullc_release,

};

//

static void scullc_setup_cdev(struct scullc_dev *dev, int index)

{

    int err, devno = MKDEV(scullc_major, index);

    cdev_init(&dev->cdev, &scullc_fops); //注册接口

    dev->cdev.owner = THIS_MODULE;

    dev->cdev.ops = &scullc_fops;

    err = cdev_add (&dev->cdev, devno, 1);

    /* Fail gracefully if need be */

    if (err)

    printk(KERN_NOTICE "Error %d adding scull%d", err, index);

}

模块化C代码与UML对象模型之间的映射(4)——常见问题小结

下面是零零散散做的一些笔记。

1、为什么说双向关联往往是设计的坏味道?

从可复用性的角度来看,如果两个类间的关系是双向的,则每个类都需要知道对方,因此两者都不能复用。说明单向关联有助于标识可复用的类。

2、聚合组合方式会遇到对象生命期管理的问题,怎么解决?

例如,A_Create(B_Create()->IA, B),即类B实现接口IA供类A内部使用,那么IA的生命期该由谁来管理呢?常见的做法是在IA中加入Destroy接口,那么A可以由此释放IA。可是这违背了一个原则,一般内存管理是谁申请谁释放的。我知道的另一个办法是借鉴Linux设备驱动程序的做法,把IA定义为全局变量。“生命期管理是个很大的课题。”后续再慢慢研究。

3、如何区别泛化与实现关系

我是这样理解的:泛化(继承)对应抽象类;实现对应接口。

4、关联与依赖有何区别?

这个问题有很多种版本的解释,摘要如下:

依赖是比关联弱的关系,依赖是两个事物之间的语义关系,而关联代表一种结构化的关系。体现在代码中:

(1)关联有双向与单向之分。若类A与类B双向关联,则A与B相互作为对方的attribute;若类A单向关联指向类B,则在类A中存在一个attribute B b*。

(2)依赖就只有单向的。若类A依赖类B,则不会有B这个属性。类A依赖类B有三种方式:一是类B是全局的,二是类B在类A中实例化,三是类B作为参数被传递。

5、继承or聚合/组合,这是一个问题

根据不同角度的理解,设计上会做出不同的抉择,但

请慎用继承,因为:

(1)继承是过紧的耦合,每当父类变化,子类也得跟着变,违背了开闭原则。

(2)继承不支持多态,父类与子类之间的关系在编译时就静态绑定了。

“所以一般都提倡,只继承接口不继承实现,通过组合达到代码重用的目的。”

btw:关于C语言实现面向对象机制,我目前只是初探,目的是在必要的时候能够有效的驾驭和简化用C语言开发的代码复杂度。

但如果需求已经足够简单,代码已经足够清晰,那么杀鸡焉用牛刀呢。


 
相关文章

UML概览
UML图解:用例图(Use case diagram )
UML图解:活动图(activity diagram )
UML图解:类图(class diagram )
UML图解:对象图(object diagram)
UML图解:顺序图( sequence diagram )
 
相关文档

模型跟踪:跟踪图、矩阵、关系(建模工具EA)
自定义表格(Custom Table)在EA中的使用
元素的详情浏览控制
UAF 1.2规范解读(DMM 和 UAFML )
EA中支持的各种图表
EA中的界面原型建模
 
相关课程

UML与面向对象分析设计
UML + 嵌入式系统分析设计
业务建模与业务分析
基于SysML和EA进行系统设计与建模
基于模型的需求管理
业务建模 & 领域驱动设计


如何向妻子解释OOD
OOAD与UML笔记
UML类图与类的关系详解
UML统一建模语言初学
总结一下领域模型的验证
基于 UML 的业务建模


UML如何表达2个系统的接口
UML的标准规范有么
UML交流报名
应用UML语言系统性建立模型
UML应用经验不多,如何培养自己
用UML拖长了时间


UML与面向对象分析设计


面向对象的分析设计
基于UML的面向对象分析设计
UML + 嵌入式系统分析设计
关系数据库面向OOAD设计
业务建模与业务架构
使用用例进行需求管理


某航空IT部门 业务分析与业务建模
联想 业务需求分析与建模
北京航管科技 EA工具与架构设计
使用EA和UML进行嵌入式系统分析
全球最大的茶业集团 UML系统分析
华为 基于EA的嵌入式系统建模
水资源服务商 基于EA进行UML建模
更多...