私は新しい機能の既存のクラスを拡張していますが、どの設計ソリューションを使用するかについて疑問があります。いくつかあり、それぞれに長所と短所があります。私の場合は次のとおりです。特別な形式のファイル ヘッダーがあり、それを読み取って保存します。FileHeader という名前のクラスがあり、ストリームとの間のシリアル化とその他の機能を既に実装しています。私のタスク リストの項目の 1 つは、特定のタイム スタンプ機能を追加することです。タイムスタンプは、1994 年 1 月 1 日 00:00:00 からの秒数として読み取り/保存する必要があります。ただし、FileHeader クラスは日付と時刻を 2 つの個別の変数に格納します。したがって、秒から日付と時刻への変換を記述する必要があります。問題は、この機能をどこに配置するかです。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);
これはかなり簡単です。しかし、私が気に入らないのは、すでに非常に重いクラスに責任が追加されることです。あなたはルールを知っています: 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 の save メソッドと restore メソッド。まあ、私はそれが好きです。私はヘッダーを台無しにしませんでした。クラスの 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 を残さない可能性が非常に高くなります...現在、 TimeConverter は他のクラス (FileHeader のみ) では使用されませんが、これを変更すると、独自のヘッダーとソース ファイルに簡単に移動できます。
コードを書いているとき、これが新しい実装の詳細の既存のクラスの機能を拡張する私の通常の方法であることに気付きました。私はかなりの頻度でそれを行っているので、他の人が何を使用しているのか、またその理由について興味があります。私の経験によると、95% の開発者がオプション A を使用してクラスを拡張しています。だからここに質問があります:
他に良い便利なオプションはありますか?
これらのオプションを使用することの重要な側面や意味を見逃していませんか?
更新:以下の回答の1つからのアドバイスに従って、オプション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 よりも優れている点と、その逆の点はどうなのか。長所と短所は何ですか?