Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Observer

1 view
Skip to first unread message

Helge Duif

unread,
Nov 5, 2005, 10:02:27 AM11/5/05
to
Hej, er der nogen her, der er god til at skære ting ud i pap...??

Jeg skal lave en opgave i C#, windowsapplication, hvor jeg skal slå to
terninger x antal gange og så lave statistik over antal seksere, to ens osv.
Terningens værdi skal vises grafisk. Alt det har jeg fundet ud af, men nu er
opgaven blevet udvidet til, at vi skal bruge Observer i løsningen, og det
kan jeg ikke rigtig finde ud af....

Lige nu virker det sådan, at der ved tryk på en Button afvikles følgende:

Tilfældig tal mellem 1 og 6 findes (køres to gange, da der skal slå's to
terninger)
Der sættes nogle tællere til statistikken
Jeg laver en Switch til den første terning, for at vise det grafiske
billede, som hører til den værdi, der er slået
Jeg laver det samme til terning 2
Det kører i løkke det antal gange som der skal slås terninger

Så vidt jeg har forstået bryder Observer den direkte afhængighed, men jeg
kan ikke rigtig finde ud af, hvordan jeg får den bygget ind i mit
program...., jeg har en eller anden ide om at jeg gerne vil have den
grafiske fremstilling af terningerne gjort indirekte afhængig.

Er der nogen, der kan give mig et hint eller to (ikke en komplet løsning,
jeg skulle jo gerne selv løse opgaven)

Jeg bruger Visual Studio Express Beta2

mvh. Helge


Bjarke Lindberg

unread,
Nov 5, 2005, 2:13:39 PM11/5/05
to
Helge Duif wrote:

> Hej, er der nogen her, der er god til at skære ting ud i pap...??

Nah - men man kan vel forsøge? :)

> Så vidt jeg har forstået bryder Observer den direkte afhængighed, men jeg
> kan ikke rigtig finde ud af, hvordan jeg får den bygget ind i mit
> program...., jeg har en eller anden ide om at jeg gerne vil have den
> grafiske fremstilling af terningerne gjort indirekte afhængig.

Observermønstret har et subjekt og 1 - n observers. I dit tilfælde kunne
dit subjekt være en klasse som representerer en terning (den har en
værdi og den kan kastes) og din observer kunne være en GUI label, som
viser terningens aktuelle værdi - eller en "tegning" af en terning, som
viser den grafisk.

Et subjekt vil som regel skulle implementere tre metoder for at være
"observerbart":

public interface ISubject {
//Tilføjer en klasse til listen af observers.
void Attach(IObserver observer);
//Fjerner en klasse fra listen af observers.
void Detach(IObserver observer);
//Notificerer alle observere om at vores subjekt er ændret.
void Notify()
}

En observer vil som regel implementere en enkelt metode:

public interface IObserver {
//Kaldes fra vores subjekt.
void Update();
}

Din klasse terning vil dermed kun kende til interfacet IObserver og ikke
behøve at vide noget om, hvordan din aktuelle observer har tænkt sig at
vise sig selv. F.eks:

public class Dice : ISubject {
IList observers;

[...]

public void Attach(IObserver observer) {
observers.Add(observer);
}

public void Detach(IObserver observer) {
observers.Remove(observer);
}

public void Notify() {
foreach (IObserver o in observers) {
o.Update();
}
}

int value = 1;
public int Value {
get {return value;}
}

//Slår et slag med terningen.
public void Roll() {
value = GetRandomNumberBetweenOneAndSix();
//Notificér evt. observers om, at vores subjekt (værdien) er ændret.
Notify();
}
}

Din klasse DiceControl, som viser selve terningen behøver altså bare
"tegne sig selv", når den får en besked fra sit subjekt om, at den er
ændret:

public class DiceLabel : System.Windows.Forms.Label, IObserver {

private Dice d;

public void DiceControl(Dice d) {
//Vi fortæller lige vores "model/subjekt", at vi gerne vil
//underrettes når den har slået et slag.
this.dice = d;
d.Attach(this);
}

//Implementerer IObserver.
//Bliver kaldt af Dice når den har slået et slag.
public void Update() {
if (!InvokeRequired) {
this.Text = d.Value;
} else {
Invoke(new UpdateDelegate(Update));
}
}
}

Okay - så langt så godt. Du bad jo specifikt om ikke at få løsningen. Og
ovennævnte vil heller ikke være løsningen hvis du implementerer det i C#
(eller et andet CLI compliant sprog).

Observermønstret er nok et af de mest brugte mønstre - og Microsoft har
faktisk været ganske flinke og har gjort det virkeligt nemt (og elegant)
at bruge det i .NET. De har nemlig sørget for, at vi ikke skal til at
implementere diverse interfaces (IObserver eller ISubject) og slet ikke
skal holde styr på hvilke observers der abonnerer på vores subjekt, men
bare kan deklarere et event i stedet.

