懸念事項を分離する必要があると思います。
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)