[java] MVC kan veel generieker

Pagina: 1
Acties:
  • 180 views sinds 30-01-2008
  • Reageer

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024
Ik heb me al een tijdje lopen ergeren aan het mvc gebeuren van Swing, of eigelijk het gebrek aan allerlei support classes. Je hebt bv de PropertyChangeSupport en daarmee houd het wel op. Als je bv ook een voor List`s of Map ofzo wilt hebben dan zal je hem zelf moeten schrijven. Ik heb hier intussen al een aantal classes voor geschreven en zal binnenkort hier even nieuweren posten waar geen memory leaks meer in voor kunnen komen (memory leaks kunnen onstaan omdat views als listener is geregistreerd en zodoende schermen niet voor gc in aanmerking komen).

Ik neem aan dat jullie ook altijd de standaard 'controller' code maken voor bv een string veld.
code:
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
public class Persoon{
    public final static String VOORNAAM = "voornaam";    

    private String _voornaam;
    
    private PropertyChangeSupport _pcs = new PropertyChangeSupport(this);

    public Persoon(Strng voornaam){
        setVoornaam(voornaam);
    }

    public void setVoornaam(String voornaam){
        ..null check
        ..trim+lengte check
        String oldValue = _voornaam;
        _voornaam = voornaam;
        _pce.propertyChange(VOORNAAM,oldValue,_voornaam);
    }

    public void addPropListener(String property, PropertyChangeListener l){
        _pcs.addListener(property,propertyChangeListener);    
    }
}

public VoornaamLabel extends JLabel{
    public VoornaamLabel(Persoon persoon){
        persoon.addListener(Persoon.VOORNAAM,new PropertyChangeListenerImpl());
    setText(persoon.getVoornaam());
}

     private class PropertyChangeListenerImpl implements PropertyChangeListener{
        public void propertyChange(PropertyChangeEvent pce){
            setText(pce.getNewValue());
        }
    }
}

public VoornaamTextField extends JTextField{
    
    private Persoon _persoon;
    
    public VoornaamTextField(Persoon persoon){
        ..null check
        _persoon = persoon;
        _persoon.addListener(Persoon.VOORNAAM,new PropertyChangeListenerImpl());
        setText(persoon.getVoornaam());
        addActionListener(new ActionListenerImpl());
    }

    
    private class PropertyChangeListenerImpl implements PropertyChangeListener{
        public void propertyChange(PropertyChangeEvent pce){
            setText(pce.getNewValue());
        }
    }

    private class ActionListenerImpl implements ActionListener{
        public void actionPerformed(ActionEvent e){
            try{
                _persoon.setVoornaam(getText());
            }catch(IllegalArgumentException ex){
                setText(_model.getValue());
                JOptionPane.showMessageDialog(StringTextField.this,e.getMessage(),"", JOptionPane.ERROR_MESSAGE);
            }
                
        }
    }
}

Maar iedere keer dat event gebeuren, en dan die controllers nog een keer aansluiten op de gui, dat is altijd zo`n hoop dom werk. Het probleem zit hem in het feit dat de properties nog lang niet slim genoeg hebben gemaakt. Eigelijk zou iedere propertie zelf een propertychangeSupport moeten hebbem. Ik ben intussen uitgekomen op de volgende genieke objecten. (nog niet helemaal klaar).
code:
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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
public class StringEvent extends EventObject{
    
    private String _oldValue;
    private String _newValue;
    
    public StringEvent(Object source, String oldValue, String newValue){
        super(source);
        
        _oldValue = oldValue;
        _newValue = newValue;
    }
    
    public String getNewValue(){
        return _newValue;
    }
    
    public String getOldValue(){
        return _oldValue;
    }
}


public interface StringListener extends EventListener{
    
    public void fire(StringEvent e);
}

public interface StringVetoListener extends EventListener{
    
    public void validate(StringEvent stringEvent)throws IllegalArgumentException;
}

public class StringModel<T>{
    
    private String _value;
    private StringController _controller;
    
