这种C ++习惯用法是否有名称,其中某个类型出售包装器以扩展其接口?

时间:2019-01-28 21:48:58

标签: c++ inheritance design-patterns naming-conventions idioms

我本质上是一组类型,它们彼此共享一些共同的属性。实际上,我可以使用C ++类继承对这种关系进行建模。但是,我还需要在代码中传递并存储这些对象,并且将每个实例都保留为多态堆引用是很痛苦的。

这是初始情况:

具有所有“子类”值的枚举类型:

enum class PhoneType {
    landline,
    cell
}

存储并传递给 lot 的类型:

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

问题是一旦呼叫者确定它正在处理AppList电话,如何使外界访问电话的cell。请注意,我不想简单地出售可选类型,因为这会将大量错误检查代码压入调用函数,这不是我想要的(在大多数情况下,调用者知道{{ PhoneType中的1}}甚至不需要检查就可以通过,因此售卖Phone只是不必要的痛苦。

我可以将额外的访问器添加到optional<>类中,并记录它们抛出/崩溃/等的情况。如果接收到的Phone不代表Phone电话。但是,在实际代码中,还有更多此类属性需要更多访问器,并且在调用站点读取这些访问器时,每个访问器的先决条件都不清楚。

长话短说,经过一番考虑,我最终想到了这个成语:

cell定义之前:

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 }; 的{​​{1}}部分中:

Phone

public的{​​{1}}部分中:

// (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()

(愚蠢的示例,但您明白了。我知道我可以在这里使用访问模式,但实际代码并不完全是这样。)

我喜欢我正在做的设计。问题是,我不太知道如何命名自动包装器类型。我目前正在使用phonecell等模式,但是我知道“ lens”在编程中已经具有其他含义。也许这不是一个大问题,但是我想问一下,确保我没有错过一个更完善的命名方案。

这个模式/习惯用法是否有一个确定的名称,其中某个类型出售包装器以扩展其接口?

1 个答案:

答案 0 :(得分:1)

不幸的是,您的意图对我而言并不十分清楚。

起初,我认为您只是重新发明了 decorator pattern ,在其中您可以向现有对象动态添加一些职责(访问者)。

但仔细观察,我认为这一切都像是一个反模式,一个依赖混乱和一个有缺陷的设计:在基类中,您需要成为派生类的朋友上课时,您应该有响起的警钟。

您最好设计更简洁的设计,而不要寻找名称。忘记枚举,然后去一个抽象的LandLine基类,它的抽象功能对应于每部电话应具有的功能。然后创建两个派生的具体类:CellPhonePhone,它们都从LandLine继承。

现在,您可以考虑获取应用程序列表是所有电话的常用功能,而TelepathyPhone仅返回一个空列表。然后,您所有的代码都将仅使用build in polymorphism以适当的可扩展方式完成此工作:

  • 如果明天某人会发明一个TelepathyPhone::displayBrainWavelength(),则只需实现抽象接口所需的通用功能,所有使用代码仍将保持不变。
  • 在最坏的情况下,如果您真的需要调用通用接口中完全未知的非常特定的类相关函数(例如if),则可以使用dynamic_cast <div>。至少您每次创建新的派生类时都避免创建新的枚举。