2009年8月3日星期一

以四色原型改善資料庫設計之三-2

前面提到了角色的設計,我們來看另一種設計方法。這應該是Peter Coad大師在書上提過的作法,只是我沒看過書就是了。
Role Object:
Person類別不再實作所有角色的介面,而改成內部使用一個集合來擺放可能的角色。
public class Person
{
private IList<PersonRole> _roles = new List<PersonRole>();

public string Name{get;set;}
public string Address{get;set;}
public string Tel{get;set;}

public void AddRole(PersonRole role)
{
_roles.Add(role);
}

public T RoleOf<T>() where T:PersonRole
{
foreach(PersonRole role in _roles)
{
if(role.HasType<T>())
{
return (T)role;
}
}

return null;
}
}
依據Matin Fowler大師的建議,替PersonRole加上HasType這個方法,日後我們的角色如果成長成一顆繼承樹時(Sequenal Roles),就會很有用。例如專任教師、科任教師的父角色都是教師,而教師的父角色可能是教職員工,這裏就可以寫些判斷的方法。
public abstract class PersonRole
{
public bool HasType<T>()
{
if(this is T)
{
return true;
}
else
{
return false;
}
}
}
Customer與Employee就只要繼承PersonRole就好了
public class Customer : PersonRole
{
public string DeliverAddress{get;set;}
}

public class Employee : PersonRole
{
public int MonthSalary{get;set;}
}

測試碼如下:
[Test]
public void TestMakeCustomer()
{
Person person = new Person();

Customer customer = new Customer();
customer.DeliverAddress = "台北市忠孝東路";
person.AddRole(customer);

Customer customerRole = person.RoleOf<Customer>();

Assert.IsNotNull(customerRole);
Assert.AreEqual("台北市忠孝東路",customerRole.DeliverAddress);
}

[Test]
public void TestMakeEmployee()
{
Person person = new Person();

Employee employee = new Employee();
employee.MonthSalary = 40000;
person.AddRole(employee);

Employee employeeRole = person.RoleOf<Employee>();

Assert.IsNotNull(employeeRole);
Assert.AreEqual(40000,employeeRole.MonthSalary);
}

2009年7月23日星期四

關於類別圖的一些思考之三

自身關聯:
自身關聯分成兩種,一種是一對多的自身關聯,一種是多對一的自身關聯,但在資料庫的設計上都是一樣的,每一筆資料多開出一個欄位,用來關聯同一資料表內的其他資料。自身關聯通常用來表示樹狀結構如組織圖、BOM表。

1對多:
1對多通常是用來表示整體部分的關係,這不用多說。以這種方式取出的物件,還有一個好處,就是使用Composition Pattern的手法,對某個Org下所有子Org遍覽一次。

多對1:
看起來多對1就僅只在表示某些物件有關聯而已。
合併使用:
很普遍的狀況,以一對多包含下層結點,多對一包含一個上層結點,典型的樹狀結構。


結點重複的畫法:
所謂的結點重複,代表一顆樹中,組成的元素是會重複的,如下
如之前的文章所述,這時的Aggregation暗藏著多對多的訊息,所以就如此重繪。


多對多:
最近在思考這種關係如何表達
畫抽象一點,就是一個多對多的自身關聯。
如果是上列關係,倒可視為如下關係的簡化。

2009年7月22日星期三

以四色原型改善資料庫設計之三-1

接下來我們繼續研究Supplier、Customer、Employee、Shipper的設計。我們就這麼假定,Employee就只能是Person,Customer可以是Person與Company,Supplier與Shipper則是只能是Company。當我們想使用繼承的觀念來處理這幾個主檔時,Custmer就會帶來很大的困擾。因為就概念上我們不能取出一個Customer,然後他的父類別居然是可變的。注意是可變的,並不是Customer同時是Person與Company的多重繼承觀念。

這個問題稱之為Role問題,Customer、Supplier、Shipper是Company的角色,Customer、Employee是Person的角色。Peter Coad在他的書上提到了這個Pattern,但我只能在Matin Fowler的網站上看到示範的Code。
Fowler所提的解法,我歸類為兩種,由於解法不同,資料庫的設計也就不同。

Role SubType:
首先準備如下的介面
public interface IPerson
{
string Name{get;set;}
string Address{get;set;}
string Tel{get;set;}
}
public interface ICustomer : IPerson
{
string DeliverAddress{get;set;}
}
public interface IEmployee : IPerson
{
int MonthSalary{get;set;}
}

再準備如下的抽象類別及其實作
public abstract class PersonRole
{
public virtual string DeliverAddress
{
get{throw new ApplicationException("not implement");}
set{throw new ApplicationException("not implement");}
}

public virtual int MonthSalary
{
get{throw new ApplicationException("not implement");}
set{throw new ApplicationException("not implement");}
}
}

public class Employee : PersonRole
{
private int _monthSalary;
public override int MonthSalary
{
get{return _monthSalary;}
set{_monthSalary = value;}
}
}

