Qt源碼中的設(shè)計(jì)模式:視頻播放器與外觀模式

外觀模式

外觀模式(Facade Pattern)是一種結(jié)構(gòu)型設(shè)計(jì)模式,它提供了一個(gè)統(tǒng)一的接口颅停,用來(lái)訪問(wèn)子系統(tǒng)中的一群接口尺迂。外觀模式定義了一個(gè)高層接口,使得客戶(hù)端可以更加方便地使用子系統(tǒng)中的功能硅则,同時(shí)也降低了客戶(hù)端和子系統(tǒng)之間的耦合度淹父。

外觀模式UML類(lèi)圖

外觀模式主要解決的問(wèn)題是:當(dāng)一個(gè)系統(tǒng)中的多個(gè)組件之間存在復(fù)雜的依賴(lài)關(guān)系時(shí),客戶(hù)端需要了解和調(diào)用多個(gè)組件才能完成一個(gè)操作怎虫,這樣會(huì)導(dǎo)致客戶(hù)端代碼和子系統(tǒng)之間的耦合度較高暑认,維護(hù)和擴(kuò)展也較為困難。而外觀模式通過(guò)提供一個(gè)統(tǒng)一的接口大审,將多個(gè)組件封裝在一起蘸际,從而降低了客戶(hù)端代碼和子系統(tǒng)之間的耦合度,使得客戶(hù)端更加容易使用子系統(tǒng)中的功能徒扶。同時(shí)粮彤,外觀模式也使得子系統(tǒng)的接口更加易于維護(hù)和擴(kuò)展,因?yàn)橥庥^類(lèi)可以為客戶(hù)端提供功能的同時(shí),隱藏子系統(tǒng)中的復(fù)雜性导坟。

總的來(lái)說(shuō)屿良,外觀模式可以讓客戶(hù)端更加方便地訪問(wèn)子系統(tǒng)中的功能,同時(shí)也可以降低客戶(hù)端和子系統(tǒng)之間的耦合度惫周,提高系統(tǒng)的靈活性和可維護(hù)性尘惧。下面給出一個(gè)程序示例:


#include <iostream>
// 子系統(tǒng)類(lèi),實(shí)現(xiàn)具體的功能
class Subsystem1
{
public:
    void operation1() { std::cout << "Subsystem1 operation1." << std::endl; }
};

class Subsystem2
{
public:
    void operation2() { std::cout << "Subsystem2 operation2." << std::endl; }
};

class Subsystem3
{
public:
    void operation3() { std::cout << "Subsystem3 operation3." << std::endl; }
};

// 外觀類(lèi)闯两,提供簡(jiǎn)單的接口給客戶(hù)端使用
class Facade
{
public:
    void operation()
    {
        std::cout << "Facade operation." << std::endl;
        m_subsystem1.operation1();
        m_subsystem2.operation2();
        m_subsystem3.operation3();
    }

private:
    Subsystem1 m_subsystem1;
    Subsystem2 m_subsystem2;
    Subsystem3 m_subsystem3;
};

int main()
{
    Facade facade;
    facade.operation();

    return 0;
}

在上述代碼中褥伴,我們定義了一個(gè)外觀類(lèi) Facade,它提供了簡(jiǎn)單的接口 operation 給客戶(hù)端使用漾狼。這個(gè)接口實(shí)際上是由三個(gè)子系統(tǒng)的具體操作組合而成的重慢。這三個(gè)子系統(tǒng)分別是 Subsystem1Subsystem2Subsystem3逊躁,它們實(shí)現(xiàn)了具體的功能似踱。在外觀類(lèi)中,我們將這三個(gè)子系統(tǒng)的實(shí)例作為私有成員變量稽煤,并在 operation 方法中依次調(diào)用它們的具體操作核芽。

客戶(hù)端使用外觀模式時(shí),只需要通過(guò)外觀類(lèi)的接口來(lái)調(diào)用功能酵熙,而不需要直接與子系統(tǒng)類(lèi)交互轧简。這樣可以減少客戶(hù)端代碼和子系統(tǒng)類(lèi)之間的耦合度,使系統(tǒng)更加靈活匾二、易于維護(hù)哮独。

當(dāng)運(yùn)行上述代碼時(shí),我們可以看到輸出結(jié)果如下:

Facade operation.
Subsystem1 operation1.
Subsystem2 operation2.
Subsystem3 operation3.

可以看到察藐,在客戶(hù)端調(diào)用外觀類(lèi)的接口 operation 時(shí)皮璧,系統(tǒng)依次調(diào)用了三個(gè)子系統(tǒng)的具體操作。這就是外觀模式的一種實(shí)現(xiàn)方式分飞。

