Author Archive

第三部分:自定义node类型

Drupal系统本质上是一个CMS系统Framework,你可以在此基础上为满足自己的要求自由的扩展。从Drupal的观点看,所有的内容对象都应该是节点(node),整个Framework都基于这个假设来运转,从一个熟悉OOP的程序员的观点来看,node就是对象,处理不同类型的node就象处理从node类派生的各种子类。当然,这不过是PHP程序而已,你尽可以想怎样就怎样,但是,和OOP的继承的好处一样,将内容纳入node的管理体系至少可以立刻获得如下的功能和好处:

1) Drupal核心中node的基本增删修改删除功能,评论功能,分类功能,审核功能,日志功能,状态统计功能,搜索功能,各种node相关管理功能;
2) 所有支持node的drupal扩展模块提供的功能,包括但不仅限于节点附件上传,节点计划发布/隐藏,所见即所得编辑器,图片插入助理等等;
3) 核心和扩展模块的升级将立刻使你的节点类型享受增强的功能或特性;
4) 核心和扩展模块的安全补丁将使你的节点类型更加安全;
等等……,这一切只需要你定义一下节点类型即可获得,何乐而不为呢?如果你将内容放入drupal进行管理却不遵循node定义和管理的方式,我可以肯定地说你会在系统升级方面遇到困难和麻烦——简言之就是大伙儿都不是你这么想的,你的代码和别人不兼容。这可能会是你在开源系统里能够遇到的最糟糕的一件事了。

定义新的node类型其实非常简单,现在仅需一个hook_node_info(),而不向4.6版本时需要 hook_node_name() 和 hook_node_types()。模块文件名叫node_example.module:
function node_example_node_info() {
return array(‘node_example’ => array(‘name’ => t(‘example node’), ‘base’ => ‘node_example’));
}

hook_node_info()钩子是必须要实现的。此函数描述了这个模块可提供的节点。’name’的值是节点的人性化名字,’base’就是drupal系统识别的名字了。Durpal通过’base’的值知道操作这种类型的节点时应当在hook函数将加上什么样的前缀:如果’base’为“node_foo”,那么插入一个这种节点时调用的对应函数是’node_foo_insert’。

为界面友好的目的,我们一般还会实现hook_help(),这样用户创建节点时可以看到诸如介绍一类的东西:
function node_example_help($section) {
switch ($section) {
case ‘admin/modules#description’:
// 在admin/modules页面显示的模块描述信息
return t(‘An example module showing how to define a custom node type.’);
case ‘node/add#node_example’:
// 在node/add“创建内容”页面上显示的此类型节点的帮助信息。
return t(‘This is an example node type with a few fields.’);
}
}

自然我们也要定义hook_perm以实现节点的权限控制。我们定义了三个权限,一个用来限制谁可以浏览此种节点,另一个用来限制谁可以创建此种节点,最后一个用来限制是否允许用户修改自己创建的节点。为什么需要第三个权限呢?如果管理员允许未注册用户创建此种节点,那么管理员应该同时取消未注册用户修改自己创建的节点的权限,因为未注册用户对系统来说都是uid为0的同一个用户。
function node_example_perm() {
return array(‘view example node’, ‘create example node’, ‘edit own example nodes’);
}

另外节点权限管理需要定义的钩子是hook_access(),这个钩子可以自定义用户对节点的增删修改等操作的权限。下面是最常见的定义模式:
function node_example_access($op, $node) {
global $user;
if ($op == ‘view’) {
// 有浏览权限的用户才能浏览此种节点
return user_access(‘view example node’);
}
if ($op == ‘create’) {
// 有创建权限的用户才能创建此种节点
return user_access(‘create example node’);
}
// 如果有需要的权限,创建节点的用户可以修改和删除它
if ($op == ‘update’ || $op == ‘delete’) {
if (user_access(‘edit own example nodes’) && ($user->uid == $node->uid)) {
return TRUE;
}
}
}