public class Customer : PersonRole
{
private string _deliverAddress;
public override string DeliverAddress
{
get{return _deliverAddress;}
set{_deliverAddress = value;}
}
}

Person類別就這麼寫:

public class Person : IPerson,IEmployee,ICustomer
{
private PersonRole _personRole;
public string Name{get;set;}
public string Address{get;set;}
public string Tel{get;set;}

public string DeliverAddress
{
get{return _personRole.DeliverAddress;}
set{_personRole.DeliverAddress = value;}
}

public int MonthSalary
{
get{return _personRole.MonthSalary;}
set{_personRole.MonthSalary = value; }
}

public static IEmployee MakeEmployee()
{
Person person = new Person();
person._personRole = new Employee();
return person;
}

public static ICustomer MakeCustomer()
{
Person person = new Person();
person._personRole = new Customer();
return person;
}
}

測試代碼如下:

[Test]
public void TestMakeEmployee()
{
IEmployee employee = Person.MakeEmployee();
employee.MonthSalary = 40000;

Assert.AreEqual(40000,employee.MonthSalary);
}

[Test]
public void TestMakeCustomer()
{
ICustomer customer = Person.MakeCustomer();
customer.DeliverAddress = "台北市忠孝東路";

Assert.AreEqual("台北市忠孝東路",customer.DeliverAddress);
}

關於類別圖的一些思考之二

多對多:
之前提到的association可以用來表達多對一,aggregation可以用來表達一對多,如果Method的責任就這麼指定,我覺得是合理的。但對於多對多的狀況,就必須另找答案。

我們都了解多對多在資料庫是採用中介資料表來處理,但實際辨識上,卻可以想成是這種關係的簡化。如果是這種關係,我們自然可以很好推測Student那邊有AddStudentSubject(),而StudentSubject那邊有StudentSubject.GetStudentSubjectBySubject()這樣的Method。

經過簡化後,我就在Student那邊加上AddSubject(),以及Student.GetStudentBySubject()。當然Subject那邊也可以加上Subject.GetSubjectByStudent。那為何不乾脆Subject那邊也加上AddStudent算了?我只能強調多對多就是上列關係的簡化,不當的雙向關聯,只會讓語意產生模糊。

不明確的表示法:
有時候會看見這種aggregation畫法,沒有標示多重性,會讓人誤解只是單純的一對多。
但事實上很明顯的,一個學生可以選擇多堂課,而一堂課也可以被不同學生選取啊,這分明是多對多的關係。這樣畫對UML來說完全是對的,但無論是對意義的傳達上,還是拿來設計資料庫,都是有問題的。最好還是標上多重性。

2009年7月14日星期二

以四色原型改善資料庫設計之二

主檔:

原來資料庫中的主檔如下:

仔細看過他們的欄位,無非都是名稱、地址、電話,換句話說他們都是Party的一種,所以我們建立起一個共用的Party當成共同主檔。再來Party主檔之下,還可以再區分兩個繼承主檔,Person與Company。
使用資料庫實現繼承的方法有三種,這裏我只想表達出概念如此,Party與Person或Company都是1對0..1的關係。這樣整理之後,就會發現Supplier、Shipper、Employee、Customer剩不了什麼自己的欄位了。

2009年7月9日星期四

關於類別圖的一些思考之一

Association(關係),Aggregation(聚合),Composition(組合):
思考了一下三者的關聯,網路上找不到太明確的解釋,只好自己下定義。
  1. association:關聯的兩方都是單獨的個體。一的那方不保有多的那方的集合,多的那方有一的參考。當一的那方想取得多的那方的物件時,應該透過多的那方的Method進行讀取,如Building.GetBuildingsByOwner(Owner)。
  2. aggregation:代表兩方有整體與部分的關係。最明顯的,一的那方會有集合保有多的那方的物件集合。就外部來看,想取得多的那方的物件,也只能透過一的那方的Method。所以會是order.AddDetail(OrderDetail)與order.OrderDetails[2]。
  3. composition:關係與aggregation一樣,一般而言指的是物件的內部集合,加不進去,也取不到的關係,可以說非常少見。

2009年7月2日星期四

系統分析設計相關技能概論

需求收集
其目的是在替需求建立文字性的描述,以一種連鎖的關連組織需求。通常還帶有規範功能粒度的目的。
  1. Use Case:這步驟算是最為知名,也最容易為人誤解。將它當成OOA的必要技術。很可惜,它跟OO一點關連也沒有,甚至也不能算分析的一種。Use Case的優點在於可以明確的記錄使用者與系統的互動,依據RUP的流程,這就是系統開發與專案管理的基礎。但缺點在於文字並不是溝通以及建立概念的好工具,在這一階段容易與使用者耗費過久的時間確定文字的內容,這點是相當不利的。
  2. User Story:做為XP的需求收集工具,自然崇尚簡單,User Story間沒有太嚴謹的組織關連,也不必要精確的記錄每個步驟。通常只是一段散文式的情節。缺點也在於它的簡單含糊,所以事後得花更多的時間與使用者確認,不過這也符合XP快速開發的意思,如果不會愈改愈慘的話。
