本文为 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. CentOS和Ubuntu下安装配置Greenplum数据库集群(包括安装包和源码编译安装)

    首先说一下,无论是CentOS/RedHat还是Ubuntu都可以按源码方式.安装包方式编译安装. 1.   规划 192.168.4.93(h93)       1个主master  2个主segm ...

  2. Python 之WEB前端插件

    1.Font Awesome ---- 设计字体,图标 2.EasyUI ---- 各种功能 3.JqueryUI ---- 类似EasyUI 4.bootstrap ---- 必须引入JQuery( ...

  3. Jmeter上传文件

    Jmeter上传文件 一.Fiddler抓包获取表单信息 操作被测系统,上传文件,Fiddler抓包获取提交表单信息如下:

  4. random、面向对象编程

    一.random模块:随机数 import random print random.random() print random.randint(,) print random.randrange(,) ...

  5. 《C++编程规范:101条规则、准则与最佳实践》学习笔记

    转载:http://dsqiu.iteye.com/blog/1688217 组织和策略问题 0. 不要为小事斤斤计较.(或者说是:知道什么东西不需要标准化) 无需在多个项目或者整个公司范围内强制实施 ...

  6. HTML 透明、阴影,圆角等知识点

     table两个属性:cellpadding:内容与单元格边框的距离,内部距离cellspacing:单元格之间的距离,外部距离    table合并边框线:  border-collapse: co ...

  7. (转载)iOS系统Crash文件分析方法

    转自: http://ios-iphone.diandian.com/post/2012-05-18/19440182 Xcode 4.3的symbolicatecrash的位置和老版本的不一致了. ...

  8. spring junit

    转载自 http://blog.csdn.net/funi16/article/details/8691575 在写单元测试的时候,一般是对数据库进行增删改查的操作,这个时候,如果之前删除了某条记录, ...

  9. Python-内置类属性

    Python内置类属性 __dict__ : 类的属性(包含一个字典,由类的数据属性组成) __doc__ :类的文档字符串 __name__: 类名 __module__: 类定义所在的模块(类的全 ...

  10. 创建solr集群简述

    综述: 用两台服务器,每台服务器上启动两个solr实例(端口分别为8983.7574),即一共有2x2=4个节点.4个节点分散在两个分片上,每台机器上存放两个分片的各一个replica,这样等于每台机 ...