Предположим, у меня есть абстрактный базовый класс под названием Component, который является корнем иерархии компонентов GUI.В таком случае у нас может быть два подкласса, Button и Label, оба из которых также являются абстрактными классами и существуют как корень своих собственных соответствующих иерархий конкретных классов.
Конкретные классы, наследуемые от Button, могут включать RoundButtonи SquareButton.
Конкретные классы, наследуемые от Label, могут включать TextLabel и PictureLabel.
Наконец, предположим, что есть агрегатный класс Container, который содержит коллекцию объектов Component.
Проблема в том, чтоУ меня есть указатели на Компонентные объекты, но мне нужно идентифицировать их как кнопки или метки.Например, если я хочу указать, что все кнопки должны иметь больший шрифт для внутреннего текста, я мог бы перебрать все объекты Component в контейнере и каким-то образом определить, какие из них являются кнопками, и вызвать некоторый метод, специфичный для кнопок.
Один из способов, с помощью которого эти "семьи" Компонента могут идентифицировать себя, - это строки.
class Component {
public:
virtual char const * const getFamilyID() const = 0;
};
// In Button.h
char const * const COMPONENT_BUTTON = "button";
class Button : public Component {
public:
virtual char const * const getFamilyID() const { return COMPONENT_BUTTON; };
};
// Code sample
if (strcmp(component->getFamilyID(),COMPONENT_BUTTON) == 0)
// It's a button!
Это слишком сложно, так как Компонент оставляет задачу определения этих семейств своим детям;это не требует знания того, какие семьи существуют.Клиенту необходимо знать о различных семействах компонентов, но если он пытается нацелить конкретный компонент на какую-то операцию, этого нельзя избежать.
Однако предположим, что у нас действительно высокие требования к производительности, и мыхочу избежать сравнения строк.Также было бы неплохо избежать виртуализации этой функции, чтобы мы могли встроить ее.Кроме того, если каждому подклассу Component потребуется объявить глобальную константу, было бы неплохо как-то изменить класс Component, чтобы либо сделать это требованием, либо сделать его ненужным.
Одним из решений этой проблемы являетсячтобы определить перечислитель в Component.h
enum COMPONENT_FAMILY {
COMPONENT_BUTTON = 0,
COMPONENT_LABEL,
// etc...
};
В этом случае getFamilyID () может просто возвращать перечисление COMPONENT_FAMILY, и мы можем просто сравнивать целые числа.К сожалению, это означает, что любые новые семейства компонентов должны быть «зарегистрированы» в этом перечислении, что легко, но не совсем интуитивно понятно для других программистов.Кроме того, метод все еще должен быть виртуальным, если мы не сделаем нестатический элемент COMPONENT_FAMILY, который, как мы знаем, будет иметь чрезвычайно низкое количество элементов (не идеально).
Что может быть хорошим способом решения этой проблемы?В моем случае производительность является ключевым фактором, и хотя что-то похожее на решение enum кажется достаточно простым, мне остается только задуматься, не пропускаю ли я лучший способ.
--- EDIT ---
Я понимаю, что, вероятно, следует указать, что в реальной системе эквивалент Контейнера может хранить только 1 компонент из каждого семейства.Следовательно, компоненты на самом деле хранятся на карте, например:
std:map<COMPONENT_FAMILY, Component*>
Применительно к моему простому примеру здесь это будет означать, что контейнер может содержать только 1 кнопку, 1 метку и т. Д.
Это позволяет очень легко проверять наличие определенного типа Компонента (время регистрации).Поэтому этот вопрос больше о том, как представлять COMPONENT_FAMILY и как определить тип компонента, когда я добавляю его на карту.
Другими словами, единственная цель компонента - идентифицировать его как определенныйчасть функциональности, добавленная в Контейнер, и вместе все компоненты определяют конкретное поведение для контейнера.
Итак, мне не нужно знать тип Компонента, это уже подразумевается.Я специально спрашиваю Контейнер для конкретного типа Компонента.Мне нужен способ, чтобы Компонент мог сообщить свой тип, чтобы он мог отображаться в первую очередь.