本文为 Dennis Gao 原创技术文章,发表于博客园博客,未经作者本人允许禁止任何形式的转载。
现在,我们需要设计一个项目管理系统,目前我们收集到了如下这些需求:
- REQ1:一个项目内有多名项目成员
- REQ2:一名项目成员只能被指派给一个项目
- REQ3:一个项目内仅有一名项目成员被指派为项目经理负责管理项目
- REQ4:所有项目成员均是公司员工
- REQ5:公司员工的薪水由基本工资和项目奖金组合而成
- REQ6:项目经理的项目奖金由项目的成败决定
- REQ7:项目中包含项目计划
- REQ8:一个项目计划由多个项目计划项组成
根据上面的需求描述,我们首先识别出若干个概念名词:
- 项目(Project)
- 项目成员(Project Member)
- 项目经理(Project Manager)
- 公司员工(Employee)
- 薪水(Salary)
- 基本工资(Base Salary)
- 项目奖金(Project Bonus)
- 项目计划(Schedule)
- 项目计划项(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
参考资料
- Introduction to Object Oriented Programming Concepts (OOP) and More
- Understanding Association, Aggregation, and Composition
- Aggregation vs Composition
- What is the difference between dependency and association?
- UML类图基本元素符号
- UML中依赖(Dependency)和关联(Association)之间的区别
- UML用例图
- UML序列图
- 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(, @"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 原创技术文章,发表于博客园博客,未经作者本人允许禁止任何形式的转载。