I'm using observer pattern to write a simple group chat program.
Group1: A, B, and C Group2: A and C
A is a sender/subject with two lists, one for Group1 and one for Group2.
Is having two lists in subject class a good way to do this?
Is this violating observer pattern's feature?
A sender can have two groups of observers.
public class Sender {
private List<Receiver> group1 = new ArrayList<Receiver>();
private List<Receiver> group2 = new ArrayList<Receiver>();
private String msg;
private String name;
public Sender(String name) {
this.name = name;
}
public void sendMsg(int group, String msg, JTextArea display) {
this.msg = msg;
String output = name + ": " + msg;
display.append(output + "\n\r");
if(group == 1) {
notifyAllObservers(group1);
} else {
notifyAllObservers(group2);
}
}
public void register(int group, Receiver receiver) {
if(group == 1) {
group1.add(receiver);
} else {
group2.add(receiver);
}
}
public void notifyAllObservers(List<Receiver> group) {
for (Receiver receiver : group) {
receiver.update(msg);
}
}
public String toString() {
return name;
}
}
public class Receiver {
public Sender sender;
private JTextArea display;
public Receiver(int group, Sender sender, JTextArea display) {
this.sender = sender;
this.display = display;
this.sender.register(group, this);
}
public void update(String msg) {
display.append(sender.toString() + ": " + msg + "\n\r");
}
}
Your current design is not necessarily contradicting the Observer pattern, but it is rigid. As of now, every Sender
can be in up to two groups. What if every sender should be in up to 10 groups? 100 groups? To keep this design flexible, I propose to model Group
s as objects aswell. The idea is that every Group
is an Observable
and an Observer
at the same time. Each Group
has a List<Sender> senders
, to which it automatically registers as Observer
. If a Group
receives some Event
from one of the Observable
s it is registered to, it will forward this event to its Observers
. The following code is a rough sketch of my proposal.
public interface Observer {
public void receiveEvent(Observable source, Event event);
}
public interface Observable {
public void addObserver(Observer observer);
public void removeObserver(Observer observer);
public Collection<Observer> getObservers();
default public void notifyAllObservers(Event event) {
for (Observer observer : this.getObservers()) {
observer.receiveEvent(this, event);
}
}
}
public interface Event { }
public abstract class AbstractObservableImpl implements Observable {
private Set<Observer> observers = new HashSet<>();
@Override
public final void addObserver(final Observer observer) {
this.observers.add(observer);
}
@Override
public final void removeObserver(final Observer observer) {
this.observers.remove(observer);
}
@Override
public final Collection<Observer> getObservers() {
return Collections.unmodifiableCollection(this.observers);
}
}
public class Sender extends AbstractObservableImpl { }
public class Group extends AbstractObservableImpl implements Observer {
private List<Sender> senders = new ArrayList<>();
@Override
public final void receiveEvent(final Observable source, final Event event) {
for (Observer observer : this.getObservers()) {
observer.receiveEvent(this, event);
}
}
public final void addSender(Sender sender) {
if (this.senders.contains(sender) == false) {
this.senders.add(sender);
}
this.senders.get(this.senders.indexOf(sender)).addObserver(this);
}
public final void removeSender(Sender sender) {
final int index = this.senders.indexOf(sender));
if (index >= 0) {
this.senders.get(index).removeObserver(this);
}
this.senders.remove(sender);
}
}
Some remarks on the design:
abstract class AbstractObserverImpl
is not essential. I was just too lazy to repeat code and since Sender
and Group
do not inherit from anything else, I let them inherit from AbstractObserverImpl
.AbstractObserverImpl
is abstract
. For me, there would be no point in allowing instantiation of this class since it is missing its actual functionality (the part firing the Event
s).Event
interface. This is arbirtrary aswell. Whether you use Object
s as events, or enums, or an interface, or a class or a different approach is totally up to you. As I said: this is only a rough sketch.null
-safe. There are quite some possibilites to cause NullPointerException
s.