Qt中的媒體播放框架

QMediaPlayer其實(shí)就是外觀模式中的外觀(Facade)角色悴务,它為復(fù)雜的音視頻播放系統(tǒng)提供了一個(gè)簡(jiǎn)單的接口。在QMediaPlayer的內(nèi)部譬猫,它使用了許多其他的類(lèi)和組件來(lái)實(shí)現(xiàn)音視頻播放的功能讯檐,這些類(lèi)和組件可以看作是外觀模式中的子系統(tǒng)。

具體來(lái)說(shuō)染服,以下是一些在QMediaPlayer內(nèi)部可能會(huì)使用到的子系統(tǒng):

  1. QMediaContent:這個(gè)類(lèi)用來(lái)表示一個(gè)可以播放的媒體內(nèi)容裂垦,例如一個(gè)視頻文件或者一個(gè)網(wǎng)絡(luò)流。它可以被QMediaPlayer用來(lái)加載音視頻內(nèi)容肌索。

  2. QMediaService:這個(gè)類(lèi)提供了媒體播放的服務(wù)蕉拢,包括音頻解碼特碳、視頻解碼等。QMediaPlayer可能會(huì)使用這個(gè)類(lèi)來(lái)獲取必要的媒體播放服務(wù)晕换。

  3. QMediaPlaylist:這個(gè)類(lèi)表示一個(gè)媒體播放列表午乓。QMediaPlayer可以使用這個(gè)類(lèi)來(lái)管理和播放多個(gè)音視頻文件。

  4. QAbstractVideoSurface:如果QMediaPlayer用于播放視頻闸准,那么可能會(huì)使用這個(gè)類(lèi)來(lái)顯示視頻畫(huà)面益愈。

這些都是在Qt媒體播放框架中可能會(huì)使用到的類(lèi)。需要注意的是夷家,QMediaPlayer的實(shí)現(xiàn)可能會(huì)根據(jù)不同的平臺(tái)和版本有所不同蒸其,所以實(shí)際使用的類(lèi)和組件可能會(huì)有所不同。但是不論實(shí)現(xiàn)如何库快,QMediaPlayer都會(huì)為用戶(hù)提供一個(gè)統(tǒng)一簡(jiǎn)單的接口摸袁,這就是外觀模式的主要目的。

下面給出QMediaPlayer源碼如何體現(xiàn)外觀模式的一種程序示例义屏。請(qǐng)注意靠汁,這里的程序示例與實(shí)際代碼有一定的差異,隱藏了許多細(xì)節(jié)闽铐。但示意Qt源碼如何使用外觀模式蝶怔,應(yīng)該是勉強(qiáng)足夠的。另外兄墅,這里出現(xiàn)了QMediaPlayerPrivate類(lèi)踢星,并使用了Q_DECLARE_PRIVATE宏將其聲明為QMediaPlayer的私有實(shí)現(xiàn)類(lèi)。實(shí)際上隙咸,這種設(shè)計(jì)方式稱(chēng)為Pimpl沐悦,即將類(lèi)的實(shí)現(xiàn)再封裝到一個(gè)叫私有實(shí)現(xiàn)類(lèi)的地方,一定程度上減少了編譯依賴(lài)扎瓶。這里不展開(kāi)講所踊,讀者若有興趣可以自行查閱相關(guān)資料泌枪,與本文的主題關(guān)系不大概荷。但要注意的是,Pimpl的實(shí)現(xiàn)方式在Qt源碼中是慣用手法碌燕,讀者最好能夠領(lǐng)會(huì)其含義以及習(xí)慣其代碼風(fēng)格误证,否則調(diào)試起來(lái)會(huì)有一些棘手。

class QMediaPlayerPrivate
{
public:
    QMediaPlayerPrivate();
    ~QMediaPlayerPrivate();

    void setMedia(const QMediaContent &media);
    void setPlaylist(QMediaPlaylist *playlist);
    void setVideoOutput(QAbstractVideoSurface *surface);
    
    // Other methods...

    QMediaContent currentMedia;
    QMediaPlaylist *playlist;
    QMediaService *service;
    QAbstractVideoSurface *videoOutput;
};

class QMediaPlayer : public QMediaObject
{
    Q_OBJECT
    // ...

public:
    explicit QMediaPlayer(QObject *parent = nullptr);
    ~QMediaPlayer();

    void setMedia(const QMediaContent &media);
    void setPlaylist(QMediaPlaylist *playlist);
    void setVideoOutput(QAbstractVideoSurface *surface);

    // ...

public Q_SLOTS:
    void play();
    void stop();
    void pause();

