Ontwerpbeslissing - Commando meegeven aan knop

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • Alain
  • Registratie: Oktober 2002
  • Niet online
Stel, je hebt vier knoppen. De eerste zet de wasmachine 1 aan, de tweede zet wasmachine 2 aan, de derde zet de magnetron op defrost aan, de vierde zet de magnetron op heat aan. Logischerwijs veronderstel ik dat je dan 3 classes maakt, namelijk Knop, Wasmachine en Magnetron.

Stel:

Java:
1
2
3
4
5
6
7
8
9
10
11
12
class Knop {
  void clicked() {}
}

class Wasmachine {
  void start() {}
}

class Magnetron {
  void defrost() {}
  void heat() {}
}


Hoe koppel ik nu een functie aan de knop?

Optie's:

1:

Je geeft een abstracte Command class met een execute methode mee aan de knop. Maar in dit geval leg je het probleem bij de class die de button aanmaakt. Die heeft (misschien) niks te maken met de apparaten die er zijn.

2:

Er is een Command Controller class die alle commando's van een systeem doorstuurt naar het betreffende apparaat (mits aanwezig). Dit zal bij een uitgebreid systeem een erg grote class opleveren, maar hierdoor hoeft een knop (of de bovenliggende laag) geen weet te hebben van de apparaten in het systeem.

Ik neig naar de tweede optie, ook omdat je hieraan makkelijk een check kunt toevoegen wie welke functie's uit mag voeren.

Hoe lossen jullie dit op?

You don't have to be crazy to do this job, but it helps ....


Acties:
  • 0 Henk 'm!

  • NMe
  • Registratie: Februari 2004
  • Laatst online: 01-05 10:36

NMe

Quia Ego Sic Dico.

Met een parent class Apparaat die een method heeeft voor zetAan en een voor zetUit (of gewoon zetAanUit, wat jij wil). Vervolgens Wasmachine en Magnetron beiden afleiden van Apparaat.

Je "maar" bij optie 1 is trouwens niet relevant want een Wasmachine zou in dat geval zijn eigen Knop moeten bijhouden en daar een public facing method voor aanbieden, IMO. De steeds alle onderdelen van de Wasmachine apart moeten initialiseren terwijl dat prima in de Wasmachine zelf kan zou jammer zijn. ;)

Optie 2 kan een optie zijn wanneer je vele verschillende, complexe commando's moet kunnen sturen terwijl de klasses zelf niet echt wetenschap hebben van wat die commando's zijn. Bijvoorbeeld als je een generieke Wasmachine hebt die wel functies heeft voor alle features maar die niet weet welk signaal die feature door moet sturen naar diens hardware omdat dat per merk verschillend is. Maar in het voorbeeld dat je hier beschrijft lijkt dat me mateloos overgecompliceerd. Dus tenzij je een heel wat complexere use case hebt dan hier omschreven zou ik gewoon voor die parent class gaan. ;)

'E's fighting in there!' he stuttered, grabbing the captain's arm.
'All by himself?' said the captain.
'No, with everyone!' shouted Nobby, hopping from one foot to the other.


Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Nu online
Meestal is het zo dat de clicked methode een (lijst van) listeners afgaat. Als het past zou je deze direct aan de apparaatactie kunnen koppelen, maar waarschijnlijk moet er een mediator tussen.
Hoe groot die wordt is erg afhankelijk van hoe die mediator zijn routing logica implementeert.

[edit]
De magnetron kan defrosten en heaten : hoe vang je dat in zetAanUit ?

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Laatst online: 00:10

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

