Java重构代码重复

Java refactoring code duplication

我有很多重复的代码,但我不知道如何正确重构它。

我有一个 class Foo,它解析从套接字接收的网络 messages/data 并调用相应的 onEvent() 方法。 Foo 纯粹是网络消息的解析器,对于接收到的事件采取什么行动没有逻辑。任何想要添加此类逻辑的人都必须 subclass Foo 并覆盖 onEvent() 方法。

abstract class Foo {
    void processNetwotkMessage(String message) {
        ...
        onEvent1(arg1, arg2, arg3)
        reutn;
        ...
        onEvent2(arg4);
        return;
        ...
        onEvent3()
        return;
        ...
        onEvent999(arg1337);
    }

    abstract protected void onEvent1(Arg1 arg1, Arg2 arg2, Arg3 arg3);
    abstract protected void onEvent2(Arg4 arg4);
    abstract protected void onEvent3();
    ...
    abstract protected void onEvent999(Arg1337 arg1337);
}

现在,我的程序应该是模块化的,我有许多单独的模块 classes 想要接收这些事件并处理它们。这些模块实现了 Plugin 接口。该接口与 Foo 中的 onEvent() 方法匹配,但添加 PluginContext ctx 作为第一个参数。

interface Plugin {
    void onEvent1(PluginContext ctx, Arg1 arg1, Arg2 arg2, Arg3 arg3);
    void onEvent2(PluginContext ctx, Arg4 arg4);
    void onEvent3(PluginContext ctx);
    ...
    void onEvent999(PluginContext ctx, Arg1337 arg1337);
}

现在,为了将事件分派给模块,我创建了一个 Foo 的模块感知子 class,名为 PluginSupporingFoo

class PluginSupporingFoo extends Foo implements PluginContext {
    List<Plugin> plugins;

    @Override
    protected void onEvent1(Arg1 arg1, Arg2 arg2, Arg3 arg3) {
        synchronized (plugins) {
            for (Plugin p : plugins) {
                p.onEvent1(this, arg1, arg2, arg3);
            }
        }
    }

    @Override
    protected void onEvent2(Arg4 arg4) {
        synchronized (plugins) {
            for (Plugin p : plugins) {
                if (PluginIsAllowedToBeAwareOfThisEvent(p, arg4)) {
                    p.onEvent2(this, arg4);
                }
            }
        }
    }

    @Override
    protected void onEvent3() {
        synchronized (plugins) {
            for (Plugin p : plugins) {
                p.onEvent3(this);
            }
        }
    }

    ...

    @Override
    protected void onEvent999(Arg1337 arg1337) {
        synchronized (plugins) {
            for (Plugin p : plugins) {
                p.onEvent999(this, arg1337);
            }
        }
    }
}

如您所见,每当 Foo 调用其中一个 onEvent() 方法时,都会调用 PluginSupporingFoo 中的覆盖方法,然后通过调用Plugin 接口的相应 onEvent() 方法,添加了一个额外的参数 -- PluginContext ctx。有时还有一个条件是否告诉模块有关事件的信息,就像您在 PluginSupporingFoo.onEvent2().

中看到的那样

现在,我想删除大量重复代码。

  1. 首先,Plugin接口和Fooclass有几乎相同的方法。事实上,Plugin 接口需要 Foo 拥有的所有 onEvent 方法,但是 PluginContext ctx 作为额外的第一个参数。

  2. 另一个代码重复在 PluginSupporingFoo 中。所有 onEvent() 方法几乎都是彼此的副本:

.

protected void on${EventName} ( ${ArgList} ) {
    synchronized (plugins) {
        for (Plugin p : plugins) {
            ${ OPTIONAL: if (filter(p, ${ArgList}.arg1)) { }
                p.on{EventName}(this, ${ArgList}.allArgs);
            ${ OPTIONAL: } }
        }
    }
}

考虑到有很多 onEvent 方法,复制粘贴代码如此之多令人沮丧,如果需要,很难全部修改。

哇...这是一个糟糕的设计 eventXX( foo, bar, baz) 等等,因为每次添加新事件时都必须添加相应的侦听器方法。

也许更好的设计是重构它,这样你的 Foo class 就只有几个或理想情况下只有一个 onEvent() 方法需要一个新的 Event 接口

public class Foo{

  void onEvent(Event e){ ... }

} 

public interface Event{
     Object[] getArgs();

     //other Event specific methods
     ...
}

那么每个 eventXX 方法都将是 Event 接口的新实现。

public class Event2 implements Event{

   public Object[] getArgs(){
       //Arg4 like in your code
       return new Object[]{ new Arg4() };
   }
}

插件可能也可能同样只有 1 个方法

interface Plugin{

   onEvent(PluginContext ctx, Event e);

}

现在,只要您需要添加一个新的事件,它只是一个新的事件实现,这些接口不需要任何额外的方法。

处理程序可以检查事件的类型,或者您可以根据需要制作 EventType 或其他类型的鉴别器。

 class MyPlugin implements Plugin{
     public void onEvent(PluginContext ctx, Event e){
         //this is only useful if we only care about a few types
         if( e instanceOf Event2){
            //we know this is Arg4
            Arg4 arg4 = (Arg4) e.getArgs()[0];
            ...
         }

     }
 }

现在有了 Java Lambda,我们甚至可以有一个处理程序 Map<Class<? extends Event>, Function> 如果你想变得更漂亮的话。