分类归档: Algorithm

[转]Windows API

1.什么是Windows API?

Windows 这个多作业系统除了协调应用程式的执行、分配内存、管理系统资源…之外, 她同时也是一个很大的服务中心,调用这个服务中心的各种服务(每一种服务就是一个函数),可以帮应用程式达到开启视窗、描绘图形、使用周边设备…等目的, 由於这些函数服务的对象是应用程式(Application), 所以便称之为 Application Programming Interface,简称 API 函数。WIN32 API也就是MicrosoftWindows 32位平台的应用程序编程接口。

凡是在 Windows 工作环境底下执行的应用程式, 都可以调用Windows API。

2.Windows API的历史与现状

当WINDOWS操作系统开始占据主导地位的时候,开发WINDOWS平台下的应用程序成为人们的需要。而在WINDOWS程序设计领域处于发展的初 期,WINDOWS程序员所能使用的编程工具唯有API函数,这些函数是WINDOWS提供给应用程序与操作系统的接口,他们犹如“积木块”一样,可以搭 建出各种界面丰富,功能灵活的应用程序。所以可以认为API函数是构筑整个WINDOWS框架的基石,在它的下面是WINDOWS的操作系统核心,而它的 上面则是所有的华丽的WINDOWS应用程序。

但是,没有合适的Windows编程平台,程序员想编写具有Windows风格的软件,必须借助API,API也因此被赋予至高无上的地位。那时的 WINDOWS程序开发还是比较复杂的工作,程序员必须熟记一大堆常用的API函数,而且还得对WINDOWS操作系统有深入的了解。然而随着软件技术的 不断发展,在WINDOWS平台上出现了很多优秀的可视化编程环境,程序员可以采用“即见即所得”的编程方式来开发具有精美用户界面和功能强大的应用程 序。

这些优秀可视化编程环境操作简单、界面友好(诸如VB、VC++、DELPHI等),在这些工具中提供了大量的类库和各种控件,它们替代了API的神 秘功能,事实上这些类库和控件都是构架在WIN32 API函数基础之上的,是封装了的API函数的集合。它们把常用的API函数的组合在一起成为一个控件或类库,并赋予其方便的使用方法,所以极大的加速了 WINDOWS应用程序开发的过程。有了这些控件和类库,程序员便可以把主要精力放在程序整体功能的设计上,而不必过于关注技术细节。

实际上如果我们要开发出更灵活、更实用、更具效率的应用程序,必然要涉及到直接使用API函数,虽然类库和控件使应用程序的开发简单的多,但它们只提 供WINDOWS的一般功能,对于比较复杂和特殊的功能来说,使用类库和控件是非常难以实现的,这时就需要采用API函数来实现。

这也是API函数使用的场合,所以我们对待API函数不必刻意去研究每一个函数的用法,那也是不现实的(能用得到的API函数有几千个呢)。正如某位 大虾所说:API不要去学,在需要的时候去查API帮助就足够了。但是,许多API函数令人难以理解,易于误用,还会导致出错,这一切都阻碍了它的推广。 本专题就是想帮助那些想快速掌握API函数用法的同志们。通过对API函数的分类,结合一些有趣的实例,应该可以达到快速掌握的目的。

Read: 747

在数据库中存储层次数据

作者:Gijs Van Tulder
翻译:ShiningRay @ NirvanaStudio

无论你要构建自己的论坛,在你的网站上发布消息还是书写自己的cms [1]程序,你都会遇到要在数据库中存储层次数据的情况。同时,除非你使用一种像XML [2]的数据库,否则关系数据库中的表都不是层次结构的,他们只是一个平坦的列表。所以你必须找到一种把层次数据库转化的方法。

存储树形结构是一个很常见的问题,他有好几种解决方案。主要有两种方法:邻接列表模型和改进前序遍历树算法

在本文中,我们将探讨这两种保存层次数据的方法。我将举一个在线食品店树形图的例子。这个食品店通过类别、颜色和品种来组织食品。树形图如下:

1105_tree

本文包含了一些代码的例子来演示如何保存和获取数据。我选择PHP [3]来写例子,因为我常用这个语言,而且很多人也都使用或者知道这个语言。你可以很方便地把它们翻译成你自己用的语言。

邻接列表模型(The Adjacency List Model)

我们要尝试的第一个——也是最优美的——方法称为“邻接列表模型”或称为“递归方法”。它是一个很优雅的方法因为你只需要一个简单的方法来在你的树中进行迭代。在我们的食品店中,邻接列表的表格如下:

1105_table1

如你所见,对每个节点保存一个“父”节点。我们可以看到“Pear [4]”是“Green”的一个子节点,而后者又是“Fruit”的子节点,如此类推。根节点,“Food”,则他的父节点没有值。为了简单,我只用了“title”值来标识每个节点。当然,在实际的数据库中,你要使用数字的ID。

显示树

现在我们已经把树放入数据库中了,得写一个显示函数了。这个函数将从根节点开始——没有父节点的节点——同时要显示这个节点所有的子节点。对于这些子节点,函数也要获取并显示这个子节点的子节点。然后,对于他们的子节点,函数还要再显示所有的子节点,然后依次类推。

也许你已经注意到了,这种函数的描述,有一种普遍的模式。我们可以简单地只写一个函数,用来获得特定节点的子节点。这个函数然后要对每个子节点调用自身来再次显示他们的子节点。这就是“递归”机制,因此称这种方法叫“递归方法”。

