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.

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

  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(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);
    }
  }
}
复制代码
目录
相关文章
|
4月前
|
uml
UML 类图几种关系(依赖、关联、泛化、实现、聚合、组合)及其对应代码
UML 类图几种关系(依赖、关联、泛化、实现、聚合、组合)及其对应代码
605 0
|
6月前
|
Java 程序员 C#
程序员必知:UML关联聚合组合关系
程序员必知:UML关联聚合组合关系
70 0
|
uml
UML——同步消息和异步消息的区别(顺序图中)
UML——同步消息和异步消息的区别(顺序图中)
1581 0
|
uml C++ 容器
「软件设计」UML中关联,聚合和组合的区别是什么?
「软件设计」UML中关联,聚合和组合的区别是什么?
|
uml C++ 容器
「软件设计」UML中关联,聚合和组合区别
「软件设计」UML中关联,聚合和组合区别
|
测试技术 uml
UML图的依赖、关联、聚合、组合关系(突击软考)
UML图的依赖、关联、聚合、组合关系(突击软考)
229 0
|
Java uml
UML类图关系(泛化 、继承、实现、依赖、关联、聚合、组合)
UML类图关系(泛化 、继承、实现、依赖、关联、聚合、组合) 继承 指的是一个类(称为子类、子接口)继承另外的一个类(称为父类、父接口)的功能,并可以增加它自己的新功能的能力,继承是类与类或者接口与接口之间最常见的关系;在Java中此类关系通过关键字e...
1206 0
|
uml
分分钟弄明白UML中泛化 , 实现 , 关联, 聚合, 组合, 依赖
在UML类图中,常见的有以下几种关系: 泛化(Generalization),  实现(Realization), 关联(Association), 聚合(Aggregation), 组合(Composition), 依赖(Dependency) 1.
1688 0