具有多个类实现相同接口的方法

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

method with multiple class implementing same interface

问题

我有一个接口:

public interface I {
   getBar();
}

我有两个类:AB 实现了 I

我想要做的是:

public void foo(List<I> items) {
   for (I item : items){
      if (item instanceof A) this.append((A) item);
      if (item instanceof B) this.append((B) item);
   }
}

截止到Java 8,是否有一种方式可以避免使用instanceof,并像这样调用它?

items.foreach(this::append);

注意:
我可以像这样做:item.add(this),这将把自身附加到this,但是那样我会调用一个修改参数(仅限于它自己)的方法,但根据Robert C. Martin的“Clean Code”所述,这是不好的做法:
> 参数最自然地被解释为函数的输入。
>
> 任何强迫你检查函数签名的东西都相当于两次看。这是一种认知性的打断,应该避免。在面向对象编程之前的时代,有时需要输出参数。然而,在面向对象的语言中,对输出参数的需求大大减少。

英文:

I have an interface :

public interface I {
   getBar();
}

I have 2 class : A and B implementing I.

What I want to do is :

public void foo(List&lt;I&gt; items) {
   for (I item : items){
      if (item instanceof A) this.append((A) item);
      if (item instanceof B) this.append((B) item);
   }
}

Is there something as of java 8 that would allow me to not have that list of instanceof and call it like this ?

items.foreach(this::append);

Note :
I could do something like item.add(this) which would append itself to this but then I would call a method on an object that modifies the parameter (and only it) but this is bad practice as stated by Robert C. Martin's "Clean Code" :

> Arguments are most naturally interpreted as inputs to a function.
>
> Anything that forces you to check the function signature is equivalent to a double-take. It’s a cognitive break and should be avoided. In the days before object oriented programming it was sometimes necessary to have output arguments. However, much of the need for output arguments disappears in OO languages

答案1

得分: 1

instanceof相关的部分移到一个通用的追加函数中:

private void append(I item) {
  if (item instanceof A) ...
  if (item instanceof B) ...
}

然后你可以使用:

items.foreach(this::append);
英文:

just move the instanceof stuff to a generic append func

private void append(I item) {
  if (item instanceof A) ...
  if (item instanceof B) ...
}

then you can use

items.foreach(this::append);

答案2

得分: 0

  1. 定义 Appender
interface Appender {
    void append(I item);
}
  1. 创建用于 A、B 和什么都不做的默认 appender
  2. 将 appenders 放入 Map<Class, Appender>
  3. 修改您的代码为
for (I item : items) {
    appenders.getOrDefault(item.getClass(), DEFAULT_APPENDER).append(item);
}
英文:

what if you take more OOP approach,

  1. define Appender

> interface Appender{
> void append(I item);
> }

  1. create your appenders for A B and default appender that do nothing
  2. put appenders into map Map<Class,Appender>
  3. change your code to

> for (I item : items){
> appenders.getOrDefault(item.class,DEFAULT_APPENDER).append(item);
> }

答案3

得分: 0

你的想法完全颠倒了。

你的类获取这些I的实例,并根据类型有不同的重载方法。这个重载意味着它需要知道所有的实现,以提供不同的重载。

显然,问题在于列表必须随着实现的添加而增长。

解决方案是让I实现类负责知道它们如何被附加。

public interface I {
    Bar getBar(); /* 你漏掉了返回类型,但无论如何 */
    
    void appendTo(SomeType foo);
}

你的方法简单地变成了:

public void foo(List<I> items) {
    for (I item : items){
        item.appendTo(this); //或者是某个可附加的实例,而不是this
    }
}

如果有人添加了一个新的I实现,他们被迫在接口的契约中编写一个附加方法。

英文:

You're thinking about it upside down.

Your class gets these instances of I and has a different overloaded method based on the type. This overload means it needs to be aware of all implementations, to provide a different overload for each.

The problem, obviously, is that the list must grow as implementations are added.

The solution is to make the implementations of I responsible for knowing how they are appended.

public interface I {
    Bar getBar(); /* you missed the return type, but anyway */

    void appendTo(SomeType foo);
}

Your method simply becomes

public void foo(List&lt;I&gt; items) {
   for (I item : items){
      item.appendTo(this); //or instead of this, some append-able instance 
   }
}

If someone adds a new implementation of I, they are forced to write an append method as part of the interface's contract.

答案4

得分: 0

实际答案:这是一个双重分派问题,而Java中心的解决方案是访问者模式,这需要做相当多的更改。

愚蠢的答案:只是为了好玩,这里没有instanceof

    private static final Map&lt;Class&lt;?&gt;, Consumer&lt;I&gt;&gt; DISPATCHER = Map.of(
      A.class, this::appendA,
      B.class, this::appendB
    );

    items.forEach(i -&gt; DISPATCHER.get(i.getClass()).accept());

是的,它仍然需要分支,并且必须运行更多指令才能获得相同的结果 具有多个类实现相同接口的方法

英文:

Actual answer: This is a double-dispatch problem and the Java-centric solution is the Visitor Pattern which is quite a lot of change.

Silly answer: Just for kicks, there's no instanceof here:

    private static final Map&lt;Class&lt;?&gt;, Consumer&lt;I&gt;&gt; DISPATCHER = Map.of(
      A.class, this::appendA,
      B.class, this::appendB
    );

    items.forEach(i -&gt; DISPATCHER.get(i.getClass()).accept());

Yeah, it's still branchy and must run many more instructions to get the same result 具有多个类实现相同接口的方法

huangapple
  • 本文由 发表于 2020年7月29日 18:20:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/63151419.html
匿名

发表评论

匿名网友

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

确定