Wednesday, November 11, 2015

UML中关联(Association)、聚合(Aggregation)和合成(Composition)之间的区别

现在,我们需要设计一个项目管理系统,目前我们收集到了如下这些需求:
  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 类覆写父类的薪水计算方法。
复制代码
 1   public abstract class Employee
 2   {
 3     public Employee(int id, string name)
 4     {
 5       ID = id;
 6       Name = name;
 7     }
 8 
 9     public int ID { get; private set; }
10     public string Name { get; private set; }
11 
12     public double CalculateSalary()
13     {
14       return GetBaseSalary() + GetProjectBonus();
15     }
16 
17     protected abstract double GetBaseSalary();
18     protected abstract double GetProjectBonus();
19   }
20 
21   public class ProjectMember : Employee
22   {
23     public ProjectMember(int id, string name)
24       : base(id, name)
25     {
26     }
27 
28     public Project AssignedProject { get; private set; }
29 
30     public void AssignProject(Project project)
31     {
32       AssignedProject = project;
33     }
34 
35     protected override double GetBaseSalary() { return 1000; }
36     protected override double GetProjectBonus() { return 200; }
37   }
复制代码
根据需求 “REQ3:一个项目内仅有一名项目成员被指派为项目经理负责管理项目”,可以推断出 ProjectManager 与 ProjectMember 的关系。
ProjectManager 继承自 ProjectMember。ProjectMember 类覆写父类的薪水计算方法,以实现需求 “REQ6:项目经理的项目奖金由项目的成败决定”。
复制代码
 1   public class ProjectManager : ProjectMember
 2   {
 3     public ProjectManager(int id, string name)
 4       : base(id, name)
 5     {
 6     }
 7 
 8     protected override double GetBaseSalary() { return 2000; }
 9 
10     protected override double GetProjectBonus()
11     {
12       return AssignedProject.IsSuccess ? 800 : 0;
13     }
14   }
复制代码
由下面三个需求可以识别出 Project 与 ProjectMember/ProjectManager 之间的关系。
REQ1:一个项目内有多名项目成员
REQ2:一名项目成员只能被指派给一个项目
REQ3:一个项目内仅有一名项目成员被指派为项目经理负责管理项目
Project 聚合(Aggregation)了 ProjectMember,ProjectMember 当不在该项目中时仍然可以存在,比如转去做其他项目。
Project 关联(Association)了 ProjectManager,ProjectManager 当不在该项目时,需要转换为 ProjectMember。
ProjectManager 的薪水将由所负责的项目的成败决定,会调用 Project 的状态以计算薪水。
复制代码
 1   public class Project
 2   {
 3     private ProjectManager _manager;
 4     private List<ProjectMember> _members = new List<ProjectMember>();
 5     private Schedule _schedule = new Schedule();
 6 
 7     public Project(string name, ProjectManager manager)
 8     {
 9       Name = name;
10       _manager = manager;
11     }
12 
13     public string Name { get; private set; }
14     public ProjectManager Manager { get { return _manager; } }
15     public ReadOnlyCollection<ProjectMember> Members { get { return _members.AsReadOnly(); } }
16     public Schedule Schedule { get { return _schedule; } }
17     public bool IsSuccess { get { return (new Random().Next(1, 100) % 2) > 0; } }
18 
19     public void AssignMembers(IEnumerable<ProjectMember> members)
20     {
21       _members.AddRange(members);
22       _members.ForEach(m => m.AssignProject(this));
23     }
24 
25     public void AddScheduleItem(ScheduleItem item)
26     {
27       _schedule.Add(item);
28     }
29   }
复制代码
根据需求 “REQ7:项目中包含项目计划” 可得出 Project 与 Schedule 的关系。
根据需求 “REQ8:一个项目计划由多个项目计划项组成” 可得出 Schedule 与 ScheduleItem 的关系。
Project 聚合(Aggregation)了 Schedule。Schedule 由多个 ScheduleItem 组成(Composition)。
复制代码
 1   public class Schedule : List<ScheduleItem>
 2   {
 3   }
 4 
 5   public class ScheduleItem
 6   {
 7     public string Description { get; set; }
 8     public DateTime BeginTime { get; set; }
 9     public DateTime EndTime { get; set; }
10   }
复制代码
由此,我们得到了满足全部需求的类图:
现在,我们可通过以上类的定义来组织业务逻辑。
复制代码
 1   class Program
 2   {
 3     static void Main(string[] args)
 4     {
 5       ProjectManager manager = new ProjectManager(1, @"Dennis Gao");
 6       ProjectMember member2 = new ProjectMember(2, @"Super Man");
 7       ProjectMember member3 = new ProjectMember(3, @"Iron Man");
 8       ProjectMember member4 = new ProjectMember(3, @"Spider Man");
 9 
10       var projectMembers = new List<ProjectMember>() { manager, member2, member3, member4 };
11 
12       Project project = new Project("EarnMoney", manager);
13       project.AssignMembers(projectMembers);
14 
15       ScheduleItem item1 = new ScheduleItem()
16       {
17         Description = "Team Building",
18         BeginTime = DateTime.Now.AddDays(5),
19         EndTime = DateTime.Now.AddDays(6),
20       };
21       project.AddScheduleItem(item1);
22 
23       Console.WriteLine("Salary List of Project [{0}] Members:", project.Name);
24       foreach (var member in project.Members)
25       {
26         Console.WriteLine(
27           "\tProject Member [{0}] has TotalSalary [{1}].",
28           member.Name, member.CalculateSalary());
29       }
30 
31       Console.WriteLine();
32       Console.WriteLine("[{0}] members will have a [{1}] on [{2}].",
33         project.Name, project.Schedule.First().Description,
34         project.Schedule.First().BeginTime);
35 
36       Console.ReadKey();
37     }
38   }
复制代码
由于在业务逻辑中,ProjectManager 的项目奖金由项目的成败来决定,但是项目的成败又多少带了点运气。
1 public bool IsSuccess { get { return (new Random().Next(1, 100) % 2) > 0; } }
1     protected override double GetProjectBonus()
2     {
3       return AssignedProject.IsSuccess ? 800 : 0;
4     }
所以,我们可能会得到两种输出结果,成功的项目和失败的项目。
失败的项目没有项目奖金:
成功的项目拿到了项目奖金:
我们给出 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.
我们可以从不同的角度来理解和区分这三种关系:
 AssociationAggregationComposition
OwnerNo owner
 Single owner
Single owner
LifetimeHave their own lifetime
Have their own lifetime
Owner's lifetime
Child ObjectChild objects all are independent
Child objects belong to a single parent
Child objects belong to single parent
所以,总结来说,聚合(Aggregation)是一种特殊的关联(Association),合成(Composition)是一种特殊的聚合(Aggregation)。
Association->Aggregation->Composition

参考资料

No comments:

Post a Comment