﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-loop_in_codes-随笔分类-other</title><link>http://www.cppblog.com/kevinlynx/category/19776.html</link><description>低调做技术__欢迎移步我的独立博客 &lt;a href="http://codemacro.com"&gt;codemaro.com&lt;/a&gt; 微博 &lt;a href="http://weibo.com/kevinlynx"&gt;kevinlynx&lt;/a&gt;</description><language>zh-cn</language><lastBuildDate>Tue, 09 Sep 2014 16:53:41 GMT</lastBuildDate><pubDate>Tue, 09 Sep 2014 16:53:41 GMT</pubDate><ttl>60</ttl><item><title>理解git常用命令原理</title><link>http://www.cppblog.com/kevinlynx/archive/2014/09/09/208257.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Tue, 09 Sep 2014 13:35:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2014/09/09/208257.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/208257.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2014/09/09/208257.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/208257.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/208257.html</trackback:ping><description><![CDATA[<div class="entry-content">
<p>git不同于类似SVN这种版本管理系统，虽然熟悉常用的操作就可以满足大部分需求，但为了在遇到麻烦时不至于靠蛮力去尝试，了解git的原理还是很有必要。</p>
<h2>文件</h2>
<p>通过git管理的文件版本信息全部存放在根目录<code>.git</code>下，稍微看下：</p>
<pre><code>$ ls .git
COMMIT_EDITMSG HEAD branches description index logs packed-refs
FETCH_HEAD ORIG_HEAD config hooks info objects refs
</code></pre>
<p>git除了提供给我们平时常用的一些命令之外，还有很多底层命令，可以用于查看以上部分文件表示的东西。</p>
<h2>三个区域/三类对象</h2>
<p>理解git里的三个区域概念非常重要。git里很多常用的命令都是围绕着这三个区域来做的。它们分别为：</p>
<ul>
     <li>working directory，也就是你所操作的那些文件</li>
     <li>history，你所提交的所有记录，文件历史内容等等。<strong>git是个分布式版本管理系统，在你本地有项目的所有历史提交记录；文件历史记录；提交日志等等。</strong>
     </li>
     <li>stage(index)，暂存区域，本质上是个文件，也就是<code>.git/index</code>
     </li>
</ul>
<!-- more -->
<p>git中还有三类常用对象（实际不止三种），理解这三类对象也很重要。分别为：</p>
<ul>
     <li>blob，用于表示一个文件</li>
     <li>tree，用于表示一个目录，索引到若干文件或子目录</li>
     <li>commit，用于表示一次提交(commit)</li>
