2013-04-24 51 views
0

我有以下類:裝飾用於特定的子類

class Stream {}; 

class FileStream : public Stream {}; 

class NetworkStream : public Stream {}; 

每個類都有一個write()方法(虛擬)。

我可以在兩種類型的介質上使用FileStream:HDD和SSD。如果我正在寫入硬盤,我不會對FileStream已提供的內容做任何特別的處理。但是,如果我寫這麼一個SSD,在調用write()之前需要執行一些邏輯。在FileStream之內,我不知道我在寫什麼媒體。只有呼叫站點知道。我想在這裏使用裝飾器,但裝飾器旨在與所有流一起使用。我只想在某些情況下擴展NetworkStream的功能。這裏有一些裝飾形式適合嗎?如果不是,我應該使用什麼設計模式?如果我們假設FileStream::write()只是將整個內部緩衝區刷新到文件並保存到磁盤上,那麼裝飾器需要在寫入流之前將一些元數據寫入到流中。

我想創建不使用繼承,像這樣一個簡單的裝飾類:

class FileStreamDecorator 
{ 
public: 
    FileStreamDecorator(FileStream& stream) : m_stream(stream) {} 

    void write() { 
    m_stream << "Some Metadata"; 
    m_stream.write(); 
    } 

private: 
    FileStream& m_stream; 
}; 

而且它會像這樣使用:

FileStream stream; 
stream << "Complete file data"; 

// At this point we know we are writing to SSD, so we must use the decorator 
FileStreamDecorator decorator(stream); 
decorator.write(); 

這會是一個合適的解決方案?任何人都可以想出更好的方法嗎?

+0

只要你正在創建類,爲什麼不是'SSDFileStream'和'HDDFileStream'? – Xymostech 2013-04-24 15:07:53

+0

你也可以從FileStream派生新的類,覆蓋寫入方法並從內部調用基本方法=>不需要成員變量然後...但要注意虛擬性 – Incubbus 2013-04-24 15:08:03

+1

需要什麼樣的邏輯SDD,而不是在HDD上? – didierc 2013-04-24 15:29:45

回答

1

這是一個合適的解決方案嗎?任何人都可以想出更好的方法嗎?

不是。您正在創建裝飾類並依賴客戶代碼在需要時不要忘記使用它。

如果客戶端代碼忘記執行額外的步驟,代碼看起來沒問題(客戶端代碼中沒有任何內容會建議裝飾器應該在那裏初始化)。

在一個月內(或者五年),您將會忘記這些事情,或者轉移到其他項目上,並且維護此項目的任何人都必須意識到需要在客戶端初始化一個新對象。

你最好用SSDFileStream專業化,覆蓋基類的write()(默認行爲),在內部調用基類版本,然後執行任何額外的步驟。

我能想到的最好的實現:

class FileStream { 
    virtual void write(); 
}; 

class SSDFileStream: public FileStream { 
    virtual void write() { 
     FileStream::write(); 
     write_ssd(); 
    } 
protected: 
    void write_ssd(); 
}; 

的方法,另外,你可以做的FileStream抽象,並添加HDDFileStream專業化。如果HDDFileStream檢測到它正在SSD上寫入,則可能會引發異常。如果您要求它在HDD路徑上寫入,則SSDFileStream可能也會這樣做。

這將使客戶端代碼易於正確寫入,並且不可能'*寫入不正確。


「*寫作車/不穩定/脆/醜陋的代碼是永遠不可能的,但你仍然可以使其難以實現。

+0

我編輯了答案,所以「*」導致最後一行。我的意思是說,無論庫代碼如何,客戶端代碼都可能很難看 - 但無論如何,您的庫代碼仍然應該努力正確使用並且難以正確使用。謝謝。 – utnapistim 2013-04-24 15:35:35