Design Patterns - Observer
Side note: the change of style drift patterns by the fact that Netbeans works very badly with KDE 4.1 I am trying in recent days. These diagrams are drawn with Dia. We know that in summer the heat makes it difficult to sleep as soon as laid la testa sul cuscino. Skep è perseguitato inoltre dall'idea di migliorare il RTS che sta progettando, in ogni modo possibile.
L'idea di inserire missioni con un protagonista (che chiaramente devo essere protetto per sopravvivere fino a fine missione), ha spinto Skep a progettare un sistema per il quale dei soldati specializzati nella cura, appena il protagonista inizia a perdere troppa energia, lasciano i loro compiti per andare a curarlo. La prima implementazione del sistema prevede un solo medico durante la missione, e lo schema delle classi può essere così semplicemente descritto
Sorvoliamo il fatto che chiaramente ManToSave avrebbe dovuto implementare un qualche tipo di interfaccia che definisse a more general type of unit. For now we assume that ManToSave is the only unit to check with healers. The code to represent this pattern was similar to that
classManToSave
{public static final int MAX_LIFE = 100;
private Healer healer;
private int life;
public int getLife () {return
life;
} public void setLife (int l) {
life = l;}
hurt public void (int damage) {
life -= damage;
if (life \u0026lt;20) {
healer.heal (this )
}} {Healer class
Public void
heal (ManToSave mts) {
mts.setLife (ManToSave.MAX_LIFE)
}}
Whenever ManToSave takes a hit, communicate all'healer that if the health has reached a level critic, he begins to treat him. The idea could work, but erred in Skep: This is a planning to implementation, not suitable for the reuse of code for further development. Suppose Skep, a great writer, who decides when life comes at a critical point ManToSave the morale of enemy troops could get up allowing them to be more precise or more powerful. Suppose that in fact the death of ManToSave is provided by the plot and therefore, poco prima della morte, sia necessario far accadere qualcosa di particolare, magari un salvataggio in extermis.
Per fare ciò introduciamo due nuovi oggetti, Enemy e MissionNode, che sono anch'essi dipendenti dalla vita del ManToSave per compiere un qualche tipo di azione. Vediamo come cambia lo schema della classi ora
Il codice del metodo hurt di ManToSave dovrebbe essere cambiato in questo modo
....
if(life < 30) {
healer.update(this);
enemy.update(life);
missionNode.update(life);
}
...
Questo tipo di architettura rappresenta una classica relazione di uno-a-molti, exactly where various objects are dependent in their behavior by an entity and its characteristics.
As you can see, there are various problems in this implementation:
1. Poor maintainability
: whenever I add an object that depends on the state of ManToSave ManToSave I change the code, which turns out to be costly and mostly illogical. In addition, changes to sensitive data fields ManToSave are costly in terms of changing the parameters of the update methods.
2.
little possibility of code reuse : ManToSave is closely related to its implementation, a shift in another context, perhaps another mission, would result in the almost complete rewrite.
3. Statism
: this implementation is strictly static, nothing can be decided at runtime. If a game can also be acceptable, say ManToSave is a central server with data and various healers, and enemy instead missionNode are clients that need to be updated every time something important happens to the server. It 'impossible to predict in advance how many clients will connect and also limit the number of clients to a single class of objects would be simplistic to say the least.
To resolve these and other problems will take care of a nice design pattern, or Observer. Let's first formal definition of the pattern:
The Observer pattern defines a relazione uno-a-molti tra oggetti in modo che quando un oggetto cambia stato, tutti gli oggetti che dipendono da esso siano notificati e aggiornati automaticamente
.
Gamma, Helm, Johnson, Vlissides, Design Patters Vediamo ora il diagramma delle classi tipico di questo design pattern
Ecco le considerazioni da fare sullo schema presentato:
. Subject è una classe astratta che deve essere supertipo di ogni oggetto che voglia accettare degli osservatori del suo stato. Definisce 3 metodi che servono a registrare gli osservetori, rimuoverli e aggiornarli. La scelta fra interfaccia e classe astratta è poco influente in questo caso, usare una classe astratta permette can not define a one-time registration methods, which remain unchanged in subclasses.
. All potential observers have inherited from the Observer, which requires the implementation of an update method. Through this method, called by the Subject parameter giving himself, the observer is updated on the status of the subject and can act accordingly. E 'update then the only way that change from classroom to classroom.
. The rest of the work is done by a proper use of polymorphism, which allows us to treat classes as if they were RealSubject and RealObserver Subject and Observer.
The Observer pattern implements a very important relationship in object-oriented programming, or the annual report
Loose coupling (weak coupling), which means that two classes are related among them statically knowing almost nothing of each other, even the existence, in most cases. The only thing I know is the subject of the observer that implements the Observer interface, and what's more than enough. The minimization of the interdependence between classes through this type of report is the first step towards a perfect
maintainability of the code.
One other big advantage is that the subject
changes do not require changes to the observers as a parameter of the update have not passed the data fields of realsubject but the object directly: realObserver will then decide how to handle the incoming data.
Last but not least, the system just described is absolutely
dynamic in the sense that every time we add to realObserver realSubject not preclude the operation, since its implementation, as mentioned, is completely independent from realObserver .
Skep has discovered a very important tool, we see how to put it into practice
Let's see how Skep decide to implement the code in the form of its design
/
* * Subject: I prefer an abstract class just because an interface so I can define methods *
one time, without needing to overload in subclasses. In fact in a java interface would be much more
* indicated, since it allows freedom of inheritance
* / abstract class Subject {
LinkedList \u0026lt;Observer> Observers;
protected Subject () {
Observers = new LinkedList \u0026lt; Observers> ();}
public void attachObs (Observer obs) {
observers.add (obs);}
public void detachObs (Observer obs) {
observers.remove (obs);}
public void notify() {
Iterator<Observer> i = observers.iterator();
while(i.hasNext()) {
i.next().update(this);
}
}
}
/*
* Observer
*/
interface Observer {
public void update(Subject);
}
/*
* Implementazione dell'uomo da salvare
*/
class ManToSave extends Subject {
public static final int MAX_LIFE = 100;
int life;
ManToSave() {
life = MAX_LIFE;
}
public void hurt(int damage) {
this.life -= damage;
if (life \u0026lt;30)
/ / notify observers
notifyAll ();}
public getLife () {return
this.life;}
} / * * Example
observer
* / Enemy
class implements Observer {private int
moral
{
moral = 50;}
public void update (Subject s) {
ManToSave mts = (ManToSave) s;
if (mts.getLife () \u0026lt;20)
moral = 100;}
}
There is potential for an Observer to observe more than one object
thanks a device in the implementation: moving the update method of the Observer a type of marine biotoxins Subject, thanks to the polymorphism can do a downcast to obtain the dynamic type of Subject.
One problem to consider is this: who starts
notifications?
. In many cases
Observers may be that the change in some way the state of the Subject, they launched a notification to all other peers . It 'important that the Observer calls the Notify method to perfection and above all remember to do it every time you change the status of realSubect.
. Another possibility is that the call to notify
is carried out esclusivamente da metodi del realSubject , che ne cambiano lo stato (cosa assolutamente legittima da pensare se si è progettata la classe realSubject con una ottima incapsulazione e information hiding). In quest'ultimo modo i client sono esenti da possibili errori, ma potrebbe capitare che le modifiche siano talmente granulari che il numero di chiamate notify diventi oneroso in termini di prestazioni.
Scegliere tra uno di questi due metodi è una scelta importante che può costare caro in termini di scalabilità e dipende da come si pensa che possa essere esteso.