</ul>
<p>所有对象都会以文件的形式保存在<code>.git/objects</code>目录，一个对象一个文件。</p>
<p>接下来把上面所有的内容关联起来。做以下操作：</p>
<pre><code>$ mkdir test &amp;&amp; cd test
$ git init
$ ls -a .git/objects # 没有文件
. .. info pack
$ touch readme # working directory里增加了一个readme文件
$ git add readme # 添加一个文件到stage区域
$ git ls-files --stage # 这个命令可以查看stage区域里的内容，可以看到有readme
100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 readme
$ ls -a .git/objects # 同时.git/objects增加了一个e6的目录
. .. e6 info pack
$ ls -a .git/objects/e6/ # e6目录下增加了一个文件
. .. 9de29bb2d1d6434b8b29ae775ad8c2e48c5391
</code></pre>
<p>上面的操作展示了git中三个区域三个对象的部分关联关系。git中每个对象都以一个40个字符长度的SHA-1哈希值为标识，以这40个字符的前2个字符作为文件夹，以后38个字符为文件名。</p>
<p>基于以上继续操作：</p>
<pre><code>$ git commit -m 'first commit' # commit会将stage里标识的文件提交到history区域
[master (root-commit) 8bf6969] first commit
0 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 readme
$ ls -a .git/objects # 增加了2个文件，也就是2个对象
. .. 8b e6 e8 info pack
$ git ls-files --stage # stage仅表示当前被版本管理的文件，所以内容不变
100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 readme
# git cat-file 命令可以用于查看.git/objects下的文件，意即可用于查看对象
$ git cat-file -t e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 # 这个是之前git add readme产生的文件对象 blob
blob
# 同样我们来查看git commit -m后新增的两个对象
$ ls .git/objects/8b/
f696927c17526eb8f0c6dae8badb968a001ed0
$ git cat-file -t 8bf696927c17526eb8f0c6dae8badb968a001ed0 # 记得带上8b这个文件夹名，才算一个完整的对象ID。这是一个commit对象
commit
$ ls .git/objects/e8
0ad49ace82167de62e498622d70377d913c79e
$ git cat-file -t e80ad49ace82167de62e498622d70377d913c79e # tree对象
tree
</code></pre>
<p>区域和对象如何交互的可以用下图描述：</p>
<p><img src="http://codemacro.com/assets/res/git-objects.png" alt="" /></p>
<p>通过<code>git cat-file -p</code>可以查看对象的更多描述，<code>git cat-file -t</code>仅获取对象的类型。做以下操作获得更深的认识：</p>
<pre><code># 这个commit对象记录了提交者的信息，还包括指向的tree对象
$ git cat-file -p 8bf696927c17526eb8f0c6dae8badb968a001ed0
tree e80ad49ace82167de62e498622d70377d913c79e
author Kevin Lynx &lt;kevinlynx@gmail.com&gt; 1410090424 +0800
committer Kevin Lynx &lt;kevinlynx@gmail.com&gt; 1410090424 +0800
first commit
# 查看tree对象可以看出tree指向的blob对象
$ git cat-file -p e80ad49ace82167de62e498622d70377d913c79e
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 readme
</code></pre>
<p>即使是已经被版本管理的文件，发生改动后（正常改动或合并）都使用<code>git add</code>来重新mark它。创建第二次提交进一步认识：</p>
<pre><code>$ echo 'hello git' &gt; readme
$ touch install
$ git ls-files --stage # 不使用git add，暂存区域内容没变
100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 readme
# 此时stage里内容未变，提示no changes added to commit
$ git commit
# On branch master
# Changed but not updated:
# (use "git add &lt;file&gt;..." to update what will be committed)
# (use "git checkout -- &lt;file&gt;..." to discard changes in working directory)
#
# modified: readme
#
# Untracked files:
# (use "git add &lt;file&gt;..." to include in what will be committed)
#
# install
no changes added to commit (use "git add" and/or "git commit -a")
$ git add readme
$ ls .git/objects/ # git add之后.git/objects下新增文件
8b 8d e6 e8 info pack
$ ls .git/objects/8d/
0e41234f24b6da002d962a26c2495ea16a425f
$ git cat-file -p 8d0e41234f24b6da002d962a26c2495ea16a425f # 查看该新增对象
hello git
# 这个时候还可以在提交前撤销git add readme
$ git reset readme # 从history到stage
Unstaged changes after reset:
M readme
$ cat readme
hello git
$ git checkout readme # 从stage到working directory
$ cat readme # 没有内容，回到第一个版本
$ git add install # 添加新创建的文件
$ git ls-files --stage # stage中的内容是最新的readme和新添加的install
100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0 install
100644 8d0e41234f24b6da002d962a26c2495ea16a425f 0 readme
$ ls .git/objects/
8b 8d e6 e8 info pack
</code></pre>
<p>以上，发现一个有趣的现象：新加的<code>install</code>文件的SHA-1哈希值和之前的<code>readme</code>相同，这是因为这2个文件都是空的，内容相同。继续：</p>
<pre><code>$ git commit -m 'second commit'
$ ls .git/objects/ # 提交后新增2个对象
45 72 8b 8d e6 e8 info pack
$ ls .git/objects/72/
b94e949c5fca6092cc74c751a7bb35ee71c283
$ git cat-file -p 72b94e949c5fca6092cc74c751a7bb35ee71c283
tree 45cf0bd049d7eea4558b14f33a894db27c7c1130 # 新创建的tree对象
parent 8bf696927c17526eb8f0c6dae8badb968a001ed0 # commit对象有parent，正是上一次提交
author Kevin Lynx &lt;kevinlynx@gmail.com&gt; 1410094456 +0800
committer Kevin Lynx &lt;kevinlynx@gmail.com&gt; 1410094456 +0800
second commit
# 新创建的tree对象指向了2个文件
$ git cat-file -p 45cf0bd049d7eea4558b14f33a894db27c7c1130
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 install
100644 blob 8d0e41234f24b6da002d962a26c2495ea16a425f readme
</code></pre>
<p>需要注意，有时候我们使用<code>git commit -a</code>，它会直接将已经加入版本管理的文件一起提交，从而跳过了<code>git add</code>这个过程。同git很多操作一样，它只是一个快捷操作。</p>
<h2>总结</h2>
<p>从上面的内容其实已经可以看出git的优势所在，它可以完全不需要服务器就完成一个版本控制系统的所有事情。在.git文件中它记录了所有的文件的所有历史提交，记录了每一次提交的信息。</p>
<p>git的常用操作中还会涉及到分支、远端仓库等，空了再写。</p>
<h2>参考文档</h2>
<ul>
     <li><a href="http://www.nowamagic.net/academy/detail/48160210">Git的思想和基本工作原理</a></li>
     <li><a href="http://marklodato.github.io/visual-git-guide/index-zh-cn.html?no-svg">图解Git</a></li>
     <li><a href="http://blog.jobbole.com/26209/">Git详解之九：Git内部原理</a></li>
     <li><a href="http://www.oschina.net/translate/git-fetch-and-merge">Git 少用 Pull 多用 Fetch 和 Merge</a></li>
