Это расточительный / плохой дизайн - использовать вектор / список, где в большинстве случаев он будет иметь только один элемент? - PullRequest
5 голосов
/ 14 июня 2010

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

пример:

class dragon
{
    ArrayList<head> = new ArrayList<head> Heads;
    tail Tail = new tail();
    body Body = new body();

    dragon()
    {
        theHead=new head();
        Heads.add(theHead);
    }

    void nod()
    {
        for (int i=0;i<Heads.size();i++)
        {
            heads.get(i).GoUpAndDown();
        }
    }
}

class firedragon extends dragon
{
}

class icedragon extends dragon
{
}

class lightningdragon extends dragon
{
}

// 10 other one-headed dragon declarations here


class hydra extends dragon
{
    hydra()
    {
        anotherHead=new head();
        for (int i=0;i<2;i++)
        {
            Heads.add(anotherHead);
        }
    }
}

class superhydra extends dragon
{
    superhydra()
    {
        anotherHead=new head();
        for (int i=0;i<4;i++)
        {
            Heads.add(anotherHead);
        }
    }
}

РЕДАКТИРОВАТЬ: (часть 2 вопроса)

Спасибо за ответы. Они были очень полезны. Я действительно сталкивался с этой ситуацией более одного раза, и я хотел бы привести второй пример, не связанный с наследованием. Это на самом деле пример из реальной жизни, и хотя я решил, что мой текущий проект достаточно мал, чтобы быть безопасным при использовании векторов, основанных на ваших ответах, я полагаю, что в некоторых случаях я буду использовать его в гораздо большем масштабе. точка.

Создавая движок для моего текущего игрового проекта для Android, я счел необходимым создать объект Imagepoint, который в основном представляет собой набор координат XY, которые используются для отслеживания значительных частей изображения спрайта. Допустим, у вас есть персонаж из кости, состоящий из нескольких растровых изображений, полезно знать, где шея прикреплена к туловищу, предплечье к бицепсу и т. Д. Эти данные смещения xy используются позже для расчета положения для различных целей, например, где позиционировать другие спрайты с помощью тригонометрических функций, чтобы найти смещение по оси x с учетом текущего угла.

Большинству спрайтовых объектов потребуется только одна точка изображения. Бицепсу нужен один для предплечья, предплечью нужен один для руки, шее нужен один для головы и т. Д. Туловища в моих нынешних моделях персонажей являются исключением, и им нужно несколько для плеч, ног, шеи и любые съемные декоративные спрайты.

Спрайт - это объект, который содержит векторы точек изображения, унаследованных классов не будет, потому что каждый: торс, нога и т. Д. Являются просто экземпляром класса спрайта, который является строго графическим объектом, содержащим указатель на растровое изображение, эти точки изображения и различные данные о положении / ориентации, и не будут использоваться для каких-либо специализированных целей. Если бы я использовал эту концепцию в крупномасштабной игре с огромным количеством экземпляров, где большинству потребовалась бы только одна точка изображения, с несколькими объектами, требующими нескольких или ни одного, и где не было бы особых случаев, заслуживающих использования наследования. Что бы вы подумали?

Ответы [ 6 ]

2 голосов
/ 14 июня 2010

Если заголовок общедоступен, он должен быть доступен в виде массива / списка / коллекции, чтобы потребители могли иметь с ним дело.

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

say("Dragon, would you like a yummy princess?");
if(dragon.Stomach.Contents.IsEmpty)
{
    dragon.Nod();
}

по сравнению с:

say("Dragon, would you like a yummy princess?");
dragon.offerFood(princess);

// in dragon class
void offerFood(Food yummyFood)
{
   if(Stomach.Contents.Empty)
   {
        head.Nod(); // can be overridden by subclasses.
   }
}

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

Я называю это «поведенческим», потому что интерфейсы фокусируются на взаимодействиях между объектами (драконом и всем, что предлагает вкусная принцесса), а не состоянием дракона (его голов).

2 голосов
/ 14 июня 2010

Да и нет.

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

interface BigLizard
{
  void nod();
}

class Dragon implements BigLizard
{
  Head head;

  void nod() { head.upAndDown(); }
}

class ReallyScaryDragon extends Dragon { ... }

class Hydra implements BigLizard
{
  ArrayList<Head> heads;

  void nod() { for (Head h : heads) h.upAndDown(); }
}

И так далее. Этот подход подобен «иметь именно то, что вам нужно, для ваших объектов, не заставляя ничего быть конкретным случаем» Конечно, это означает, что он будет иметь две различные реализации nod(), но когда вам не нужно оптимизировать (и вам следует заботиться об этом только в конце), просто придерживайтесь ваших личных предпочтений, предполагая, что это не идет против любого стандартного, широко принятого соглашения.

Вам не нужно делать что-либо наследуемым от одного объекта, просто настройте иерархию так, как вам нравится ... также составное решение (как уже предлагалось) было бы решением также, если бы понятие "головной части" не было то же самое для вашего Dragon и для вашего Hydra ..

Третьим решением для вашего конкретного случая может быть использование LinkedList вместо ArrayList. В этом случае, когда у вас есть только одна голова, вы не будете тратить пространство (за исключением создания списка), и у вас будет только указатель next головы (из списка: D), указывающий в никуда.

1 голос
/ 14 июня 2010

В этом конкретном случае, вероятно, было бы лучше переопределить nod () для ваших многоголовых драконов.Независимо от того, имеют ли они 1 голову или 5, это деталь, о которой на самом деле не стоит беспокоиться в базовом классе (которая должна быть настолько простой, насколько это возможно).

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

1 голос
/ 14 июня 2010

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

0 голосов
/ 14 июня 2010

Я бы реализовал такие функции в производных классах. Чтобы это поведение работало, вы должны иметь возможность переопределять методы (в вашем случае, метод nod ()). Базовый класс не должен обслуживать все возможности, он должен представлять базовый вариант, который может быть расширен. Просто сделайте соответствующие методы переопределяемыми, чтобы производные классы могли реализовывать определенное поведение, и все будет хорошо.

0 голосов
/ 14 июня 2010

Вы можете изменить реализацию Head на Composite type . Таким образом, каждому расширению Dragon требуется только один экземпляр Head, а Head заботится о GoUpAndDown().

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...