2010-01-05 3 views
1

Итак, я использую композицию для объединения коллекции объектов, все из которых производятся из базового класса, скажем, Component. Например:Альтернатива конструкции? Состав и конструкция

class Component { 
public: 
    Component(); 
    ... 
private: 
    int m_address; 
    ... 
}; 

class SpecializedComponent: public Component { 
public: 
    SpecializedComponent() 
    ... //and so on 
}; 

class SpecializedComponent2: public Component { 
public: 
    SpecialIzedComponent2() 
    ... //and so on 
}; 

class ComponentHolder{ 
    SpecializedComponent* m_descriptiveName; 
    SpecializedComponent2* m_descriptiveName2; 
    // and so on... many different types of components 
} 

Таким образом, каждый SpecializedComponentX будет обмениваться данные по сети с отдельным источником данных, каждый со своим уникальным адресом. Эти адреса указаны в файле параметров. На данный момент я разбираю файл параметров, а m_address инициализируется в конструкторе производного класса - это потому, что каждый m_address задается типом объекта, который мы инициализируем.

Каждый SpecializedComponentX имеет некоторые общие функции, которые я хочу выполнить в базовом классе Component. Итак, я создаю поток, связанный с базовым классом Component, правильно? Конечно - имеет смысл. Пока я не пойму, что у меня еще нет адреса назначения для этого компонента, потому что объект не был полностью сконструирован. Я хочу развернуть поток базового класса в ctor, но пока не знаю m_address.

Единственный способ, которым я могу обойти это, - предоставить (простую) виртуальную функцию void start(), которую производный класс может вызвать, чтобы развернуть поток, как только объект будет полностью построен. Является ли это действительным и подходит выбор дизайна или есть шаблон, который я мог бы игнорировать? Благодарю.

ответ

0

Компонент может иметь конструктор с одним аргументом, который инициализирует m_address.

+0

да, конечно. иногда очевидная вещь - правильная вещь. – jdt141

0

Если эта общая функциональность каким-либо образом зависит от состояния SpecializedComponent, SpecializedComponent2 или от того, что это SpecializedComponent или SpecializedComponent2, то вы не можете это сделать в конструкторе Component, если вы не передадите параметры к нему. Иногда бывает необходимым злом передать идентификатор типа конструктору Component для выполнения такой инициализации.

В этом случае, однако, вы можете создать виртуальную функцию, которую может вызвать производный класс. Однако предположим, что вы поместили этот вызов виртуальной функции в конструктор SpecializedComponent. Если позже вы получите следующий класс (SuperSpecializedComponent) и ovrerride этой виртуальной функции, вызов, который вы делаете из конструктора SpecialzedComponent, даже не ударит. Мораль: не вызывайте виртуальные функции из конструктора.

Я думаю, что самый чистый способ - иметь 2-фазную конструкцию. Конструктор, который выполняет базовую проводку объекта и метод Init(), который должен быть вызван до его использования. Код клиента (ComponentHolder?) Может вызвать этот Init после того, как все объекты будут полностью построены.

0

Почему метод start() должен быть виртуальным?

Вы могли бы сделать его невиртуальным и реализовать производный Конструкторы так:

SpecializedComponent::SpecializedComponent() { 
    m_address = getAddressFromParameterFile("SpecializedComponent"); 
    start(); 
} 

Это предпочтительнее вызов Start() после того, как объект полностью построен, поскольку два этапа строительство ошибки -prone. (Если вы потребности двухступенчатой ​​конструкции, рассмотрю завод (или фабричный метод) для уверить существуют только полностью построенные объекты. Сделайте конструктор приватного и завод (метод) друг.)

Другим способом переместить расчет адреса из объекта .Это привело бы к коду, как это:

SpecializedComponent::SpecializedComponent(const std::string& address) 
: Component(address) 
{} 

Component::Component(const std::string& address) 
: m_address(address) 
{ 
    start(); 
} 

Такой подход увеличивает тестируемость производных SpecializedComponents, потому что он снимает зависимость от файла параметров.

Для удобства, вы можете предоставить статический фабричный метод для экземпляра ваших SpecializedComponents:

SpecializedComponent* SpecializedComponent::create() { 
    std::string address = getAddressFromParameterFile("SpecializedComponent"); 
    return new SpecializedComponent(address); 
} 

КСТАТИ: рассмотреть вопрос о проведении shared_ptr лет в ComponentHolder, вместо сырых указателей.

Смежные вопросы