[英]C++ class design: class or functions in unnamed namespace or private class method?
我正在擴展現有的一類新功能,我懷疑使用哪種設計解決方案。 有幾個,每個都有利弊。 我的情況是這樣的:我有一個特殊格式的文件頭,我將閱讀並保存它。 有一個名為FileHeader的類,它已經實現了從/到流的一些序列化和一些其他功能。 我的任務列表中的一項是添加特定時間戳功能。 自1994年1月1日00:00:00起,時間戳應以秒為單位讀取/存儲。 但是,FileHeader類將日期和時間存儲在兩個單獨的變量中。 因此,我需要編寫從/到秒到日期和時間的轉換。 問題是這個功能應該駐留在哪里。 我使用secondsPerDay(60 * 60 * 24)和dateOrigin(1/1/1994)作為常量。
我看到有以下選項:
A)將轉換實現為FileHeader類的私有方法。 然后,secondsPerDay和dateOrigin將成為該類的靜態私有常量。
//fileheader.h
class FileHeader
{
private:
static const unsigned secondsPerDate = 60 * 60 * 24;
static const Date dateOrigin;
const Date &m_date;
const Time &m_time;
unsigned convertToSeconds() const; // convert m_date and m_time to seconds
void fromSeconds(unsigned secs); // convert and store to m_date and m_time
public:
void saveToStream(Stream &s) const;
void restoreFromStream(const Stream &s);
//... other methods
}
//fileheader.cpp
const Date FileHeader::dateOrigin = Date(1994, 1, 1);
這很簡單直截了當。 但我不喜歡的是它為已經相當沉重的課程增加了更多的責任。 你知道規則:一個類=一個責任。 例如維護很難。 如果有人決定將秒數改為分鍾或其他什么,他會重寫方法,但如果不小心可能會留下靜態常量secondsPerDay,盡管不再需要它。 等等。此外,我不喜歡我必須更新頭文件的事實,盡管它只影響實現細節。
B)僅在.cpp文件中的未命名命名空間中執行實現,並使用普通函數和靜態變量:
namespace
{
const unsigned secondsPerDay = 60 * 60 * 24;
const Date dateOrigin = Date(1994, 1, 1);
unsigned dateTimeToSeconds(const Date &d, const Time &t) ...
Date secondsToDate(unsigned secs) ...
Time secondsToTtime(unsigned secs) ...
}
然后FileHeader的保存和恢復方法會調用這些函數。 好吧,我更喜歡它。 我沒有弄亂標題,類'FileHeader責任沒有增長。 但是如果有人決定將算法更改為使用分鍾而不是秒,他可以更改函數,但如果不小心,他會留下不必要的secondsPerDay靜態變量,即使它不再需要。
C)在FileHeader.cpp中使用未命名的命名空間,並在其中使用專用的類。
namespace
{
class TimeConverter
{
private:
static const unsigned secondsPerDay = 60 * 60 * 24;
static const Date dateOrigin;
public:
static unsigned secondsFromDateTime(const Date &date, const Time &time) //implementation here...
static Date dateFromSeconds(unsigned seconds) //implementation here...
static Time timeFromSeconds(unsigned seconds) //implementation here...
};
const Date TimeConverter::dateOrigin = Date(1994, 1, 1);
}
然后,FileHeader保存和恢復將調用這些靜態方法,例如
m_date = TimeConverter::dateFromSeconds(secs);
m_time = TimeConverter::timeFromSeconds(secs);
我個人選擇了這個解決方案。 它沒有弄亂標題,它在視覺上限制了靜態變量的范圍,因此如果有人將TimeConverter的實現從幾秒鍾改為幾分鍾,很可能他不會留下不必要的靜態變量secondsPerDay ...目前任何其他類(只是FileHeader)都不使用TimeConverter,但如果更改了它,則可以輕松地將其移動到自己的頭文件和源文件中。
在編寫代碼時,我意識到這是我通常的方式,我擴展了現有類的新實現細節的功能。 正如我經常這樣做,我很好奇其他人正在使用什么以及為什么。 根據我的經驗,95%的開發人員使用選項A並擴展課程。 所以這里是問題:
有沒有其他好的和有用的選擇?
我是否會錯過使用這些選項的一些重要方面或含義?
更新:根據以下答案之一的建議,我在此提出選項D:
namespace TimeConverter
{
const unsigned secondsPerDay = 60 * 60 * 24;
const Date dateOrigin = Date(1994, 1, 1);
unsigned secondsFromDateTime(const Date &date, const Time &time)
{
return (date - dateOrigin) * secondsPerDay + time.asSeconds();
}
Date dateFromSeconds(unsigned seconds)
{
return dateOrigin + seconds / secondsPerDay;
}
Time timeFromSeconds(unsigned seconds)
{
return Time(seconds % secondsPerDay);
}
}
以及隨后的問題 - D如何比C好,反之亦然。 優缺點都有什么?
就個人而言,我會選擇選項B.如果我曾經需要重新使用該功能,我會將其改編為C.但我認為為每一件小事做一個課可能會導致過多的膨脹和樣板。 我喜歡在需要時抽象,而不是在...之前... YAGNI的變體。 選項B在同一位置定義和使用功能,這使其更易於閱讀。 另外,正如您所說,它不會使頭文件混亂。
如果我已正確理解,您要應用的修改只是實現細節。 用戶永遠無法修改它。
這個函數在我看來非常通用,可以在其他地方使用,所以我會把它放在一個不同標題的命名空間中。
//date time conversions function header
namespace foo
{
unsigned secondsPerDay();
unsigned secondsFromDateTime(
const Date &date,
const Time &time,
const Date& startOfTime);
Date dateFromSeconds(unsigned seconds, const Date& startOfTime);
Time timeFromSeconds(unsigned seconds, const Date& startOfTime);
}
我將介紹函數secondsPerDay而不是全局變量,只是為了風格和清晰度。 我相信性能上的差異是可以忽略的(只有剖析才能說明)。
真正的區別在於使函數采用額外的參數。 您將單獨測試這些函數,並且可以在FileHeader類以外的其他上下文中重用它們。
最后,在FileHeader.cpp文件中,您將包含標題,您將定義開始日期。
關於選項C的最終評論。沒有必要在C ++中創建只使用靜態方法的類(例如,在java中需要它,不允許使用自由函數)。 命名空間是C ++實現它的方式。
絕對不要選擇A.如果你把它變成私有成員,它仍然是該類接口的一部分,這只會使它變得混亂。
我不會選擇C.我不喜歡所有函數和成員都是靜態的類。 這並不是一種真正的東西。 它只是分組相關的東西。 這就是命名空間的用途。
我會選擇D.我會把它拉出來用它自己的.h和.cpp文件來使編寫單元測試變得方便,然后#include它只在.cpp文件中,因為它是你的類的實現細節,而不是界面的一部分。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.