使用组合来利用Java抽象集合类的继承,这是否是一种不好的实践?

huangapple go评论58阅读模式
英文:

Is using composition to leverage inheritance from Java abstract collections classes bad practice?

问题

很多时候我发现自己(看起来是)在从抽象集合类继承时结合了组合和继承,而当我这样做时,我禁不住感觉这在技术上是冲突的设计范例。

/** 自定义字符串列表实现 */
public class MyStringList extends AbstractList<String>
{
    private final AbstractList<String> _myStringList;

    public MyStringList()
    {
        this._myStringList = new ArrayList<String>();
    }

    public MyStringList(Collection<String> stringCollection)
    {
        if (stringCollection.contains(null))
        {
            throw new NullPointerException("MyStringList 不允许使用空值");
        }
        else
        {
            this._myStringList = new ArrayList<String>(stringCollection);
        }
    }

    @Override
    public String get(int index)
    {
        return this._myStringList.get(index);
    }

    @Override
    public int size()
    {
        return this._myStringList.size();
    }

    @Override
    public String set(int index, String aString)
    {
        ++this.modCount;
        return this._myStringList.set(index, Objects.requireNonNull(aString));
    }

    @Override
    public void add(int index, String aString)
    {
        ++this.modCount;
        this._myStringList.add(index, Objects.requireNonNull(aString));
    }

    @Override
    public boolean add(String aString)
    {
        ++this.modCount;
        return this._myStringList.add(Objects.requireNonNull(aString));
    }

    @Override
    public String remove(int index)
    {
        ++this.modCount;
        return this._myStringList.remove(index);
    }

    // 为了回答这个 StackOverflow 问题的示例而使此列表实现与 ArrayList 有所区别的方法
    public String[] getNumericContaining()
    {
        return this._myStringList.stream()
                                 .filter(aString -> aString.codePoints().anyMatch(Character::isDigit))
                                 .toArray(String[]::new);
    }

    // 为了回答这个 StackOverflow 问题的示例而使此列表实现与 ArrayList 有所区别的另一个方法
    public String[] getUpperCaseContaining()
    {
        return this._myStringList.stream()
                                 .filter(aString -> aString.codePoints().anyMatch(Character::isUpperCase))
                                 .toArray(String[]::new);
    }
}

这种在类继承过程中,使用内部抽象集合对象作为支持对象(组合),并从该对象继承(继承)的设计,是否被认为是利用 java.util 包中的各种抽象集合类的“正确”方式?

英文:

A lot of times I find myself (what appears to be) combining composition and inheritance when inheriting from abstract collection classes and I can't help but feel like it's technically conflicting design paradigms while I do it.

/** Custom String List implementation */
public class MyStringList extends AbstractList&lt;String&gt;
{
    private final AbstractList&lt;String&gt; _myStringList;

    public MyStringList()
    {
        this._myStringList = new ArrayList&lt;String&gt;();
    }

    public MyStringList(Collection&lt;String&gt; stringCollection)
    {
        if (stringCollection.contains(null))
        {
            throw new NullPointerException(&quot;Null values not permitted in MyStringList&quot;);
        }
        else
        {
            this._myStringList = new ArrayList&lt;String&gt;(stringCollection);
        }
    }

    @Override
    public String get(int index)
    {
        return this._myStringList.get(index);
    }

    @Override
    public int size()
    {
        return this._myStringList.size();
    }

    @Override
    public String set(int index, String aString)
    {
        ++this.modCount;
        return this._myStringList.set(index, Objects.requireNonNull(aString));
    }

    @Override
    public void add(int index, String aString)
    {
        ++this.modCount;
        this._myStringList.add(index, Objects.requireNonNull(aString));
    }

    @Override
    public boolean add(String aString)
    {
        ++this.modCount;
        return this._myStringList.add(Objects.requireNonNull(aString));
    }

    @Override
    public String remove(int index)
    {
        ++this.modCount;
        return this._myStringList.remove(index);
    }

    // Method to make this list implementation distinct from ArrayList for the sake of
    // this StackOverflow question example
    public String[] getNumericContaining()
    {
        return this._myStringList.stream()
                                 .filter(aString -&gt; aString.codePoints().anyMatch(Character::isDigit))
                                 .toArray(String[]::new);
    }

    // Another method to make this list implementation distinct from ArrayList for
    // the sake of this StackOverflow question example
    public String[] getUpperCaseContaining()
    {
        return this._myStringList.stream()
                                 .filter(aString -&gt; aString.codePoints().anyMatch(Character::isUpperCase))
                                 .toArray(String[]::new);
    }
}

Is this design of having an internal abstract collection object being the backing object (composition) of the class this object inherits from (inheritance) considered the "correct" way of leveraging the various abstract collection classes defined in the java.util package?

答案1

得分: 1

你正在不必要地组合事物。如果你想要在示例中拥有的内容,可以采纳 Lino 的建议:

public class MyStringList extends ArrayList<String> {

    public String[] getNumericContaining() {
        return this.stream()
                   .filter(aString -> aString.codePoints().anyMatch(Character::isDigit))
                   .toArray(String[]::new);
    }

    public String[] getUpperCaseContaining() {
        return this.stream()
                   .filter(aString -> aString.codePoints().anyMatch(Character::isUpperCase))
                   .toArray(String[]::new);
    }
}

然而,通常情况下你只需要一个普通的 List<String>,因为你可以将额外的行为提取到封装列表的类中,例如:

public class MySomeService {
    
    // 可以从外部传入,从某处加载等等
    private List<String> foo;

    public void serviceMethod() {
        doSomething();
        String[] numbers = getNumericContaining();
        doSomethingElse(numbers);
    }

    // 前面的方法
    private String[] getNumericContaining() {
        foo.stream(). // 以此类推
}

这取决于你的列表是否真的是一个独立的类,还是仅仅是你在某个时候需要的一种特殊类型。如果是前者,可能值得为它创建一个独立的类;如果是后者,则不需要。

最后,尽管面向对象编程告诉你将数据和行为放在同一个地方,但有时将它们放在不同的地方也是有道理的,这样一个方法或函数就可以被多个地方使用。只需将列表作为参数传递,它将适用于所有的 Collection 类,而不仅仅是你的特殊类。

英文:

You're combining things unnecessarily. If you want to have what you have in the example, go with Lino's suggestion:

public class MyStringList extends ArrayList&lt;String&gt; {
public String[] getNumericContaining() {
return this.stream()
.filter(aString -&gt; aString.codePoints().anyMatch(Character::isDigit))
.toArray(String[]::new);
}
public String[] getUpperCaseContaining() {
return this.stream()
.filter(aString -&gt; aString.codePoints().anyMatch(Character::isUpperCase))
.toArray(String[]::new);
}
}

however many times a regular List&lt;String&gt; is all you need, since you can extract extra behaviour to the class that encapsulates the list, e.g.

public class MySomeService {
// This could be passed in, loaded from somewhere, etc.
private List&lt;String&gt; foo;
public void serviceMethod() {
doSomething();
String[] numbers = getNumericContaining();
doSomethingElse(numbers);
}
// The previous methods
private String[] getNumericContaining() {
foo.stream(). // and so on
}

It depends on whether your list really is a class in its own right, or whether it's just a special flavor that you need at some point. If it's the first, it may deserve its own class, if it's the latter, it doesn't.

Finally, even though OOP tells you to put data and behaviour in the same place, sometimes it makes sense to keep them in different places, so a method or a function can be used. Just pass the list as a parameter, and it'll work for all Collection classes not just your special one.

huangapple
  • 本文由 发表于 2020年9月16日 15:04:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/63914804.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定