<?php
// $parent 是我们要查看的子节点的父节点
// $level 会随着我们深入树的结构而不断增加,
// 用来显示一个清晰的缩进格式
function display_children($parent, $level) {
// 获取$parent的全部子节点
$result = mysql_query('SELECT title FROM tree '.
'WHERE parent="'.$parent.'";');

// 显示每个节点
while ($row = mysql_fetch_array($result)) {
// 缩进并显示他的子节点的标题
echo str_repeat(' ',$level).$row['title']."n";

// 再次调用这个函数来显着这个子节点的子节点
display_children($row['title'], $level+1);
}
}
?>

要实现整个树,我们只要调用函数时用一个空字符串作为$parent$level = 0: display_children('',0); 函数返回了我们的食品店的树状图如下:

Food
Fruit
Red
Cherry
Yellow
Banana
Meat
Beef
Pork

注意如果你只想看一个子树,你可以告诉函数从另一个节点开始。例如,要显示“Fruit”子树,你只要display_children('Fruit',0);

The Path to a Node节点的路径

利用差不多的函数,我们也可以查询某个节点的路径如果你只知道这个节点的名字或者ID。例如,“Cherry”的路径是“Food”> “Fruit”>“Red”。要获得这个路径,我们的函数要获得这个路径,这个函数必须从最深的层次开始:“Cheery”。但后查找这个节点的父 节点,并添加到路径中。在我们的例子中,这个父节点是“Red”。如果我们知道“Red”是“Cherry”的父节点。

<?php
// $node 是我们要查找路径的那个节点的名字
function get_path($node) {
// 查找这个节点的父节点
$result = mysql_query('SELECT parent FROM tree '.
'WHERE title="'.$node.'";');
$row = mysql_fetch_array($result);

// 在这个array [5] 中保存数组
$path = array();

// 如果 $node 不是根节点,那么继续
if ($row['parent']!='') {
// $node 的路径的最后一部分是$node父节点的名称
$path[] = $row['parent'];

// 我们要添加这个节点的父节点的路径到现在这个路径
$path = array_merge(get_path($row['parent']), $path);
}

// 返回路径
return $path;
}
?>

这个函数现在返回了指定节点的路径。他把路径作为数组返回,这样我们可以使用print_r(get_path('Cherry')); 来显示,其结果是:

Array
(
[0] => Food
[1] => Fruit
[2] => Red
)

不足

正如我们所见,这确实是一个很好的方法。他很容易理解,同时代码也很简单。但是邻接列表模型的缺点在哪里呢?在大多数编程语言中,他运行很慢,效率很差。这主要是“递归”造成的。我们每次查询节点都要访问数据库。

每次数据库查询都要花费一些时间,这让函数处理庞大的树时会十分慢。

造成这个函数不是太快的第二个原因可能是你使用的语言。不像Lisp这类语言,大多数语言不是针对递归函数设计的。对于每个节点,函数都要调用他自 己,产生新的实例。这样,对于一个4层的树,你可能同时要运行4个函数副本。对于每个函数都要占用一块内存并且需要一定的时间初始化,这样处理大树时递归 就很慢了。

改进前序遍历树

现在,让我们看另一种存储树的方法。递归可能会很慢,所以我们就尽量不使用递归函数。我们也想尽量减少数据库查询的次数。最好是每次只需要查询一次。

我们先把树按照水平方式摆开。从根节点开始(“Food”),然后他的左边写上1。然后按照树的顺序(从上到下)给“Fruit”的左边写上2。这 样,你沿着树的边界走啊走(这就是“遍历”),然后同时在每个节点的左边和右边写上数字。最后,我们回到了根节点“Food”在右边写上18。下面是标上 了数字的树,同时把遍历的顺序用箭头标出来了。

1105_numbering

我们称这些数字为左值和右值(如,“Food”的左值是1,右值是18)。正如你所见,这些数字按时了每个节点之间的关系。因为“Red”有3和6 两个值,所以,它是有拥有1-18值的“Food”节点的后续。同样的,我们可以推断所有左值大于2并且右值小于11的节点,都是有2-11的 “Food”节点的后续。这样,树的结构就通过左值和右值储存下来了。这种数遍整棵树算节点的方法叫做“改进前序遍历树”算法。

在继续前,我们先看看我们的表格里的这些值:

1105_table2