Soms maak je gewoon een draad aan een knop en knoop je de andere kant aan de gloeilamp / wasmachine / lanceerknop...
En als je écht fancy wil doen gebruik je een delegate / eventlistener / actionlistener (of whatever the hell ze 't in Java noemen :P ).

[ Voor 38% gewijzigd door RobIII op 02-09-2015 23:45 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Acties:
  • 0 Henk 'm!

  • NMe
  • Registratie: Februari 2004
  • Laatst online: 01-05 10:36

NMe

Quia Ego Sic Dico.

farlane schreef op woensdag 02 september 2015 @ 23:33:
[edit]
De magnetron kan defrosten en heaten : hoe vang je dat in zetAanUit ?
Niet, maar dat is een feature die uniek is voor een magnetron en dus in de class zelf zou kunnen. Listeners kan natuurlijk ook, overigens, maar dat leek me voor het gestelde voorbeeld veel te complex.

Oh, wacht. Ik heb de vraag verkeerd gelezen, die knoppen zijn allemaal extern. Dan zijn mijn oplossingen redelijk pointless en zou ik inderdaad naar listeners kijken. :)

[ Voor 18% gewijzigd door NMe op 02-09-2015 23:44 ]

'E's fighting in there!' he stuttered, grabbing the captain's arm.
'All by himself?' said the captain.
'No, with everyone!' shouted Nobby, hopping from one foot to the other.


  • Alain
  • Registratie: Oktober 2002
  • Niet online
NMe schreef op woensdag 02 september 2015 @ 23:30:
Je "maar" bij optie 1 is trouwens niet relevant want een Wasmachine zou in dat geval zijn eigen Knop moeten bijhouden en daar een public facing method voor aanbieden,
Bedankt voor je reactie, dit zet me aan het denken.

Een wasmachine krijgt alleen het signaal dat deze moet starten. Hoe de knop eruit ziet, waar deze zit en hoeveel knoppen er zijn is geen verantwoordelijkheid van de wasmachine IMO.
Dus tenzij je een heel wat complexere use case hebt dan hier omschreven zou ik gewoon voor die parent class gaan. ;)
De use case is redelijk complex, maar jullie hebben er niets aan om de volledige use case te kennen. Met meerdere instantie's van dezelfde class en meerdere methodes per class leg ik het probleem bloot. :)
farlane schreef op woensdag 02 september 2015 @ 23:33:
Meestal is het zo dat de clicked methode een (lijst van) listeners afgaat.
Ga ik naar kijken. :)
RobIII schreef op woensdag 02 september 2015 @ 23:40:
Soms maak je gewoon een draad aan een knop en knoop je de andere kant aan de gloeilamp / wasmachine / lanceerknop...
En als je écht fancy wil doen gebruik je een delegate / eventlistener / actionlistener (of whatever the hell ze 't in Java noemen :P ).
Bedankt voor je reactie ... 8)7 (Zeg dan niks)
NMe schreef op woensdag 02 september 2015 @ 23:42:
Oh, wacht. Ik heb de vraag verkeerd gelezen, die knoppen zijn allemaal extern. Dan zijn mijn oplossingen redelijk pointless en zou ik inderdaad naar listeners kijken. :)
Ok, ga ik doen. :)

You don't have to be crazy to do this job, but it helps ....


  • Alain
  • Registratie: Oktober 2002
  • Niet online
Als ik het goed begrijp wordt het zoiets:

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Button implements MouseListener {

    @Override
    public void mouseClicked(MouseEvent e) { 
        // Notify all listeners
    }

    // Other events
}

class ButtonPanelListener implements ActionListener {

    @Override
    public void actionPerformed(ActionEvent e) {
        if (e.getActionCommand().equals("defrost magnetron")) {
            magnetron.defrost();
        }
        // etc.
    }
}


Kan dit kloppen?

You don't have to be crazy to do this job, but it helps ....


Acties:
  • 0 Henk 'm!

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 14-04 17:27
Knuffelbeer schreef op woensdag 02 september 2015 @ 23:16:
Stel, je hebt vier knoppen. De eerste zet de wasmachine 1 aan, de tweede zet wasmachine 2 aan, de derde zet de magnetron op defrost aan, de vierde zet de magnetron op heat aan. Logischerwijs veronderstel ik dat je dan 3 classes maakt, namelijk Knop, Wasmachine en Magnetron.
...
Je geeft een abstracte Command class met een execute methode mee aan de knop. Maar in dit geval leg je het probleem bij de class die de button aanmaakt. Die heeft (misschien) niks te maken met de apparaten die er zijn.
Waarom maakt iemand een Knop aan voor een Magnetron als die iemand niets afweet van Magnetrons? Je hebt een gezond wantrouwen hier, maar je kijkt in de verkeerde richting.