    public StringModel(T source, String value){
        _value = value;
        _controller = new StringController(source);
    }
    
    
    public StringModel(T source){
        _controller = new StringController(source);
    }
    
    
    public String getValue(){
        return _value;
    }
    
    
    public synchronized void setValue(String newValue)throws IllegalArgumentException{
        String oldValue = _value;
        _controller.request(oldValue,newValue);
        _value = newValue;
        _controller.handleValueChange(oldValue,newValue);
    }
    
    
    public StringController getController(){
        return _controller;
    }
    
    
    private class StringController{
        
        private List<WeakReference<StringListener>> _listenerList = new LinkedList<WeakReference<StringListener>>();
        private List<WeakReference<StringVetoListener>> _vetoList = new LinkedList<WeakReference<StringVetoListener>>();
        private T _beanSource;
        
        public StringController(T beanSource){
            _beanSource = beanSource;
        }
        
        public void request(String oldValue, String newValue)throws IllegalArgumentException{
            StringEvent stringEvent = new StringEvent(_beanSource,oldValue,newValue);
            for(int k=0;k<_vetoList.size();k++){
                StringVetoListener l = _vetoList.get(k).get();
                if(l!=null){
                    l.validate(stringEvent);
                }
            }
        }
        
        public void handleValueChange(String oldValue, String newValue){
            StringEvent stringEvent = new StringEvent(_beanSource,oldValue,newValue);
            
            
            for(int k=0;k<_listenerList.size();k++){
                StringListener l = _listenerList.get(k).get();
                if(l!=null){
                    l.fire(stringEvent);
                }
            }
        }
        
        public void addListener(StringListener listener){
            if(listener == null){
                throw new NullPointerException("listener can`t be null");
            }
            
            int indexOf = indexOf(listener);
            if(indexOf!=-1){
                throw new IllegalArgumentException("listener:"+listener+" already is registered in StringController");
            }
            _listenerList.add(new WeakReference<StringListener>(listener));
        }
        
        
        public void addVetoListener(StringVetoListener listener){
            if(listener == null){
                throw new NullPointerException("listener can`t be null");
            }
            _vetoList.add(new WeakReference<StringVetoListener>(listener));
        }
        
        
        public synchronized void removeListener(StringListener listener){
            if(listener == null){
                throw new NullPointerException("stringListener can`t be null");
            }
            
            int indexOf = indexOf(listener);
            if(indexOf == -1){
                throw new IllegalArgumentException("listener:"+listener+" is not found in StringController");
            }
            _listenerList.remove(indexOf);
        }
        
        
        public synchronized void removeAllListeners(){
            _listenerList.clear();
        }
        
        
        public boolean hasListeners(){
            return _listenerList.size()>0;
        }
        
        
        private synchronized int indexOf(StringListener listener){
            assert listener!=null:"listener can`t be null";
            
            int index = 0;
            int result = -1;
            while(index<_listenerList.size()){
                if(_listenerList.get(index).get() == listener){
                    result = index;
                    index = _listenerList.size();
                }else{
                    index++;
                }
            }
            
            return result;
        }
        
        private void removeDeadRefs(){
        
        }
    }
}

public class StringLabel extends JLabel{
    
    private StringListener _stringListener = new StringListenerImpl();
    
    public StringLabel(StringModel model){
        if(model == null){
            throw new NullPointerException("model can`t be null");
        }
        
        model.getController().addListener(_stringListener);
        setText(model.getValue());
    }
    
    private class StringListenerImpl implements StringListener{
        public void fire(StringEvent e){
            if(e == null){
                throw new NullPointerException("e can`t be null");
            }
            
            setText(e.getNewValue());
        }
    }
}


public class StringTextField extends JTextField{
    
    private StringModel _model;
    private StringListener _stringListenr = new StringListenerImpl();
    
    public StringTextField(StringModel model){
        if(model == null){
            throw new NullPointerException("model can`t be null");
        }
        _model = model;
        _model.getController().addListener(_stringListenr);
        addActionListener(new ActionlistenerImpl());
        setText(_model.getValue());
    }
    
    private final class StringListenerImpl implements StringListener{
        public void fire(StringEvent e){
            if(e == null){
                throw new NullPointerException("e can`t be null");
            }
            
            setText(e.getNewValue());
        }
    }
    