Altså vil du i din Dice klasse kunne deklarere et event:

public event EventHandler DiceRolled;


Og i din GUIklasse kan du abbonnere på dette event:

//Tilføjer en observer til listen af observers på
//dice instancen.
dice.DiceRolled += new EventHandler(diceRolled);

[..]
private void diceRolled(object sender, EventArgs e) {
Dice d = sender as Dice;
if (d != null) {
//update label.
}
}


Så din button_Click metode som du beskrev i dit oprindelige indlæg kunne
altså se nogenlunde sådan her ud:

foreach (Dice d in minListeOverDiceInstancer) {
d.Roll();
}

Hver Dice instance har så en (eller flere) observers, som abonnerer på
DiceRolled eventet og dermed opdaterer sig selv.

Var det for langhåret eller hur?


/B.rolling :)

Helge Duif

unread,
Nov 6, 2005, 6:13:23 AM11/6/05
to
Hej Bjarke, tak for det udførlige svar..., men det kan godt være at det er
noget af det grundlæggende i Visual Studio jeg ikke helt har fat i...:

når jeg forsøger at implementere den del, som du har skrevet som:

dice.DiceRolled += new EventHandler(diceRolled); //jeg har kaldt den
noget andet

starter der en hel masse fejlmeddelelser, uanset om jeg forsøger at placere
den i Main'en eller i Form Load'en

Så det kan godt være, at det er selve hirakiet i Visual Studio, som jeg ikke
har fået helt fat i....

mvh. Helge


Bjarke Lindberg

unread,
Nov 6, 2005, 11:49:54 AM11/6/05
to
Helge Duif wrote:
>
> Hej Bjarke, tak for det udførlige svar..., men det kan godt være at det er
> noget af det grundlæggende i Visual Studio jeg ikke helt har fat i...:

Hej Helge.

Prøv at se bort fra Visual Studio. Det er reelt bare en editor. Det er
C# det handler om lige nu. Det var min intention at lægge op til, at du
kiggede lidt nærmere på events i C#.

> dice.DiceRolled += new EventHandler(diceRolled); //jeg har kaldt den
> noget andet

Det her var bare et eksempel på en deklaration af et event.

> starter der en hel masse fejlmeddelelser, uanset om jeg forsøger at placere
> den i Main'en eller i Form Load'en

He - bliver nødt til at se fejlmeddelelserne før man har en anelse om,
hvad fejlen går ud på :)

Prøv at kigge på denne basis-implementation og se om du måske hitter
ideen i det. (ej kompileret)

public class Dice {

public event EventHandler DiceRolled;

private object onDiceRolledLock = new object();
protected virtual void OnDiceRolled(EventArgs e) {
lock (onDiceRolledLock) { //Lås tilgang til listen af events.
if (DiceRolled != null) { //Check om nogen har abonneret på
//denne event.
DiceRolled(this, e); // Fyr den af.
}
}
}

private int value = 1;


public int Value {
get {return value;}
}

public void RollDice() {
Random r = new Random();
value = r.NextValue(6) + 1;
OnDiceRolled(EventArgs.Empty); //Rejs eventen.
}
}

Ovenstående er vores subjekt.


Nu kommer vi til en simpel observer:

public class DiceLabel : Label {

//Terningen vi skal observere.
private Dice dice;
public void Dice Dice {
get {return dice;}
set {
if (dice != null && dice != value) {
//vi detacher fra den gamle dice.
dice.DiceRolled -= new EventHandler(diceRolled);
}
dice = value;
if (dice != null) {
//Her tilføjer vi os til listen af observere på
//DiceRolled eventen på den nye dice.
dice.DiceRolled += new EventHandler(diceRolled);
}
}
}

private void diceRolled(object sender, EventArgs e) {

if (!InvokeRequired) {
//Opdaterer vores Text til at vise værdien
//af vores subjekt.
this.Text = dice.Value.ToString();
} else {
Invoke(new EventHandler(diceRolled));
}
}
}

Nu har vi et subjekt og en observer. I din hovedform tilføjer du altså
så dine observers. A'la så'n her:

[...]

public Form1() {
/bla bla InitializeComponent og hva' VS nu har genereret.
Dice dice1 = new Dice();
Dice dice2 = new Dice();

DiceLabel label1 = new DiceLabel();
label1.Dice = dice1;
DiceLabel label2 = new DiceLabel();
label2.Dice = dice2;

this.components.Add(label1);
this.components.Add(label2);

}

private void button1_Click(object sender, EventArgs e) {
dice1.RollDice();
dice2.RollDice();
[...]
}

Håber du fandt lyset..


/B.Yatzy!


0 new messages