Je applicatie suggereert een soort centrale keuken-controller UI. In zo'n UI wil je een model hebben voor alle soorten apparaten. Dit model is niet de magnetron zelf, maar een lokale representatie ervan. Dit model weet de details van jouw specifieke magnetron. En als je een grote keuken hebt, dan heb je misschien twee instances.

Bij dit model kan een view horen. Die view bestaat als die nodig is. Als je bezig bent met de afwasmachine is het heel goed mogelijk dat de magnetron view niet bestaat. Deze is conceptueel met het model verbonden, en in deze view leven de Magnetron Knoppen. De view maakt die knoppen dus aan. Het is denkbaar dat je 1 view class hebt voor een hele serie magnetron-modellen die genoeg op elkaar lijken. Dan kan het zo zijn dat je dynamich checkt of Magnetron.HasGrill( ) voordat je een GrillKnop aanmaakt.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


Acties:
  • 0 Henk 'm!

  • Alain
  • Registratie: Oktober 2002
  • Niet online
Ik denk dat ik je reactie begrijp. Je redeneert dat een model (zoals apparaat) 1 of meerdere views heeft (MagnetronView, WasmachineView etc.). Dit is inderdaad een heel andere richting als de mijne. Ik moet dit nog even op me in laten werken. :)

Ik zal even wat dieper inzoomen op de use case.

Ik bouw schermen op met grafische componenten die signalen kunnen sturen naar verschillende componenten (intern en extern). Het afvangen van signalen vanuit de grafische omgeving gaat goed. Nu nog het doorvertalen naar het betreffende component. Dit concept ga ik naar aanleiding van de reactie van MSalters nog herzien.

Ik heb de volgende classes:

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
public class MainWindow extends JFrame {
  ...
  this.setContentPane(new Panel());
  ...
}

public class Panel extends Template {
  ...
  this.addComponent(new AButton(0, 0 100, 100);
  ...
}

public abstract class Template extends JPanel implements MouseListener {
  private ArrayList<AComponent> componentList = new ArrayList<>();
  private ArrayList<AComponent> pressedComponentList
  ...
  this.addMouseListener(this);
  ...

  public void addComponent(AComponent c) {
    if (c != null) {
      this.componentList.add(c);
    }
  }

  ...

  @Override
  public void paint(Graphics g) {
    super.paint(g);
    ...
    for (int i = 0; i < this.componentList.size(); i++) {
      this.componentList.get(i).paint(g);
    }
  }

  ...

  @Override
  public void mousePressed(MouseEvent e) {
    pressedComponentList = new ArrayList<>();
    for (int i = 0; i < this.componentList.size(); i++) {
      if (
        componentList.get(i).getX() <= e.getX() && 
        componentList.get(i).getX() + componentList.get(i).getWidth() >= e.getX() &&
        componentList.get(i).getY() <= e.getY() &&
        componentList.get(i).getY() + componentList.get(i).getHeight() >= e.getY()) {       
      pressedComponentList.add(componentList.get(i));
      componentList.get(i).mousePressed();
      }
    }
  }

  @Override
  public void mouseReleased(MouseEvent e) {
    for (int i = 0; i < this.pressedComponentList.size(); i++) {
      pressedComponentList.get(i).mouseReleased();
    }
  }
}

public abstract class AComponent {
  protected int x;
  protected int y;
  protected int width;
  protected int height;