另一个需要定义权限的地方是节点添加的页面和路径,下面按照国际惯例定义hook_menu以控制谁可以访问增加节点的页面:
function node_example_menu($may_cache) {
$items = array();
if ($may_cache) {
// 此处只定义访问路径的权限即可
$items[] = array(‘path’ => ‘node/add/node_example’, ‘title’ => t(‘example node’),
‘access’ => user_access(‘create example node’));
}
return $items;
}

节点定义和用户权限都有了,下面我们可以开始完善节点的增删修改浏览的功能。这里使用的节点例子类型叫node_example,它允许用户节点中保存“颜色(color)”和“数量(quantity)”两种自定义信息。因为要存储这些附加信息,我们需要在数据库中增加额外的表。
数据库定义:
CREATE TABLE node_example (
vid int(10) unsigned NOT NULL default ’0′,
nid int(10) unsigned NOT NULL default ’0′,
color varchar(255) NOT NULL default ”,
quantity int(10) unsigned NOT NULL default ’0′,
PRIMARY KEY (vid, nid),
KEY `node_example_nid` (nid)
);

事实上,如果不需处理这些额外的附加信息,你会发现有上述函数的模块已经能够处理新定义的节点类型了,前面叙述的你该即刻享受的系统好处你都能享受到了,呵呵。但要处理节点类型特有的附加信息,我们还有许多工作要做:

(一) 增加自定义节点

首先,虽然基本的增加节点的提交页面系统已经提供了,但是上面没有我们要的自定义信息的输入框或选择框,这样我们就必须实现hook_form()以在提交页面上收集自定义信息。这个钩子要求我们返回一个数组值,其中包含了定制表单各元素的定义信息,注意这也是drupal 4.7的标准表单定义方法。
function node_example_form(&$node) {
// 用来输入节点标题的文本框
$form['title'] = array(
‘#type’ => ‘textfield’, // 表单元素的类型
‘#title’ => t(‘Title’), // 表单元素的标题
‘#required’ => TRUE, // 是否必须输入的值
‘#default_value’ => $node->title, //缺省值
‘#weight’ => -5
);
// 如果我们希望表单元素按特定的顺序排列,我们可以设置元素的Weight值。
// 但是其他模块可能也插入设置了weight值的表单元素,这样元素顺序可能仍然
// 不是我们想要的样子。为避免这一情况,我们可以把元素放到子数组中,一个
// 数组中的元素总是可以保证顺序的了吧!
$form['body_filter']['body'] = array(
‘#type’ => ‘textarea’,
‘#title’ => t(‘Body’),
‘#default_value’ => $node->body,
‘#required’ => FALSE
);
$form['body_filter']['filter'] = filter_form($node->format);
// 上面是普通应有的标题和正文,下面是我们特有的节点信息了
$form['color'] = array(
‘#type’ => ‘textfield’,
‘#title’ => t(‘Color’),
‘#default_value’ => $node->color
);
$form['quantity'] = array(
‘#type’ => ‘textfield’,
‘#title’ => t(‘Quantity’),
‘#default_value’ => $node->quantity,
‘#size’ => 10,
‘#maxlength’ => 10
);
return $form;
}

用户用上面的表单提交数据,drupal自动会过滤提交的数据,对于表单里没有定义却被提交的值,drupal会自动把它们过滤掉以保证系统安全。然后,我们可以通过hook_validate()验证提交的有效数据是不是我们真正想要的,下面我们验证一下用户提交的quantity值是不是一个数字,不是就报告错误,用户会被要求重新输入,如果没有数据提交(因为前面表单定义里并没有要求此值是必须的),则认为quantity是0:
function node_example_validate(&$node) {
if ($node->quantity) {
if (!is_numeric($node->quantity)) {
form_set_error(‘quantity’, t(‘The quantity must be a number.’));
}
}
else {
// Let an empty field mean “zero.”
$node->quantity = 0;
}
}