    private final class ActionlistenerImpl implements ActionListener{
        public void actionPerformed(ActionEvent ae){
            try{
                _model.setValue(getText());
            }catch(IllegalArgumentException e){
                setText(_model.getValue());
                JOptionPane.showMessageDialog(StringTextField.this,e.getMessage(),"", JOptionPane.ERROR_MESSAGE);
            }
        }
    }
}

dit is dan de persoon model:
code:
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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
public class Persoon{
    
    private StringModel<Persoon> _voornaamM = new StringModel<Persoon>(this);
    private StringVetoListener _voornaamVetoListener = new VoornaamVetoListener();
    
    private StringModel<Persoon> _achternaamM = new StringModel<Persoon>(this);
    private StringVetoListener _achternaamVetoListener = new AchternaamVetoListener();
    
    private StringModel<Persoon> _woonplaatsM = new StringModel<Persoon>(this);
    private StringVetoListener _woonPlaatsVetoListener = new WoonplaatsVetoListener();
    
    public Persoon(String voornaam,String achternaam,String woonplaats){
        _voornaamM.getController().addVetoListener(_voornaamVetoListener);
        _voornaamM.setValue(voornaam);
        
        _achternaamM.getController().addVetoListener(_achternaamVetoListener);
        _achternaamM.setValue(achternaam);
        
        _woonplaatsM.getController().addVetoListener(_woonPlaatsVetoListener);
        _woonplaatsM.setValue(woonplaats);
    }
        
    
    public StringModel<Persoon> getAchternaamM(){
        return _achternaamM;
    }
    
    
    public StringModel<Persoon> getVoornaamM(){
        return _voornaamM;
    }
    
    
    public StringModel<Persoon> getWoonplaatsM(){
        return _woonplaatsM;
    }
    
    
    private class VoornaamVetoListener implements StringVetoListener{
        
        public void validate(StringEvent e){
            if(e == null){
                throw new NullPointerException("e can`t be null");
            }
            
            if(e.getNewValue() == null){
                throw new IllegalArgumentException("voornaam can`t be null");
            }
            
            if(e.getNewValue().trim().length() == 0){
                throw new IllegalArgumentException("voornaam moet meer zijn dan alleen een lege string");
            }
        }
    }
    
    private class AchternaamVetoListener implements StringVetoListener{
        
        public void validate(StringEvent e){
            if(e == null){
                throw new NullPointerException("e can`t be null");
            }
            
            if(e.getNewValue() == null){
                throw new IllegalArgumentException("achternaam can`t be null");
            }
            
            if(e.getNewValue().trim().length() == 0){
                throw new IllegalArgumentException("achternaam moet meer zijn dan alleen een lege string");
            }
        }
    }
    
    private class WoonplaatsVetoListener implements StringVetoListener{
        
        public void validate(StringEvent e){
            if(e == null){
                throw new NullPointerException("e can`t be null");
            }
            
            if(e.getNewValue() == null){
                throw new IllegalArgumentException("achternaam can`t be null");
            }
            
            if(e.getNewValue().trim().length() == 0){
                throw new IllegalArgumentException("woonplaats moet meer zijn dan alleen een lege string");
            }
        }
    }
}

en dit is de aansluiting in de gui:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class TestPanel extends JPanel
{
    private Persoon _persoon = new Persoon("jan","bakker","slochteren");
    
    public TestPanel(){
        super(new VerticalFlowLayout());
        
        add(new StringLabel(_persoon.getVoornaamM()));
        add(new StringTextField(_persoon.getVoornaamM()));
        
        add(new StringLabel(_persoon.getAchternaamM()));
        add(new StringTextField(_persoon.getAchternaamM()));
    
        add(new StringLabel(_persoon.getWoonplaatsM()));
        add(new StringTextField(_persoon.getWoonplaatsM()));
    }
    
    public static void main(String[] args){
        JFrame frame = new JFrame();
        frame.setContentPane(new TestPanel());
        frame.setSize(300,300);
        frame.setVisible(true);
    }
}

Afbeeldingslocatie: http://www.alarmnummer.net/images/model.JPG

