本文为 Dennis Gao 原创技术文章,发表于博客园博客,未经作者本人允许禁止任何形式的转载。

现在,我们需要设计一个项目管理系统,目前我们收集到了如下这些需求:

  1. REQ1:一个项目内有多名项目成员
  2. REQ2:一名项目成员只能被指派给一个项目
  3. REQ3:一个项目内仅有一名项目成员被指派为项目经理负责管理项目
  4. REQ4:所有项目成员均是公司员工
  5. REQ5:公司员工的薪水基本工资项目奖金组合而成
  6. REQ6:项目经理的项目奖金由项目的成败决定
  7. REQ7:项目中包含项目计划
  8. REQ8:一个项目计划由多个项目计划项组成

根据上面的需求描述,我们首先识别出若干个概念名词:

  1. 项目(Project)
  2. 项目成员(Project Member)
  3. 项目经理(Project Manager)
  4. 公司员工(Employee)
  5. 薪水(Salary)
  6. 基本工资(Base Salary)
  7. 项目奖金(Project Bonus)
  8. 项目计划(Schedule)
  9. 项目计划项(Schedule Item)

根据需求 “REQ4:所有项目成员均是公司员工”,我们可以得到 Employee 与 ProjectMember 的关系。

类 ProjectMember 实现了抽象类 Employee。Employee 类中包含计算薪水(Salary)操作,并负责封装需求 “REQ5:公司员工的薪水基本工资项目奖金组合而成”。ProjectMember 类覆写父类的薪水计算方法。

   public abstract class Employee
{
public Employee(int id, string name)
{
ID = id;
Name = name;
} public int ID { get; private set; }
public string Name { get; private set; } public double CalculateSalary()
{
return GetBaseSalary() + GetProjectBonus();
} protected abstract double GetBaseSalary();
protected abstract double GetProjectBonus();
} public class ProjectMember : Employee
{
public ProjectMember(int id, string name)
: base(id, name)
{
} public Project AssignedProject { get; private set; } public void AssignProject(Project project)
{
AssignedProject = project;
} protected override double GetBaseSalary() { return ; }
protected override double GetProjectBonus() { return ; }
}

根据需求 “REQ3:一个项目内仅有一名项目成员被指派为项目经理负责管理项目”,可以推断出 ProjectManager 与 ProjectMember 的关系。

ProjectManager 继承自 ProjectMember。ProjectMember 类覆写父类的薪水计算方法,以实现需求 “REQ6:项目经理的项目奖金由项目的成败决定”。

   public class ProjectManager : ProjectMember
{
public ProjectManager(int id, string name)
: base(id, name)
{
} protected override double GetBaseSalary() { return ; } protected override double GetProjectBonus()
{
return AssignedProject.IsSuccess ? : ;
}
}

由下面三个需求可以识别出 Project 与 ProjectMember/ProjectManager 之间的关系。

REQ1:一个项目内有多名项目成员

REQ2:一名项目成员只能被指派给一个项目

REQ3:一个项目内仅有一名项目成员被指派为项目经理负责管理项目

Project 聚合(Aggregation)了 ProjectMember,ProjectMember 当不在该项目中时仍然可以存在,比如转去做其他项目。

Project 关联(Association)了 ProjectManager,ProjectManager 当不在该项目时,需要转换为 ProjectMember。

ProjectManager 的薪水将由所负责的项目的成败决定,会调用 Project 的状态以计算薪水。

   public class Project
{
private ProjectManager _manager;
private List<ProjectMember> _members = new List<ProjectMember>();
private Schedule _schedule = new Schedule(); public Project(string name, ProjectManager manager)
{
Name = name;
_manager = manager;
} public string Name { get; private set; }
public ProjectManager Manager { get { return _manager; } }
public ReadOnlyCollection<ProjectMember> Members { get { return _members.AsReadOnly(); } }
public Schedule Schedule { get { return _schedule; } }
public bool IsSuccess { get { return (new Random().Next(, ) % ) > ; } } public void AssignMembers(IEnumerable<ProjectMember> members)
{
_members.AddRange(members);
_members.ForEach(m => m.AssignProject(this));
} public void AddScheduleItem(ScheduleItem item)
{
_schedule.Add(item);
}
}