要注意的是告诉系统出错了并不是返回一个什么值,而是调用form_set_error设置表单错误,这样可以在一个函数里设定多个错误。

如果数据验证成功,则新节点就会生成了,新节点信息会写入到数据库中。且慢,我们好像并没有写入附加信息到数据库的程序段。当节点插入时,drupal会调用所有实现hook_insert()的函数,这让我们有机会向数据库写入我们自己的节点自定义信息:
function node_example_insert($node) {
db_query(“INSERT INTO {node_example} (vid, nid, color, quantity) VALUES (%d, %d, ‘%s’, %d)”, $node->vid, $node->nid, $node->color, $node->quantity);
}

格式很简单,你不用考虑用户的提交是怎么被处理的,你只要直接引用它们就可以了,$node->color、$node->quantity什么的,它们一定正确的在那里。

(二) 修改自定义节点

Drupal本身支持节点拥有多个版本,我们在这里也需要考虑到这一点。
function node_example_update($node) {
// 处理产生一个新版本的情况
if ($node->revision) {
node_example_insert($node);
}
else {
db_query(“UPDATE {node_example} SET color = ‘%s’, quantity = %d WHERE vid = %d”, $node->color, $node->quantity, $node->vid);
}
}

(三) 删除自定义节点

删除很简单,系统本来就实现了节点的删除功能,我们需要做的是在适当的时候把我们写入数据库的附加信息给清除掉。当节点删除时,drupal会调用所有实现hook_delete()的函数,这样各个模块都有机会清理自己生产的垃圾了。
function node_example_delete($node) {
// 注意这里我们删除了指定nid的所有版本
db_query(‘DELETE FROM {node_example} WHERE nid = %d’, $node->nid);
}

(四) 与系统其他部分互动

负责载入节点对象的node_load()函数会发出hook_load()调用,以便扩展模块将自定义属性补充进它返回的$node对象中。因此我们还必须实现hook_load(),附加信息才会在载入节点对象时自动也被载入。
function node_example_load($node) {
$additions = db_fetch_object(db_query(‘SELECT color, quantity FROM {node_example} WHERE vid = %d’, $node->vid));
return $additions;
}

(五) 自定义节点显示格式

下面的代码实现了hook_view(),这个钩子在用户浏览节点时被调用。实现得很简单,节点的正文(body)和节选(teaser)都是系统功能提供和实现的,我们只需要把自定义的信息加到其中即可。这里我们用的是最简单的方式,直接加在正文和节选的后面就可以了。实际情况下,这个显示的格式是可以任意调整的。
function node_example_view(&$node, $teaser = FALSE, $page = FALSE) {
$node = node_prepare($node, $teaser);
$order_info = ‘

‘;
$order_info .= t(‘The order is for %quantity %color items.’, array(‘%quantity’ => $node->quantity, ‘%color’ => $node->color));
$order_info .= ‘

 

‘;
$node->body .= $order_info;
$node->teaser .= $order_info;
}

写到这里,发现要用好自定义节点类型要实现的hook还是蛮多的,不过只要按部就班,也应该能很快实现,就跟定义一个类然后实现些方法差不多。然后这个定义的节点就可以在系统中被自由使用了,似乎比OOP还要略微简单点。

第二部分:模块的自定义页面显示方法

许多时候我们需要为一些数据显示一个自定义格式的页面。熟悉模板的同志们可能曾经失望的发现,模板只能控制除$content之外的那部分页面。在模板里,内容区之外的其他部分你想怎么定义都行,但要控制内容的格式,对不起,它是由一个名为$content的变量一次输出了整个内容正文。

这就决定了,一般情况下,内容的格式控制只能通过模块来实现,呵呵——不会写程序的同志们可能要晕倒了;其实别的方法也不是没有,就是……通过能写模块的人来实现,哈哈哈,这个是开玩笑哈,另一个方法是修改theme引擎来实现(这个另文阐述,放在这里离题了)。还有非标准的方法,那些个就不介绍了,会误导群众,最后必将导致网站维护、升级异常困难等后果。