Zoals je zit is het super simpel, omdat je totaal niet meer hoeft te denken om allerlei event afhandel zaken, dat gebeurt nu generiek. Ik ga hem nog generieker maken (auto support voor andere types, lijsten, map, sets etc) omdat je niet alleen met string wilt werken, maar dit is mijn idee. Wat vinden jullie hiervan?

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 15-02 11:20
offtopic:
VOlgens mij hebben we een opvolger gevonden voor mr. Bravenboer.


Ik zie trouwens dat je gebruik maakt van de generics. Komen deze nu definitief in de taal?

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.


  • Skinkie
  • Registratie: Juni 2001
  • Laatst online: 09-06-2020

Skinkie

Op naar de 500

ik dacht dat ik toch redelijk java kon schrijven en lezen... maar wat doen die | en << >> ?

Steun Elkaar, Kopieer Nederlands Waar!


  • whoami
  • Registratie: December 2000
  • Laatst online: 00:19
Op maandag 08 juli 2002 09:34 schreef Skinkie het volgende:
ik dacht dat ik toch redelijk java kon schrijven en lezen... maar wat doen die | en << >> ?
In C is die | een bitwise or, dus ik vermoed dat dat in Java ook zo is?
<< en >> zijn 'bitshifters' vermoed ik...

https://fgheysels.github.io/


  • Skinkie
  • Registratie: Juni 2001
  • Laatst online: 09-06-2020

Skinkie

Op naar de 500

Op maandag 08 juli 2002 09:36 schreef whoami het volgende:

[..]

In C is die | een bitwise or, dus ik vermoed dat dat in Java ook zo is?
<< en >> zijn 'bitshifters' vermoed ik...
moet je met toch is vertellen wat er ge-or-t in een
public void blaa|String bla){

Steun Elkaar, Kopieer Nederlands Waar!


  • whoami
  • Registratie: December 2000
  • Laatst online: 00:19
Op maandag 08 juli 2002 09:39 schreef Skinkie het volgende:

[..]

moet je met toch is vertellen wat er ge-or-t in een
public void blaa|String bla){
quote het even?
* whoami vind het niet onmiddellijk.
Misschien is het wel gewoon een typo.

https://fgheysels.github.io/


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024
Op maandag 08 juli 2002 09:20 schreef farlane het volgende:
offtopic:
VOlgens mij hebben we een opvolger gevonden voor mr. Bravenboer.
Ik beschouw het maar als een compliment ;)
Ik zie trouwens dat je gebruik maakt van de generics. Komen deze nu definitief in de taal?
Als het goed is komen ze er vanaf jdk1.5 standaard in. Maar je kan nu al gewoon een nieuwe compiler en aangepaste libs ophalen om er zelf mee te experimenteren.

dus al die '<' en '>' tekens zijn gewoon stukken van generics. Je mag er in principe gewoon overheen lezen.

  • Skinkie
  • Registratie: Juni 2001
  • Laatst online: 09-06-2020

Skinkie

Op naar de 500

Sorrie laat maar... brak netscape lettertype |:( | == (

Steun Elkaar, Kopieer Nederlands Waar!


Verwijderd

Ik heb me al een tijdje lopen ergeren aan het mvc gebeuren van Swing, of eigelijk het gebrek aan allerlei support classes. Je hebt bv de PropertyChangeSupport en daarmee houd het wel op.
Als je de Observer en Observable klasses gebruikt kan je toch al een stuk meer (zo niet alles)?
... en zal binnenkort hier even nieuweren posten waar geen memory leaks meer in voor kunnen komen ...
/me houdt het topic in de gaten :)

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024
Op zaterdag 27 juli 2002 19:56 schreef Lanfear het volgende:
Als je de Observer en Observable klasses gebruikt kan je toch al een stuk meer (zo niet alles)?
Daar kan je zeker niet alles mee. Je hebt bv geen support classes voor lists en maps. En als je gaat werken met vetolisteners, dan gaat alles de soep in.

[url="http://gathering.tweakers.net/forum/list_messages/513353"]Dit[/url] is een link naar een van mijn eerdere probeersels. Misschien heb je er nog iets aan.
/me houdt het topic in de gaten :)
Ik ben voorlopig bezig met een typesysteem voor oa een functionele programmeertaal en daarom staat dit op een laag pitje. Zo gauw ik daar even flauw van ben ga ik misschien hier wel weer even mee aan de slag.
Pagina: 1