Здравствуйте, Andreev, Вы писали:
A>У меня возникли сомнения: в своем проекте, который, должен возвращать по текущей позиции указателя на экране элемент, написал следующий код:
A>enum EL_TYPES
A>{
A> EL_NULL, EL_TEXT, EL_LABEL, EL_EDIT, EL_LIST
A>};
Антипаттерн «Код типа». Возможные последствия: трудноподдерживаемые длинные switch (code) { }. Лечение: рефакторинг Replace Type Code with Subclasses [1].
class Element
{
public:
virtual ~Element() = 0;
};
Element::~Element() {}
class NullElement : public Element
{
public:
~NullElement()
{}
};
class TextElement : public Element // …
// …
A> CElement& get_element( int x, int y )
A> {
A> for ( int i = 0; i < count(); i++ )
A> {
A> if ( elements[i]->y == y &&
elements[i]->x <= x &&
elements[i]->x + elements[i]->width > x )
{
return *elements[i];
}
A> }
A> return *((CElement*)NULL);
A>}
Ссылка не имеет права быть нулевой. Верни, например, new NullElement [2].
A>Вопрос сводится к вызову метода get_element и оператора [] в классе CScreen. Красиво ли будет выглядеть код:
A>CScreen& s = get_current_screen();
A>CElement& e = s.get_element( m_caret_pos.x, m_caret_pos.y );
A>if ( &e && e.type() == EL_TEXT )
A>{
A> . . .
A>}
A>или все-таки вместо ссылке здесь красивее было бы использовать указатель:
A> CElement* get_element( int x, int y )
A>соответственно:
A>CElement* e = s.get_element( m_caret_pos.x, m_caret_pos.y );
A>if ( e && e->type() == EL_TEXT )
Красивее — будет такое:
s.get_element(caretPos).doSomething(может, быть, ещё, какие-нибудь, параметры);
а уже doSomething реализован в NullElement как {}, в TextElement — как надо, в LabelElement — как-то ещё. Если таких блоков много, то можно использовать паттерн Visitor [3]:
class ElementVisitor;
class Element
{
public:
virtual void accept(ElementVisitor&) = 0;
};
class NullElement : public Element
{
public:
void accept(ElementVisitor&);
};
class TextElement : public Element
{
public:
void accept(ElementVisitor&);
};
//…
class ElementVisitor
{
public:
virtual void visit(NullElement&) = 0;
virtual void visit(TextElement&) = 0;
//…
};
void NullElement::accept(ElementVisitor& v)
{
v.visit(*this);
}
void TextElement::accept(ElementVisitor& v)
{
v.visit(*this);
}
//…
// где-то далеко
class DoSomethingWithTextElement : public ElementVisitor
{
public:
void visit(NullElement&) {}
void visit(TextElement& te)
{
// do something with te
}
//…
};
s.get_element(pos).accept(DoSomethingWithTextElement());
A>Почему я использовал ссылку – потому, что можно перегрузить оператор EL_TYPES:
A> operator EL_TYPES ( void ) { return type(); };
A>и, тогда:
A>if ( &e && e == EL_TEXT )
A>{
A> . . .
A>}
A>, что мне кажется – очень удобным.
А мне — опасным. Неявные преобразования типов вообще нужно хорошо обдумывать.
Ссылки на литературу:
[1] Martin Fowler, Kent Beck et al. Refactoring: Improving the Design of Existing Code. p. 181.
[2] Jim Hyslop, Herb Sutter.
Null References. // C++ Report, 12(6), June 2000.
[3] Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software. p. 331.