  public abstract void mousePressed();
  public abstract void mouseReleased();  
  public abstract paint(Graphics g);
}

public class AButton extends AComponent {}


Daarnaast heb ik modules die bepaalde functie's uitvoert. Zoals:
  • Alarm - Stuurt commando's naar / haalt informatie van een alarm server. Alarm servers zijn redundant, dus ook hier zit logica achter.
  • Trend - Stuurt commando's naar / haalt informatie van een trend server. Wederom redundant.
  • Local - Voert bewerkingen uit op lokale variabelen.
  • etc.
Hiervoor heb ik iets in gedachte als:

Java:
1
2
3
4
5
6
7
8
9
10
11
12
public abstract /* or interface */ class Module;
public class Alarm extends /* or implements */ Module {
  public void accept(int id) {}
  public void accept(ArrayList<Integer> ids) {}
  ...
}

public class Trend extends /* or implements */ Module {
  public TrendPin getTrendPin(TrendSignal t, Date from, Date to, int numSamples) {}
  ...
}
...


Volgens mijn redenering moet ik die twee aan elkaar knopen. Het zou wel erg hip zijn als dit @runtime zou kunnen. Ik zie de volgende mogelijkheden:
  • java bestanden genereren uit configuratie bestanden. (Dit voelt niet goed en moet opnieuw gecompileerd worden)
  • Door middel van Listeners wat strings rondsturen om de functie te bepalen. Hoe ga ik nu parameters meesturen?
  • Een @runtime oplossing. Dit is voor mij nog een utopie, maar wel een doel eigenlijk.
Tips zijn welkom en wordt vervolgt. :)

[ Voor 0% gewijzigd door Alain op 08-09-2015 00:00 . Reden: Een getter kan geen void opleveren ]

You don't have to be crazy to do this job, but it helps ....


Acties:
  • 0 Henk 'm!

  • Alain
  • Registratie: Oktober 2002
  • Niet online
Ik denk dat ik weet waar ik de plank mis sla. Allereerst even een reactie op MSalters, alhoewel hij de ingezoomde versie natuurlijk nog niet kende.

Wanneer ik jou methode probeer krijg ik iets als:

Java:
1
2
3
4
5
6
7
public class AlarmInterface {
  public AButton getAcceptButton() {
    AButton acceptButton = new AButton();
    // Help, ik weet niet hoe AButton er uit moet gaan zien. Tekst, plaatje???
    return acceptButton;
  }
}


Wat ik mij bedacht is dat een grafisch object nooit universeel kan zijn. Als ik bijvoorbeeld een functie Alarm.accept(ArrayList<Integer> ids) wil uitvoeren, moet die ArrayList met id's wel bestaan. Deze id's zijn alleen beschibaar wanneer het object die deze functie uitvoert, aanwezig is op een AlarmPanel (het is toevallig dat een object die een functie op module Alarm wil uitvoeren op een alarm pagina moet staan. Een object dat Alarm.stopHorn wil uitvoeren kan op iedere willekeurige pagina staan.)

Mijn idee is nu om een class te maken van elk object die een functie uitvoert. De classes die aangeroepen worden hebben enkel static methods, die de gewenste functie uitvoeren. Dus:

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Alarm extends Module {
  public static void accept(ArrayList<Integer> ids) {
    // Do something
  }
  public static void stopHorn() {
    // Do something
  }
}

public class AAcceptPageButton extends AButton {
  public void mousePressed() {
    Alarm.accept(this.alarmPanel.getAlarmIds());
  }
}
public class AStopHornButton extends AButton {}
  public void mousePressed() {
    Alarm.stopHorn();
  }
...


Hiermee is alleen het @runtime aanpassen niet mogelijk. Hoe meer ik daar over nadenk, hoe meer hoofdpijn ik krijg. :P

Als ik nu nog steeds de plank mis sla hoor ik het uiteraard graag. :)

You don't have to be crazy to do this job, but it helps ....


Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Nu online
Alhoewel ik niet een compleet beeld heb van wat je probeert te bereiken denk ik dat je een gedrocht van een hiërarchie aan het maken bent :P

In mijn beleving heeft het UI element dat iets met alarmen kan ( AlarmPanel? ) een knop. De panel bepaalt dus hoe dat ding er uit ziet.
Het AlarmPanel weet ook hoe het object er uit ziet wat de alarmen beheert ( AlarmManager ) en weet dus dat hij van dat object de accept() methode moet aanroepen als de operator op de Accept knop drukt.
Ook weet het AlarmPanel hoe een Alarm er uit ziet, want hij zal deze moeten weergeven in een lijst oid.

De AlarmManager beheert Alarm objecten die worden gegeneert door de andere entiteiten in je systeem. Die entiteiten zullen dus iets van de AlarmManager moeten weten, of Alarm objecten genereren in events die vervolgens door een andere "controller achtige" entiteit aan de AlarmManager worden toegevoegd.

Volgens mij probeer je het allemaal te generiek te maken waardoor je straks een brei van classes en interfaces hebt. Misschien eerst een concreet systeem bouwen en daarna de generieke dingen er uit refactoren?

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

  • Alain
  • Registratie: Oktober 2002
  • Niet online
farlane schreef op dinsdag 08 september 2015 @ 22:58:
Volgens mij probeer je het allemaal te generiek te maken waardoor je straks een brei van classes en interfaces hebt. Misschien eerst een concreet systeem bouwen en daarna de generieke dingen er uit refactoren?
Ik denk dat dit een goed idee is.

Om nog even een voorbeeld te geven van een AlarmPanel:

Een AlarmPanel geeft alarmen weer voor een operator. Zo wil (in een bakkerij) de operator van de deegmachine geen alarmen zien van de oven, en andersom. De manager wil misschien wel alle alarmen zien.

De alarmen van een deegmachine kunnen alleen op het operator panel geaccepteerd worden en dus niet door de manager. Laat staan degene die bij de oven staat.

Hieruit haal ik de volgende stappen:
  • Haal alarmen op die voor mij van toepassing zijn via de alarm module
  • Wanneer de accept knop wordt ingedrukt wordt een actie gestart.
  • De alarm module vangt het commando en zet de alarm client in gang
  • De alarm client checkt welke server primary is en stuurt deze het commando (geen onderdeel van deze vraag)
  • De server checkt of de client de bewerking uit mag voeren (geen onderdeel van deze vraag)
  • Server verwerkt het commando (geen onderdeel van deze vraag)
Maar nogmaals het probleem is generiek. Hoe koppel je een (G)UI aan een class die daadwerkelijk iets gaat doen?

You don't have to be crazy to do this job, but it helps ....


Acties:
  • 0 Henk 'm!

  • bomberboy
  • Registratie: Mei 2007
  • Laatst online: 08-05 21:39

bomberboy

BOEM!

Knuffelbeer schreef op dinsdag 08 september 2015 @ 23:42:
Maar nogmaals het probleem is generiek. Hoe koppel je een (G)UI aan een class die daadwerkelijk iets gaat doen?
Ik denk dat je op zoek bent naar het Model-View-Controller (MVC) design pattern: Wikipedia: Model–view–controller

Dat biedt een antwoord op de vragen die je tot nu toe gesteld hebt. Er wordt wel op gealludeerd in de verschillende posts, maar niet expliciet bij naam genoemd. In veel applicaties zijn de View en Controller geïntegreerd, maar dat hoeft zeker niet en je kan ook meerdere views en meerdere controllers hebben. (wat ook terug komt in je vragen)

Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Nu online
Knuffelbeer schreef op dinsdag 08 september 2015 @ 23:42:
Maar nogmaals het probleem is generiek. Hoe koppel je een (G)UI aan een class die daadwerkelijk iets gaat doen?
Het is een generiek probleem misschien, maar afhankelijk van je framework is dat al voor je uitgedacht. uitgaande van Java ( want daar lijkt de code op die je post ) zal er het iets zijn in de trend van:
Java:
1
2
3
4
5
6
7
8
9
10
11
12
public class AlarmPanel extends ActionListener
{
    public AlarmPanel(){
        Button b = AcceptButton;
        b.addActionListener(this);
    }

    public void actionPerformed(ActionEvent e) {
        AlarmManager mgr = AlarmManager.GetInstance(); // Uitgaande van een Singleton
        mgr.accept();
    }
}


De koppeling van UI naar het object dat iets doet ( AlarmManager ) zit dus hier in het UI component. Je kunt het ook naar buiten het panel brengen met een event en daar de koppeling maken, maar het is geen ramp om het in je UI component te hebben.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

  • Alain
  • Registratie: Oktober 2002
  • Niet online
Yes, bedankt. Daar kan ik mee uit de voeten.

You don't have to be crazy to do this job, but it helps ....

Pagina: 1