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

本文为 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 类覆写父类的薪水计算方法。

 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

参考资料

完整代码

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

本文为 Dennis Gao 原创技术文章,发表于博客园博客,未经作者本人允许禁止任何形式的转载。

转载于:https://www.cnblogs.com/gaochundong/p/uml_association_aggregation_composition.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值