这个 C++ 习语是否有一个名称,其中类型出售扩展其接口的包装器?
Is there a name for this C++ idiom in which a type vends a wrapper that expands its interface?
我有一个本质上是相互共享一些共同属性的类型族。我实际上可以用 C++ class 继承相当体面地模拟这种关系。但是,我还需要在我的代码周围传递和存储这些对象,并且将每个实例都保留为多态堆引用是一件很痛苦的事情。
这是初始情况:
具有所有值的枚举类型 "subclasses":
enum class PhoneType {
landline,
cell
}
存储和传递的类型 很多:
class Phone {
public:
static Phone landline(PhoneNumber number) {
return Phone(PhoneType::landline, number);
}
static Phone cell(PhoneNumber number, optional<AppList> apps) {
return Phone(PhoneType::cell, number, apps)
}
PhoneType type() { return _type; }
PhoneNumber number() { return _number; }
private:
PhoneType _type;
PhoneNumber _number;
optional<AppList> _apps;
Phone(PhoneType type, PhoneNumber number) :
_type(type), _number(number)
{}
Phone(PhoneType type, PhoneNumber number, optional<AppList> apps) :
_type(type), _number(number), _apps(apps)
{}
};
PhoneType
列举了不同可能的 Phone
类型,它们都有一个 PhoneNumber
并且可能有也可能没有 AppList
.
问题是一旦调用者确定它正在处理 cell
[=76=,如何让外界访问 phone 的 AppList
].请注意,我不想简单地提供可选类型,因为这会将大量错误检查代码推送到调用函数中,这不是我想要的(在大多数情况下,调用者知道 [= Phone
中的 16=] 甚至无需检查就通过了,因此出售 optional<>
只是不必要的痛苦)。
我可以将额外的访问器添加到 Phone
class,并记录它们 throw/crash/etc。如果接收 Phone
不代表 cell
phone。然而,在实际代码中还有更多这样的属性需要更多的访问器,而这些访问器中的每一个在调用站点读取时都不清楚其前提条件。
长话短说,经过一番考虑,我最终得出了这个成语:
定义前Phone
:
class CheckedPhoneRef {
public:
CheckedPhoneRef() = delete;
Phone& phone() const { return * _phone; }
protected:
Phone* _phone;
CheckedPhoneRef(Phone* phone) : _phone(phone) {}
private:
friend class Phone;
};
class LandlineCheckedPhoneRef : public CheckedPhoneRef {
public:
using CheckedPhoneRef::CheckedPhoneRef;
};
class CellCheckedPhoneRef : public CheckedPhoneRef {
public:
using CheckedPhoneRef::CheckedPhoneRef;
AppList apps() const; // accesses private member of referenced Phone
};
在 Phone
的 public
部分:
// (Comment above declarations in header):
// These assert that this Phone is of the correct PhoneType.
LandlineCheckedPhoneRef landline_ref() {
assert(_type == PhoneType::landline);
return LandlineCheckedPhoneRef(this);
}
CellCheckedPhoneRef cell_ref() {
assert(_type == PhoneType::cell);
return CellCheckedPhoneRef(this);
}
// (Plus const versions)
在 Phone
的 private
部分:
friend LandlineCheckedPhoneRef;
friend CellCheckedPhoneRef;
现在很清楚在任何给定的调用站点做出的假设是什么:如果我说 phone.cell_ref()
那么我清楚地断言这个 phone
是 cell
phone,例如,
void call(Phone& phone) {
if (phone.type() == PhoneType::cell) {
if (has_facetime(phone.cell_ref())) ...
} else {
...
}
}
bool has_facetime(CellCheckedPhoneRef cell_phone) {
return cell_phone.apps() ...
}
(愚蠢的例子,但你明白了。我知道我可以在这里使用访问模式,但真正的代码并不完全像这样。)
我喜欢我正在做的这个设计。问题是,我不太清楚如何命名出售的包装器类型。我目前正在使用 LandlinePhoneLens
、CellPhoneLens
等模式,但我知道 "lens" 在编程中已经具有其他含义。也许这不是什么大问题,但我想问一下以确保我没有错过更成熟的命名方案。
是否有此 pattern/idiom 的既定名称,其中类型出售扩展其接口的包装器?
很遗憾,我并不完全清楚您的意图。
起初,我以为您只是重新发明了 decorator pattern,您可以在其中向现有对象动态添加一些职责(访问器)。
但仔细观察,我认为这一切看起来都像是一个 反模式、一个依赖混乱和一个有缺陷的设计:每一次在基础 class 你需要成为 derived class 的朋友,你应该开始敲响警钟了。
与其寻找名称,不如设计一个更简洁的设计。忘记枚举,去寻找一个抽象的 Phone
基础 class,抽象函数对应于每个 phone 应该能够做的事情。然后创建两个派生的具体 classes:LandLine
和 CellPhone
,它们都继承自 Phone
。
现在您可以考虑获取应用程序列表是所有类型 phone 的通用功能,而 LandLine
只是 returns 一个空列表。然后,您所有的代码都将只使用内置多态性以适当的可扩展方式完成工作:
- 如果明天有人发明一个
TelepathyPhone
,你只需要实现抽象接口需要的常用功能,所有的使用代码仍然可以正常工作。
- 在最坏的情况下,如果您真的需要调用一个非常具体的 class 依赖函数,而该函数在公共接口(例如
TelepathyPhone::displayBrainWavelength()
)中是完全未知的,您可以使用if
使用 dynamic_cast
。至少你会避免每次发明一个新的派生 class 时都创建一个新的枚举。
我有一个本质上是相互共享一些共同属性的类型族。我实际上可以用 C++ class 继承相当体面地模拟这种关系。但是,我还需要在我的代码周围传递和存储这些对象,并且将每个实例都保留为多态堆引用是一件很痛苦的事情。
这是初始情况:
具有所有值的枚举类型 "subclasses":
enum class PhoneType {
landline,
cell
}
存储和传递的类型 很多:
class Phone {
public:
static Phone landline(PhoneNumber number) {
return Phone(PhoneType::landline, number);
}
static Phone cell(PhoneNumber number, optional<AppList> apps) {
return Phone(PhoneType::cell, number, apps)
}
PhoneType type() { return _type; }
PhoneNumber number() { return _number; }
private:
PhoneType _type;
PhoneNumber _number;
optional<AppList> _apps;
Phone(PhoneType type, PhoneNumber number) :
_type(type), _number(number)
{}
Phone(PhoneType type, PhoneNumber number, optional<AppList> apps) :
_type(type), _number(number), _apps(apps)
{}
};
PhoneType
列举了不同可能的 Phone
类型,它们都有一个 PhoneNumber
并且可能有也可能没有 AppList
.
问题是一旦调用者确定它正在处理 cell
[=76=,如何让外界访问 phone 的 AppList
].请注意,我不想简单地提供可选类型,因为这会将大量错误检查代码推送到调用函数中,这不是我想要的(在大多数情况下,调用者知道 [= Phone
中的 16=] 甚至无需检查就通过了,因此出售 optional<>
只是不必要的痛苦)。
我可以将额外的访问器添加到 Phone
class,并记录它们 throw/crash/etc。如果接收 Phone
不代表 cell
phone。然而,在实际代码中还有更多这样的属性需要更多的访问器,而这些访问器中的每一个在调用站点读取时都不清楚其前提条件。
长话短说,经过一番考虑,我最终得出了这个成语:
定义前Phone
:
class CheckedPhoneRef {
public:
CheckedPhoneRef() = delete;
Phone& phone() const { return * _phone; }
protected:
Phone* _phone;
CheckedPhoneRef(Phone* phone) : _phone(phone) {}
private:
friend class Phone;
};
class LandlineCheckedPhoneRef : public CheckedPhoneRef {
public:
using CheckedPhoneRef::CheckedPhoneRef;
};
class CellCheckedPhoneRef : public CheckedPhoneRef {
public:
using CheckedPhoneRef::CheckedPhoneRef;
AppList apps() const; // accesses private member of referenced Phone
};
在 Phone
的 public
部分:
// (Comment above declarations in header):
// These assert that this Phone is of the correct PhoneType.
LandlineCheckedPhoneRef landline_ref() {
assert(_type == PhoneType::landline);
return LandlineCheckedPhoneRef(this);
}
CellCheckedPhoneRef cell_ref() {
assert(_type == PhoneType::cell);
return CellCheckedPhoneRef(this);
}
// (Plus const versions)
在 Phone
的 private
部分:
friend LandlineCheckedPhoneRef;
friend CellCheckedPhoneRef;
现在很清楚在任何给定的调用站点做出的假设是什么:如果我说 phone.cell_ref()
那么我清楚地断言这个 phone
是 cell
phone,例如,
void call(Phone& phone) {
if (phone.type() == PhoneType::cell) {
if (has_facetime(phone.cell_ref())) ...
} else {
...
}
}
bool has_facetime(CellCheckedPhoneRef cell_phone) {
return cell_phone.apps() ...
}
(愚蠢的例子,但你明白了。我知道我可以在这里使用访问模式,但真正的代码并不完全像这样。)
我喜欢我正在做的这个设计。问题是,我不太清楚如何命名出售的包装器类型。我目前正在使用 LandlinePhoneLens
、CellPhoneLens
等模式,但我知道 "lens" 在编程中已经具有其他含义。也许这不是什么大问题,但我想问一下以确保我没有错过更成熟的命名方案。
是否有此 pattern/idiom 的既定名称,其中类型出售扩展其接口的包装器?
很遗憾,我并不完全清楚您的意图。
起初,我以为您只是重新发明了 decorator pattern,您可以在其中向现有对象动态添加一些职责(访问器)。
但仔细观察,我认为这一切看起来都像是一个 反模式、一个依赖混乱和一个有缺陷的设计:每一次在基础 class 你需要成为 derived class 的朋友,你应该开始敲响警钟了。
与其寻找名称,不如设计一个更简洁的设计。忘记枚举,去寻找一个抽象的 Phone
基础 class,抽象函数对应于每个 phone 应该能够做的事情。然后创建两个派生的具体 classes:LandLine
和 CellPhone
,它们都继承自 Phone
。
现在您可以考虑获取应用程序列表是所有类型 phone 的通用功能,而 LandLine
只是 returns 一个空列表。然后,您所有的代码都将只使用内置多态性以适当的可扩展方式完成工作:
- 如果明天有人发明一个
TelepathyPhone
,你只需要实现抽象接口需要的常用功能,所有的使用代码仍然可以正常工作。 - 在最坏的情况下,如果您真的需要调用一个非常具体的 class 依赖函数,而该函数在公共接口(例如
TelepathyPhone::displayBrainWavelength()
)中是完全未知的,您可以使用if
使用dynamic_cast
。至少你会避免每次发明一个新的派生 class 时都创建一个新的枚举。