簡體   English   中英

C ++類設計:未命名的命名空間或私有類方法中的類或函數?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM