類的設計

一、類的基礎:抽象數據類型ADT

ADT最大的益處是 實現細節隱藏,把關鍵數據隱藏在接口內,數據改動不會影響接口函數。把常用的底層數據類型創建爲ADT,並使用這些ADT,而不需要使用底層數據(底層數據操作在ADT內部實現),也就是說儘可能的使用更高的抽象層次。

二、良好的類接口

1、類的接口應該具有一致的抽象層次。即每個類的接口僅能實現一個ADT的操作,包含多個ADT的操作功能的類都是不明確的類接口。
如 :不同混合層次的類

class EmployeeCensus : public List
{
    public:
    void AddEmployee(Employee);
    void RemoveEmployee(Employee);
    ....
    Employee NextItemList();
    Employee firstItem();   
    Employee LastItem();  
}

該類是一個混合類,
void AddEmployee(Employee);
void RemoveEmployee(Employee);是EmployeeCensus類的ADT功能

而 Employee NextItemList();
Employee firstItem();
Employee LastItem(); 是List類的功能。

出現該問題的原因是,沒有理清楚底層內部ADT類(List)的實現和實際提供的更高層次抽象的ADT(Employeecensus)之間的關係。
所以正確的做法應該是將底層ADT實現放在類內部處理,而不是暴露出接口。

class EmployeeCensus 
{
    public:
    void AddEmployee(Employee);
    void RemoveEmployee(Employee);
    Employee NextItemList();
    Employee firstItem();   
    Employee LastItem();  
    private:
    List m_list;
}

2、一定要理解類所實現的抽象是什麼,然後暴露出合適的類接口。
暴露出類的無用接口過多,當一旦類的底層細節需要改變時,就會加大對類的維護的工作量。

3、提供成對服務的類接口,大多數類操作都會有其相對的,相反的操作。

4、把不相關的信息轉移到其他類。有時候,一個類中一半函數使用其中的一部分數據,而另一些函數使用另外的類內部數據,就應該合理 的將類分開。

5、儘可能的讓接口可編程,而不是語義表達。
每個接口都是由可編程部分和語義部分組成,可編程部分由接口中的數據類型和其他屬性構成,編譯器能強制要求他們,而語義部分則是由”本接口將會被怎樣調用”的假定構成,而這些都是無法通過編譯器來強制實施的。
一個接口中任何無法通過編譯器強制實施的部分,就是可能誤用的部分,要想辦法將語義接口轉化爲可編程接口,如asserts

6、要考慮抽象性和內聚性。
不要添加和類不一致的 公有成員。

7、良好的 封裝。
抽象是通過提供一個可以讓你忽略實現細節的模型來管理複雜度,而封裝則強制阻止你看到細節。
抽象和封裝是相關的,沒有封裝,抽象很容易被打破,抽象和封裝要麼兩者皆有,要麼兩者皆失。

儘可能限制類和成員的可訪問性。
讓可訪問性低是促成封裝的基本原則之一。爲保護好接口抽象的完整性,具體做法是:如果暴露一個 子函數不會讓抽象變得不一致,那麼就是可行的,否則或者如果不確定,那就隱藏起來。

三、類設計和實現的問題

1、包含關係纔是面向對象編程的 主要技術。

2、繼承

  • 如果派生類不完全遵循 基類定義的接口,繼承就不是最合適,
  • 當你選擇繼承時,如果是繼承接口才繼承實現或者是要繼承實現才繼承接口,這2種都值得考慮,而如果只是想使用某個類的接口,那麼應該使用包含。

3、避免的類

  • 避免創建萬能類。如果一個類花在GET()或者SET()上向其他類索取數據,也就是深入到其他類,指導該類如何做,則應該考慮把這些功能移到其他類中去。
  • 消除無關緊要的類。如果一個類只包含數據而不包含行爲,則應該考慮把這個類降級,讓他的數據成員成爲其他類的屬性。
  • 避免用動詞命名的類。一個類只有行爲而沒有數據,則考慮將行爲移到其他類中。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章