文章出處

本文為 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.

我們可以從不同的角度來理解和區分這三種關系:

  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);
    }
  }
}
View Code

本文為 Dennis Gao 原創技術文章,發表于博客園博客,未經作者本人允許禁止任何形式的轉載。


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()