懸念事項を分離する必要があると思います。
first_nameがなくても意味がありlast_nameますか?
first_nameとがlast_nameなくても意味がありidますか?
first_name、last_name、またはが無効であることは理にかなっていますかid?
すべての質問に対する答えは「いいえ」だと思います。そのため、3 つのエンティティは、Identifier(または類似の) という抽象的な概念の中に一緒に属していると思います。これは、提供する必要のあるチェックと保証が重要であるためです。
クラスは次のようになります。
class Identifier {
public:
static void ValidateName(const std::string& name) {
if(name.size() <= 0) {
std::stringstream ss;
ss<<"Invalid name: "<<name<<" (expected non-empty string)";
throw std::invalid_argument(ss.str());
}
}
static void ValidateId(const std::string& id) {
if(id.size() != 10) {
std::stringstream ss;
ss<<"Invalid id: "<<id<<" (expected string of length 10)";
throw std::invalid_argument(ss.str());
}
}
Identifier(const std::string& first, const std::string& last, const std::string& id)
: m_first(first),
m_last(last),
m_id(id) {
Identifier::ValidateName(m_first);
Identifier::ValidateName(m_last);
Identifier::ValidateId(m_id);
}
const std::string& first_name() const {
return m_first;
}
const std::string& last_name() const {
return m_last;
}
const std::string& id() const {
return m_id;
}
private:
std::string m_first;
std::string m_last;
std::string m_id;
};
クラスが設計されている方法を考えると、無効な を作成することは不可能ですIdentifier。すべてのテストに合格するか、Identifierそもそも が作成されないかのいずれかです。
さらに、Identifier一度作成された を無効にする方法はないことに注意してください。Identifierこれにより、 のインスタンスが存在する場合、その存在全体にわたって有効であることが保証されます。
その保証があれば、 ;Personを取るコンストラクターを介してインスタンスを作成できるようになりました。Identifierコンストラクターは決してスローしないため、チェックを行う必要はありません。クラスは次のPersonようになります。
class Person {
public:
Person(const Identifier& identifier) noexcept(true)
: m_identifier(identifier) { }
const std::string& first_name() const {
return m_identifier.first_name();
}
const std::string& last_name() const {
return m_identifier.last_name();
}
const std::string& id() const {
return m_identifier.id();
}
private:
Identifier m_identifier;
};
この方法で懸念事項を分離することにより、次のようになります。
Identifierクラスに触れることなく、追加のチェックまたはフィールドでクラスを拡張できPersonます。クラスは独立して進化できます。
Identifierチェックを繰り返さなくても、他の場所で s を使用できます。たとえば、 を作成Identifierしてデータベースに導入できます。
最終的なコードは次のとおりです。
#include<iostream>
#include<sstream>
#include<stdexcept>
class Identifier {
public:
static void ValidateName(const std::string& name) {
if(name.size() <= 0) {
std::stringstream ss;
ss<<"Invalid name: "<<name<<" (expected non-empty string)";
throw std::invalid_argument(ss.str());
}
}
static void ValidateId(const std::string& id) {
if(id.size() != 10) {
std::stringstream ss;
ss<<"Invalid id: "<<id<<" (expected string of length 10)";
throw std::invalid_argument(ss.str());
}
}
Identifier(const std::string& first, const std::string& last, const std::string& id)
: m_first(first),
m_last(last),
m_id(id) {
Identifier::ValidateName(m_first);
Identifier::ValidateName(m_last);
Identifier::ValidateId(m_id);
}
const std::string& first_name() const {
return m_first;
}
const std::string& last_name() const {
return m_last;
}
const std::string& id() const {
return m_id;
}
private:
std::string m_first;
std::string m_last;
std::string m_id;
};
class Person {
public:
Person(const Identifier& identifier) noexcept(true)
: m_identifier(identifier) { }
const std::string& first_name() const {
return m_identifier.first_name();
}
const std::string& last_name() const {
return m_identifier.last_name();
}
const std::string& id() const {
return m_identifier.id();
}
private:
Identifier m_identifier;
};
int main() {
Identifier id("John", "Doe", "1234567890");
Person p(id); // cannot throw because id has already been
// constructed
std::cout<<p.last_name()<<", "<<p.first_name()<<" Id: "<<p.id()<<std::endl;
try {
Identifier id2("Sue", "Smith", "126789");
Person p2(id2);
std::cout<<p2.first_name()<<std::endl;
} catch(const std::exception &e) {
std::cout<<e.what()<<std::endl;
}
try {
Identifier id3("", "Smith", "126789");
Person p3(id3);
std::cout<<p3.first_name()<<std::endl;
} catch(const std::exception &e) {
std::cout<<e.what()<<std::endl;
}
return 0;
}
次のコマンドを使用してコンパイルできます (OS X 10.7.4 の GCC 4.8.1)
g++ validated_names.cpp -std=c++11 -Wall -Wextra
次の出力が生成されます。
./a.out
Doe, John Id: 1234567890
Invalid id: 126789 (expected string of length 10)
Invalid name: (expected non-empty string)