UML中关联(Association)、聚合(Aggregation)和合成(Composition)之间的区别
本文为 Dennis Gao 原创技术文章,发表于博客园博客,未经作者本人允许禁止任何形式的转载。
现在,我们需要设计一个项目管理系统,目前我们收集到了如下这些需求:
- REQ1:一个项目内有多名项目成员
- REQ2:一名项目成员只能被指派给一个项目
- REQ3:一个项目内仅有一名项目成员被指派为项目经理负责管理项目
- REQ4:所有项目成员均是公司员工
- REQ5:公司员工的薪水由基本工资和项目奖金组合而成
- REQ6:项目经理的项目奖金由项目的成败决定
- REQ7:项目中包含项目计划
- REQ8:一个项目计划由多个项目计划项组成
根据上面的需求描述,我们首先识别出若干个概念名词:
- 项目(Project)
- 项目成员(Project Member)
- 项目经理(Project Manager)
- 公司员工(Employee)
- 薪水(Salary)
- 基本工资(Base Salary)
- 项目奖金(Project Bonus)
- 项目计划(Schedule)
- 项目计划项(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
参考资料
- Introduction to Object Oriented Programming Concepts (OOP) and More
- Understanding Association, Aggregation, and Composition
- Aggregation vs Composition
- What is the difference between dependency and association?
- UML类图基本元素符号
- UML中依赖(Dependency)和关联(Association)之间的区别
- UML用例图
- UML序列图
- UML类图
完整代码
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)之间的区别的更多相关文章
- UML的关联(Association), 聚合(Aggregation), 组合(Composition)区别
转载:http://blog.csdn.net/ocean181/article/details/6117369 UML的关联(Association), 聚合(Aggregation), 组合(Co ...
- UML中关联(Association)和依赖(Dependency)的区别
原文转自:http://blog.csdn.net/metasearch/article/details/2334853 在UMLCHINA精华区,看到了一些关联和依赖的讨论,似乎越讲越糊涂.我想谈一 ...
- socket编程中write、read和send、recv之间的区别~转载
socket编程中write.read和send.recv之间的区别 http://blog.csdn.net/petershina/article/details/7946615 一旦,我们建立 ...
- Jquery中.bind()、.live()、.delegate()和.on()之间的区别详解
简介 最近了解到很多网页开发者对jquery中的 .bind() .live() .delegate() 和 .on() 方法存在很多的疑惑.这些疑惑通常是关于它们之间真正的区别是什么啊,什么时候该使 ...
- C#中IEnumerable、ICollection、IList、List之间的区别
IEnumerable.ICollection.IList.List之间的区别,本文分别分析了它的实现源码,从而总结出了它们之间的关系和不同之处. 首先我看看 IEnumerable: // 摘要: ...
- Python中的赋值(复制)、浅拷贝、深拷贝之间的区别
1.赋值: 只是复制了新对象的引用,不会开辟新的内存空间. 2.浅拷贝: 创建新对象,其内容是原对象的引用. 浅拷贝有三种形式:切片操作,工厂函数,copy模块中的copy函数. 如: ...
- socket编程中write、read和send、recv之间的区别
http://blog.csdn.net/petershina/article/details/7946615 一旦,我们建立好了tcp连接之后,我们就可以把得到的fd当作文件描述符来使用. 由此网络 ...
- Swift中元组(Tuples),结构体(Struct),枚举(Enums)之间的区别
Swift有许多种存储数据方式,你可以用枚举(enums),元组(tuples),结构体(structs),类(classes),在这篇文章中我们将比较枚举.元组.结构体之间区别,首先从最简单的开始- ...
- SERVLET中的doGet与doPost两个方法之间的区别
get和post是http协议的两种方法,另外还有head, delete等 这两种方法有本质的区别,get只有一个流,参数附加在url后,大小个数有严格限制且只能是字符串.post的参数是通过另外的 ...
随机推荐
- 15 个有用的 MySQL/MariaDB 性能调整和优化技巧(转载的一篇好文)
MySQL 是一个强大的开源关系数据库管理系统(简称 RDBMS).它发布于 1995 年(20年前).它采用结构化查询语言(SQL),这可能是数据库内容管理中最流行的选择.最新的 MySQL 版本是 ...
- Scala class的构造方法与继承
有java背景的人,很清楚java是如何定义构造方法以及继承的.在scala里面,继承和java有些相似.但是构造方法的定义,就不大一样了,应该说是差别还是很大的.在java里面,定义构造方法,就是定 ...
- TS 流的解码过程(系摘抄)
TS 流解码过程: 1. 获取TS中的PAT 2. 获取TS中的PMT 3. 根据PMT可以知道当前网络中传输的视频(音频)类型(H264),相应的PID,PCR的PID等信息. 4. 设置demux ...
- jqeury datatable
1.自定义列信息 "aoColumnDefs":[ { ...
- codevs 1163 访问艺术馆
1163 访问艺术馆 时间限制: 1 s 空间限制: 128000 KB 题目描述 Description 皮尔是一个出了名的盗画者,他经过数月的精心准备,打算到艺术馆盗画.艺术馆的结 ...
- 【python】密码生成器
#!/usr/bin/env python#-*- coding:UTF-8 -*- import random #导入random模块import string #导入string模块 sal ...
- 也说面试 - 一个努力的iOS Dev
你们在金色的余晖中回家,而我却在银色的温柔中,匆匆潜行-----这是我的现状. 今年的招工形式不是很好,难找工作:也难招人.写这篇博客,是为了给各位在找工作的iOS dev 一些参考. 上篇:换坑(去 ...
- mysql操作--高级
1.视图 2.储存过程 3.SQL编程
- [转] 在Linux平台使用mhVTL虚拟化磁带库
原文来自:LIUBINGLIN ---- http://blog.itpub.net/23135684/viewspace-1307626/ <在Linux平台安装mhVTL虚拟化磁带库> ...
- 【网站运营】网站被K的原因大总结
对于广大的站长来说网站被K或者是被降权是经常有的事情,不过我基本上还没有看见过Google的K站情况,也就是给网站降个权什么的处罚.如果你是用了很严重的作弊手段的话,那指定会是被Google给K掉的. ...