</ul>
<p class="post-footer">
原文地址：
<a href="http://codemacro.com/2014/09/09/understand-git/">http://codemacro.com/2014/09/09/understand-git/</a><br />
written by <a href="http://codemacro.com">Kevin Lynx</a>
&nbsp;posted at <a href="http://codemacro.com">http://codemacro.com</a>
</p>
</div>
<img src ="http://www.cppblog.com/kevinlynx/aggbug/208257.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2014-09-09 21:35 <a href="http://www.cppblog.com/kevinlynx/archive/2014/09/09/208257.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>浅析软件工程开发方法学RUP</title><link>http://www.cppblog.com/kevinlynx/archive/2013/03/21/198692.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Thu, 21 Mar 2013 13:41:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2013/03/21/198692.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/198692.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2013/03/21/198692.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/198692.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/198692.html</trackback:ping><description><![CDATA[
<div class="entry-content"><h2>前言</h2> <p>因为之前一直处在游戏开发行业，由于种种原因一直对软件工程中的项目管理、项目开发方法缺乏体验。虽然项目中也曾倡导编写更多的文档，无论是模块说明文档还是设计文档，但效果一直不好。不甚理想的地方主要体现在文档的规范性欠缺、不统一、浮于表面没有实质内容。文档的编写缺乏详尽的方法指导，那么所谓的设计文档要么是用来敷衍上级要么就是随着开发人员的水平不一而千差万别。</p> <p>当我开始目前这个非游戏项目时，我也曾想，前期做好结构设计，制定好关键问题的解决方案，那么要完成这个项目就不在话下了。但是我很快就面临了一个问题：需求不定。回想身处游戏公司的那些日子，程序员总是抱怨策划需求变更过快过多，在每一次策划提出一个需求变更时，谨慎的程序员都会再三让策划保证：放心，不会变了。而我面临的问题则更为严峻。我意识到，项目的需求，就连用户也无法一一罗列出来。我们需要的是需求调研。但就算你将客户的所有需求全部挖掘出来后（这几乎不可能，因为他们自己也不太清楚自己想要什么），当你交付了第一个软件版本，几乎可以肯定客户会提出一大堆的需求变更：我要的不是这个，我要的那个怎么没有，哦，我当初以为你说的是另一个意思。</p> <p>当然，需求调研这种工作不是让程序员去做的（那会更悲剧，无论是对客户还是对程序员而言，他们都是在对牛弹琴）。但需求的不确定性也总是存在的。</p> <p>事实上，需求变化本身就是一个很正常的现象。我一向愿意更悲观地处理软件开发方面的问题，所谓小心使得万年船。基于此，我决定摆好心态学学软件开发的方法学。</p> <h2>概要</h2> <p>本文简要描述、总结了RUP开发方法学的主要内容，结合我自己的感受阐述了一些RUP的核心原则。我相信我所理解的内容是肤浅的，对于非代码的表达我更相信其是存在歧义的。所以本文仅当是一种经验参考，不必当真。</p> <p>RUP据传是用于指导大型甚至超大型项目开发的，我们做的不是这样规模的项目。但是我们需要记录下整个项目的开发过程，通过这个过程中产出的<strong>工件</strong>任何一个人可以看出这个项目是如何实现出来的，其目的在于规避唯有从海量代码中才能熟悉项目实现这种问题。这里出现了一个概念：工件，其指的是软件项目开发过程中任何留下记录的事物，例如文档、图、代码等。<strong>RUP的一个重要思想，在于其整个软件开发过程都是可推导的</strong>。例如我们通常说的软件架构，或小一点的模块结构，都是通过开发过程中前面阶段产出的工件推导得出，而<strong>不是凭借程序员的经验拍脑袋想出来的</strong>（经验不太可靠，并且千差万别，而<strong>推导</strong>意味着将每个环节变得可靠）。我们借助RUP的这个特性，创建这些工件，用以建立起软件实现的可靠知识库。</p> <!-- more --> <h2>RUP概览</h2> <p>以下均摘自&lt;Thinking in UML>中对RUP的描述：</p> <blockquote><p>统一过程归纳和集成了软件开发活动中的最佳实践，它定义了软件开发过程中最重要的阶段和工作（四个阶段和九个核心工作流），定义了参与软件开发过程的各种角色和他们的职责，还定义了软件生产过程中产生的工件，并提供了模板。最后，采用演进式软件生命周期（迭代）将工作、角色和工件串在一起，形成了统一过程。</p> <p>统一过程是一种追求稳定的软件方法，它追求开发稳定架构，控制变更</p> <p>统一过程集成了面向对象方法、UML语言、核心工作流、工件模板和过程指导等知识</p></blockquote>
<p>简单来说，RUP作为一种软件项目开发方法学，<strong>它定义了软件开发的每一个过程，最重要的是它指导了在每一个过程需要产出什么，这些产出又是怎样得到。它试图规范化整个流程，以规避需求变更，项目参与者水平不一等带来的项目不可控等问题，以期一个软件产品稳定地开发出来</strong>。在一个项目开发过程中，最核心的资源是人，最不可控的亦是人。</p> <h2>RUP过程与实践</h2> <p>我觉得要快速学习一种知识，需要首先获得这门知识的总体框架。另一方面，在我们获得更多信息后，我们需要挖掘出这门知识的核心思想。学习RUP我觉得从这两方面入手是相对比较快速和有效的手段。</p> <h3>RUP框架</h3> <p><strong>RUP定义了软件开发过程的四个阶段，以及9个工作流程</strong>。一张极为经典的RUP开发过程框架图如下：</p>
<p><img src="http://codemacro.com/assets/res/rup/rup.png" alt="rup" /></p> <p>RUP将整个软件开发过程分为四个阶段：</p> <ul>
<li>先启(Inception)、</li>
<li>精化(Elaboration)</li>
<li>构建(Construction)</li>
<li>产品化(Transition)</li>
</ul> <p>每一个阶段的工作分为9个流程：</p> <ul>
<li>业务建模</li>
<li>需求</li>
<li>分析设计</li>
<li>实施</li>
<li>测试</li>
<li>部署</li>
<li>配置与变更管理</li>
<li>项目管理</li>
<li>环境</li>
</ul> <p>其中，前6个流程被统称为&#8221;engineering disciplines&#8221;，后3个流程被称为&#8221;supporting disciplines&#8221;。当然，我们主要关注前6个流程。那么，这些工作流程和开发阶段又有什么关系呢？上图中其实已经阐明了这些关系。</p> <p>RUP指导迭代开发。在软件开发的这4个阶段中，每一个阶段会被分为若干次迭代。而每一次迭代则涵盖了这9个工作流程。随着开发阶段向产品化靠近，自然而然地，需求的变更、增加自然会减少，所以从图中可以看出，开发过程越到后期，其工作流程中关于需求的工作则越少。同样，在先启阶段，其需求相关的工作则占据了该阶段的主要工作内容。</p>
<p>RUP中的迭代要求在每一次迭代中，都会完整地实施一遍整个工作流程。在软件实施阶段，甚至会在每一个迭代过程完后输出一个可运行的软件版本。这个版本可能会被交付给客户，以期进一步地在功能需求上取得与客户一致的意见。这倒是同敏捷开发有点类似。</p> <p>既然制定了工作流程，那每一个工作流程该如何去实施呢？<strong>RUP制定了每个工作流程需要参与的角色，这些角色需要从事的活动，以及这些活动产生的工件。</strong></p> <p>这句话实际上反映了RUP的一个重要信息，摘自wiki：</p> <blockquote><p>RUP is based on a set of building blocks, or content elements, describing what is to be produced, the necessary skills required and the step-by-step explanation describing how specific development goals are to be achieved. The main building blocks, or content elements, are the following:</p> <ul>
<li><strong>Roles (who)</strong> &#8211; A Role defines a set of related skills, competencies and responsibilities.</li>
<li><strong>Work Products (what)</strong> &#8211; A Work Product represents something resulting from a task, including all the documents and models produced while working through the process.</li>
<li><strong>Tasks (how)</strong> &#8211; A Task describes a unit of work assigned to a Role that provides a meaningful result.</li>
</ul>
</blockquote> <h3>RUP建模</h3> <p>在我看来，RUP每个工作流程所完成的工作，就是一个建模的过程。所谓建模，简单来说就是将需要描述的事物通过更系统的形式表达出来，以期获得对该事物更深入的理解。&lt;Thinking in UML>中定义建模概念为：</p>
<blockquote><p>建模(modeling)，是指通过对客观事物建立一种抽象的方法用以表征事物并获得对事物本身的理解，同时把这种理解概念化，将这些逻辑概念组织起来，构成一种对所观察的对象的内部结构和工作原理的便于理解的表达。</p></blockquote> <p>在这里，建模的过程需要使用一些工具。在RUP中建模使用UML来完成。在&lt;Thinking in UML>中讲述了UML的核心模型，包括：</p> <ul>
<li>业务用例模型</li>
<li>概念用例模型</li>
<li>系统用例模型</li>
<li>领域模型</li>
<li>分析模型</li>
<li>软件架构和框架</li>
<li>设计模型</li>
<li>组件模型</li>
<li>实施模型</li>
</ul> <p>可能在大家的普遍认识中，UML无非就是几种图，并且粗看一眼理解起来也不困难，甚至还能用来画画类图做下代码结构设计。但UML的作用不仅仅如此。</p> <p>以上所描述的UML核心模型中，每个模型并不单指的的是一种UML图。每个模型实际上都会包含几种UML图，会包含若干张UML图。这些模型基本上渗透于RUP的9个工作流程中，只不过不同的工作流程使用的模型比重不一而已。</p>
<p>例如在&#8220;分析设计&#8221;工作流程中，可能会使用到系统用例模型、分析模型、软件架构和框架、设计模型等，而业务用例模型可能在这个流程中根本不会用到；相反，业务用例模型则可能在&#8220;业务建模&#8221;流程中被广泛使用。</p> <p>前已述及在RUP的每个工作流程中，RUP定义了该流程需要参与的角色，以及这些角色需要进行的活动，例如这里可以看看&#8220;分析设计&#8221;流程中的角色和活动集（摘自&lt;Thinking in UML>）：</p> <p><img src="http://codemacro.com/assets/res/rup/analyse-action.png" alt="analyse-action" /></p> <p>相应的，在该工作流程中需要产出的工件集为（摘自&lt;Thinking in UML>）：</p> <p><img src="http://codemacro.com/assets/res/rup/analyse-ar.png" alt="analyse-ar" /></p> <p>既然使用了UML作为建模工具，所以可以简单地说这些工件主要就是UML图，当然也会有其他文档性质的事物，例如网络协议结构、数据库表等UML无法描述的东西则通过普通文字性文档描述。</p>
<h3>RUP最佳实践</h3> <p>到目前为止我们已经了解到RUP定义了开发过程(phase)，定义了每个过程包含的若干工作流程，还定义了每个工作流程需要哪些角色从事哪些活动来完成哪些工件。除此之外，RUP还提供了6条最佳实践用以指导软件开发：</p> <ul>
<li>迭代开发</li>
<li>管理需求</li>
<li>使用基于组件的架构</li>
<li>可视建模</li>
<li>持续的质量验证</li>
<li>控制变更</li>
</ul> <p>这些实践在我看来仅仅是一些项目开发的指导原则，它们渗透到每一个过程，每一个工作流程。在项目过程中实践这些原则，用以确保项目的成功。例如我们使用UML建模，以达到&#8220;可视建模&#8221;。我们通过建立需求用例，以&#8220;管理需求&#8221;。</p> <h2>RUP核心思想</h2> <p>似乎没有文档来专门阐述RUP的核心思想，但我觉得掌握其核心思想才是学习的要点所在。要理解一种软件开发方法学的核心思想，其实最好是将其与别的方法学做比较。这里先就我的一些感想做阐述。</p> <h3>用例驱动</h3> <p>用例驱动指的是整个软件项目的推进过程，是依靠&#8220;用例&#8221;来完成。&lt;Thinking in UML>：</p> <blockquote><p>在实际的软件项目中，一个软件要实现的功能通过用例来捕获，接下来的所有分析、设计、实现、测试都由用例来取得，即以实现用例为目标。在统一过程中用例能够驱动的不仅仅是分析设计。</p></blockquote> <p>用例简单来说就是描述了一个系统功能。但必须注意的是，这仅仅是它定义的一小部分。用例主要分布在&#8220;业务建模&#8221;、&#8220;需求&#8221;、&#8220;分析设计&#8221;这些工作过程中。在不同的过程中用例的粒度和性质都不一样。例如对于一个借书系统而言，在业务建模阶段，我们可以获取出一个&#8220;借书&#8221;用例，其系统边界甚至不是系统而可能仅关注这个业务本身（因为这个阶段还没有考虑到计算机如何实现这个借书业务）；在系统分析阶段，我们就可以将&#8220;借书&#8221;这个用例细化为用户和计算机软件系统的交互；进一步地，我们可能会进一步精化这个用例，例如用户通过网页终端&#8220;借书&#8221;。（这里描述了很多建模的细节，可不必深究，本文只给出一个概要性的介绍）</p> <p>我们说用例驱动软件开发，但它如何驱动的呢？我在实际的建模过程中，最明显的感受就是用例驱动了整个建模过程。</p> <ul>
<li>在需求分析阶段，我以系统使用者的角度绘制出了一份用例图，用于表达使用者对该系统的需求</li>
<li>然后我绘制序列图（活动图等）来实现这些用例，也就是阐述使用者具体是如何与系统交互的</li>
<li>从之前的建模过程中我获得对系统功能需求方面的认识</li>
<li>基于前面的分析我可以绘制出系统用例图，以明确系统的各个功能需求</li>
<li>同样针对用例绘制用例实现图</li>
<li>用例本身应该包含更多的文档，因此我编写用例规约用以详细描述各个用例</li>
<li>从用例规约、用例实现中我可以抽离出一些分析类（较设计类更高抽象的类），包含用例场景中涉及到的实体，控制逻辑</li>
<li>细化这些分析类，将分析类组织起来形成系统，我会用界面类去衔接各个控制类</li>
<li>将得到的分析类按模块来划分，从而可以得到一个初步的系统架构</li>
<li>初步考虑系统实现，我甚至会得到一个初步是的系统部署图</li>
<li>回过头不断审视系统用例，以确认我是否实现了所有用例，这可以保证我的分析实现了所有需求，<strong>我不用枚举所有系统特性是否被我考虑周全，我仅需在已有用例图中核实</strong></li>
<li>基于模块实现各个用例，或者基于分析类来实现系统用例</li>
<li>通过重新绘制以及核实用例，可以进一步精化分析类，分析类在很大程度上会一一对应到设计类，而设计类则对应到实际的代码</li>
<li>可以进入设计阶段，设计阶段会考虑到系统的实现细节，例如使用的语言，使用的框架等</li>
</ul> <p>进入设计阶段后，虽然可以进一步建模，得到会直接映射到代码的类图、序列图等，但这样的图在面临需求变更时，基本上会面临修改，这意味着维护这些文档需要耗费精力。所以，&lt;Thinking in UML>中主张将精力放在维护分析类模型中，而通过其他约定实现从分析类到实际代码的转换。我觉得这个也在理。</p> <h3>规范化整个过程</h3> <p>我个人觉得RUP的一大特点在于规范化了整个软件开发过程，每一个步骤需要哪些角色参与，该干什么，怎么去干都有指导。加之这些活动的&#8221;可推导性&#8220;，这意味着不论参与角色属于什么水平，都可以稳固地推进项目进程。</p>
<p>此外，这种规范化也会给项目留下详细的演化过程。你可以明确地看到整个软件是如何演化出最终的产品代码，可以深入地理解项目代码中的设计。</p> <h2>总结</h2> <p>我只是一个RUP新手，即便如此，我依然不觉得RUP是软件开发的万能药。我相信任何软件开发方法都是有局限性的。我们在实际使用的时候也只是吸取其精华。不同的开发方法其适用范围也是不一样的。正如有人将RUP和XP做比较时说，如果你使用RUP去开一个杂货铺，在没开张之前你就已经破产了；同样如果你用XP去做飞机，飞机毁了十来次也许才能做出来（from &lt;Thinking in UML> again）。</p> <h2>参考文档</h2> <ul>
<li><a href="http://blog.csdn.net/coffeewoo/">&lt;Thinking in UML></a></li>
<li><a href="http://www.uml.org.cn/SoftWareProcess/2009031017.asp">RUP和IPD流程的优缺点</a></li>
<li><a href="http://en.wikipedia.org/wiki/IBM_Rational_Unified_Process">IBM Rational Unified Process(wiki)</a></li>
<li><a href="http://blog.roodo.com/rocksaying/archives/2051417.html">軟體工程三大陣營, RUP, CMMI, Agile Method</a></li>
<li><a href="http://incredibleagile.com/download/xpVsrup.pdf">XP与RUP的比较</a></li>
</ul> <p class='post-footer'> 原文地址： <a href='http://codemacro.com/2013/03/21/rup/'>http://codemacro.com/2013/03/21/rup/</a><br/> written by <a href='http://codemacro.com'>Kevin Lynx</a>
 &nbsp;posted at <a href='http://codemacro.com'>http://codemacro.com</a> </p> </div>
