Re: [Figleaf-developer] Validation and PropertyDescriptor
Status: Alpha
Brought to you by:
steckman
|
From: Greg S. <ste...@on...> - 2004-07-07 16:10:17
|
We might be splitting hairs on this topic.
I think we can express a couple of rules that the system needs to follow:
1. A single object instance implements all behavior required of that object.
2. A single class implementation can implement all behavior required of
that class.
3. Optionally, multiple classes can be defined which when taken together
implement all required behavior.
Rule 2 allows you to choose to implement all behavior in one class, and
that class will be dependent on Figleaf. The approach of using proxies
is to be able to factor out Figleaf dependent code into helper classes
(rule 3), and have dynamic proxies created to make a class that looks
like it is behaviorially complete, but really under the covers there are
several classes doing the work.
sam...@ma... wrote:
>>Let's consider an implementation of a class that implements the
>>Informative interface itself.
>>
>>Case A: Current implementation + modify PropertyDescriptor to add the
>>methods:
>>void addValidationListener(ValidationListener listener);
>>void removeValidationListener(ValidationListener listener);
>>
>>
>
>What would listen to validation events? The GUI - in fact parts of the GUI would
>only need to listen to specific properties. As such I think a more fine-grained
>validation listener approach would benifit us - allow validation callbacks on
>specific property calls. By passing a callback into a validation call (or even
>as a result of calling get) the Informative object doesn't need to manage
>multiple ValidationListeners, and the GUI doesn't have to worry about detaching
>listeners
>
>
I think we need validation at two levels. First the individual
properties, and second on the whole object.
>>public class MyInformative implements Informative{
>> PropertyDescriptor[] pds;
>> Object propA;
>>
>> public void setPropertyA(Object value){
>> boolean invalid=true;
>>
>> //do some validation logic
>> //set invalid as required
>>
>>
>
>
>
>> if(invalid){
>> pds[0].fireValidationInvalid("reason string goes here");
>> }else{
>> propA=value;
>> pds[0].fireValidationValid();
>> }
>> }
>>
>>
>
>
>You are now poluting property accesing methods with figleaf dependant code.
>Developers now need to explicitly invoke validation for all their set methods,
>and have to do so in a way that ties their object to figleaf. I prefer to deal
>with validation at the point we describe the properties (or general 'action'
>methods) to the figleaf system, that is in the PropertyDescriptor.
>
>
Polluting was intentional on my part, as I envision this as one primary
way to write classes for the system if you don't care about portability.
It is potentially the most straightforward and powerful way to write
behaviorially complete objects: but they get contaminated with Figleaf
APIs.
>>Case B: The implementation you suggest:
>>
>>public class MyInformative implements Informative{
>> public void setPropertyA(Object value){...}
>> public Object getPropertyA(){...}
>>
>> PropertyDescriptor[] getPropertyDesctiptors(){
>> PropertyDescriptor[] pds=new PropertyDescriptor[1];
>> pds[0]=new MyInformativePropertyDescriptor(
>>
>>getMethod("getPropertyA", null).
>>
>>getMethod("setPropertyA", new Class[]{Object.class}),
>>
>>"Property A",
>>
>>"Description of Property A");
>> return pds;
>> }
>>
>> class MyInformativePropertyDescriptor implements PropertyDescriptor{
>> private Method readMethod, writeMethod;
>> private String name, description;
>>
>> MyInformativePropertyDescriptor(Method readMethod, Method
>>writeMethod, String name, String desc){
>> this.readMethod=readMethod;
>> this.writeMethod=writeMethod;
>> this.name=name;
>> this.description=desc;
>> }
>>
>> public Object read(){
>> return readMethod.invoke(this.MyInformative, null);
>> }
>>
>> public void write(Object o, ValidationCallback callback){
>> //do validation
>>
>> if(valid){
>> writeMethod.invoke(this.MyInformative, new Object[]{o});
>> }
>> }
>> }
>>}
>>
>>
>
>I wouldn't call methods like that - I'd call them directly on the object to gain
>compile-time checking and simpler looking code.
>
How would you implement a PropertyDescriptor to call the method directly
without needing one PropertyDescriptor class for each individual property?
>>In the first case I used an object that's defined elsewhere so that
>>makes the code look shorter but in reality it probably won't be. I
>>prefer the listener approach of Case A because I think it has more
>>flexibility and is a standard pattern so users will understand the API
>>right away.
>>
>>
>
>But users have to define validation logic and invoke it themselves - my approach
>just has them define validation rules, and everything else is handled for them,
>making thw system simpler for them to use.
>
>
I was thinking that we could proxy the property methods so the
validation logic could be implemented elsewhere. I think the same
results can be had with either approach.
>>If we do that, I don't see the benefit of not using Methods
>>in the PropertyDescriptor.
>>
>>
>
>You then require that figleaf has to proxy method calls. You also expose
>implementation detail. The UI then has to invoke the method itself, catch the
>exceptions etc. What about remoting? What if invoking the property actually
>invokes a webservices call? Now we have to create a fake Method which we can't
>easily do (Method is a final class).
>
>
Remoting is a whole can of worms. Any method should be taggable as
"remote", which means it is executed on the server object, not on the
remote side. Again it requires proxies. I don't see a way around that in
any case. One advantage with using a Method, Figleaf itself can alter
the method returned by the PropertyDescriptor to point to a Figleaf
implemented interceptor.
>>I also prefer using the Methods because I
>>think they are more flexible. They can reference any method in any class
>>without having to implement an extra class just for redirection.
>>
>>
>
>I don't agree really - where would you need an object to expose the method of
>another class?
>
If PropertyDescriptor is a separate class, then the Method it refers to
is in a different object.
>>If a class doesn't implement Informative itself, then everything needs
>>to be handled by the Proxy in either case.
>>
>>Greg
>>
>>
>
>Which I already do - exposing the method is no simpler than directly invoking it
>inside the property, I've had proxies working in both ways.
>
That's why I think we might be splitting hairs. Probably everything can
be done either way. It's not even clear to me one implementation is
better than the other at this point.
>In reality validation at a property-by-property basis is simple from developer
>terms, but is often not good enough, for example what if a customer bean has 3
>telephone number fields, and one of them has to be non-null? Neither of our
>approaches work, as we are still validating on a property-by-property basis -
>even a final-mile validation check on the object as a whole happens to late, as
>by then the object might be in an invalid state.
>
If the object is in an invalid state that is OK. The important thing is
to not persist data that is invalid. If the object is deemed to be
invalid, it can be reloaded.
>I say lets rethink the problem. We need to not only validate a single property
>change, but the validaity of a set of changes on an object. Lets add the
>following method to Informative:
>
>boolean commitChanges(ChangeSet set)
>
>Lets keep your add and remove validation listeners on Informative. A ChangeSet
>is a set of changes on an object, and now becomes the only mechanism by which
>figleaf writes changes to an object - it consists of a series of property change
>messages. I'll knock up a prototype in the next day or so and see if its a
>workable idea.
>
>sam
>http://www.magpiebrain.com/
>
>
>
This gets back to whether the object is persisted after each property
set call, or only after a "save event" occurs (clicking a save button,
for example). The latter is a simpler case to handle, because you can
call all the property setters, and then before you want to save the
object call a single validate() method. The object then validates
itself. A proxy can intercept the call to validate() and redirect it to
another object that has the validation logic if required.
I don't see a problem with the object transiently becoming invalid. As
long as it's not saved or used for anything while it's invalid. It is
essentially a data holder for input forms.
Greg
|