C++ 類階層的設計方法學

來源:互聯網
上載者:User

作者:Breaker <breaker.zy_AT_gmail>

關於 C++ 類階層的設計方法學,note-to-self + keynote + cross-reference 式筆記

本文精鍊於 [CPP LANG] 12.4, 15.2 的 BBWindow 樣本,只涉及 design
Syntax 參考 [CPP LANG] Ch12, 15; [CPP PRIMER] Ch17, 18
Play with bits 參考 [CPP OBJMODEL] 5.2

keyword: class hierarchy, multiple inheritance, abstract class, virtual base class, abstract factory, clone

目錄

  • 樣本情境
  • 分離實現和介面
  • 替換實現
  • 重複基類
  • 共用實現
  • 抽象工廠
  • Clone 模式
樣本情境^

IValBox: 取得使用者輸入整數的 GUI 元素之抽象類別,它不綁定具體 GUI 元素,如 slider 滑塊, dial 撥盤

IValSlider: 滑塊式 IValBox 實現,類似的還有 IValDial 撥盤式實現,代表 IValBox 類層次中具體的 GUI 元素。這些類可以進一步擴充,如從 IValSlider 派生出 PopupIValSlider

BBWindow: 第三方提供的 GUI 元素實現,類似 MFC 的 CWnd 等。IValBox 的類層次依靠 BBWindow 的類層次實現 GUI 特性(畫圖之類)。BBWindow 只是形式名,它可以替換,其意義就像將 MFC 換成 Qt 一樣,這使得 IValBox 的類層次能夠減小對特定 GUI 元素實現的依賴

UML: Old Hierarchy

設計要義:

  1. 使用 BBWindow 不是 IValBox 概念的基本部分。過分依賴 BBWindow,使得 BBWindow 難於替換

  2. 可變資料是實現的部分,當它侵入介面(抽象類別)時,會影響介面的靈活性

  3. Two-type interfaces: public interface vs. protected interface

    interface: 有譯介面,有譯介面;有時代表函數,有時代表函數之聚集處(類、名字空間),憑上下文判斷。按 [EFFECT CPP] Item 18 的說法:每一種介面都是客戶與你的代碼互動的手段

    public interface: 給使用者使用
    protected interface: 給衍生類別使用

    Strict Guide: Data members are better kept private so that writers of derived classes cannot mess with them.
    推論: A protected interface should contain only functions, types, and constants.
    更多 private data member 的討論見 [EFFECT CPP] Item 22

    Over strict?
    Old Hierarchy 是不是 bad design?

    實際上我用 MFC 時,大多數時候都這樣做,這是大多數人用 G使用者介面架構的方法
    問題不在絕對的 bad design 或 good design,而在於目標是什嗎?Application Development vs. Library Development, Cross-platform vs. Dedicated-platform

    下面的改進設計比 Old Hierarchy 靈活而光鮮,但也帶來的額外的負擔(如理解和維護),Pros and Cons 自行抉擇

分離實現和介面^

UML: Separate Implementation and Interface

介面線:介面繼承形成的層次
實現線/擴充線:實現繼承形成的層次

設計要義:

  1. public 繼承 vs. protected/private 繼承

    另一種表述是:介面繼承 vs. 實現繼承,見 [EFFECT CPP] Item 40。注意 Item 34 和這個設計無關,雖然標題相同,但那個是 for member function 的,這裡是 for class 的

    public 繼承塑模 "is-a" 的關係(見 [EFFECT CPP] Item 32),而 protected/private 塑模 "implemented-in-terms-of" 的關係(見 [EFFECT CPP] Item 39)

    protected 和 private 的區別:private 之實現止於直接衍生類別,而 protected 之實現可以進一步擴充

  2. 替代技術:public 繼承 + Composition 複合

    當用於實現域時,複合塑模 "implemented-in-terms-of" 的關係(和 protected/private 繼承相同),見 [EFFECT CPP] Item 38

    採用 protected/private 繼承還是複合的判斷,見 [EFFECT CPP] Item 39,如繼承會造成 EBO (Empty Base Optimization) 空白基類最佳化

替換實現^

這是分離實現和介面後得到的益處

UML: Substitute Implementation

