题
我有一个 java 类,它触发自定义 java 事件。代码结构如下:
public class AEvent extends EventObject {
...
}
public interface AListener extends EventListener {
public void event1(AEvent event);
}
public class A {
public synchronized void addAListener(AListener l) {
..
}
public synchronized void removeAListener(AListener l) {
..
}
protected void fireAListenerEvent1(AEvent event) {
..
}
}
一切正常,但我想创建 A 的新子类(称为 B),它可能会触发新事件。我正在考虑进行以下修改:
public class BEvent extends AEvent {
...
}
public interface BListener extends AListener {
public void event2(BEvent event);
}
public class B extends A {
public synchronized void addBListener(BListener l) {
..
}
public synchronized void removeBListener(BListener l) {
..
}
protected void fireBListenerEvent2(AEvent event) {
..
}
}
这是正确的方法吗?我在网上搜索示例,但找不到任何示例。
这个解决方案有一些我不喜欢的地方:
BListener
有两种方法可供使用AEvent
其他用途BEvent
作为参数。B
类都有addAListener
和addBListener
方法。我应该用 private 关键字隐藏 addAListener 吗? [更新:不可能用 private 关键字隐藏]- 类似的问题与
fireAListenerEvent1
和fireBListenerEvent1
方法。
我正在使用 Java 版本 1.5。
解决方案
我不明白为什么 BListener
应该延长 AListener
.
你真的想强迫每个人都感兴趣吗 B
还实施的事件 event1()
?
而且你也不能添加 addAListener()
, ,因为派生类不能降低父类中存在的方法的可见性。另外,您不应该需要这样做,否则您将违反 里氏替换原则 (每个 B 必须能够做 A 能做的一切)。
作为最后一句话,我想说的是 fire*()
方法受到保护。通常根本没有理由将它们保持公开,并且减少公共成员的数量可以保持公共界面的干净。
其他提示
不要使用继承,这不是您想要的,并且会导致设计脆弱且难以更改。组合是一种更灵活、更好的设计方法。始终尝试尽可能细化地设计界面,因为它们不应该被更改。它们是您与系统其他部分的合同。如果需要添加新功能,第一个选项是向事件添加更多信息。如果这不合适,那么您应该设计一个新的界面来传递该事件。这可以避免更改任何不受影响的现有代码。
这是我最喜欢的模式,我相信它通常被称为观察者。
创建一个新接口,定义该事件类型的方法(fooEvent()addFooEventListener()removeFooEventListener())。在生成这些事件的具体类中实现此接口。(我通常称之为 SourcesFooEvent、FiresFooEvent、FooEventSource 等)
如果您想减少代码重复,您可以构造一个辅助类来处理侦听器的注册,将它们存储在集合中,并提供用于发布事件的 fire 方法。
泛型在这里可以提供帮助。首先,一个通用的监听器接口:
public interface Listener<T> {
void event(T event);
}
接下来是匹配的EventSource接口:
public interface EventSource<T> {
void addListener(Listener<T> listener);
}
最后是一个抽象基类,用于快速构造一个帮助器类来处理侦听器的注册和事件调度:
public abstract class EventDispatcher<T> {
private List<Listener<T>> listeners = new CopyOnWriteArrayList<T>();
void addListener(Listener<T> listener) {
listeners.add(listener);
}
void removeListener(Listener<T> listener) {
listeners.remove(listener);
}
void fireEvent(T event) {
for (Listener<T> listener : listeners) {
listener.event(event);
}
}
}
您可以通过封装来使用抽象 EventDispatcher,从而允许任何其他类轻松实现 EventSource,而不需要它扩展任何特定类。
public class Message {
}
public class InBox implements EventSource<Message> {
private final EventDispatcher<Message> dispatcher = new EventDispatcher<Message>();
public void addListener(Listener<Message> listener) {
dispatcher.addListener(listener);
}
public void removeListener(Listener<Message> listener) {
dispatcher.removeListener(listener);
}
public pollForMail() {
// check for new messages here...
// pretend we get a new message...
dispatcher.fireEvent(newMessage);
}
}
希望这说明了类型安全性(重要)、灵活性和代码重用之间的良好平衡。
我从你对 saua 的评论中了解到,射击 B 会自动射击 A。
为什么不使用单一类型的侦听器,然后混合一些继承、委托和泛型呢?
class AEvent {}
class BEvent extends Event{}
interface EventListner<E extends AEvent>
{
onEvent(E e);
}
class ListenerManager<E extends AEvent>{
addListner(EventListener<? extends E>){}
removeListner(EventListener<? extends E>){}
fire(E e);
}
class A extends ListenerManager<AEvent>
{
}
class B extends ListenerManager<BEvent>
{
A delegatorA;
@Override addListener(EventListener<? extends BEvent> l)
{
super.addListner(l);
delegatorA.addListener(l);
}
@Override removeListener(EventListener<? extends BEvent> l)
{
super.removeListner(l);
delegatorA.removeListener(l);
}
@Override fire(BEvent b)
{
super.fire(b);
a.fire(b)
}
}
解释:用于管理侦听器的代码是在基类侦听器管理器中共享的。由于泛型编译时检查,B 只能接收 BListener。射击 B 将自动射击 A。
在我看来,你可以让事情变得非常简单。
我的理解
你有基础课 A 执行一些 基本操作
您可能有更具体的子类 乙 另外还可以执行更多操作 具体操作
如果是这种情况,您需要处理这两个事件( 基本的 对于 A 和 基本的 + 具体的 对于B)
好吧,您不需要重载方法来执行此操作,您唯一需要做的就是为特定事件添加特定处理程序(或侦听器)。
可能该事件是“基本”的,那很好。
但当事件具体时,你需要做出相应的反应。所以,我要做的就是添加一张支票 在 特定的听众来区分 具体的 像这样的事件:
if( whichEvent instanceof SpecificEvent ) {
SpecificEvent s = ( SpecificEvent ) whichEvent;
// Do something specific here...
}
就是这样。
您对问题的描述过于抽象,因此无法提出具体的解决方案。然而,如果很难解释你想要实现的目标,那么你可能需要首先重新分析问题所在。
如果我上面的理解是正确的(你需要处理 基本的 + 具体的 有时)以下冗长的代码可能会有所帮助。
此致
import java.util.*;
class A {
// All the listener will be kept here. No matter if basic or specific.
private List<Listener> listeners = new ArrayList<Listener>();
public void add( Listener listener ) {
listeners.add( listener );
}
public void remove( Listener listener ) {
listeners.remove( listener );
}
// In normal work, this class just perform a basic operation.
public void normalWork(){
performBasicOperation();
}
// Firing is just firing. The creation work and the
// operation should go elsewhere.
public void fireEvent( Event e ) {
for( Listener l : listeners ) {
l.eventHappened( e );
}
}
// A basic operation creates a basic event
public void performBasicOperation() {
Event e = new BasicEvent();
fireEvent( e );
}
}
// Specialized version of A.
// It may perform some basic operation, but also under some special circumstances
// it may perform an specific operation too
class B extends A {
// This is a new functionality added by this class.
// Hence an specifi event is fired.
public void performSpecificOperation() {
Event e = new SpecificEvent();
// No need to fire in different way
// an event is an event and that's it.
fireEvent( e );
}
// If planets are aligned, I will perform
// an specific operation.
public void normalWork(){
if( planetsAreAligned() ) {
performSpecificOperation();
} else {
performBasicOperation();
}
}
private boolean planetsAreAligned() {
//return new Random().nextInt() % 3 == 0;
return true;
}
}
// What's an event? Something from where you can get event info?
interface Event{
public Object getEventInfo();
}
// This is the basic event.
class BasicEvent implements Event{
public Object getEventInfo() {
// Too basic I guess.
return "\"Doh\"";
}
}
// This is an specific event. In this case, an SpecificEvent IS-A BasicEvent.
// So , the event info is the same as its parent. "Doh".
// But, since this is an SpecificEvent, it also has some "Specific" features.
class SpecificEvent extends BasicEvent {
// This method is something more specific.
// There is no need to overload or create
// different interfaces. Just add the new specific stuff
public Object otherMethod() {
return "\"All I can say is , this was an specific event\"";
}
}
// Hey something just happened.
interface Listener {
public void eventHappened( Event whichEvent );
}
// The basic listner gets information
// from the basic event.
class BasicEventListener implements Listener {
public void eventHappened( Event e ) {
System.out.println(this.getClass().getSimpleName() + ": getting basic functionality: " + e.getEventInfo());
}
}
// But the specific listner may handle both.
// basic and specific events.
class SpecificListener extends BasicEventListener {
public void eventHappened( Event whichEvent ) {
// Let the base to his work
super.eventHappened( whichEvent );
// ONLY if the event if of interest to THIS object
// it will perform something extra ( that's why it is specific )
if( whichEvent instanceof SpecificEvent ) {
SpecificEvent s = ( SpecificEvent ) whichEvent;
System.out.println(this.getClass().getSimpleName() + ": aaand getting specific functionality too: " + s.otherMethod() );
// do something specific with s
}
}
}
// See it run.
// Swap from new A() to new B() and see what happens.
class Client {
public static void main( String [] args ) {
A a = new B();
//A a = new A();
a.add( new BasicEventListener() );
a.add( new SpecificListener() );
a.normalWork();
}
}
示例输出:
BasicEventListener: getting basic functionality: "Doh"
SpecificListener: getting basic functionality: "Doh"
SpecificListener: aaand getting specific functionality too: "All I can say is , this was an specific event"
更进一步,您甚至可以摆脱界面以使其更简单
如果
public class BEvent extends AEvent {
...
}
public interface BListener extends AListener {
public void event2(BEvent event);
}
你不能做这样的事情:
public class B extends A {
@Override
public synchronized void addAListener(AListener l) {
if (l instanceof BListener) {
...
} else {
super.addAListener(l);
}
}
...
}
正如我在评论中所说,我不确定您真正想要实现什么?谁从哪里被调用,当它被调用时需要做什么?
根据我们所掌握的关于两者之间关系的少量信息 A
& B
, ,我认为制作起来很混乱 BListener
的子接口 AListener
. 。顾名思义,一个 BListener
应该是听 BEvent
s,分别是 已经 的一个子类 AEvent
s。为了清楚起见,听众应该有明确的目的;它们不应不必要地重叠。此外,不需要这种重叠的侦听器,因为您已经在类中定义了单独的方法 B
处理不同类型的听众。
为了说明我的观点,请考虑这个根据您的代码设计的示例:
public class MovableMouseEvent extends EventObject
public class ClickableMouseEvent extends MovableMouseEvent
public interface MovableMouseListener extends EventListener
// mouseMoved(MovableMouseEvent)
public interface ClickableMouseListener extends MovableMouseListener
// mouseClicked(ClickableMouseEvent)
public class MovableMouseWidget
// {addMovableMouseListener,removeMovableMouseListener}(MovableMouseListener)
// fireMovableMouseEvent(MovableMouseEvent)
public class ClickableMouseWidget extends MovableMouseWidget
// {addClickableMouseListener,removeClickableMouseListener}(ClickableMouseListener)
// fireClickableMouseEvent(ClickableMouseEvent)
这种设计有效,但令人困惑,因为 ClickableMouseListener
处理两种事件,并且 ClickableMouseWidget
正如您正确指出的那样,处理两种听众。现在,考虑以下使用组合而不是继承的替代方案:
public class MouseMoveEvent extends EventObject // note the name change
public class MouseClickEvent extends EventObject // don't extend MouseMoveEvent
public interface MouseMoveListener extends EventListener
// mouseMoved(MouseMoveEvent)
public interface MouseClickListener extends EventListener // don't extend MouseMoveListener
// mouseClicked(MouseClickEvent)
public interface MouseMoveObserver
// {addMouseMoveListener,removeMouseMoveListener}(MouseMoveListener)
// fireMouseMoveEvent(MouseMoveEvent)
public interface MouseClickObserver
// {addMouseClickListener,removeMouseClickListener}(MouseClickListener)
// fireMouseClickEvent(MouseClickEvent)
public class MovableMouseWidget implements MouseMoveObserver
public class ClickableMouseWidget implements MouseMoveObserver, MouseClickObserver