系統分析:
分析的目的在將蒐集來的資訊重新組織,以便更能進入程式碼的狀態。所以這個階段是實際編程的起步,應該融合了領域知識的概念還有程式的抽象概念。
  1. Domain Model:以類別圖描述業務邏輯,就是這種分析法的宗旨。建立Domain Model是一項晦澀難明的事,相關的書籍只有兩大天書Java Modeling With Color UML與Analysis Pattern。建立Domain Model通常沒有需求收集階段,基本假設就是拿Domain Model與客戶面對面溝通,而不是先使用文字記錄。
  2. 四色原型:就是由Java Modeling With Color UML所提的,一組接近人類思維表達法(人地物、事件、角色、描述),建立模型。
  3. Analysis Pattern:蒐集了大量OO建模的技巧,粗看相當頊碎,而且沒有歸納出一套推理法則。不過最近我在畫Domain Model時,常常是先用四色原型勾勒出大概,再用Analysis Pattern進行下一步的設計考量。
  4. CRC卡分析:XP的開發人員依據User Story,利用CRC卡(一種像類別圖的卡片),直接進行程式責任的指定,沒有一套理論教導我們如何進行這項工作,也沒有理論告訴我們該做到什麼程度是合適的。所以我只能將這一步驟擺在分析與設計之間。
  5. 文本分析&強固圖&循序圖:這是RUP開發的流程,我順便補上了ICONIX流程所說的強固圖
    • 文本分析:將還在文字階段的Use Case進行名詞剖析,做出靜態的類別圖(無方法)。
    • 強固圖分析:上一步的類別圖會轉成Entity,Use Case的流程轉成一個接一個的Control,UI轉成Boundary,畫出Robustness Diagram。
    • 循序圖分析:將行為加到Control上面,畫出Sequence Diagram。

  6. ProtoType分析:這裏我只想強調,ProtoType就只是在分析UI的行為,沒法取代前述的分析方法。其實我比較認同應該設立業務分析師與UI分析師兩種職位。
  7. BDD&DSL:對這兩種全新的分析方法,我還在了解中。
  8. DFD:對於這個有點年紀的分析方法,我大概沒時間了解了。
系統設計:
設計有兩種職位,一種是延續分析的工作,將業務邏輯設計的更具擴充性、與維護性。一種則是純技術的好手,負責規畫非功能需求的解決方案。我列出的技能都是屬於第一種。
Martin Flowler有說到業務邏輯的設計方式有三,一是Domain Model,二是Transaction Script,三是Table Model。個人覺得設計方式的不同,也連帶的影響了分析的方式。順著Domain Model的分析便流到了Domain Model的設計,順著RUP分析的便流到了Microsoft TLSA。
  1. Domain Model Driven Design:是一本Domain Model設計方法的書籍,且有很大的影響力。個人覺得DDD的位置介於分析與設計之間,有點像JMCU式規範業務類別的種類,又有點像Enteripse Pattern式的列出最佳設計原則。DDD目前已經有不少的現成框架,可以一氣呵成的完成Domain Model式的開發方法。
  2. Microsoft TLSA:相信很多人不清楚這是什麼東西,簡單言之它就是PetShop4的示範框架(Three Layer Service Application應該吧)。就是WindowWorkFlow包Business Component,Business Component包Business Entity,這種實作方式被Matin Fowler大師說是一種貧血模型,並不能說是真正的OO系統。但我又為何把RUP分析的結果直接導入這個框架?因為如果深思一番,你就會發現Business Component很像Control,Business Entity很像Entity,這種緊密的連續思路,當然要把這兩者扯在一起。所以RUP是OO的開發方法嗎?我只能說,可以是,也可以不是。
  3. ORM:從趨勢上來看,這種東西快成必需品,也許很快新人工程師就會忘了這世界上有DataSet這回事。
  4. IOC、AOP:我想會再寫一篇文來介紹這兩種觀念,IOC是Stragery模式的實務應用方式,AOP是Decorator模式的延伸應用。
  5. MVC、MVP:這世界總是會有人強調寫UI沒這些架構也活的好好的,若這麼說寫程式確實不須這麼多的理論,只要有一顆清楚的腦袋。所以發明架溝的人,主要是讓腦子不那麼清楚的工程師可以有個方向。
  6. Design Pattern:這本書的重要就不多說了,就算你23個Pattern,用不到三分之一或五分之一,也請謹記基本的OOD守則,SRP(單一責任)、OCP(開放封閉)、LSP、DIP(依賴反轉)、(ISP)介面分離。
  7. Enteripse Pattern:光看Design Pattern會讓人覺得太學術,那Enterprise Pattern就好比基本物理對上應用物理的,針對商業系統所歸納出的一連串設計原則。