圖中的 BBSlider 表示 BBIValSlider 可藉由以存在的更特定的 GUI 元素實現,而不是更一般的 BBWindow,就像是從 MFC 的 CWnd 改為了 CSliderCtrl

使用同樣的方法進行擴充就能得到 Big One:

UML: Substitute Implementation, Complicated

無需糾結具體含義,這裡的設計推論更有意思:一個系統展現給使用者的應該是一個抽象類別的階層,其實現所用的是一個傳統的階層
有點教條吧?試問,傳統的層級結構從何而來?我們要做一個橫跨多種 G使用者介面架構之上的架構嗎?

重複基類^

用 virtual 基類消除 replicated base class 重複基類,見 [EFFECT CPP] Item 40

這種用法的目的有二:

  1. 粘合 (glue) 兩個不相干的類。這是我們應該忘掉的 virtual 多繼承,連 Bjarne 也說它是 crude, effective, and important, but not very interesting.
  2. 為了下面的共用實現,這是有邏輯意義的
共用實現^

UML: Share Implementation

這裡 Diamond-shaped Inheritance 鑽石形繼承的意義:讓實現 PopupIValSlider 的類(即 BBPopupIValSlider)共用實現 IValSlider 的類(即 BBIValSlider)中的實現,以減少編碼

於是另一個有意思的推論:組成應用之介面的抽象類別的所有派生都應該是 virtual 的,[EFFECT CPP] Item 40 也說 public 繼承應該總是 virtual
但是現實不能如此,最簡短的反駁是效率因素,見 [CPP LANG] 15.2.5 和 [EFFECT CPP] Item 40

可藉由無 data member class 進行重複基類方式的最佳化
於是,饒了一圈又回來了

抽象工廠^

建構函式不可能是 virtual 的,道理很簡單:不知道對象的確切類型,又如何構造它(建構函式的實質是對象內布局的 bits 初始化)

抽象工廠和 Clone 模式被戲稱為 virtual constructor 虛擬建構函式,因為它們用 virtual 函數迂迴完成建構函式的任務:根據某些線索建立對象

  • 對於真的建構函式,線索是建構函式之參數
  • 對於抽象工廠,線索是程式初始化時的預先設定。它的形態(典型的)可以是類的 UUID 標識 (e.g. COM's IClassFactory)
  • 對於 Clone 模式,線索是當前對象之確切類型

絕對的抽象建立(沒有線索)是不可能的,即沒有文法支援(virtual 建構函式),也沒有邏輯意義:當你想要鉛筆時,可以說我要鉛筆,也可以說我要鉛筆盒中的東西(帶線索的抽象),但不能只說我要東西(不帶線索的抽象)

Why abstract class?

UML: Abstract Factory

減小對象建立時,對特定實作類別(建構函式)的依賴,如當建立 IValDial 時,必須使用 BBIValDial 或 LSIValDial 的建構函式

當抽象類別階層較複雜時,並且有從一個實現系統變為另一個實現系統(如從 BBWindow 變為 LSWindow)的預期時,需要一種一次性裝入實現系統中各種建立對象的方法,這時抽象工廠就會發揮作用:

  1. 建立具體工廠類對象
  2. 將具體工廠類對象裝入抽象工廠類對象(引用、指標)
  3. 用抽象工廠類對象建立抽象構造塊類
  4. 使用抽象構造塊類的方法,它動態綁定到具體構造塊類的方法

1、2 是程式初始化階段執行的設定,3、4 是程式例行階段的行為

於是,抽象工廠是和抽象類別層次伴生的

Clone 模式^

UML: Clone Pattern

Why clone?

手上有一個對象,只知道它的抽象類別型(確切類型已丟失),要複製這種對象的大量副本,並且副本要和其確切類型一致

可以定義一個 Clonable 抽象基類,以規約 clone 函數,但不是必須的

Clone 模式可從函數 override 的傳回值類型的 covariance 協變中受益。VC 2005+ 支援協變

參考書籍^
  • [CPP LANG] "The C++ Programming Language, Special Ed", Bjarne Stroustrup
  • [EFFECT CPP] "Effective C++, 3Ed", Scott Meyers
  • [CPP PRIMER] "C++ Primer, 3Ed", Stanley Lippman, Josee Lajoie
  • [CPP OBJMODEL] "Inside The C++ Object Model", Stanley Lippman
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.