下面是一个最简单的页面显示函数,输出我们要的内容:
function example_foo() {
$content = ‘

The quick brown fox jumps over the lazy dog.

‘;
return $content;
}

真够简单,问题是我们该访问那个URL这个页面才会出来呢?下面drupal的url定义钩子hook_menu()隆重登场:
function example_menu($may_cache) {
$items = array();
// $may_cache参数用来将菜单项分为两类。
// $may_cache 为 TRUE时返回的菜单项对当前用户在任何时候都可用(并被缓存);
// 其他的则是可更改的或只在一定的路径下才被定义的,例如带参数的动态路径。
// 绝大多数模块都会有可缓存的菜单项。
if ($may_cache) {
// 这是你必须提供的最基本信息
$items[] = array(‘path’ => ‘foo’,
‘title’ => t(‘foo’),
‘callback’ => ‘example_foo’,
‘access’ => TRUE);
}
return $items;
}

好了,现在访问http://example.com/foo,你可以看到输出的内容了……“Ooops,显示说404 not found”。这个还是有可能的,因为$may_cache的菜单项都被缓存了,新的路径还没有刷新到缓存里面,此时,要么用devel模块clear cache,要么浏览一下管理页面中的“菜单”页面就会刷新菜单路径缓存了。现在应该可以了,这样,你在example_foo()的$content里想怎么构建你的页面都行,那个只是html+css的问题了。

顺带讲一下hook_menu()。这个hook的用法蛮灵活,复杂的导航必备,例如通过定义MENU_LOCAL_TASK类型的路径,可以在其他模块产生的页面上的Secondary Tabs部分嵌入自己的页面。前面的例子里,我们在$items数组里用数组定义了页面的URL相对路径“path”,页面标题“title”,回调函数“callback”和权限控制“access”,整一个意思就是告诉drupal的菜单系统“如果访问foo这个路径,那就调用example_foo这个函数,产生的页面的标题是foo,同时允许任意用户访问(因为access始终是TRUE)”。

下面是一个带参数的url地址定义:
function example_menu($may_cache) {
$items = array();
// 通过使用MENU_CALLBACK类型的路径,我们可以为指定路径注册一个
// 回调函数而不出现在菜单项列表里,管理员也不能在菜单管理里禁用这个路径
$items[] = array(‘path’ => ‘bar/baz’, ‘title’ => t(‘baz’),
‘callback’ => ‘example_baz’,
‘access’ => TRUE,
‘type’ => MENU_CALLBACK);
// 下面的菜单项没有注册回调函数,此时,属性就会从父路径继承。
// 例如,这里也会使用父路径的权限控制。不过如果路径上有指定参数的话,
// 我们重定义了标题。
// 注意:如果没有’type’属性,此项会在菜单中显示,也就是说’type’不被继承。
$items[] = array(‘path’ => ‘bar/baz/52/97′,
‘title’ => t(‘the magic numbers’),
‘type’ => MENU_CALLBACK);
}
return $items;
}
/**
* 一个更加复杂的带参数的页面回调函数。
*
* 参数是从页面的URL中传递的。参数位于页面访问路径的后面两个元素。
* 正因为如此,如果页面的URL发生了改变,这个函数也不需要重写。
* 在这里为参数提供缺省的值是个良好的习惯,这样页面才总是可以访问的。
*/
function example_baz($alice = 0, $bob = 0) {
// 永远不要相信URL中传来的值是安全的!一定要记得检查这些值。
if (!is_numeric($alice) || !is_numeric($bob)) {
// 如果参数都不是数字,我们将显示一个标准的“你无权访问”的页面
drupal_access_denied();
return;
}
$list[] = “Alice’s number was $alice.”;
$list[] = “Bob’s number was $bob.”;
$list[] = ‘The total was ‘. ($alice + $bob) .’.';
// 调用theme函数实现输出的格式化,theme_item_list()只是许多theme函数中的一个
$content = theme(‘item_list’, $list);
return $content;
}

