http://www.tuicool.com/articles/ra6BJb
现在,我们需要设计一个项目管理系统,目前我们收集到了如下这些需求:
- 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 类覆写父类的薪水计算方法。
- 1 public abstract class Employee
- 2 {
- 3 public Employee(int id,string name)
- 4 {
- 5 ID = id;
- 6 Name = name;
- 7 }
- 8
- 9 int ID { get; private set; }
- 10 string Name { 11
- 12 double CalculateSalary()
- 13 14 return GetBaseSalary() + GetProjectBonus();
- 15 16
- 17 protected GetBaseSalary();
- 18 19 }
- 20
- 21 ProjectMember : Employee
- 22 23 public ProjectMember(24 : base(id,name)
- 25 26 27
- 28 public Project AssignedProject { 29
- 30 void AssignProject(Project project)
- 31 32 AssignedProject = project;
- 33 34
- 35 override double GetBaseSalary() { return 100036 double GetProjectBonus() { 20037 }
根据需求 “REQ3:一个项目内仅有一名项目成员被指派为项目经理负责管理项目”,可以推断出 ProjectManager 与 ProjectMember 的关系。
ProjectManager 继承自 ProjectMember。ProjectMember 类覆写父类的薪水计算方法,以实现需求 “REQ6:项目经理的项目奖金由项目的成败决定”。
由下面三个需求可以识别出 Project 与 ProjectMember/ProjectManager 之间的关系。
REQ1:一个项目内有多名项目成员
REQ2:一名项目成员只能被指派给一个项目
REQ3:一个项目内仅有一名项目成员被指派为项目经理负责管理项目
Project 聚合(Aggregation)了 ProjectMember,ProjectMember 当不在该项目中时仍然可以存在,比如转去做其他项目。
Project 关联(Association)了 ProjectManager,ProjectManager 当不在该项目时,需要转换为 ProjectMember。
ProjectManager 的薪水将由所负责的项目的成败决定,会调用 Project 的状态以计算薪水。
根据需求 “REQ7:项目中包含项目计划” 可得出 Project 与 Schedule 的关系。
根据需求 “REQ8:一个项目计划由多个项目计划项组成” 可得出 Schedule 与 ScheduleItem 的关系。
Project 聚合(Aggregation)了 Schedule。Schedule 由多个 ScheduleItem 组成(Composition)。
由此,我们得到了满足全部需求的类图:
现在,我们可通过以上类的定义来组织业务逻辑。
由于在业务逻辑中,ProjectManager 的项目奖金由项目的成败来决定,但是项目的成败又多少带了点运气。
所以,我们可能会得到两种输出结果,成功的项目和失败的项目。
失败的项目没有项目奖金:
成功的项目拿到了项目奖金:
我们给出 UML 中的相关定义:
我们可以从不同的角度来理解和区分这三种关系:
所以,总结来说,聚合(Aggregation)是一种特殊的关联(Association),合成(Composition)是一种特殊的聚合(Aggregation)。
Association->Aggregation->Composition
参考资料
- Introduction to Object Oriented Programming Concepts (OOP) and More
- Understanding Association,Aggregation,and Composition
- UML类图基本元素符号
完整代码
- using System;
- System.Collections.Generic;
- System.Collections.ObjectModel;
- System.Linq;
- namespace UML
- {
- Program
- {
- [] args)
- {
- ProjectManager manager = );
- ProjectMember member2 = );
- ProjectMember member3 = );
- ProjectMember member4 = );
- = ScheduleItem()
- {
- Description = = DateTime.Now.AddDays( project.Members)
- {
- Console.WriteLine(
- Employee
- {
- name)
- {
- ID = id;
- Name = name;
- }
- ; }
- ; }
- CalculateSalary()
- {
- GetProjectBonus();
- }
- GetBaseSalary();
- GetProjectBonus();
- }
- ProjectMember : Employee
- {
- name)
- : AssignProject(Project project)
- {
- AssignedProject = project;
- }
- ; }
- }
- ProjectManager : ProjectMember
- {
- GetProjectBonus()
- {
- ;
- }
- }
- class Schedule : List<ScheduleItem>
- {
- }
- ScheduleItem
- {
- Project
- {
- ProjectManager _manager;
- ();
- Schedule();
- name;
- _manager = manager;
- }
- _manager; } }
- _members.AsReadOnly(); } }
- _schedule; } }
- ; } }
- members)
- {
- _members.AddRange(members);
- _members.ForEach(m => m.AssignProject());
- }
- AddScheduleItem(ScheduleItem item)
- {
- _schedule.Add(item);
- }
- }
- }