Poniżej znajduje się coś w rodzaju podobnego do odpowiedzi @OldCurmudgeon.
Punktem kluczowym jest również pole listeners
. Ale deklaruję to tak:
private final Map<Class<?>, DelegatingPacketListener> listeners
Chodzi o to, że pozbywamy się listy jako typu wartości mapy. DelegatingPacketListener
jest zadeklarowana następująco:
public class DelegatingPacketListener implements PacketListener<Packet> {
private final List<PacketListener<Packet>> packetListeners;
public DelegatingPacketListener(List<? extends PacketListener<Packet>> packetListeners) {
super();
this.packetListeners = new ArrayList<PacketListener<Packet>>(packetListeners);
}
@Override
public void onOutgoingPacket(Streamer streamer, Packet packet) {
for(PacketListener<Packet> packetListener : packetListeners) {
packetListener.onOutgoingPacket(streamer, packet);
}
}
@Override
public void onIncomingPacket(Streamer streamer, Packet packet) {
for(PacketListener<Packet> packetListener : packetListeners) {
packetListener.onIncomingPacket(streamer, packet);
}
}
public List<PacketListener<Packet>> getPacketListeners() {
return Collections.unmodifiableList(packetListeners);
}
}
Teraz DelegatingPacketListener
obsługuje tylko słuchaczy typu Packet
musimy jeszcze jedną konkretną realizację PacketListener
:
public class WrappingPacketListener<T extends Packet> implements PacketListener<Packet> {
private final Class<T> packetClass;
private final PacketListener<T> wrapped;
public WrappingPacketListener(Class<T> packetClass, PacketListener<T> delegate) {
super();
this.packetClass = packetClass;
this.wrapped = delegate;
}
@Override
public void onOutgoingPacket(Streamer streamer, Packet packet) {
if(packetClass.isInstance(packet)) {
T genericPacket = packetClass.cast(packet);
wrapped.onOutgoingPacket(streamer, genericPacket);
}
}
@Override
public void onIncomingPacket(Streamer streamer, Packet packet) {
if(packetClass.isInstance(packet)) {
T genericPacket = packetClass.cast(packet);
wrapped.onIncomingPacket(streamer, genericPacket);
}
}
}
Zwróć uwagę, że parametr typu T
nie jest stosowany w implementuje klauzulę. Dotyczy tylko zastosowanej implementacji. Owińemy każdy PacketListener
przekazany do API w WrappingPacketListener
. Zatem realizacja jest tak:
public List<PacketListener<Packet>> getPacketListeners(Class<?> clazz) {
return Collections.<PacketListener<Packet>>singletonList(listeners.get(clazz));
}
public <T extends Packet> void addPacketListener(Class<T> clazz, PacketListener<T> listener) {
if (listeners.containsKey(clazz) == false) {
listeners.put(clazz, new DelegatingPacketListener(Collections.singletonList(new WrappingPacketListener<T>(clazz, listener))));
return;
}
DelegatingPacketListener existing = listeners.get(clazz);
List<PacketListener<Packet>> newListeners = new ArrayList<PacketListener<Packet>>(existing.getPacketListeners());
newListeners.add(new WrappingPacketListener<T>(clazz, listener));
listeners.put(clazz, new DelegatingPacketListener(newListeners));
}
private <T extends Packet> void notifyListeners(T packet) {
List<PacketListener<Packet>> listeners = streamer.getPacketListeners(packet.getClass());
if (listeners != null) {
for (PacketListener<Packet> packetListener : listeners) {
packetListener.onIncomingPacket(streamer, packet);
}
}
}
API nieznacznie zmieniło getPacketListeners
który nie robi używać typu rodzajowego więcej.
W porównaniu do rozwiązania OldCurmudgeon, ten jest zgodny z istniejącym już interfejsem PacketListener
i nie wymaga zastosowania odznaczonego rzutowania.
Należy pamiętać, że implementacja nie jest bezpieczna dla wątków, ponieważ implementacja addPacketListener
wymaga synchronizacji na kluczu mapy (ponieważ potrzebny jest również oryginalny kod). Jednak enkapsulowanie listy detektorów pakietów w niezmiennej wersji DelegatingPacketListener
jest prawdopodobnie lepiej dostosowane do celów współbieżności.
Staraj się przestrzegać zasady PECS. Sprawi, że twoje rzeczy będą łatwe. http://stackoverflow.com/questions/2723397/java-generics-what-is-pecs – Pranalee
Nie należy dodawać, że mimo użycia 'ConcurrentMap' kod w' addPacketListener' nie jest bezpieczny dla wątków i wymagałby synchronizacji na mapie klawisz. – SpaceTrucker
@SpaceTrucker, dlaczego tak się dzieje? – Antoniossss