如果用户访问http://example.com/?q=bar/baz,菜单系统会执行example_baz(),如果用户访问http://example.com/?q=bar/baz/1/2,菜单系统会首先查找bar/baz/1/2,如果找不到对应定义会接着找bar/baz/1。如果又找不到,它就会找bar/baz,这样它就会执行example_baz(1,2)。注意路径中的数字部分作为参数传递给了函数,这个实在是非常好用。

如果用户访问http://example.com/?q=bar/baz/52/97,菜单系统找到了匹配,但由于回调函数被省略,因此它最终调用的是example_baz(52,97)。有什么不同呢,此时页面标题不再是“baz”,而是“the magic numbers”了。

前面所有的路径定义中access都为TRUE,但通常我们都希望对页面内容的访问作一些权限控制。此时我们需要在模块里实现hook_perm()函数:
function example_perm() {
return array(‘access foo’, ‘access baz’);
}

现在,你在“访问控制”页面就可以分配这两个权限给指定的角色了。同时,你还要在hook_menu里这样定义’access’:
‘access’ => user_access(‘access foo’),

user_access()函数会访问$user全局变量和权限设定以确定访问页面的当前用户是否有’access foo’这个权限,有就返回TRUE,没有返回FALSE。用户ID为1的用户默认拥有任何权限,即user_access(‘任意值’)对他来说都会返回TRUE;权限管理对他完全没用,乖乖。——所以,有特殊要求时,记得还要控制uid=1的用户。

好了,我相信你已经能够用模块定义自己的页面了——虽然看似有些繁琐,但恭喜你已经迈出模块之旅的第一步。

适用于:初级drupal开发者
基本要求:php编程-基础,drupal管理-熟练
鸣谢:drewish 的模块样例

第一部分:基本模块结构

事实上,模块真正必须实现的函数只有一个,那就是hook_help()。这里我们约定在本文中对drupal的系统“钩子”我们都写为“hook_钩子名”的形式,你实现的时候的函数名实际上是“模块名_钩子名”的形式。

什么是钩子(hook)?
Drupal是一个内容管理系统的编程框架,其核心功能已经很完备了。我们编写扩展模块时并不需要自己完成每一个功能,大多数扩展系统的方法只是按命名规则写好钩子(Hook)的实现函数,系统就会在特定的时候来调用你的函数,这样你的扩展功能就被实现了。记住,不是你去调用系统,而是系统来调用你,我觉得这个和Windows的消息循环是有点类似的。

Hook_help()可以告诉drupal系统模块的信息以使模块在“模块”管理页面上列出来,这样用户就可以选择是否启用或禁用此模块了。模块一但被启用,模块文件里的全部代码都被包含到系统中,同时,模块的函数也可以调用drupal的所有可用函数和访问所有可用变量了。

下面是最简单的hook_help()写法,这里的模块文件名是example.module,模块名是example,记得在php文件要有〈?php和?〉标记在文件首尾。

function example_help($section) {
switch ($section) {
case ‘admin/modules#description’:
// 下面的描述会显示在admin/modules路径下的模块列表里
return t(‘An example module showing how to develop a module for Drupal.’);
}
}

只要将包含上面函数的example.module文件放到drupal目录下的modules子目录里,访问“模块”管理页面(在admin/modules)就可以启用这个模块了,简单吧。

注:上面的返回值中我们使用了t()函数,这个函数用以实现drupal的本地化,虽然代码里是写的英文,但如果我们有对应的本地语言的翻译,用户会看到对应的本地化字符串。

