我有一个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
方法。我应该使用私有关键字隐藏addAListener吗? [更新:无法隐藏私人关键字] fireAListenerEvent1
和fireBListenerEvent1
方法类似的问题。我正在使用Java 1.5版。
答案 0 :(得分:12)
我没有看到BListener
应该延伸AListener
的原因。
您是否真的想强迫所有对B
事件感兴趣的人也实施event1()
?
此外,您无法添加addAListener()
,因为派生类无法降低父类中存在的方法的可见性。此外,你不应该,或者你会违反Liskov substitution principle(每个B必须能够做A所能做的一切)。
作为最后一句话,我会使fire*()
方法受到保护。通常没有理由让它们公开,减少公共成员的数量可以保持公共界面的清洁。
答案 1 :(得分:6)
不要使用遗产,这不是你想要的,会导致脆弱和难以改变的设计。组合是一种更灵活,更好的设计方法。始终尝试尽可能精细地设计接口,因为它们不应该被更改为事件。它们是您与系统其余部分的合同。如果需要添加新功能,则第一个选项是向事件添加更多信息。如果这不合适,那么您应该设计一个新的界面来提供该事件。这可以防止必须更改任何不受影响的现有代码。
这是我最喜欢的模式,我相信它通常被称为观察者。
创建一个新接口,定义该事件类型的方法(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);
}
}
希望这说明了类型安全(重要),灵活性和代码重用之间的良好平衡。
答案 2 :(得分:3)
我从你对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)
}
}
说明:在基类Listener Manager中共享用于管理侦听器的代码。 由于泛型编译时检查,B只能接收BListener。 点火B将自动触发A.
答案 3 :(得分:3)
在我看来,你可以保持简单。
我的描述
您有一个基本类 A 执行一些 basicOperation
您可能有一个更具体的子类 B ,此外还可以执行更多 specificOperations
如果是这种情况,则需要处理这两个事件(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"
进一步说,你甚至可以摆脱接口以保持简单
答案 4 :(得分:1)
如果
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);
}
}
...
}
正如我在评论中所说,我不确定你到底想要达到什么目的?从哪里召唤谁,在被召唤时需要做什么?
答案 5 :(得分:1)
根据我们对A
&amp; B
,我认为让BListener
成为AListener
的子接口令人困惑。顾名思义,BListener
用于监听BEvent
,它们已经是AEvent
的子类。为清楚起见,听众应具有辨别力;他们不应该不必要地重叠。此外,不需要这样重叠的侦听器,因为您已经在类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