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

来源:互联网 发布:哪个淘宝课程好 编辑:程序博客网 时间:2024/05/20 11:33

http://www.tuicool.com/articles/ra6BJb

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

  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 : Employee22   {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.

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

AssociationAggregationCompositionOwnerNo 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

参考资料

  1. Introduction to Object Oriented Programming Concepts (OOP) and More
  2. Understanding Association, Aggregation, and Composition
  3. 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(1, @"Dennis Gao");      ProjectMember member2 = new ProjectMember(2, @"Super Man");      ProjectMember member3 = new ProjectMember(3, @"Iron Man");      ProjectMember member4 = new ProjectMember(3, @"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(5),        EndTime = DateTime.Now.AddDays(6),      };      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 1000; }    protected override double GetProjectBonus() { return 200; }  }  public class ProjectManager : ProjectMember  {    public ProjectManager(int id, string name)      : base(id, name)    {    }    protected override double GetBaseSalary() { return 2000; }    protected override double GetProjectBonus()    {      return AssignedProject.IsSuccess ? 800 : 0;    }  }  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(1, 100) % 2) > 0; } }    public void AssignMembers(IEnumerable<ProjectMember> members)    {      _members.AddRange(members);      _members.ForEach(m => m.AssignProject(this));    }    public void AddScheduleItem(ScheduleItem item)    {      _schedule.Add(item);    }  }}
View Code
0 0