现在,我们有了一个可启用/禁用的模块了,在开始为它添加功能之前,我们先了解一下Drupal的模块开发编程规范(全文见 http://cvs.drupal.org/viewcvs/*checkout*/drupal/contributions/CODING_STA… )。Drupal为了最大程度的兼容PHP的不同版本,它不使用命名空间或类的封装特性,因此,我们需要特别注意命名的问题,一般的约定是:

1)函数和方法使用小写字符,并用下划线分隔单词。函数应当使用模块名作为前缀以避免不同模块间的函数名冲突。私有的函数和方法再在名称前加一个下划线。
2)常量名完全大写,并用下划线分隔单词。前面应当加上大写的模块名前缀。
3)你定义的全局变量的名字前加上“_模块名_”前缀。
其他的约定请参见编码规范。

模块可以实现的功能很多,一般分为如下几个功能类别:
1)产生页面显示内容;
2)自定义node类型;
3)扩展现有的node类型;
4)生成定制的区块(block);
5)控制node的显示权限;
6)文件上传;
7)定制过滤器(Filter);
8)其他高级应用。

下面我们以自定义页面显示内容为例开始module开发之旅。

Object Oriented Analyzing
Object Oriented Design
Object Oriented Programming

OOA(object oriented analysis)面向对象分析
OOD(object oriented design)面向对象设计

