5

我意识到这个问题已被覆盖到死亡,但我仍然挣扎,并可以做一些具体的帮助。松散耦合的观察者模式

我的目标是在某种可观察的(让我们说一个Dog)和某种监听器(让我们说Owner)之间实现一个简单的Observer模式。

最终,所有者将是一个'视图'和一个MVC范式中的'模型'。我正在使用Dog和Owner来尝试并简化这里的事情。我已经尝试使用内建在Observer/Observable类中的Java,但已经意识到Observers update()方法有多糟糕 - 它接收到一个POJO,并且我需要在update()方法中检查/转换POJO。我更愿意让我的'update()'方法获得它可以预期的结果。

于是,我跟着一些教程,包括这一个,它使用狗/业主为例:

http://www.youtube.com/watch?v=qw0zZAte66A

在这里,我已经展示了如何推出自己的观察/观察类。在伪代码中,我现在拥有的是:

Dog/Model { 

    List listeners; 

    public fireDogHungryEvent() { 

     foreach listener { 
      listener.onDogHungry(this); 
     } 
    } 

    public fireDogPeeEvent() { 

     foreach listener { 
      listener.onDogNeedsToPee(this); 
     } 
    } 

    public getHungerLevel() { return hungerLevel; } 
    public getBladderCapacity() { return bladderCapacity; } 
} 

Owner/View { 

    public onDogHungry(model) { 
     println(model.getHungerLevel()); 
    } 

    public onDogNeedsToPee(model) { 
     println(model.getBladderCapacity()); 
    } 
} 

因此,现在不是一个update()方法,而是具有处理特定事件的方法。辉煌。我目前很满意Owner/view类。它知道狗/模型的方法,这很好(我认为)。

我不喜欢的是狗/模型引用了所有者/视图中的方法。我读了无数次,完全同意模型不应该与它的观点紧密结合,比如它似乎在上面。我也不热衷于狗/模型中的所有'火'方法,它们几乎都是一样的;循环所有它的监听器,并在每个监听器上调用不同的方法。

是否有可能进一步解耦这种关系,并且没有Dog/model调用特定的方法?如果是这样,将狗/模型数据接收到所有者/视图并适当使用它的最佳方式是什么?

感谢

+0

“Dog”的哪些方法引用了视图?我没有看到任何。 – SJuan76 2013-05-11 14:53:19

+0

最好的方法是使用视频教程建议的界面。监听器是您实现中的接口吗? – 2013-05-11 14:56:27

+0

SJuan76 - 我正在谈论的引用是在Dog'fire'方法中调用listener.onDogHungry和listener.onDogNeedsToPee。 – whoshotdk 2013-05-11 15:31:23

回答

3

你应该从两个Observerinterface远离具体实施的知识和Observable

public enum EventType { 

    HUNGRY, 
    PEE; 
} 

public interface DogEvent { 

    EventType getType(); 
} 

public interface DogListener { 

    void fireEvent(DogEvent event); 
} 

public class Dog { 

    private final Set<DogListener> listeners = new CopyOnWriteArraySet<DogListener>(); 

    public void register(final DogListener dogListener) { 
     listeners.add(dogListener); 
    } 

    public void unregister(final DogListener dogListener) { 
     listeners.remove(dogListener); 
    } 

    public void firePeeEvent() { 
     fireEvent(new DogEvent() { 
      @Override 
      public EventType getType() { 
       return EventType.PEE; 
      } 
     }); 
    } 

    public void fireHungryEvent() { 
     fireEvent(new DogEvent() { 
      @Override 
      public EventType getType() { 
       return EventType.HUNGRY; 
      } 
     }); 
    } 

    private void fireEvent(final DogEvent dogEvent) { 
     for (final DogListener listener : listeners) { 
      listener.fireEvent(dogEvent); 
     } 
    } 
} 

public class Owner implements DogListener { 

    @Override 
    public void fireEvent(DogEvent event) { 
     switch (event.getType()) { 
      case PEE: 
       System.out.println("Someone take the dog out"); 
       break; 
      case HUNGRY: 
       System.out.println("I can't believe the dog is hungry _again_!"); 
       break; 
     } 
    } 
} 

在这种情况下,Dog不知道该Owner它只是知道,OwnerDogListener的实现。

另一方面Owner不知道Dog它只知道它有一个传入DogEvent

+0

我喜欢这个答案,因为它的清晰度,谢谢鲍里斯。我不特别*喜欢那里需要检查发生什么事件的案例陈述,但我想它不会发生在魔法之中:D我会给这个观察者/听众几个看一看,看看我如何继续。今天我会在某个时候接受这个答案,如果我能够与它合理地合作一些事情的话! – whoshotdk 2013-05-11 15:39:20

+0

啊,我刚刚意识到这是一个更好的执行我以前尝试过的东西;除了我使用反射来调用基于字符串名称而不是事件对象的方法。我认为这是行得通的:D – whoshotdk 2013-05-11 15:41:23

+0

'enum'只是一个建议 - 你可以使用访问者模式来充分利用多态性。 – 2013-05-11 15:41:36

1

对于MVC的目的(特别是对MVP)我已经收集了事件基地规划好经验。所以你需要一个EventBus,它将被Dog和Owner使用。业主将订阅HungerLevelIncrease等特定的活动类别,并且该狗将触发此类活动。对于你的狗主人的例子,这将有点奇怪,因为所有者不知道他的狗,但对于GUI编程,这是一个很好的和简单的解决方案,特别是在其他控制器之间解耦。

您可以轻松地创建自己的巴士,也可以使用google guava中的巴士。

这是我知道创建一个非常松散耦合的最好的方式。

+0

这对我来说最初看起来是个好主意,但经过一番阅读后,其他人已经注意到,这种模式基本上使每个可观察/聆听者都需要知道一条总线。增加新的事件类型也意味着改变那辆公共汽车。请参阅:http://stackoverflow.com/questions/3987391/why-people-use-message-event-buses-in-their-code。也许这不像看起来那么糟糕,idk。 – whoshotdk 2013-05-11 15:37:02

+1

@ user2373021你有没有看过像[mbassador](https://github.com/bennidi/embassador)这样的东西 - 它是基于注释的,所以你需要的只是注释你的听众,并且它会推断他们想要从方法参数。它也可以异步发送消息... – 2013-05-11 15:43:34

+0

@ user2373021它确实需要将总线添加到所有需要他的类。然而,大多数情况下,无论如何,您将添加一个依赖注入容器到更大的应用程序,这可以为您注入eventBus。但是添加新的事件类型不应该影响你的公交车。如果你只有几个班级,我也会选择简单的听众解决方案,但是如果你获得更多的对话并且有多个对话与其他班级(例如服务器)进行对话。我会尝试使用EventBus机制。 – mszalbach 2013-05-11 16:06:30