根据需求 “REQ7:项目中包含项目计划” 可得出 Project 与 Schedule 的关系。

根据需求 “REQ8:一个项目计划由多个项目计划项组成” 可得出 Schedule 与 ScheduleItem 的关系。

Project 聚合(Aggregation)了 Schedule。Schedule 由多个 ScheduleItem 组成(Composition)。

   public class Schedule : List<ScheduleItem>
{
} public class ScheduleItem
{
public string Description { get; set; }
public DateTime BeginTime { get; set; }
public DateTime EndTime { get; set; }
}

由此,我们得到了满足全部需求的类图:

现在,我们可通过以上类的定义来组织业务逻辑。

   class Program
{
static void Main(string[] args)
{
ProjectManager manager = new ProjectManager(, @"Dennis Gao");
ProjectMember member2 = new ProjectMember(, @"Super Man");
ProjectMember member3 = new ProjectMember(, @"Iron Man");
ProjectMember member4 = new ProjectMember(, @"Spider Man"); var projectMembers = new List<ProjectMember>() { manager, member2, member3, member4 }; Project project = new Project("EarnMoney", manager);
project.AssignMembers(projectMembers); ScheduleItem item1 = new ScheduleItem()
{
Description = "Team Building",
BeginTime = DateTime.Now.AddDays(),
EndTime = DateTime.Now.AddDays(),
};
project.AddScheduleItem(item1); Console.WriteLine("Salary List of Project [{0}] Members:", project.Name);
foreach (var member in project.Members)
{
Console.WriteLine(
"\tProject Member [{0}] has TotalSalary [{1}].",
member.Name, member.CalculateSalary());
} Console.WriteLine();
Console.WriteLine("[{0}] members will have a [{1}] on [{2}].",
project.Name, project.Schedule.First().Description,
project.Schedule.First().BeginTime); Console.ReadKey();
}
}

由于在业务逻辑中,ProjectManager 的项目奖金由项目的成败来决定,但是项目的成败又多少带了点运气。

 public bool IsSuccess { get { return (new Random().Next(, ) % ) > ; } }
     protected override double GetProjectBonus()
{
return AssignedProject.IsSuccess ? : ;
}

所以,我们可能会得到两种输出结果,成功的项目和失败的项目。

失败的项目没有项目奖金:

成功的项目拿到了项目奖金:

我们给出 UML 中的相关定义:

元素名称 符号图例 含义
Association

A 和 B 相互调用和访问对方的元素。

A and B call and access each other’s elements.

Aggregation

A 中拥有一个 B,但 B 脱离于 A 仍然可以独立存活。

A has a B, and B can outlive A.

A "uses" B = Aggregation : B exists independently (conceptually) from A.

Composition

A 中拥有一个 B,B 脱离 A 后在系统中没有任何存活的意义。

A has a B, and B depends on A.

A "owns" B = Composition : B has no meaning or purpose in the system without A.

我们可以从不同的角度来理解和区分这三种关系:

  Association Aggregation Composition
Owner No owner

Single owner

Single owner

Lifetime Have their own lifetime

Have their own lifetime

Owner's lifetime

Child Object Child objects all are independent

Child objects belong to a single parent

Child objects belong to single parent

所以,总结来说,聚合(Aggregation)是一种特殊的关联(Association),合成(Composition)是一种特殊的聚合(Aggregation)。

Association->Aggregation->Composition

参考资料