    // ...

private:
    Q_DISABLE_COPY(QMediaPlayer)
    Q_DECLARE_PRIVATE(QMediaPlayer)
};

在這個(gè)例子中修壕,QMediaPlayerPrivateQMediaPlayer的私有實(shí)現(xiàn)類(lèi)愈捅,它包含了實(shí)際的媒體內(nèi)容、播放列表慈鸠、媒體服務(wù)和視頻輸出蓝谨。QMediaPlayerPrivate有幾個(gè)私有成員變量來(lái)持有當(dāng)前的媒體內(nèi)容、播放列表、媒體服務(wù)和視頻輸出譬巫。這些成員變量代表了媒體播放系統(tǒng)的不同部分或子系統(tǒng)咖楣。

QMediaPlayer類(lèi)在最后聲明了Q_DECLARE_PRIVATE(QMediaPlayer)宏,實(shí)際相當(dāng)于聲明QMediaPlayer有一個(gè)QMediaPlayerPrivate的類(lèi)成員變量芦昔。QMediaPlayer類(lèi)提供了一些公共方法來(lái)設(shè)置這些成員變量诱贿。例如,setMedia()方法用于設(shè)置要播放的媒體內(nèi)容咕缎;setPlaylist()方法用于設(shè)置播放列表珠十;play()stop()pause()等方法來(lái)控制媒體的播放凭豪。這些方法將媒體播放的復(fù)雜性隱藏在QMediaPlayer類(lèi)的內(nèi)部焙蹭,只提供一個(gè)簡(jiǎn)單的接口供用戶(hù)使用。需要注意墅诡,通過(guò)Pimpl的設(shè)計(jì)方式壳嚎,實(shí)際的程序邏輯是在QMediaPlayerPrivate中進(jìn)行的。

總結(jié)

與組合模式一樣末早,外觀模式實(shí)際上更顯得“不值一提”烟馅。當(dāng)我們學(xué)習(xí)任何一門(mén)OO語(yǔ)言時(shí),都會(huì)使用外觀模式然磷。但實(shí)際上郑趁,在日常編程中很容易就會(huì)將一個(gè)類(lèi)在邏輯上的所有組成部分都塞到一個(gè)類(lèi)的實(shí)現(xiàn)里面,造成類(lèi)的實(shí)現(xiàn)過(guò)大姿搜,接口過(guò)于復(fù)雜寡润。

外觀模式是單一職責(zé)原則和最少知道原則的完美體現(xiàn)。我們應(yīng)有意識(shí)地去通過(guò)創(chuàng)建子系統(tǒng)類(lèi)舅柜,通過(guò)組合/聚合的方式組織在一起梭纹,并提供簡(jiǎn)單的接口對(duì)外界隱藏復(fù)雜性。而這個(gè)過(guò)程致份,就是實(shí)現(xiàn)外觀模式变抽。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市氮块,隨后出現(xiàn)的幾起案子绍载,更是在濱河造成了極大的恐慌,老刑警劉巖滔蝉,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件击儡,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡蝠引,警方通過(guò)查閱死者的電腦和手機(jī)阳谍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)蛀柴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人矫夯,你說(shuō)我怎么就攤上這事名扛。” “怎么了茧痒?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵肮韧,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我旺订,道長(zhǎng)弄企,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任区拳,我火速辦了婚禮拘领,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘樱调。我一直安慰自己约素,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布笆凌。 她就那樣靜靜地躺著圣猎,像睡著了一般。 火紅的嫁衣襯著肌膚如雪乞而。 梳的紋絲不亂的頭發(fā)上送悔,一...
    開(kāi)封第一講書(shū)人閱讀 51,718評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音爪模,去河邊找鬼欠啤。 笑死,一個(gè)胖子當(dāng)著我的面吹牛屋灌,可吹牛的內(nèi)容都是我干的洁段。 我是一名探鬼主播,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼共郭,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼祠丝!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起落塑,我...
    開(kāi)封第一講書(shū)人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤纽疟,失蹤者是張志新(化名)和其女友劉穎罐韩,沒(méi)想到半個(gè)月后憾赁,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡散吵,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年龙考,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蟆肆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡晦款,死狀恐怖炎功,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情缓溅,我是刑警寧澤蛇损,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站坛怪,受9級(jí)特大地震影響淤齐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜袜匿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一更啄、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧居灯,春花似錦祭务、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至岩灭,卻和暖如春缨该,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背川背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工贰拿, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人熄云。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓膨更,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親缴允。 傳聞我的和親對(duì)象是個(gè)殘疾皇子荚守,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容