Search code examples
javapackagenaming

How to implement an Observer-pattern with two packages


A friend and I have a discussion of how to implement an observer pattern with two packages.

Here is a fictional example: Package "Gaming" contains the class "Battlefield". Package "Players" contains "IPlayer" (and various implementations of this interface).

Now we want the battlefield to observe the player. For this reason we create an interface "IPlayerObserver" and "IOberservablePlayer". IPlayer inherits IOberservablePlayer.

In which package should we put "IPlayerObserver"?

// Package: Players
public interface IPlayer extends IOberservablePlayer {}

// Package: Gaming
public class Battlefield implements IPlayerObserver {}

// Which package?
public interface IPlayerObserver {
     void notify(IPlayer player);
}

public interface IOberservablePlayer {
     void addObserver(IPlayerObserver observer);
}

Thanks for all answers in advantage.


Solution

  •     core
        ^  ^
        |  |
        | player
        |    ^
        |    |
        gaming
    
    • Keep wholly Player-focused items with player
    • Don't call a spade a spade. IEverything pattern harks back to Hungarian notation and simply isn't needed these days. If your interface is the primary thing code will reference then give it the cleanest name.
    • You can probably genericise the Observable interface as I have done below and put it in the common "core" package
    package com.yourname.player;
    public interface Player extends Oberservable<PlayerObserver>
    
    package com.yourname.gaming;
    public class Battlefield implements PlayerObserver 
    
    package com.yourname.player;
    public interface PlayerObserver {
         void notify(Player player);
    }
    
    package com.yourname.core;
    public interface Oberservable<T> {
         void addObserver(T observer);
    }
    

    Further, it is slightly cleaner not to implement such listeners directly on other important classes as it exposes methods like Battlefield.notify(Player) to everyone when only Player is meant to call it.

    Try doing this inside Battlefield:

    player.addObserver(this::playerNotification)
    ...
    private void playerNotification(Player p) {}