完整代码

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq; namespace UML
{
class Program
{
static void Main(string[] args)
{
ProjectManager manager = new ProjectManager(, @"Dennis Gao");
ProjectMember member2 = new ProjectMember(, @"Super Man");
ProjectMember member3 = new ProjectMember(, @"Iron Man");
ProjectMember member4 = new ProjectMember(, @"Spider Man"); var projectMembers = new List<ProjectMember>() { manager, member2, member3, member4 }; Project project = new Project("EarnMoney", manager);
project.AssignMembers(projectMembers); ScheduleItem item1 = new ScheduleItem()
{
Description = "Team Building",
BeginTime = DateTime.Now.AddDays(),
EndTime = DateTime.Now.AddDays(),
};
project.AddScheduleItem(item1); Console.WriteLine("Salary List of Project [{0}] Members:", project.Name);
foreach (var member in project.Members)
{
Console.WriteLine(
"\tProject Member [{0}] has TotalSalary [{1}].",
member.Name, member.CalculateSalary());
} Console.WriteLine();
Console.WriteLine("[{0}] members will have a [{1}] on [{2}].",
project.Name, project.Schedule.First().Description,
project.Schedule.First().BeginTime); Console.ReadKey();
}
} public abstract class Employee
{
public Employee(int id, string name)
{
ID = id;
Name = name;
} public int ID { get; private set; }
public string Name { get; private set; } public double CalculateSalary()
{
return GetBaseSalary() + GetProjectBonus();
} protected abstract double GetBaseSalary();
protected abstract double GetProjectBonus();
} public class ProjectMember : Employee
{
public ProjectMember(int id, string name)
: base(id, name)
{
} public Project AssignedProject { get; private set; } public void AssignProject(Project project)
{
AssignedProject = project;
} protected override double GetBaseSalary() { return ; }
protected override double GetProjectBonus() { return ; }
} public class ProjectManager : ProjectMember
{
public ProjectManager(int id, string name)
: base(id, name)
{
} protected override double GetBaseSalary() { return ; } protected override double GetProjectBonus()
{
return AssignedProject.IsSuccess ? : ;
}
} public class Schedule : List<ScheduleItem>
{
} public class ScheduleItem
{
public string Description { get; set; }
public DateTime BeginTime { get; set; }
public DateTime EndTime { get; set; }
} public class Project
{
private ProjectManager _manager;
private List<ProjectMember> _members = new List<ProjectMember>();
private Schedule _schedule = new Schedule(); public Project(string name, ProjectManager manager)
{
Name = name;
_manager = manager;
} public string Name { get; private set; }
public ProjectManager Manager { get { return _manager; } }
public ReadOnlyCollection<ProjectMember> Members { get { return _members.AsReadOnly(); } }
public Schedule Schedule { get { return _schedule; } }
public bool IsSuccess { get { return (new Random().Next(, ) % ) > ; } } public void AssignMembers(IEnumerable<ProjectMember> members)
{
_members.AddRange(members);
_members.ForEach(m => m.AssignProject(this));
} public void AddScheduleItem(ScheduleItem item)
{
_schedule.Add(item);
}
}
}

本文为 Dennis Gao 原创技术文章,发表于博客园博客,未经作者本人允许禁止任何形式的转载。

