我认为你需要分开你的担忧。
first_name
没有a有意义last_name
吗?
first_name
有 a和last_name
没有a 有意义id
吗?
first_name
无效的last_name
, 或是否有意义id
?
我相信所有问题的答案都是“不”。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
s 并将它们引入数据库。
最终代码是:
#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)