英文:
method with multiple class implementing same interface
问题
我有一个接口:
public interface I {
getBar();
}
我有两个类:A
和 B
实现了 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<I> 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
- 定义 Appender
interface Appender {
void append(I item);
}
- 创建用于 A、B 和什么都不做的默认 appender
- 将 appenders 放入 Map<Class, Appender>
- 修改您的代码为
for (I item : items) {
appenders.getOrDefault(item.getClass(), DEFAULT_APPENDER).append(item);
}
英文:
what if you take more OOP approach,
- define Appender
> interface Appender{
> void append(I item);
> }
- create your appenders for A B and default appender that do nothing
- put appenders into map Map<Class,Appender>
- 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<I> 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<Class<?>, Consumer<I>> DISPATCHER = Map.of(
A.class, this::appendA,
B.class, this::appendB
);
items.forEach(i -> 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<Class<?>, Consumer<I>> DISPATCHER = Map.of(
A.class, this::appendA,
B.class, this::appendB
);
items.forEach(i -> DISPATCHER.get(i.getClass()).accept());
Yeah, it's still branchy and must run many more instructions to get the same result
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论