注意单词“left”和“right”在SQL中有特殊的含义。因此,我们只能用“lft”和“rgt”来表示这两个列。(译注——其实Mysql 中可以用“`”来表示,如“`left`”,MSSQL中可以用“[]”括出,如“[left]”,这样就不会和关键词冲突了。)同样注意这里我们已经不 需要“parent”列了。我们只需要使用lft和rgt就可以存储树的结构。

获取树

如果你要通过左值和右值来显示这个树的话,你要首先标识出你要获取的那些节点。例如,如果你想获得“Fruit”子树,你要选择那些左值在2到11的节点。用SQL语句表达:

SELECT * FROM tree WHERE lft BETWEEN 2 AND 11;

这个会返回:

1105_table3

好吧,现在整个树都在一个查询中了。现在就要像前面的递归函数那样显示这个树,我们要加入一个ORDER BY子句在这个查询中。如果你从表中添加和删除行,你的表可能就顺序不对了,我们因此需要按照他们的左值来进行排序。

SELECT * FROM tree WHERE lft BETWEEN 2 AND 11 ORDER BY lft ASC;

就只剩下缩进的问题了。

要显示树状结构,子节点应该比他们的父节点稍微缩进一些。我们可以通过保存一个右值的一个栈。每次你从一个节点的子节点开始时,你把这个节点的右值 添加到栈中。你也知道子节点的右值都比父节点的右值小,这样通过比较当前节点和栈中的前一个节点的右值,你可以判断你是不是在显示这个父节点的子节点。当 你显示完这个节点,你就要把他的右值从栈中删除。要获得当前节点的层数,只要数一下栈中的元素。

<?php
function display_tree($root) {
// 获得$root节点的左边和右边的值
$result = mysql_query('SELECT lft, rgt FROM tree '.
'WHERE title="'.$root.'";');
$row = mysql_fetch_array($result);

// 以一个空的$right栈开始
$right = array();

// 现在,获得$root节点的所有后序
$result = mysql_query('SELECT title, lft, rgt FROM tree '.
'WHERE lft BETWEEN '.$row['lft'].' AND '.
$row['rgt'].' ORDER BY lft ASC;');

// 显示每一行
   while ($row = mysql_fetch_array($result)) {
     // 检查栈里面有没有元素
     if (count($right)>0) {
       // 检查我们是否需要从栈中删除一个节点
       while ($right[count($right)-1]<$row['rgt']) {
         array_pop($right);
       }
     }

     // 显示缩进的节点标题
     echo str_repeat(' ',count($right)).$row['title']."n";

     // 把这个节点添加到栈中
     $right[] = $row['rgt'];
   }
}
?>

如果运行这段代码,你可以获得和上一部分讨论的递归函数一样的结果。而这个函数可能会更快一点:他不采用递归而且只是用了两个查询

节点的路径

有了新的算法,我们还要另找一种新的方法来获得指定节点的路径。这样,我们就需要这个节点的祖先的一个列表。

由于新的表结构,这不需要花太多功夫。你可以看一下,例如,4-5的“Cherry”节点,你会发现祖先的左值都小于4,同时右值都大于5。这样,我们就可以使用下面这个查询:

SELECT title FROM tree WHERE lft < 4 AND rgt > 5 ORDER BY lft ASC;

注意,就像前面的查询一样,我们必须使用一个ORDER BY子句来对节点排序。这个查询将返回:

+-------+
| title |
+-------+
| Food |
| Fruit |
| Red   |
+-------+

我们现在只要把各行连起来,就可以得到“Cherry”的路径了。

有多少个后续节点?How Many Descendants

如果你给我一个节点的左值和右值,我就可以告诉你他有多少个后续节点,只要利用一点点数学知识。

因为每个后续节点依次会对这个节点的右值增加2,所以后续节点的数量可以这样计算:

descendants = (right – left - 1) / 2

利用这个简单的公式,我可以立刻告诉你2-11的“Fruit”节点有4个后续节点,8-9的“Banana”节点只是1个子节点,而不是父节点。

自动化树遍历

现在你对这个表做一些事情,我们应该学习如何自动的建立表了。这是一个不错的练习,首先用一个小的树,我们也需要一个脚本来帮我们完成对节点的计数。

让我们先写一个脚本用来把一个邻接列表转换成前序遍历树表格。

<?php
function rebuild_tree($parent, $left) {
// 这个节点的右值是左值加1
$right = $left+1;

// 获得这个节点的所有子节点
$result = mysql_query('SELECT title FROM tree '.
'WHERE parent="'.$parent.'";');
while ($row = mysql_fetch_array($result)) {
// 对当前节点的每个子节点递归执行这个函数
// $right 是当前的右值,它会被rebuild_tree函数增加
$right = rebuild_tree($row['title'], $right);
}

// 我们得到了左值,同时现在我们已经处理这个节点我们知道右值的子节点
mysql_query('UPDATE tree SET lft='.$left.', rgt='.
$right.' WHERE title="'.$parent.'";');

// 返回该节点的右值+1
return $right+1;
}
?>

这是一个递归函数。你要从rebuild_tree('Food',1); 开始,这个函数就会获取所有的“Food”节点的子节点。

如果没有子节点,他就直接设置它的左值和右值。左值已经给出了,1,右值则是左值加1。如果有子节点,函数重复并且返回最后一个右值。这个右值用来作为“Food”的右值。

递归让这个函数有点复杂难于理解。然而,这个函数确实得到了同样的结果。他沿着树走,添加每一个他看见的节点。你运行了这个函数之后,你会发现左值和右值和预期的是一样的(一个快速检验的方法:根节点的右值应该是节点数量的两倍)。

添加一个节点

我们如何给这棵树添加一个节点?有两种方式:在表中保留“parent”列并且重新运行rebuild_tree() 函数——一个很简单但却不是很优雅的函数;或者你可以更新所有新节点右边的节点的左值和右值。

第一个想法比较简单。你使用邻接列表方法来更新,同时使用改进前序遍历树来查询。如果你想添加一个新的节点,你只需要把节点插入表格,并且设置好parent列。然后,你只需要重新运行rebuild_tree() 函数。这做起来很简单,但是对大的树效率不高。

第二种添加和删除节点的方法是更新新节点右边的所有节点。让我们看一下例子。我们要添加一种新的水果——“Strawberry”,作为“Red” 的最后一个子节点。首先,我们要腾出一个空间。“Red”的右值要从6变成8,7-10的“Yellow”节点要变成9-12,如此类推。更新“Red” 节点意味着我们要把所有左值和右值大于5的节点加上2。

我们用一下查询:

UPDATE tree SET rgt=rgt+2 WHERE rgt>5;
UPDATE tree SET lft=lft+2 WHERE lft>5;

现在我们可以添加一个新的节点“Strawberry”来填补这个新的空间。这个节点左值为6右值为7。

INSERT INTO tree SET lft=6, rgt=7, title='Strawberry';

如果我们运行display_tree() 函数,我们将发现我们新的“Strawberry”节点已经成功地插入了树中:

Food
Fruit
Red
Cherry
Strawberry
Yellow
Banana
Meat
Beef
Pork

缺点

首先,改进前序遍历树算法看上去很难理解。它当然没有邻接列表方法简单。然而,一旦你习惯了左值和右值这两个属性,他就会变得清晰起来,你可以用这 个技术来完成临街列表能完成的所有事情,同时改进前序遍历树算法更快。当然,更新树需要很多查询,要慢一点,但是取得节点却可以只用一个查询。

总结

你现在已经对两种在数据库存储树方式熟悉了吧。虽然在我这儿改进前序遍历树算法性能更好,但是也许在你特殊的情况下邻接列表方法可能表现更好一些。这个就留给你自己决定了

最后一点:就像我已经说得我部推荐你使用节点的标题来引用这个节点。你应该遵循数据库标准化的基本规则。我没有使用数字标识是因为用了之后例子就比较难读。

进一步阅读

数据库指导 Joe Celko写的更多关于SQL数据库中的树的问题:
http://searchdatabase.techtarget.com/tip/1,289483,sid13_gci537290,00.html [6]

另外两种处理层次数据的方法:
http://www.evolt.org/article/Four_ways_to_work_with_hierarchical_data/17/4047/index.html [7]

Xindice, “本地XML数据库”:
http://xml.apache.org/xindice/ [8]

递归的一个解释:
http://www.strath.ac.uk/IT/Docs/Ccourse/subsection3_9_5.html [9]

Read: 1285

树形结构问题的一种解决办法

在一个标准的树形结构里面,一般要想知道的操作基本上有查询子树和查询所有父树(路径),还有就是查询子树自身的深度问题,通过前些日子的总结发现一种结构可以很好的满足这些要求。
id parentID   name        path
———————————-
1     0              图书          0,1
2     1      
      管理类       0,1,2
3     2      
     名家作品     0,1,2,3
4     2      
     国内          0,1,2,4
5     2      
     外国          0,1,2,5
6     0      
     文学类       0,6
7     6      
     小说          0,6,7

上面这种结构就是我推荐的,这种结构对于我们通常用的id-parentID结构增加了一个路径(path)列,好处就是可以直接通过查询path列来找 出某个节点的子树和路径,比如2号节点的子树可以通过先查2号子树的path,然后再用like ‘0,1,2%’方式来得到。其路径可以通过in(0,1,2)查询到。深度可以通过explode(‘,’,$path),然后看看数组个数就可以看出 节点的深度了,这样就可以构建出一个树形结构了。
path列的构建可以在原有id-parentID结构上增加,通过id和parentid可以很方便的构建出某个节点的子树的path列(需要递归算法)。
常用的函数可以有三个:
nav($node) 根据节点求出其路径,先找出节点path然后根据path用"select * from category where id in ($path)",就能求出其路径所有节点了,方便导航。
tree($node)根据节点求出其所有子树,先找出节点path,然后用"select * from category where path like ‘$path%’",就可以找出所有子节点啦,这里可以通过计算每个节点的path中有几个元素explode(‘,’$path)计算每个节点的深度。
re_path($node)根据节点,重新构建子树的path,这个需要利用ID和ParentID两列,利用递归逐步计算,因为只有在改变子树隶属关系和建立新节点的时候用到,所以计算量应该不是很多。
好了上面是这种算法的基本构想,大家有什么新的构想,提出来讨论一下。

“改进前序遍历树”方法,这里有相关中文翻译介绍,http://www.nirvanastudio.org/category/database/
我这里提的方法很像“邻接列表模型”,就是增加了一个path列,所以针对邻接列表模型所遇到的最大的问题,子树、路径和深度问题得到了改善。
而改进前序遍历树方法也是有很大的局限的,就是当某个节点变换父节点时需要做大量的修改左右数值。而上面的方法修改量要小一点!
不知道这么说是否妥当,请指教!

附常用的三个函数:
function nav($id,$db){
$rs=$db->query("select * from category where id=’".$id."’");
$row=$rs->fetch();
$rs=$db->query("select * from category where id in (".$row[‘path’].") order by path");
return $rs->fetchall();
}
function tree($id,$db){
$rs=$db->query("select * from category where id=’".$id."’");
$row=$rs->fetch();
$rs=$db->query("select * from category where path like ‘".$row[‘path’]."%’ order by path");
$tree=$rs->fetchall();
foreach($tree as $key=>$row){
$depth=explode(‘,’,$row[‘path’]);
$depth=count($depth);
$tree[$key][‘depth’]=$depth;
}
return $tree;
}

function re_path($id,$parentpath=”,$db){//此函数使用前需要先获得父节点的path
       if($parentpath<>”){$path=$parentpath.’,’.$id;}else{$path=$id;}
$sql="update category set path=’".$path."’ where id=’"$id"’";
$rs=$db->query($sql);

$sql="select * from category where parentid=’".$id."’";
$rs=$db->query($sql);
while($row=$rs->fetch()){
   re_path($row[‘id’],$path,$db);
}
}

Read: 878

再什么是构件

中科永联高级技术培训中心(www.itisedu.com

      构件是系统中实际存在的可更换部分,它实现特定的功能,符合一套接口标准并实现一组接口。构件代表系统中的一部分物理实施,包括软件代码(源代码、二进制代码或可执行代码)或其等价物(如脚本或命令文件)。在图中,构件表示为一个带有标签的矩形。

部署构件的示例

可执行文件 例如 .exe 文件
链接库 例如 .dll 文件
Applet 例如 Java 中的 .class 文件
Web 页面 例如 .htm 和 .html 文件
数据库
 

工作产品构件的示例

源代码文件 例如 C++ 和 CORBA IDL 中的 .h、.cpp 和 .hpp 文件,或 Java 中的 .java 文件
二进制文件 例如链接到可执行文件的 .o 文件和 .a 文件。
SOM 文件 IDL 和一些绑定
编译文件 例如 UNIX 中的 makefile

使用

设计中的对象被作为部署构件进行实施。您需要确定如何将设计类映射为代码;这应该在项目专用的设计指南中有所说明。

有关如何将设计类映射为代码的详细信息,请参见活动:实施构件。另请参见指南:类。

      实施构件与修改构件在项目的配置管理环境中进行。实施员在为他们提供的专用开发工作区(请参见活动:创建开发工作区)中,按照工件:工作单所指定的内容开展工作。在该工作区中,创建源元素并将其置于配置管理之下,或者在通常的检出、编辑、构建、单元测试、检入周期中进行修改(请参见活动:进行变更)。完成某个构件集(根据一个或多个工作单的定义以及即将生成的工作版本要求)后,实施员将把有关新的和修改过的构件交付(请参见活动:交付变更内容)到子系统集成工作区,以便与其他实施员的工作进行集成。最后,实施员可以在方便的时候对专用开发工作区进行更新(或者重新调整基线),使该工作区与子系统集成工作区保持一致(请参见活动:更新工作区)。

当实施类时,应遵循编程指南。

实施的主要基础是具有公有操作、属性与关联关系的类。务必要注意,并不是所有公有操作、属性与关联关系都在设计过程中定义。

实施的辅助基础是用例实现,用例实现显示了类和对象如何通过交互来执行用例。

最好以递增的方式实施类;编译、链接和运行一些回归测试,每天进行三两次。

在从零开始实施一个类之前,可考虑修改现有的实施类(一般可通过建立子类或进行实例化来修改)。

实施操作


要实施操作,请执行以下步骤:

选择算法
选择适合算法的数据结构
根据需要定义新的类和操作
编写操作代码
选择算法
许多操作都十分简单,可以从该操作及其规约中立即实施。

之所以需要特殊算法,主要是为了实施定义了规约的复杂操作,并优化那些以简单但却低效的算法为定义的操作。

选择适合算法的数据结构
选择算法包括选择算法所基于的数据结构。许多实施数据结构是容器类,例如数组、列表、队列、栈、集合、无序单位组,以及这些类的各种不同形式。许多面向对象的语言和编程环境都提供了具有这些可复用构件的类库。

根据需要定义新的类和操作
比如,可以使用新类来保存中间结果,也可对类添加新的低级操作来分解复杂操作。通常,这些操作是类的私有操作,所以在类之外看不见这些操作。

编写操作代码
要编写操作的代码,可从接口语句开始,例如 C++ 中的成员函数声明、Ada 中的子程序规约或 Visual Basic 中的方法。请遵循编程指南。

实施构件工作流程明细

实施状态


对象的状态可通过引用其属性值来实施,而不必作特殊说明。这种对象的状态转移将隐含于变化的属性值中,而变化的行为通过条件语句来编程。 但对于复杂行为,该方法不能令人满意,因为它往往会导致复杂的结构;而当添加更多状态或当行为发生变化时,将很难更改这些结构。

如果构件(或其组成部分)的行为随状态而定,则通常会有一个或多个状态图来说明组成该构件的模型元素的行为。这些状态图可用作实施过程中的重要输入。有关详细信息,请参见指南:状态图。

状态图中所示的状态机将表现对象的状态,并详尽说明状态转移及所需的行为。可以通过以下几种方法来实施状态机:

对于简单的状态机,定义一项列举可能状态的属性,然后使用该属性在 Java 或 C++ 中的 switch 语句中选择进入消息的行为。但这种方法不太适用于复杂的状态机,它可能会导致运行时性能降低。如需此方法的示例,请参见 [DOUG98],第 4 章 4.4.3
对于较复杂的状态机,可使用状态模式。有关状态模式的说明,请参见 [GAM94]。[DOUG98],第 6 章 6.2.3 状态模式也说明了这种方法
表驱动法对于极复杂的状态机十分有效,其特点是易于变更。当使用这种方法时,各个状态在表中都有相应的条目,这些条目将输入映射到后继状态和相关的转移动作。如需此方法的示例,请参见 [DOUG98],第 6 章 6.2.3 状态表模式。
要实施具有并行子状态的状态机,可以将状态管理委派给主动对象(每个对象都被委派一个并行子状态),因为并行子状态代表了独立的计算(但仍可能进行交互)。每个子状态均可通过上述方法之一来进行管理。

通过委托关系复用实施


如果一个类或一个类的某些部分可通过复用现有类来实施,则应通过委托关系(而不要继承)来实现。

委托表示一个类借助于其他类来得以实施。该类通过使用变量来引用其他类的对象。当调用某操作时,该操作将调用被引用对象(属于被复用的类)中的操作,以实际执行该操作。这样,它就将职责委派给了其他类。

实施关联关系


单向关联关系将作为指针(包含对象引用的属性)来进行实施。如果多重性为一,则将单向关联关系当作简单指针来实施。如果多重性为多个,则将其当作指针集来实施。但如果“多”端是有序排列的,则可以使用列表,而不使用集合。

双向关联关系将作为属性,使用单向关联关系的技术在两个方向上实施。

限定关联关系将作为限定对象中的查询表(如一个 Smalltalk Dictionary 类)来实施。查询表中的选择器值是限定词,而目标值是另一个类的对象。

如果必须按顺序访问限定词的值,就应将限定词组织成经过排序的数组或树。在这种情况下,访问时间将与 log N 成比例,其中 N 为限定词值的数目。

如果限定词取自于一个紧凑的有限集,就可以将限定词的值映射到一个整数范围,并将关联关系当作数组来有效地进行实施。如果关联关系已基本上填满(而不是稀疏填充),此方法会更加有效;而对于完全填满的有限集,它可以算是理想的方法。

许多面向对象的语言和编程环境都提供了具有可复用构件的类库,可用于实施不同种类的关联关系。

实施属性


可以作为内置基本变量、可复用的构件类或定义一个新类来实现属性。定义新类通常是较为灵活的方法,但它却会带来不必要的间接性。例如,实施雇员的社会保障号时,既可将它作为类型“字符串”的属性,也可将它作为一个新类。

属性的备选实施。

另一种可能的情况是:属性组组成了新类,如下例所示。这两种实施都是正确的。

将 Line 中的属性当作 Point 类的关联关系来实施。

向设计提供反馈


在以上任何步骤中,如果发现了设计错误,都必须向设计提供返工反馈。如果所需的变更较小,就可以由同一个人来设计并实施类,而无需提出正式的变更请求。他可在设计中进行变更。

如果所需的变更影响到几个类(例如在公有操作中的变更),则应向 CCB(变更控制委员会)提交正式的变更请求。请参见活动:修复缺陷。

评估代码


在开始单元测试之前,可以先作一些检查。测试是一项花费较多的工作,因此最好先执行以下几项检查:

始终对代码进行编译。将编译器的警告等级设置到最详细的程度。
通过想像对操作进行检查。通读代码,尽可能考虑到所有情况,发现各种异常情况。一旦进行了新的实施活动,就需进行此项工作。
使用工具检查代码中是否存在错误。例如,使用静态代码规则检查程序。

© 1987 – 2001 Rational Software Corporation。版权所有。

Read: 887

又关于什么是框架

中科永联高级技术培训中心(www.itisedu.com

      框架Framework)是整个或部分系统的可重用设计,表现为一组抽象构件及构件实例间交互的方法;另一种定义认为,框架是可被应用开发者定制的应用骨架。前者是从应用方面而后者是从目的方面给出的定义。

      可以说,一个框架是一个可复用的设计构件,它规定了应用的体系结构,阐明了整个设计、协作构件之间的依赖关系、责任分配和控制流程,表现为一组抽象以及其实例之间协作的方法,它为构件复用提供了上下文(Context)关系。因此构件库的大规模重用也需要框架。

      构件领域框架方法在很大程度上借鉴了硬件技术发展的成就,它是构件技术、软件体系结构研究和应用软件开发三者发展结合的产物。在很多情况下,框架通常以构件库的形式出现,但构件库只是框架的一个重要部分。框架的关键还在于框架内对象间的交互模式和控制流模式。

      框架比构件可定制性强。在某种程度上,将构件和框架看成两个不同但彼此协作的技术或许更好。框架为构件提供重用的环境,为构件处理错误、交换数据及激活操作提供了标准的方法。

      应用框架的概念也很简单。它并不是包含构件应用程序的 小片程序,而是实现了某应用领域通用完备功能(除去特殊应用的部分)的底层服务。使用这种框架的编程人员可以在一个通用功能已经实现的基础上开始具体的系 统开发。框架提供了所有应用期望的默认行为的类集合。具体的应用通过重写子类(该子类属于框架的默认行为)或组装对象来支持应用专用的行为。

      应用框架强调的是软件的设计重用性和系统的可扩充性,以缩短大型应用软件系统的开发周期,提高开发质量。与传统的基于类库的面向对象重用技术比较,应用框架更注重于面向专业领域的软件重用。应用框架具有领域相关性,构件根据框架进行复合而生成可运行的系统。框架的粒度越大,其中包含的领域知识就更加完整。

      框架,即framework。其实就是某种应用的半成品,就是一组组件,供你选用完成你自己的系统。简单说就是使用别人搭好的舞台,你来做表演。而且,框架一般是成熟的,不断升级的软件。

       框架的概念最早起源于Smalltalk环境,其中最著名的框架是Smalltalk 80的用户界面框架MVC(Model -View-Controller)。随着用户界面框架Interviews [Linton 89]和ET++ [Weinand 89] 的开发和发布,框架研究越来越受到研究人员的重视。虽然框架研究最初起源于用户界面领域,但它还被成功地应用到其他领域中,如操作系统 [Russo 90]、火警系统 [Molin 96a,Molin 96b] 等。Taligent公司于1992年成立后,框架研究受到了广泛的重视。该公司计划基于框架来开发一个完整的面向对象操作系统。另外,该公司还发布了一 套支持快速应用开发的工具集CommonPoint,其中包括了上百个面向对象框架 [Andert 94,Cotter 95]。
框架目前还没有统一的定义,其中Ralph Johnson所给出的定义基本上为大多数研究人员所接受:

      一个框架是一个可复用设计,它是由一组抽象类及其实例间协作关系来表达的 [Johnson 98]。

      这个定义是从框架内涵的角度来定义框架的,当然也可以从框架用途的角度来给出框架的定义:

      一个框架是在一个给定的问题领域内,一个应用程序的一部分设计与实现[Bosch 97]。

      从以上两个定义可以看出,框架是对特定应用领域中的应用系统的部分设计和实现,它定义了一类应用系统(或子系统)的整体结构。框架将应用系统划分为类和对象,定义类和对象的责任,类和对象如何互相协作,以及对象之间的控制线程。这些共有的设计因素由框架预先定义,应用开发人员只须关注于特定的应用系统特有部分。框架刻画了其应用领域所共有的设计决策,所以说框架着重于设计复用,尽管框架中可能包含用某种程序设计语言实现的具体类。

      一个基于框架开发的应用系统包含一个或多个框架,与框架相关的构件类,以及与应用系统相关的功能扩展。与应用系统相关的扩展包括与应用系统相关的类和对象。应用系统可能仅仅复用了面向对象框架的一部分,或者说,它可能需要对框架进行一些适应性修改,以满足系统需求

      面向对象的框架作为一种可复用的软件,在基于框架的软件开发过程中会涉及到框架的开发和利用两个方面的工作。框架的开发阶段在于产生领域中可复用的设计。 该阶段的主要结果是框架以及与框架相关的构件类。该阶段的一个重要活动是框架的演变和维护。象所有软件一样,框架也易于变化。产生变化的原因很多,如应用 出错,业务领域变化,等等。

      不论是哪一种技术,最终都是为业务发展而服务的。从业务的角度来讲。首先,框架的是为了企业的业务发展和战略规划而服务的,他服从于企业的愿景 (vision);其次,框架最重要的目标是提高企业的竞争能力,包括降低成本、提高质量、改善客户满意程度,控制进度等方面。最后,框架实现这一目标的 方式是进行有效的知识积累。软件开发是一种知识活动,因此知识的聚集和积累是至关重要的。框架能够采用一种结构化的方式对某个特定的业务领域进行描述,也 就是将这个领域相关的技术以代码、文档、模型等方式固化下来。

一、框架要解决的问题

      框架要解决的最重要的一个问题是技术整合的问题,在J2EE的 框架中,有着各种各样的技术,不同的软件企业需要从J2EE中选择不同的技术,这就使得软件企业最终的应用依赖于这些技术,技术自身的复杂性和技术的风险 性将会直接对应用造成冲击。而应用是软件企业的核心,是竞争力的关键所在,因此应该将应用自身的设计和具体的实现技术解耦。这样,软件企业的研发将集中在 应用的设计上,而不是具体的技术实现,技术实现是应用的底层支撑,它不应该直接对应用产生影响。

      要理解这一点,我们来举一些例子:

      一个做视频流应用的软件企业,他为电广行业提供整体的解决方案。他的优势在于将各种各样的视频硬件、服务器、和管理结合起来,因此他扮演的是一个集成商的 角色。因此他的核心价值在于使用软件技术将不同的硬件整合起来,并在硬件的整合层面上提供一个统一的管理平台。所以他的精力应该放在解决两个问题:

      如何找到一种方法,将不同的硬件整合起来,注意,这里的整合并不是技术整合,而是一种思路上的整合。首先要考虑的绝对不是要使用什么技术,而是这些硬件需 要提供哪些服务,需要以什么样的方式进行管理。因此,这时候做的事情实际上是对领域进行建模。例如,我们定义任何一种硬件都需要提供两种能力,一种是统一 的管理接口,用于对所有硬件统一管理;另一种是服务接口,系统平台可以查询硬件所能够提供的服务,并调用这些服务。所以,设计的规范将会针对两种能力进 行。

      另一个问题是如何描述这个管理系统的规范。你需要描述各种管理活动,以及管理中所涉及的不同实体。因为管理系统是针对硬件的管理,所以它是构架在硬件整合平台之上的。

      在完成业务层面的设计之后,我们再来看看具体的技术实现。光有规范和设计是不够的,我们还需要选择一个优秀的技术。由于是对不同硬件的整合,我们想到采用Java提供的JMX技术。JMX技术适合用来进行系统整合,它定义了一个通用的规范,并给出了远程管理端口的一些默认实现。JMX已经经过了实践的检验,不少的应用服务器都采用了以JMX为基础的结构,例如有名的JBoss。JMX已经是一个很好的开始了,但是我们还需要在JMX的基础上再做一些工作。

二、什么要用框架?

      因为软件系统发展到今天已经很复杂了,特别是服务器端软件,设计到的知识,内容,问题太多。在某些方面使用别人成熟的框架,就相当于让别人帮你完成一些基 础工作,你只需要集中精力完成系统的业务逻辑设计。而且框架一般是成熟,稳健的,他可以处理系统很多细节问题,比如,事物处理,安全性,数据流控制等问 题。还有框架一般都经过很多人使用,所以结构很好,所以扩展性也很好,而且它是不断升级的,你可以直接享受别人升级代码带来的好处。

      框架一般处在低层应用平台(如J2EE)和高层业务逻辑之间的中间层

      软件为什么要分层
      为了实现“高内聚、低耦合”。把问题划分开来各个解决,易于控制,易于延展,易于分配资源…总之好处很多啦:)。

三、为什么要进行框架开发?

      框架的最大好处就是重用。面向对象系统获得的最大的复用方式就是框架,一个大的应用系统往往可能由多层互相协作的框架组成。

      由于框架能重用代码,因此从一已有构件库中建立应用变得非常容易,因为构件都采用框架统一定义的接口,从而使构件间的通信简单。

      框架能重用设计。它提供可重用的抽象算法及高层设计,并能将大系统分解成更小的构件,而且能描述构件间的内部接口。这些标准接口使在已有的构件基础上通过 组装建立各种各样的系统成为可能。只要符合接口定义,新的构件就能插入框架中,构件设计者就能重用构架的设计。

      框架还能重用分析。所有的人员若按照框架的思想来分析事务,那么就能将它划分为同样的构件,采用相似的解决方法,从而使采用同一框架的分析人员之间能进行沟通。

      采用框架技术进行软件开发的主要特点包括:


领域内的软件结构一致性好;

建立更加开放的系统;

重用代码大大增加,软件生产效率和质量也得到了提高;

软件设计人员要专注于对领域的了解,使需求分析更充分;

存储了经验,可以让那些经验丰富的人员去设计框架和领域构件,而不必限于低层编程;

允许采用快速原型技术;

有利于在一个项目内多人协同工作;

      大粒度的重用使得平均开发费用降低,开发速度加快,开发人员减少,维护费用降低,而参数化框架使得适应性、灵活性增强。

四、与框架相关的概念

1. 白盒与黑盒框架

      框架可分为白盒(White-Box)与黑盒(Black-Box)两种框架。

       基于继承的框架被称为白盒框架。所谓白盒即具备可视性,被继承的父类的内部实现细节对子类 而言都是可知的。利用白盒框架的应用开发者通过衍生子类或重写父类的成员方法来开发系统。子类的实现很大程度上依赖于父类的实现,这种依赖性限制了重用的 灵活性和完全性。但解决这种局限性的方法可以是只继承抽象父类,因为抽象类基本上不提供具体的实现。白盒框架是一个程序骨架,而用户衍生出的子类是这个骨 架上的附属品。

      基于对象构件组装的框架就是黑盒框架。应用开发者通过整理、组装对象来获得系统的实现。用户只须了解构件的外部接口,无须了解内部的具体实现。另外,组装比继承更为灵活,它能动态地改变,继承只是一个静态编译时的概念。

      在理想情况下,任何所需的功能都可通过组装已有的构件得到,事实上可获得的构件远远不能满足需求,有时通过继承获得新的构件比利用已有构件组装新构件更容 易,因此白盒和黑盒将同时应用于系统的开发中。不过白盒框架趋向于向黑盒框架发展,黑盒框架也是系统开发希望达到的理想目标。

2. 热点、食谱以及好莱坞原则

       成功的框架开发需要确定领域专用的“热点” (Hot spot)。应用开发者在框架的基础上进行开发,只须扩展框架的某些部分,“热点”就是在应用领域的一种扩展槽,开发者根据自己的需要填充这些扩展槽。 “热点”使框架具有灵活性,如在具体的实现中,扩展槽可以被看成是一些抽象类,开发者通过重写抽象方法获得具体实现。

      “食谱” (Cookbook)就是描述如何使用框架方法的文档。在“食谱”中包含了许多“烹饪”方法,这些“烹饪”方法相当于一些具体的操作步骤,描述了为解决某一专门问题如何使用框架的详细方法。框架的内部设计和实现细节通常不出现在“食谱”中。

      框架的一个重要特征就是用户定义的方法经常被框架自身调用,而不是从用户的应用代码中调用。这种机制常称为“好莱坞原则”(Hollywood Principle)或“别调用我们,我们会调用您”。

五、常见的JAVA框架有什么?

WAF:
全称:WEB APPLICATION FRAMEWORK
主要应用方面:EJB层,(WEB层也有,但是比较弱)。
主要应用技术:EJB等
出处:java.sun.com/blueprints/code/index.html">http://java.sun.com/blueprints/code/index.html
简述:这是SUN在展示J2EE平台时所用的例子PetStore(宠物商店系统)里面的框架。是SUN蓝皮书例子程序中提出的应用框架。它实现了 MVC和其他良好的设计模式。SUN的网站上有技术资料,最好下载PetStore来研究,WEBLOGIC里自带此系统,源码在beaweblogic700samplesserversrcpetstore。这是学习了解J2EE的首选框架。
免费。

Struts:
主要应用方面:WEB层。
主要应用技术:JSP,TagLib,JavaBean,XML
出处:http://jakarta.apache.org/struts/index.html
简述:这是APACHE的开源项目,目前应用很广泛。基于MVC模式,结构很好,基于JSP。Jbuilder8里已经集成了STRUTS1.02的制作。
免费。

简述WAF+STRUTS结合的例子:WEB层用STRUTS,EJB层用WAF:
JSP(TagLib)——>ActionForm——>Action ——>
Event——>EJBAction——>EJB    ——>DAO——>Database
JSP(TagLib) (forward) <——Action <——EventResponse<——                

Turbine
主要应用方面:WEB层。
主要应用技术:servlet
出处:http://jakarta.apache.org/turbine/index.html
简述:这是APACHE的开源项目。基于SERVLET。据说速度比较快,基于service(pluggable implementation可插拔的执行组件)的方式提供各种服务。
免费。

COCOON
主要应用方面:WEB层。
主要应用技术:XML,XSP,servlet等
出处:http://cocoon.apache.org/2.0/
简述:这是APACHE的一个开源项目。基于XML,基于XSP(通俗地说,XSP是在XML静态文档中加入Java程序段后形成的动态XML文档。)。特点是可以与多种数据源交互,包括文件系统,数据库,LDAP,XML资源库,网络数据源等。
免费。

ECHO:
主要应用方面:WEB层。
主要应用技术:servlet等
出处:http://www.nextapp.com/products/echo/
简述:nextapp公司的一个开源项目。基于SERVLET。页面可以做的很漂亮,结合echopoint,可以作出很多图形效果(里面用了jfreechart包)。使用SWING的思想来作网页,把HTML当作JAVA的类来做。但是大量使用Session,页面分帧(Frame)很多,系统资源消耗很大。
免费。

JATO:
全称:SUN ONE Application Framework
主要应用方面:WEB层。
主要应用技术:JSP,TagLib,JavaBean等
出处:http://www.sun.com/
简述:这是SUN推出的一个商业性框架,一看名字就知道是结合SUN ONE的平台推出的。我下载了JATO2.0看了一下,感觉有些简单,使用了JSP+TagLib+JavaBean。如他的DOC所说JATO是适合用在小的WEB应用里。
免费。

TCF:
全称:Thin-Client Framework
主要应用方面:JAVA GUI。
主要应用技术:JAVA application等
出处:http://www.alphaworks.ibm.com/tech/tcf
简 述:这是IBM出的一个框架。基于MVC模式,基于JAVA Application。推荐一篇介绍文章:java/j-tcf1/index.shtml">http://www- 900.ibm.com/developerWorks/cn/java/j-tcf1/index.shtml
收费:每个企业对象license:2000美元。

Read: 492