有几位听众L1, L2, ...
都很相似:
interface L1 { void onL1(); }
interface L2 { void onL2(); }
...
所有这些都在课程EventMaker
中使用,如下所示:
class EventMaker {
...
List<L1> l1s = new LinkedList<>();
List<L2> l2s = new LinkedList<>();
...
void addL1(L1 l1) {...; l1s.add(l1); ...;}
void removeL1(L1 l1) {...; l1s.remove(l1); ...;}
void callL1() {...}
void addL2(L2 l2) {l2s.add(l2);}
void removeL2(L2 l2) {l2s.remove(l2);}
void callL2() {...}
...
}
我该怎么做才能减少重复代码?我试图使用泛型方法,但它在很多方面都没有解决方案。
// ********************************************* ***************************
我的解决方案:
abstract class Caller<Listener> {
List<Listener> listeners;
public Caller() {
listeners = new LinkedList<>();
}
public void add(Listener listener) {
listeners.add(listener);
}
public void remove(Listener listener) {
listeners.remove(listener);
}
public void call() {
for (Listener listener : listeners) {
onCall(listener);
}
}
public abstract void onCall(Listener listener);
}
清空侦听器界面:
interface Listener {
}
For Every-Listener:
class L1Caller extends Caller<L1> {
@Override
public void onCall(L1 listener) {
listener.l1(); // unique name
}
}
最后,我为每个listern类型添加一个调用者到我的主类。
...
L1Caller l1Caller;
L2Caller l2Caller;
...
并使用它:
l1Caller.add(...);
l2Caller.call();
答案 0 :(得分:0)
我不知道这是否是最佳做法,但这当然是可能的。
public class Caller {
public static final Token<L1> L1 = new Token<>();
public static final Token<L2> L2 = new Token<>();
public static class Token<T> {
// Making the constructor private means that no other Tokens can be made.
private Token() {}
}
private final Map<Token<?>, List<Object>> lists;
public Caller() {
lists = new HashMap<>();
Token<?>[] tokens = {L1, L2};
for (Token<?> token : tokens)
lists.put(token, new LinkedList<>());
}
public <T> void add(Token<T> token, T t) {
lists.get(token).add(t);
}
public <T> void remove(Token<T> token, T t) {
lists.get(token).remove(t);
}
}
尽管使用了通配符和Object
,但它完全是类型安全且易于使用:
caller.add(Caller.L1, () -> {
// do stuff
});
答案 1 :(得分:0)
将现有的ChangeListener与其感兴趣的源所在的ChangeEvent一起使用。改变使用泛型类型。
然后创建一个类来保存一个监听器列表,比如说
public class ChangeManager {
private final List listeners = ...
public void addChangeListener(ChangeListener listener) { ... }
public void notifyChange(ChangeEvent event) { ... }
}
这可以以垃圾收集和并发安全的方式处理侦听器。
现在你的班级代表了几个ChangeManagers。
private final ChangeManager l1 = new ChangeManager();
private final ChangeManager l2 = new ChangeManager();
private final ChangeEvent L1_EVENT = new ChangeEvent(this);
...
l1.addChangeListener(changeEvent -> {
...
});
l1.notifyChange(L1_EVENT);
答案 2 :(得分:0)
您应该将单独的add*
方法保留在API中,因为它们是有意义的,并且可以提供更清晰的API。改变API的一个选择是拥有一个add(type, listener)
并通过它路由所有调用。但是,您必须公开该类型,并且客户端必须协调将正确的侦听器添加到正确的类型。
不改变API但在内部集中管理侦听器的一种方法是这样的模式:
class EventMaker {
...
static String TYPE_L1 = "L1";
static String TYPE_L2 = "L2";
Map<String, List<Object>> listeners;
...
void addInternal(String t, Object listener) {
List<Object> list = listeners.get(t);
if (list == null) { list = new CopyOnWriteArrayList<>(); listeners.put(t, list); }
list.add(listener);
}
void removeInternal(String t, Object listener) {
List<Object> list = listeners.get(t);
if (list != null) { list.remove(listener; }
// optional
if (list != null && list.isEmpty) listeners.remove(t);
}
<ListenerType> List<ListenerType> getInternal(String t, Class<ListenerType> cls) {
List<Object> list = listeners.get(t);
if (list == null) return Collections.emptyList();
return (List<ListenerType>)list;
}
void removeInternal(String t, Object listener) {...}
void addL1(L1 l1) { addInternal(TYPE_L1, l1);}
void removeL1(L1 l1) { removeInternal(TYPE_L1, l1);}
void callL1() {
List<L1> list = getInternal(TYPE_L1, L1.class);
for (L1 ears : list) ears.onL1();
}
void addL2(L2 l2) {addInternal(TYPE_L2, l2);}
void removeL2(L2 l2) {removeInternal(TYPE_L2, l2);}
void callL2() {...}
...
}
在地图中忽略该类型,因为您要保护所有内容,并在访问其中一个侦听器列表时安全地转换。虽然通知你的听众没有共同的基础,但仍然需要这种类型。并发安全CopyOnWriteArrayList
也有一些性能影响,值得研究一下监听器列表。
我还建议返回句柄,而不是要求removeListener调用。这允许调用者在清理时不必查找事件源,但是具有执行工作的OO句柄。 java.lang.AutoCloseable
(如果使用Java 7或更高版本,则为java.lang.Runnable
)是一个不错的选择:
AutoCloseable addInternal(String t, final Object listener) {
List<Object> list = listeners.get(t);
if (list == null) { list = new ArrayList<>(); listeners.put(t, list); }
list.add(listener);
final List<Object> flist = list; // for final reference below
return new AutoCloseable() {
public void close() { flist.remove(listener); }
};
}
现在调用者只需在返回的句柄上调用.close()
即可取消注册。
如果您使用的是Java 8,则更简单:
AutoCloseable addInternal(String t, final Object listener) {
List<Object> list = listeners.get(t);
if (list == null) { list = new ArrayList<>(); listeners.put(t, list); }
list.add(listener);
return () -> list.remove(listener);
}
您还应该考虑正确同步,以确保线程安全,如果它是一个问题,使您的字段为私有和最终适当,以及我没有在这些示例中解决的其他良好做法清理。