Есть ли имя для этой идиомы C ++, в которой тип продает оболочку, расширяющую его интерфейс? - PullRequest
0 голосов
/ 29 января 2019

У меня есть то, что по сути является семейством типов, которые имеют несколько общих свойств друг с другом.Я мог бы довольно прилично смоделировать эти отношения с наследованием классов 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 перечисляет различные возможные типы Phones, которые имеют PhoneNumber и могут иметь или не иметь AppList.

Проблема заключается в том, как обеспечить доступ к внешнему миру к номеру AppList телефона, если вызывающий абонент уверен, чтоон имеет дело с cell телефоном.Обратите внимание, что я не хочу просто продавать необязательный тип, так как это приводит к большому количеству кода проверки ошибок в вызывающей функции (функциях), а это не то, что мне нужно (в большинстве случаев вызывающая сторона знает * 1021).* из 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
};

В 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 телефоном, например,

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 и т. Д., Но я знаю, что «объектив» уже имеет другое значение в программировании.Возможно, это не является большой проблемой, но я хотел спросить, чтобы убедиться, что я не пропустил более устоявшуюся схему именования.

Есть ли установленное имя для этого шаблона / идиомы, в котором тип торгуетоболочка, которая расширяет его интерфейс?

1 Ответ

0 голосов
/ 29 января 2019

Твои намерения, к сожалению, мне не до конца понятны.

Сначала я подумал, что вы просто заново изобрели шаблон декоратора , в котором вы динамически добавляете некоторые обязанности (средства доступа) к существующему объекту.

Но если присмотреться, я думаю, что все это выглядит как анти-шаблон, беспорядок зависимостей и некорректный дизайн : eveytime, что в базовом классе вы должны быть друзьямипроизводного класса, у вас должны быть звонки тревоги.

Вместо того, чтобы искать имя, вам лучше разработать более чистый дизайн .Забудьте перечисление и перейдите к абстрактному базовому классу Phone с абстрактными функциями, соответствующими тому, что должен делать каждый телефон.Затем создайте два производных конкретных класса: LandLine и CellPhone, оба наследуются от Phone.

Теперь вы можете считать, что получение списка приложений - это общая функциональность всех видов телефонов, и что LandLine просто возвращает пустой список.В этом случае весь ваш код будет просто использовать встроенный полиморфизм, чтобы сделать работу должным образом расширяемым способом:

  • Если завтра кто-то изобрел TelepathyPhone, вам просто нужно было бы реализовать общеефункции, требуемые абстрактным интерфейсом, и весь используемый код будет работать без изменений.
  • В худшем случае, если вам действительно нужно вызвать очень специфичную зависящую от класса функцию, которая была полностью неизвестна в общем интерфейсе (например, TelepathyPhone::displayBrainWavelength()), вы можете пойти на if, используя dynamic_cast.По крайней мере, вы бы избегали создавать новый enum каждый раз, когда изобретаете новый производный класс.
...