OOP(Object Oriented Programming)面向对象编程
如所熟知,面向对象作为一种程序设计技术最早于60年代后期提出,用于Simula的应用程
序开发。到了70年代,面向对象成为Smalltalk语言的一个重要特征。当时,面向对象技术主要
用于程序设计。进入90年代,人们的注意力逐渐从程序设计转向系统分析和设计,用对象的观
点来认识现实世界、设计问题的可行解,随之也就出现了许多OOA和OOD方法。但这些方法
还不很成熟,在OOA与OOD的边界划分上也存在着争议。如:有人认为面向对象软件开发
过程可以分为面向对象分析、面向对象设计和面向对象程序设计三个阶段;有人认为分析和设
计可以交叉进行不必做严格区分;还有人沿用传统方法进行分析和设计,用面向对象程序设计
语言来实现系统。O
OA/OOD的一些较有代表性的工作有Gray.Booch的OOAD方法,Coad&Yourdon的
OOA和OOD方法,Shlaer&Mellor的OOA方法,Rumbaugh的OOAD方法等。不同的方法
体系都分别体现了人们对OOA、OOD,以及面向对象软件开发过程的不同认识。本文的主要
目的就是,试图通过对现有OOA与OOD方法的共性进行纵观分析,弄清二者之间的边界问
题,评析从OOA到OOD过渡的难易,并讨论实现这种过渡所涉及的主要工作。
ooa:分析阶段所做的主要工作是理解问题和需求构模,将现实世界中的问题映射到问题域。在该
阶段,要明确用户提出了哪些功能要求,为完成这些要求,系统应有哪些构件,采用什么样的结构,并写出详细的需求规约。OOA中引入了许多面向对象的概念和原则,如,对象、属性、服务
、继承、封装等,并利用这些概念和原则来分析、认识和理解客观世界,将客观世界中的实体抽
象为问题域中的对象,即问题对象,分析客观世界中问题的结构,明确为完成系统功能,对象间
应具有的联系和相互作用。因此,下述问题是OOA阶段必须回答的:
1)为完成用户要求系统应提供哪些功能?
2)系统应有哪些对象构成?
3)每个对象应有哪些属性和服务?
4)对象间应有怎样的联系?要
回答这些问题,就需要从静态和动态两方面来认识、分析现实世界对象。具体地说,要进行
:
1)个体特征分析:标识对象及其属性和服务。有的方法在标识特征时对属性的要求可能
弱一些,这是因为对象是属性和操作的封装体,对象的访问可以通过接口–操作来实现。这
样在标识对象时对象的内部特征可暂不考虑,仅考察外部行为。每种分析方法在完成这些工作
时各具特色,如:Coad&Yourdon方法只给出了一些标识对象及其特征的一般原则和思路;R
.Abbott认为,可以通过分析非形式化英语的问题描述,将名词标识为对象,将形容词标识为属
性,将动词标识为服务;Bailin等人则采用结构化与面向对象技术相结合的方法。
2)静态分析:分析和描述系统的静态结构。一般地,对象系统中的类或对象之间存在着两
种关系:一般—特殊关系和整体—部分关系。其中,前者更具普遍性,它的一种重要实现形式
就是继承机制,也是面向对象程序设计语言所支持的语言设施。同样,大多OOA方法也都为
继承提供了相应的表示方法。因此,系统静态分析主要是分析、识别对象或类间的一般—特殊
结构,并添加一些必要的类,构造继承关系。
3)动态分析:分析对象及之间的行为及其控制关系,建立系统的动态模型。多数分析方法
要求进行这方面的工作,有的则将它放到设计阶段去完成,这主要是由OOA、OOD阶段划分
的不同所造成的。动态模型一般由一组状态转换图构成,从这组状态转换图可以映射到对象模
型。系统的动态模型从对象行为的角度刻划了系统功能,方便了从OOA到OOD的过渡。有的
方法虽未提供动态模型,但也提供了表示对象行为的类似方法。早期的OOA方法对建立系
统动态模型认识不足,这主要是因为当时的许多方法是受数据模型的启发而产生的。现在越来越
多的人认识到了系统动态分析工作的重要性,并在分析方法中引入了相应概念。除
此之外,许多OOA方法还引入了问题复杂性控制机制。如,Coad&Yourdon在其方法中
引入了主题的概念;Wirfs&Brock在其方法中引入了子系统的概念。问题复杂性控制机制主
要针对大型复杂系统,它将一组对象或类抽象成新的系统构件,以达到简化问题空间的目的
。这样,分析和设计人员就可以从宏观与微观、整体与局部等不同角度来分析问题,便于透彻地
认识和理解问题。
OOD方法:
析阶段主要是明确用户的功能需求,及满足用户所需的系统部件及其结构。设计阶段则主
要是确定实现用户需求的方法,即怎样做才能满足用户需求,并构造出系统的实现蓝图。面向
对象设计也是如此,只不过是引入了一些面向对象的概念和原则,用以指导设计工作。OOD首
先从OOA的结果开始,并将其从问题域映射到实现域;为满足实现的需要,还要增加一些类
、结构及属性和服务,并对原有类及属性进行调整。此外,还要完成应用控制、人机交互界面的设计等。在现有方法中,Coad等人的OOD就是比较全面的设计方法。OOD的主要工作有:2
问题域部分的设计问
题域部分的设计是任何OOD方法都必须完成的工作,它主要是对OOA结果进行改进和
精化,并将其由问题域转化到解域,具体来说,有以下几个方面:.
属性:有些属性在分析阶段有助于问题的理解,而到了设计阶段则可以由其他属性导出或
根本没必要保留。因此,应将它们去掉。相反地,为了实现服务算法还需要增加相应的一些属性
。.
服务:OOA只给出了服务的接口,其具体实现算法要在OOD阶段完成。.
类及对象:在OOA阶段有助于问题理解的一些类在OOD阶段成为冗余,需要删除,而为
了优化调整继承关系还要增加一些类。所有的类都确定以后还要明确哪些类的对象会引发哪
些类创建新对象。.
结构:对类间结构进行优化调整。.
对象行为:明确对象间消息传递的实现算法,依据动态模型确定对象间消息发送的先后顺
序,并设计相应算法,协调对象的行为。2
2 人机交互与应用控制部分的设计有
些设计方法并没有提到交互界面的设计,一方面是因为这些系统中交互界面不十分重要
;另一方面是因为这部分的设计很有规律,设计方法也比较成熟,但为完整起见,仍将其列出
。主要工作包括:.
交互界面子系统的设计:与界面有关的类及类间结构的设计,以及有关算法的设计。.
交互界面子系统和应用之间接口的设计。.
应用控制部分的设计:这部分对象主要完成应用的驱动工作。这部分对象不同于从现实
世界中抽象出来的对象,在现实世界和问题域中没有原型,它们同界面子系统中的对象及问题
对象发生作用,控制系统的运行。