<img src ="http://www.cppblog.com/kevinlynx/aggbug/198692.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2013-03-21 21:41 <a href="http://www.cppblog.com/kevinlynx/archive/2013/03/21/198692.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>为什么处理排序的数组要比非排序的快？</title><link>http://www.cppblog.com/kevinlynx/archive/2012/08/30/188786.html</link><dc:creator>Kevin Lynx</dc:creator><author>Kevin Lynx</author><pubDate>Thu, 30 Aug 2012 09:43:00 GMT</pubDate><guid>http://www.cppblog.com/kevinlynx/archive/2012/08/30/188786.html</guid><wfw:comment>http://www.cppblog.com/kevinlynx/comments/188786.html</wfw:comment><comments>http://www.cppblog.com/kevinlynx/archive/2012/08/30/188786.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cppblog.com/kevinlynx/comments/commentRss/188786.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/kevinlynx/services/trackbacks/188786.html</trackback:ping><description><![CDATA[<div class="entry-content">
<p>参考<a href="http://stackoverflow.com/questions/11227809/why-is-processing-a-sorted-array-faster-than-an-unsorted-array">Why is processing a sorted array faster than an unsorted array?</a></p>

<h2>问题</h2>

<p>看以下代码：</p>

<div class="highlight">
<pre><code class="c"><span class="cp">#include &lt;algorithm&gt;</span>
<span class="cp">#include &lt;ctime&gt;</span>
<span class="cp">#include &lt;iostream&gt;</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span>
<span class="p">{</span>
    <span class="c1">// generate data</span>
    <span class="k">const</span> <span class="kt">unsigned</span> <span class="n">arraySize</span> <span class="o">=</span> <span class="mi">32768</span><span class="p">;</span>
    <span class="kt">int</span> <span class="n">data</span><span class="p">[</span><span class="n">arraySize</span><span class="p">];</span>

    <span class="k">for</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="n">c</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">c</span> <span class="o">&lt;</span> <span class="n">arraySize</span><span class="p">;</span> <span class="o">++</span><span class="n">c</span><span class="p">)</span>
        <span class="n">data</span><span class="p">[</span><span class="n">c</span><span class="p">]</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">rand</span><span class="p">()</span> <span class="o">%</span> <span class="mi">256</span><span class="p">;</span>


    <span class="c1">// !!! with this, the next loop runs faster</span>
    <span class="n">std</span><span class="o">::</span><span class="n">sort</span><span class="p">(</span><span class="n">data</span><span class="p">,</span> <span class="n">data</span> <span class="o">+</span> <span class="n">arraySize</span><span class="p">);</span>


    <span class="c1">// test</span>
    <span class="kt">clock_t</span> <span class="n">start</span> <span class="o">=</span> <span class="n">clock</span><span class="p">();</span>
    <span class="kt">long</span> <span class="kt">long</span> <span class="n">sum</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

    <span class="k">for</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">100000</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span>
    <span class="p">{</span>
        <span class="c1">// primary loop</span>
        <span class="k">for</span> <span class="p">(</span><span class="kt">unsigned</span> <span class="n">c</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">c</span> <span class="o">&lt;</span> <span class="n">arraySize</span><span class="p">;</span> <span class="o">++</span><span class="n">c</span><span class="p">)</span>
        <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">data</span><span class="p">[</span><span class="n">c</span><span class="p">]</span> <span class="o">&gt;=</span> <span class="mi">128</span><span class="p">)</span>
                <span class="n">sum</span> <span class="o">+=</span> <span class="n">data</span><span class="p">[</span><span class="n">c</span><span class="p">];</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="kt">double</span> <span class="n">elapsedTime</span> <span class="o">=</span> <span class="n">static_cast</span><span class="o">&lt;</span><span class="kt">double</span><span class="o">&gt;</span><span class="p">(</span><span class="n">clock</span><span class="p">()</span> <span class="o">-</span> <span class="n">start</span><span class="p">)</span> <span class="o">/</span> <span class="n">CLOCKS_PER_SEC</span><span class="p">;</span>

    <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">elapsedTime</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
    <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"sum = "</span> <span class="o">&lt;&lt;</span> <span class="n">sum</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="p">}</span>
</code></pre>
</div>


<p>问题就在于，去掉<code>std::sort</code>那一行，以上代码将运行更长的时间。在我的机器上未去掉<code>std::sort</code>耗时8.99s，去掉后耗时24.78s。编译器使用的是gcc4.4.3。事实上，以上代码跟编译器没有关系，甚至跟语言没有关系。那这是为什么呢？</p>

<!-- more -->


<p>这跟处理这个数组的逻辑有非常大的关系。如以上代码所示，这个循环里有个条件判断。条件判断被编译成二进制代码后，就是一个跳转指令，类似：</p>

<p>具体为什么会不同，这涉及到计算机CPU执行指令时的行为。</p>

<h2>CPU的流水线指令执行</h2>

<p>想象现在有一堆指令等待CPU去执行，那么CPU是如何执行的呢？具体的细节可以找一本计算机组成原理的书来看。CPU执行一堆指令时，并不是单纯地一条一条取出来执行，而是按照一种流水线的方式，在CPU真正执行一条指令前，这条指令就像工厂里流水线生产的产品一样，已经被经过一些处理。简单来说，一条指令可能经过这些过程：取指(Fetch)、解码(Decode)、执行(Execute)、放回(Write-back)。</p>

<p>假设现在有指令序列ABCDEFG。当CPU正在执行(execute)指令A时，CPU的其他处理单元（CPU是由若干部件构成的）其实已经预先处理到了指令A后面的指令，例如B可能已经被解码，C已经被取指。这就是流水线执行，这可以保证CPU高效地执行指令。</p>

<h2>Branch Prediction</h2>

<p>如上所说，CPU在执行一堆顺序执行的指令时，因为对于执行指令的部件来说，其基本不需要等待，因为诸如取指、解码这些过程早就被做了。但是，当CPU面临非顺序执行的指令序列时，例如之前提到的跳转指令，情况会怎样呢？</p>

<p>取指、解码这些CPU单元并不知道程序流程会跳转，只有当CPU执行到跳转指令本身时，才知道该不该跳转。所以，取指解码这些单元就会继续取跳转指令之后的指令。当CPU执行到跳转指令时，如果真的发生了跳转，那么之前的预处理（取指、解码）就白做了。这个时候，CPU得从跳转目标处临时取指、解码，然后才开始执行，这意味着：CPU停了若干个时钟周期！</p>

<p>这其实是个问题，如果CPU的设计放任这个问题，那么其速度就很难提升起来。为此，人们发明了一种技术，称为branch prediction，也就是分支预测。分支预测的作用，就是预测某个跳转指令是否会跳转。而CPU就根据自己的预测到目标地址取指令。这样，即可从一定程度提高运行速度。当然，分支预测在实现上有很多方法。</p>

<p>简单的预测可以直接使用之前的实际执行结果。例如某个跳转指令某一次产生了跳转，那么下一次执行该指令时，CPU就直接从跳转目标地址处取指，而不是该跳转指令的下一条指令。</p>

<h2>答案</h2>

<p>了解了以上信息后，文章开头提出的问题就可以解释了。这个代码中有一个循环，这个循环里有一个条件判断。每一次CPU执行这个条件判断时，CPU都可能跳转到循环开始处的指令，即不执行if后的指令。使用分支预测技术，当处理已经排序的数组时，在若干次<code>data[c]&gt;=128</code>都不成立时（或第一次不成立时，取决于分支预测的实现），CPU预测这个分支是始终会跳转到循环开始的指令时，这个时候CPU将保持有效的执行，不需要重新等待到新的地址取指；同样，当<code>data[c]&gt;=128</code>条件成立若干次后，CPU也可以预测这个分支是不必跳转的，那么这个时候CPU也可以保持高效执行。</p>

<p>相反，如果是无序的数组，CPU的分支预测在很大程度上都无法预测成功，基本就是50%的预测成功概率，这将消耗大量的时间，因为CPU很多时间都会等待取指单元重新取指。</p>

<p>本文完。最后感叹下stackoverflow上这个帖子里那个老外回答问题的专业性，我要是楼主早就感动得涕泪横飞了。感谢每一个传播知识的人。</p>

<h2>参考资料</h2>

<ol>
<li><a href="http://blog.sina.com.cn/s/blog_6c673e570100zfmo.html">http://blog.sina.com.cn/s/blog_6c673e570100zfmo.html</a></li>
<li><a href="http://www.cnblogs.com/dongliqian/archive/2012/04/05/2433847.html">http://www.cnblogs.com/dongliqian/archive/2012/04/05/2433847.html</a></li>
<li><a href="http://en.wikipedia.org/wiki/Branch_predictor">http://en.wikipedia.org/wiki/Branch_predictor</a></li>
</ol>
<p class="post-footer">
            原文地址：
            <a href="http://codemacro.com/2012/08/29/branch-predictor/">http://codemacro.com/2012/08/29/branch-predictor/</a><br />
            written by <a href="http://codemacro.com">Kevin Lynx</a>
            &nbsp;posted at <a href="http://codemacro.com">http://codemacro.com</a>
            </p>

</div><img src ="http://www.cppblog.com/kevinlynx/aggbug/188786.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/kevinlynx/" target="_blank">Kevin Lynx</a> 2012-08-30 17:43 <a href="http://www.cppblog.com/kevinlynx/archive/2012/08/30/188786.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>