• [学习笔记]UML小结以及基于领域模型的系统设计初步


    UML

    UML不是OOA/D 也不是方法,它仅仅是一种图形表示法。其目的就是让人能看懂你的东西。

    clip_image001

    每一种图,都相当于一种角度。不同的图就是从不同角度来观察系统。

    比如交通图和行政区划图,从不同角度观察中国。

    必要性是画图的原则,虽然有这种关系,但不一定要画出来,如果非要画出来,则应考虑不要影响图形的美观。

    活动图

    活动图表示的是一种流程。

    例子:

    2008110810194263

    顺序图

    顺序图的目的是为对象分配职责,而不是步骤的罗列。

    clip_image002

    上图中,ActionServlet是没有必要画出来的,它是一个很稳定,也不是我们自己提供的,没有必要来说明它的对象职责。插在这里显然多余.

    如下图这样就可以了:

    clip_image003

    用例和用例图

    用例的定义:文本形式的情节描述。

    用例用于需求的发现和记录,它会影响后续的OOA/D工作

    用例不是用例图。用例图不重要,用例描述很重要。

    用例尽量不要用名词命名,尽量以动词开头,比如:管理商品。

    用例一般是用于功能性的需求而非性能性需求。

    编写用例时,在基本路径(即主成功路径)中,只书写主要的成功事件,而可能出现的其他情况(如找不到用户)应该写在扩展点中。

    用例粒度:比如:是把管理用户当做用例还是把添加用户和删除用户分别当做两个用例。

    确定用例的粒度时,应该考虑描述这个用例的基本路径需要几个步骤。十步以内,七八步比较合适。

    一个典型的用例描述

    clip_image004

    一个典型用例图

    其中销售经理和收银员之关系是泛化关系,即经理拥有收银员所拥有的一切用例。另外还有其独有的用例。

    clip_image005

    类图

    类图允许我们标记静态内容及类之间的关系,它是UML中最重要的图形,可以在任何时候尝试使用类图。

    不要使用类图描述所有的细节,保持类图的简单。

    UML中主要有三种类:边界类、控制类和实体类

    边界类位于系统与外界的交界处,例如窗体、报表、以及表示通讯协议的类、直接与外部设备交互的类、直接与外部系统交互的类等。通过用例图可以确定需要的边界类,每个Actor/Use Case对至少要一个边界类,但并非每个Actor/Use Case对要唯一的边界类。

    实体类可以通过事件流和交互图发现。通常每个实体类在数据库中有相应的表,实体类中的属性对应数据库表中的字段。

    控制类是控制其他类工作的类。控制类可以被多个用例共用。其他类并不向控制类发送很多消息,而是由控制类发出很多消息。

    类图中,要画出类之间的关系

    UML中继承、实现、依赖、关联、聚合、组合的联系与区别

    继承 (也叫泛化)

    指的是一个类(称为子类、子接口)继承另外的一个类(称为父类、父接口)的功能,并可以增加它自己的新功能的能力,继承是类与类或者接口与接口之间最常见的关系;在Java中此类关系通过关键字extends明确标识,在设计时一般没有争议性;

    clip_image006

    实现

    指的是一个class类实现interface接口(可以是多个)的功能;实现是类与接口之间最常见的关系;在Java中此类关系通过关键字 implements明确标识,在设计时一般没有争议性;

    clip_image007

    依赖

    可以简单的理解,就是一个类A使用到了另一个类B,而这种使用关系是具有偶然性的、临时性的、非常弱的,但是B类的变化会影响到A;比如某人要过河,需要借用一条船,此时人与船之间的关系就是依赖;表现在代码层面,为类B作为参数被类A在某个method方法中使用;

    clip_image008

    关联

    他体现的是两个类、或者类与接口之间语义级别的一种强依赖关系,比如我和我的朋友;这种关系比依赖更强、不存在依赖关系的偶然性、关系也不是临时性的,一般是长期性的,而且双方的关系一般是平等的、关联可以是单向、双向的;表现在代码层面,为被关联类B以类属性的形式出现在关联类A中,也可能是关联类A引用了一个类型为被关联类B的全局变量;

    clip_image009

    聚合

    聚合是关联关系的一种特例,他体现的是整体与部分、拥有的关系,即has-a的关系,此时整体与部分之间是可分离的,他们可以具有各自的生命周期,部分可以属于多个整体对象,也可以为多个整体对象共享;比如计算机与CPU、公司与员工的关系等;表现在代码层面,和关联关系是一致的,只能从语义级别来 区分;

    clip_image010

    组合

    组合也是关联关系的一种特例,他体现的是一种contains-a的关系,这种关系比聚合更强,也称为强聚合;他同样体现整体与部分间的关系,但此时整体与部分是不可分的,整体的生命周期结束也就意味着部分的生命周期结束;比如你和你的大脑;表现在代码层面,和关联关系是一致的,只能从语义级别来区 分;

    clip_image011

    总结

    继承、实现体现的是类与类、或者类与接口间的纵向关系,不易混淆;其他的四者关系则体现的是类与类、或者类与接口间的引用、横向关系,这几种关系都是语义级别的,所以从代码层面并不能完全区分开来;

    例如在关心汽车的领域里,轮胎是一定要组合在汽车类中的,因为它离开了汽车就没有意义了。但是在卖轮胎的店铺业务里,就算轮胎离开了汽车,它也是有意义的,这就可以用聚合了。

    总的来说,后几种关系所表现的强弱程度依次为:组合>聚合>关联>依赖

    示例一

    关联之特殊示例,下图表示一种树形结构类图,

    clip_image012

    可以用如下代码实现

    Public Class Node{

            Public        ID;

            Private Node        parent;

            Private Set<Node>        children;

    }

    示例二

    clip_image013

    已经在关联中表明了 Document 具有一个User类型的字段 Creator,故不必在Document中再写出Creator了。

    示例三

    由于User是另一个复杂的概念,所以要建立关联,而不是把User也作为一个简单的属性(如name那样)

    不要把复杂的领域概念建模为属性。

    clip_image014

     示例四:

    Student 与 class 之间是双向关联关系,当然也可以说成是student依赖class,因为没有class,student是不能编译通过的。但画图并不是所有存在的东西都要画出来,这里表示成关联关系更为贴切些。

    另外,关联指的是关心对方的结构,如果对象A只是用一用对象B的某个方法,比如Collections.sort(), 这显然是依赖而非关联。


    基于领域模型的系统设计初步

    设计时重要原则:

            低耦合,高内聚.

                    尽量降低对不稳定对象的依赖。对于非常稳定的东西,比如JDK的核心类库,尽可以随便依赖它。

            不要依赖于正向工程和逆向工程,如果你要让其从图形生成代码,则你不得不在图形中注意各种细节,那不如你自己写代码。

            图形是为了抽象出逻辑主干,方便人理解,它不能替代详细完整的文字描述。

    系统的核心价值

    领域模型的价值不在于它的设计优美(它只是一些对象﹐最重要的也就是对象之间的关系)﹐而在于它体现了系统的核心价值。什么是系统的核心价值呢?我想我们的图书馆系统和华尔街的一个商业系统本质的区别不在于系统用了什么语言、用了什么数据库、用的是OO还是过程,而在于系统能为使用者提供什么服务,以及提供的质量。这些通过系统的运行方式﹐系统的运行过程﹐系统的业务逻辑来体现。

    用例的价值

    系统分析员在接手一个系统后﹐首先要做到的事情就是得出系统的服务和服务场景。也就是我们经常所讲的用例(use case)

    很多人不清楚清晰的用例的价值,只是因为看别人有漂亮的图形,所以自己也画一个,其实自己都不去看它。这样的用例分析只能糊弄一下老板,给别人show一下Demo﹐而不会对系统开发什么实质作用。

    用例表示的是使用系统的一个场景﹐其本质在于详细描述了系统用户(actor)与系统是如何交互的﹐以及交互的后果是什么﹐详细而完善的用例将指导您进行系统开发的全过程

    低耦合的设计

    系统对象除了与领域模型、用户打交道以外﹐它还会与系统的其它模块交互。如持久化系统、日志系统、信息通知系统(您不能因为用户要求由邮件通知改为短信通知就修改领域模型吧),当然还有UI,这些属于系统核心(领域模型)以外的东西。

    这些模块不该参杂进业务逻辑中。应该在边界与这些模块进行接触。

    例子:

    Public void 借阅()

    {

    借阅处理者 处理者 = new 借阅处理者(当前书籍﹐当前登录人姓名);

            Bool successful = 处理者.借阅()            //這個方法主要就是上面的那2行代碼

            If(successful){

    持久化系统.add(当前书籍);

    日志系统.add日志(当前当籍,”借阅”)

    邮件系统.发送邮件(当前书籍.当前借书人姓名)

            } 

    }

    //这个例子的关键在于,你本可以在"借阅()"方法中先实现借阅的逻辑,然后顺势进行持久化、日志、邮件操作。可是这里却多弄了个"处理者"对象来专门进行借阅操作,其目的就是为了将“借阅”逻辑独立出来,与其他模块耦合更松。

    例子二:

    Void 持久化系统.add(书籍 当前书籍)

    {

    借阅关系 Bbb = new 借阅关系

            Bbb.id = 产生ID()

            Bbb.图书ID = 当前书籍.id;

            Bbb.借阅人 = 当前书籍.借阅记录.借阅人姓名

            Bbb.借阅时间 = 当前书籍.借阅记录.借阅时间

            Bbb.Save();

    }  

    在现实系统中﹐我在if(successful)这里作了一些纯软件设计﹐如利用C#具有Event特性﹐将借阅方法后公开出一个事件﹐这样我在再要添加什么外围模声时﹐只要响应事件就可以了﹐不需要再来动这个方法 。

    其它模块处理过程类似。

    业务过程是系统的核心,其他模块都依赖于它而存在。

    一个系统要变更业务逻辑﹐我们只要针对领域模型作变化即可﹐再也不需要抱怨变化了。

    参考资料:

    尚学堂UML系列视频

    <http://www.blogjava.net/lsshap/archive/2009/11/18/302755.html>

    <http://www.diybl.com/course/3_program/gcs/2008324/106156.html>

    <http://blogger.org.cn/blog/more.asp?name=nrzj&id=17433>

    <http://blog.csdn.net/sfdev/archive/2009/02/18/3906243.aspx>

    <http://www.kuqin.com/system-analysis/20080613/9441.html>

  • 相关阅读:
    容器优雅关闭方案,进程id为1
    要使vue支持ts写法
    深入理解docker信号机制以及dumbinit的使用
    elselect数据量过大引发卡顿,怎么办?
    @开发者 云端一条生产线,让软件开发更敏捷、更安全
    华为云重磅发布:“乐高式”自动驾驶研发开放平台,携手伙伴共建生态
    SRE,了解一下?35+岁程序员新选择
    开发者能力大赏,谁是技术知识达人?
    极速生成缩略图,Serverless 支撑赛事转播锁定冬奥亮点
    15 分钟实现企业级应用无损上下线
  • 原文地址:https://www.cnblogs.com/ybwang/p/1779304.html
Copyright © 2020-2023  润新知