OOA与OOD的区别
上讨论了OOA阶段和OOD阶段所应完成的主要工作,由于这两个阶段的划分没有公认
的标准,有些工作是在OOA阶段完成还是在OOD阶段完成还存在着争议。有人认为OOA和
OOD可以交叉进行;有人认为OOD是对OOA结果的改进和细化,所以只提OOA;有人则更
强调OOD。尽管OOA和OOD存在着某些交叉和联系,但它们之间仍有许多差别,如:
1)OOA将现实世界中的实体抽象为问题对象,并构造问题域中的系统需求模型;OOD将
问题对象转化为解域中的类并在解域中构造出问题的解。
2)OOA侧重于用户需求的分析和对问题域的理解,分析人员关心的是系统结构及对象间
的关系;OOD则侧重于系统的实现,设计人员关心的是对象的行为及其实现。
3)OOA标识了一组对象,并通过其相互作用来刻划系统,该阶段的工作与程序设计语言
无关;OOD定义了一组类,并设计出系统的实现蓝图,概要设计与程序设计语言无关,但详细
设计则与之有比较密切的联系。(
4)OOA识别的对象是对客观世界实体的抽象,标识对象的准则是:该对象的引入是否有助于对问题域的理解;OOD中构造类的准则是:该类的构造是否可行,是否有效地实现了抽象
数据类型,是否有助于系统的实现和提高软件质量。(
5)两个阶段都没有提及系统对象,但原因不同。在OOA阶段,分析与实现无关,分析所涉
及的范围与解域无关,系统对象自然不用考虑。OOD建立的对象模型本身就是要设计的软件
系统,对系统对象的考虑是隐含的。
6)组装结构和分类结构在两个阶段所起的作用不同。在OOA阶段,它们的引入主要是为
了理解问题;而在OOD阶段,它们的引入则主要是针对软件的构造和实现。分类结构通过继
承机制来实现,因而代码得到了有效地复用;组装结构则将一些类组合在一起构成较大的软件
构件。
7)OOA并没有考虑对象的产生问题,当其对应的实体在现实世界中出现时,它也就在问
题域中产生了。OOA也不考虑对象属性的取值和服务算法的实现。而在OOD阶段这些问题
都必须详细考虑。
8)OOD涉及到重载问题;而OOA没有考虑,因为考虑过多的实现细节对理解问题和分
析用户需求没有多大帮助。

转眼之前,2010年的春季已经悄悄然的从身边滑过,而我竟然到现在才察觉。而过去的这段时光里,我一直在挣扎、思虑、徘徊以及忙碌中渡过,却没有抓住多少有意义的东西。回想过去一两年的时光里,自己似乎没有什么进步,而且也没有找到方向,包括学习的方向和发展的方向,有点又回到04年沉迷网游时得过且过的样子,虽然也不断的告诫自己不可以这样的,但苦于没有找到那盏明灯。而其实可能那盏灯可能就在不远的转角,只是自己少走了两步;又或者可能根本就没有或者不需要什么明灯,只要自己不断的探索一样可以找到未来的方向。

春天已经过去,而我却没有计划好播撒什么样的种子。当到了别人收获的秋天时,我是否也会有自己的果实呢?还是只能看着他人的果实却咽下自己那渴望的口水,等到冬天来临之际仍然没有过冬的食物。这绝对不是我想要的人生,也绝对不会是我所要经历的人生,虽然已经赶不了别人的春播,但只要赶上夏种的季节,一样可以收获那与他人不同的秋果。

茫茫然之际,我似乎看到了希望的亮光,似乎在心中点起了明亮的灯。而给予我帮助的是亲人、朋友,包括那些可能并不熟悉的路人!