суббота, 22 января 2011 г.

Почему дизайн должен быть функциональным?

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

Чтобы подчеркнуть мысль о том, что дизайн системы должен быть прежде всего функциональным, приведу три примера. Они взяты из разных областей человеческой деятельности, в том числе, и из software design'а.

Пример 1. Панель управления лифтом

В моём доме отремонтировали лифт. Внутри кабины сделали металлическую обшивку. После ремонта панель управления выглядит красиво, но номера этажей на кнопках не различить: цифры – слишком тусклые, а кнопки – через чур блестят.



Чтобы устранить проблему, сообразительные жильцы наклеили рядом с кнопками бумажки с номерами этажей.


Пример 2. "Квейк" на машинках

Недавно играл в шутер, в котором игроки и боты ездят на машинках. Со стороны кажется, что идея "скрестить" жанры – шутер и гонки – разумна. Однако играть в эту игру оказалось невозможно. И вот, почему.

Скорость машинки гораздо выше, чем скорость человека или монстра. Чтобы машинка могла ездить свободно, для неё нужна трасса. Классический же уровень шутера представляет собой закрытое помещение с небольшими коридорами, автоматически открывающимися дверями, лифтами, джампами и телепортами. Бегать по такому уровню удобно, а вот погонять на машинке – невозможно. Потому что, не успев набрать скорость, сразу же врезаешься в стену.

Пример 3. Странный код

В одном из обсуждений на RSDN.RU одним из коллег был приведён код на C#, который я для данной статьи "перевёл" на C++.

class IWidth
{
public:

    virtual ~IWidth() {}
    virtual int Width() const = 0;
};

class IHeight
{
public:

    virtual ~IHeight() {}
    virtual int Height() const = 0;
};

class IRectangle : public IWidth, public IHeight
{
};

class Rectangle : public IRectangle
{
public:

    Rectangle(int iWidth, int iHeight)
        : m_iWidth(iWidth),
          m_iHeight(iHeight)
    {
    }
    ~Rectangle() {}

    virtual int Width() const { return m_iWidth; }
    virtual int Height() const { return m_iHeight; };

private:

    int m_iWidth;
    int m_iHeight;
};

class ISide
{
public:

    virtual ~ISide() {}
    virtual int Side() const = 0;
    virtual int Angle() const = 0;
};

class IRombus : public ISide
{
};

class Rombus : public IRombus
{
public:

    Rombus(int iSide, int iAngle)
        : m_iSide(iSide),
          m_iAngle(iAngle)
    {
    }
    ~Rombus() {}

    virtual int Side() const { return m_iSide; }
    virtual int Angle() const { return m_iAngle; }

private:

    int m_iSide;
    int m_iAngle;
};

class ISquare : public IRectangle, public IRombus
{
};

class Square : public ISquare
{
    // Реализацию пропущу.
    // ...
};

Этот код написан в соответствии с привычными парадигмами:

  1. Наследование.
Все фигуры объединены в общую иерархию классов.
  1. Выделение интерфейсов.
Для каждого класса написан соответствующий интерфейс, который этот класс реализует.

Тем не менее, у автора хочется спросить: что делают эти классы и для чего они нужны? Какие полезные функции они выполняют?

Иерархия содержит 9 классов (по три класса на фигуру!), а её функциональность – нулевая.

3 комментария:

  1. Могу засчитать только первый довод.
    2. Гм, шутер и машинки, это же Unreal Tournament - режим Onslaught, где как раз достигнут практически идеальный баланс. Вполне функциональная система
    3. Это всего лишь болванка, возможно, такой код оправдан, а может и нет. Сложно понять для чего это, если нам недоступны требования к системе.

    ОтветитьУдалить
  2. 2 dm:

    2) Для шутера на машинках нужно создавать протяжённые локации, что и реализовано в UT и Мире танков. Нельзя брать обычную "квейковскую" локацию и пытаться сделать "то же самое, но на машинках".

    3) В этом примере структурирование кода произведено до принятие решения о том, зачем этот код нужен. Если Вы заглянете в исходное обсуждение, то легко в этом убедитесь. Я бы не стал приводить этот пример, если бы, к сожалению, не видел реальных примов кода, написанных таким же образом и представляющих примерно такие же монстрообразные иерархии, потому что разработчики перед тем, как проектировать, не задумывались над вопросом, что их классы будут делать и для чего они нужны.

    ОтветитьУдалить
  3. 1. кнопочную панель дизайнили идиоты, цифры - самое важное для лифтовых кнопок.

    2. Не играл в эту конкретную игру, но на моей памяти есть отличный пример скрещения - Twisted Metal, но уровни там все окрытые конечно :) машинки все таки.

    3. Этот код вообще невозможно читать спокойно, возникает желание ругаться :) скорее всего написан студентом, под впечатлением от недавно прочитанной книжке "по архитектуре". В силу неопытности книжка была понята не правильно и готовые "паттерны" из книжки лепятся куда попало.

    ОтветитьУдалить