Должен ли я расширить ArrayList (is-a) или включить его в качестве члена (has-a)? - PullRequest
6 голосов
/ 15 марта 2011

Я делаю простую программу, которая поддерживает список чисел, и я хочу, чтобы этот список также имел имя.Каков наилучший подход: мой класс списка расширяет ArrayList или он включает член ArrayList?В обоих случаях, конечно, был бы член String "name".

Первый подход означает, что мне нужно только реализовать getter & setter для имени, но я думаю, что это слишком привязало бы мой класс кконкретная реализация?Например, если бы я захотел позже использовать Vector, то мне пришлось бы везде менять код.

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

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

Ответы [ 6 ]

3 голосов
/ 15 марта 2011

Для лучшего из обоих миров используйте Гуава ForwardingList .Вот простой пример:

public class NamedList<E> extends ForwardingList<E> implements RandomAccess {
  // could also let the user provide the delegate list
  private final List<E> delegate = Lists.newArrayList();
  private String name;

  @Override protected List<E> delegate() {
    return delegate;
  }

  // constructors, getter, setter
}

Кстати, ловушки расширения реализации конкретной коллекции вместо использования композиции обсуждаются в Effective Java пункт 16 (во 2-м изд.) "Пользуйся композицией, а не наследством.Одна из нескольких упомянутых проблем связана с неожиданным поведением, связанным с взаимодействием между методами в суперклассе (например, add и addAll).

Классы Forwarding* в Guava являются реализацией предложенного решения.есть.

3 голосов
/ 15 марта 2011

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

2 голосов
/ 15 марта 2011

Ни лучше, ни компромисс, как вы упомянули.

Если вы выполняете агрегацию (has-a), вам нужно обернуть все функции члена, который вы собираетесь использовать (т.е. реализоватьвызывающие их функции)

Если вы выполняете наследование, в ваш новый класс добавляется множество функций, которые могут оказаться ненужными, и вам также необходимо реализовать абстрактные функции.

Если выв среде C ++ частное наследование было бы вариантом, но здесь есть и плюсы, и минусы

1 голос
/ 15 марта 2011

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

public class MyClass implements List
{
    private String name;
    private List myList = new ArrayList();

    public MyClass(String name)
    {
        this.name = name;
    }

    public String getName()
    {
        return name;
    }

    public void setName()
    {
        return name;
    }

    @Override
    public void add(int index, Object element)
    {
        myList.add(index,element);
    }

    @Override
    public boolean add(Object o)
    {
        myList.add(o);
    }

    @Override
    public boolean addAll(Collection c)
    {
        myList.addAll(c);
    }

    //so on for rest of methods in List interface
}

Мы также можем включить Дженерики, если вы хотите, чтобы они были безопасны для чисел.

1 голос
/ 15 марта 2011

Состав - ХАС-А. Я предпочитаю это для коллекций всех мастей.

0 голосов
/ 15 марта 2011

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

...