本文为 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. 【转】Java日期计算之Joda-Time

    Joda-Time提供了一组Java类包用于处理包括ISO8601标准在内的date和time.可以利用它把JDK Date和Calendar类完全替换掉,而且仍然能够提供很好的集成. http:// ...

  2. 【转载】H264--2--语法及结构

    名词解释 场和帧 :    视频的一场或一帧可用来产生一个编码图像.在电视中,为减少大面积闪烁现象,把一帧分成两个隔行的场. 片:             每个图象中,若干宏块被排列成片的形式.片分为 ...

  3. MySQL_杭州北仓 12.3-12.7需求活动期间累计下单达到3天及以上的客户_20161212

    #C025_02杭州北仓 12.3-12.7需求活动期间累计下单达到3天及以上的客户明细 SELECT d.*,CASE WHEN 下单天次>=3 THEN "下单超过3天" ...

  4. PHP数组的知识

  5. Antenna Placement poj 3020(匹配)

    http://poj.org/problem?id=3020 题意:给定一个n*m的矩阵,'*'代表城市,现在想要用1*2的矩阵将所有的城市覆盖,问最少需要多少个矩阵? 分析:先为每个城市进行标号,再 ...

  6. Phone Gap [error] cmd: Command failed with exit code 1

    下投票 我不知道如何解决这个问题,但尝试了这一点,将解决肯定. 这是由于ANT工具找不到的tools.jar在JRE lib目录下.当我从复制的tools.jar JDK的lib目录下,以JRE li ...

  7. 设置阿里云maven中央仓库的settings.xml

    本来想找一个可用的设置文件,结果乱七八糟的,干脆自己做了一个,同时还放上了Spring的SNAPSHOT和MILESTONE/RELEASE仓库,希望能帮到一些人. <?xml version= ...

  8. Ubuntu10.0.4安装NDK

    android版本遇到.so文件crash,需要使用ndk来定位报错代码. 从这里下载ndk安装文件: http://www.androiddevtools.cn/ 运行 ./android-ndk- ...

  9. Windows中多个python版本共存的问题

    原创文章,未经本人允许进制转载. 在我的Windows中,先安装了python3.4,后来因为需要又安装了python2.7,结果发现: 直接双击1.py和命令行python 1.py使用的pytho ...

  10. Hive安装与部署集成mysql

    前提条件: 1.一台配置好hadoop环境的虚拟机.hadoop环境搭建教程:稍后补充 2.存在hadoop账户.不存在的可以新建hadoop账户安装配置hadoop. 安装教程: 一.Mysql安装 ...