UML中关联(Association)、聚合(Aggregation)和合成(Composition)之间的区别的更多相关文章

  1. UML的关联(Association), 聚合(Aggregation), 组合(Composition)区别

    转载:http://blog.csdn.net/ocean181/article/details/6117369 UML的关联(Association), 聚合(Aggregation), 组合(Co ...

  2. UML中关联(Association)和依赖(Dependency)的区别

    原文转自:http://blog.csdn.net/metasearch/article/details/2334853 在UMLCHINA精华区,看到了一些关联和依赖的讨论,似乎越讲越糊涂.我想谈一 ...

  3. socket编程中write、read和send、recv之间的区别~转载

    socket编程中write.read和send.recv之间的区别   http://blog.csdn.net/petershina/article/details/7946615 一旦,我们建立 ...

  4. Jquery中.bind()、.live()、.delegate()和.on()之间的区别详解

    简介 最近了解到很多网页开发者对jquery中的 .bind() .live() .delegate() 和 .on() 方法存在很多的疑惑.这些疑惑通常是关于它们之间真正的区别是什么啊,什么时候该使 ...

  5. C#中IEnumerable、ICollection、IList、List之间的区别

    IEnumerable.ICollection.IList.List之间的区别,本文分别分析了它的实现源码,从而总结出了它们之间的关系和不同之处. 首先我看看 IEnumerable: // 摘要: ...

  6. Python中的赋值(复制)、浅拷贝、深拷贝之间的区别

    1.赋值: 只是复制了新对象的引用,不会开辟新的内存空间.  2.浅拷贝: 创建新对象,其内容是原对象的引用.    浅拷贝有三种形式:切片操作,工厂函数,copy模块中的copy函数.    如: ...

  7. socket编程中write、read和send、recv之间的区别

    http://blog.csdn.net/petershina/article/details/7946615 一旦,我们建立好了tcp连接之后,我们就可以把得到的fd当作文件描述符来使用. 由此网络 ...

  8. Swift中元组(Tuples),结构体(Struct),枚举(Enums)之间的区别

    Swift有许多种存储数据方式,你可以用枚举(enums),元组(tuples),结构体(structs),类(classes),在这篇文章中我们将比较枚举.元组.结构体之间区别,首先从最简单的开始- ...

  9. SERVLET中的doGet与doPost两个方法之间的区别

    get和post是http协议的两种方法,另外还有head, delete等 这两种方法有本质的区别,get只有一个流,参数附加在url后,大小个数有严格限制且只能是字符串.post的参数是通过另外的 ...

随机推荐

  1. Ubuntu 安装 fcitx 输入法

    fcitx 和 ibus一样都是输入法框架.下面介绍ubuntu下安装fcitx输入法. 1.先卸载系统中的输入法 2.安装. 增加ppa源:sudo add-apt-repository ppa:f ...

  2. WebForm 简单控件、复合控件

    简单控件: Label:被编译成span 样式表里设置lable的高度:  display:inline-block; Text  --文本 ForeColor  --字体颜色 Visible  -- ...

  3. sql 查询分组后的数据总条数

    select count(0) from (select investor_uid from lzh_borrow_investor group by investor_uid) as temp

  4. C++中的预处理

    一.预处理的由来:      在C++的历史发展中,有很多的语言特征(特别是语言的晦涩之处)来自于C语言,预处理就是其中的一个.C++从C语言那里把C语言预处理器继承过来(C语言预处理器,被Bjarn ...

  5. JAVA学习<四>

    一. 数组: Java 中操作数组只需要四个步骤: 1. 声明数组 语法:  数据类型[ ] 数组名: 或者   数据类型 数组名[ ]: 其中,数组名可以是任意合法的变量名. 2. 分配空间 简单地 ...

  6. HDU 3974 Assign the task(dfs建树+线段树)

    题目大意:公司里有一些员工及对应的上级,给出一些员工的关系,分配给某员工任务后,其和其所有下属都会进行这项任务.输入T表示分配新的任务, 输入C表示查询某员工的任务.本题的难度在于建树,一开始百思不得 ...

  7. 問題排查:在 ServiceModel 客戶端配置部份中,找不到名稱 和協定 的終結點元素。

    同樣都是刪掉服務參考再重建重編譯重發行,為什麼之前幾次都沒事? 這次只不過是刪掉服務參考,然後換了個名稱重建而已,做完就變這樣? 後來發現問題出在 app.config,因為之前 app.config ...

  8. cookie 的“Value”=“xxxxx,xxxxx”部分无效

    cookie 的“Value”=“xxxxx,xxxxx”部分无效 在一些网站中有时候会遇到Cookie的值为逗号 但是在.Net中Cookie的值是不能直接使用逗号的 如果使用形如 C#代码 1.C ...

  9. coreseek实战(四):php接口的使用,完善php脚本代码

    coreseek实战(四):php接口的使用,完善php脚本代码 在上一篇文章 coreseeek实战(三)中,已经能够正常搜索到结果,这篇文章主要是把 index.php 文件代码写得相对完整一点点 ...

  10. Hibernate个人学习笔记(1)

    连接池c3p0所需jar包:Hiberbate开发包-lib-optional-c3p0下全部Jar包 Hiberbate连接池参数配置:Hiberbate开发包-project-etc-hibern ...