模块化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 如何表达2个系统之间的接口 兔爷传奇
UML的标准规范有么? 兔爷传奇
UML交流报名 快乐的周末
应用UML语言系统性建立模型 yaxino
对于片断性的需求变更,如何管理UML图的版本 pxg
UML应用经验不多,如何培养自己的能力? pxg
更多问题 ...


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


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


UML与面向对象分析设计


面向对象的分析设计
基于UML& OOAD的迭代开发
设计模式(Java或C++或C#)
关系数据库面向OOAD设计
业务建模与业务架构的搭建
使用用例进行需求管理

相关咨询服务
UML+OOAD项目实施
UML+OOAD项目敏捷咨询


北京 UML&OOAD
卫星导航 UML&OOAD
爱立信 使用Rhapsody及UML
丹佛斯 UML2.0在嵌入式系统
汤森路透 UML&OOAD
西门子 UML+OOAD+规范+EA
中国银行 UML技术
阿尔卡特 UML&OOAD

 
 
 

组织简介 | 联系我们 |   Copyright 2002 ®  UML软件工程组织 京ICP备10020922号

京公海网安备110108001071号