commons-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Rey Francois <Francois....@capco.com>
Subject [Design Discussion] DynaBeans Converters
Date Fri, 28 Dec 2001 13:44:56 GMT

In my previous posts I thought there was a Converter per property,
effectively resulting in multiple Converters associated to one DynaBean. I
understand now I was wrong and the Converter you both (Craig + Paulo) talk
about is more like this:
- there is only *one* Converter assigned to one DynaBean instance, or none.
- Converters are responsible for converting a given object value to a
specific type, meaning to an instance of a given Class.
- the same convert method is called for all conversion, that is to say: all
properties of the same DynaBean that need conversion will use the same
converter method (the one defined in the interface) on the same Converter
instance.

If this new understanding is right -please correct me if I still miss the
point- then I'm still not comfortable about this design. Let me give you an
example taken from a real-life project, although simplified for the purpose
of the discussion. It's about a system for managing orders of financial
funds.

Here is the value object that I receive from the backend when displaying an
order:

public class OrderData {
  public String getType(); // return "B" for "Buy" and "S" for "Sell"
  public BigInteger getQuantity();
  public BigDecimal getUnitPrice();
  ...
}

If want to display this order in Struts, using your design I would create a
DynaActionForm that stores Strings using the following Converter in the set
methods:

public OrderConverter implements Converter {

  public Object convert(Class type, Object value) {
    if (Class!=String.class) throw SomeException;
    Object result = null;
    if (value instanceof String) {
       result = value;
    } else if (value instanceof BigDecimal) {
       result = // convert BigDecimal to String
    } else if (value instanceof BigInteger) {
       result = // convert BigInteger to String
    }
    return result;
  }
}

The set method in my DynaActionForm would be like this:
  public void set(String name, Object value) {
    setInternal(name, getConverter().convert(String.class, value));
  }

So far so good, I see some value in this approach. But what if I don't want
to display "B" but "Buy" for the type? I could write this in the convert
method:
    if (value instanceof String) {
       if "B".equals(value) result = "Buy";
       ....

That would work until ... I have another property of type String that I wand
to convert as well:

public class OrderData {
  // new property:
  public String getStateId(); // returns "O" for "Open", "M" for "matched",
"R" for "Waiting for repair", etc.
  // Below is same as before:
  public String getType(); // return "B" for "Buy" and "S" for "Sell"
  public BigInteger getQuantity();
  public BigDecimal getUnitPrice();
  ...
}

Unless there is no overlap between type codes and state codes, I can
survive, but that's not a desirable situation.

So before going any further in this discussion, is my understanding of
Converters correct ?

Fr.


-----Original Message-----
From: Craig R. McClanahan [mailto:craigmcc@apache.org]
Sent: 27 December 2001 22:20
To: Jakarta Commons Developers List; paulo.gaspar@krankikom.de
Subject: RE: [Design Discussion] DynaBeans - Round 2




On Thu, 27 Dec 2001, Paulo Gaspar wrote:

> > From: Rey Francois [mailto:Francois.Rey@capco.com]

> >
> > I had to crate a similar interface in a data mapper framework I
developed,
> > however in the signature of the conversion method I included an
additional
> > parameter for the Locale. Without it, support for internationalized
> > conversions is difficult.
>
> The method I use is to have a different converter for each Locale where
such
> distinction is necessary (which happens quite often).
>
> Notice that I have the option of assigning a converter per DynaBean and
not
> per DynaClass as Craig wrote in his proposal exactly because of situations
> like this. The converter often needs some kind of contextual information.
>

I'm planning on ripping the getConverter() back out of DynaClass -- it's
too restrictive.

Paulo, did you ever run into a scenario where you had to be accessing the
same Record with different Converters at the same time ... perhaps even on
separate threads?  This would cause some grief with the idea of assigning
a Converter to a DynaBean instance.

> This situations also made me implement my converters in a hierarchical
way,
> meaning that converter may just add or override a few conversion rules to
a
> "parent" converter. Some of those conversion rules may add context
> information like "Locale" or "Base directory".
>

That makes a lot of sense.

> > Perhaps this could be added in a new method of the
> > Converter interface, or perhaps it can be included in another interface
> > extending the Converter, e.g. LocalizedConverter extends Converter.
>
> You only need to place that kind of thing in the implementation. The
> interface can stay as it is.

That's certainly obvious in retrospect :-).

I like Converter-per-Locale (or for whatever extra context information it
needs).  I'm still thinking about explicitly tying converters to DynaBean
instances ...

Paulo, do you normally design your Converters to go both directions (i.e.
so you can use them in the getters *and* the setters), or just one way?
In the latter case, you'd probably need two.

>
> > What do you think?
> >
> > Actually the more I think about this, the more I'm wondering if it makes
> > sense to include a conversion feature into DynaBeans. My gut feeling
would
> > be to restrict DynaBeans to be only dynamic data containers, and handle
> > conversions separately.
>
> Notice that my DynaBean (actually called IRecord) interface does not know
> anything about converters - only my most used implementation does so.
>

Ah ... so you really could use two one-way Converters if you wanted to ...
but the key is that support for conversion is a feature of a particular
DynaBean implementation class -- not of the top-level interface.

I'd like to keep the top-level interfaces as simple as possible, to allow
for variations in the implementations underneath.

> I found that I am converting types most of the time, like when I read to
the
> DynaBean things like:
>  - Servlet parameters;
>  - ResultSet columns;
>  - XML attributes;
>  - A big etc.
>
> Using a helper class instead of a simple set() method all the time becomes
a
> pain in the ass, especially because it would involve also getting property
> type information. So, you would need something like:
>
>     DynaClass myBeanClass = myBean.getDynaClass();
>     PropertyDescriptor propDesc =
> myBeanClass.getPropertyDescriptor("myprop");
>     myBean.set("myprop", cvt.convert(propDesc.getPropertyType(),
newValue));
>
> instead of
>     myBean.set("myprop", newValue);
>
> Notice also that:
>  - You have the opportunity to make a much more efficient implementation
>    inside the DynaBean;
>  - The DynaBean implementation can default (as mine) to the use of a
>    NoOpConverter that performs no conversion at all.
>
>
> > First not all situations require the use of conversions.
>
> The above NoOpConverter solves that. And notice that I am talking about an
> implementation - I agree that the converter should be kept away from the
> interface.
>

You could of course also implement a DynaBean base class that doesn't
support the conversion functionality at all for cases where it's not
needed.

> > Secondly if conversions need to be done, I'd probably handle it
> > separately from the original bean. Most of the time conversions are
> > necessary because of a specific usage situation requiring a different
and
> > specific format, and you cannot or do not want to change the
> > original format
> > to accommodate this new usage situation (either because you do not own
> > the original format specification, or because you don't want to make
> > it specific and prefer to keep it generic and reusable).
> > Supposing we have an EJB backend that provides a value object. Should
the
> > value object be coded to support String conversions for GUI display? You
> > can do this, but that means all your client have to follow the same
> > formatting conventions.
>
> That is why, as above, the Converter should be DynaBean specific and not
> DynaClass specific.
>
> > To avoid this you either convert on-the-fly (e.g. using some
> > kind of wrapper), or you create another data container that stores the
> > converted values.
>
> Or you assign a new Converter to the DynaBean.
>
> > Either way the conversion are handled outside the original
> > bean itself. In the case of Struts we use a separate data container
(there
> > are good reasons for this), ActionForms, and conversions are either
custom
> > coded or handled by the ConvertUtils class.
>
> > If we had to use DynaBeans in Struts for implementing
> > ActionForms, we could
> > make a case for making these "DynActionForms" able to perform some
> > conversions. We could implement transparently the use of converters in
the
> > set() method like Paulo Gaspar suggests in order to populate a
> > DynActionForm
> > from a value object (or business object). However we would probably need
> > to do the reverse conversion in the get() method so that we can populate
a
> > value object with a DynActionForm. This would require some ways of
passing
> > the target type to the get() method so it can select the appropriate
> > converter.
>
> It is too messy to have such a get() method. What I do is to wrap the
value
> object with another DynaBean implementation.
>
> I have a kind of DynaBeanWrapper class which also uses a Converter and
that
> can wrap any object with "properties" (or "fields" or "columns"). Only
bits
> of the DynaClass need to change depending of what kind of object you are
> wrapping - the setter and getter implementations.
>
> Then there is a "DynaIntrospector" that examines the object to be wrapped
> (e.g. a bean or a ResultSet) and builds an appropriate DynaClass. (For
beans
> such instrospection provides a nice place to hold an introspection cache.)
>
> Of course that you do not need such thing if you just want to convert all
> the fields of a DynaBean into text. You might use a Converter for that too
> but it is simpler to do it outside the bean, as in:
>
>   String s = cvt.convert(java.lang.String.class,
myDynaBean.get("myProp"));
>
> and the "cvt" Converter can hold the appropriate conversion rules to
format
> each data type.
>
>
> I also have a converter helper tool for the "asThis" and "asThat" stuff.
> Just to give you an idea, it is used like this:
>
>    CvtHelper cvt = new CvtHelper(myConverter);
>
>    int i  = cvt.asInt(toIntValue, -1);
>    Integer io  = cvt.asInteger(toIntValue);
>
>    String s = cvt.asString(toStrValue);
>
> So, it is a class that allows you to specify a converter to be used to
> perform the most frequent conversions to primitive and "java.lang" types.
>
> I think using such tool is much simpler and flexible that having the usual
> getSomeType() helper methods hanging in every other class. And the
> implementation is obvious. Like:
>
> public class CvtHelper
> {
>     private final Converter m_cvt;
>
>     public CvtHelper(final Converter i_cvt)
>     {   m_cvt = i_cvt;
>     }
> ...
>
>     public final String asString(final Object i_value)
>     {
>         return (String)(m_cvt.convert(String.class, i_value));
>     }
>
> ...
> }
>
>
> > Such design would also mean that we must easily differentiate
> > between the various input/output types: it should be possible for
> > a JSP page
> > to ensure it gets an appropriate display value as a String, while on the
> > other side it should be possible to get an appropriate "business" value.
> > Using the same getter method for these two scenario seems to
> > introduce some complications.
>
> As mentioned above (different converters, only one getter, etc.).
>
>
> > What if the business value is already a String, which has to
> > be properly converted to another String for display (e.g. > O<->Open)?
> > In such case you cannot really use the Class as a discriminant between
> > the different types, since both are Strings. Should we use two get
methods
> > instead?
>
> That only depends on the Converter implementation. Remember that Converter
> is an interface.
>
>
>
> > Seems like an overkill... Also doing such conversion in an ActionForm is
> > in some cases introducing some overlap with validation.
>
> It is complementary to Validation. For an input value to be valid it must
> be possible to convert it to the destination type.
>

In my Struts-based designs, I tend to check for convertability in my
ActionForm validate() methods -- conversion success is necessary but not
sufficient for validity, so you're checking for other stuff there anyway.

>
> > Sometimes being able to successfully convert a display String to a
> > business value is enough to validate it, at least partially: a String
> > successfully parsed by SimpleDateFormat is a valid date, no need to
> > validate each character, and on
> > the resulting Date object further validation can be performed (is it a
> > future date, etc.).
>
> So, this means that you also find out that it is a complementary issue.
>
>
> > Finally add the internationalization issue I started with...
>
> Again, a converter per Locale and DynaBean specific converters.
>
>
> > Ok, I will conclude here because my written thinking is getting
> > too long ;)
>
> I suffer of the same "problem".
> =;o)
>
>
> > My conclusion would be to leave aside the conversion feature and
> > focus only on the data container aspect.
>
> Containing the data is the simple bit. A Map would do.
>
> The usefulness from the DynaBean comes from it being able to help us
> performing the most usual tasks with this kind of data with less
> routine coding and with similar or improved quality (data validation
> and other).
>
>
> > Fr.
>
> Have fun,
> Paulo Gaspar
>

Craig


--
To unsubscribe, e-mail:
<mailto:commons-dev-unsubscribe@jakarta.apache.org>
For additional commands, e-mail:
<mailto:commons-dev-help@jakarta.apache.org>
************************************************************************
The information in this email is confidential and is intended solely
for the addressee(s).
Access to this email by anyone else is unauthorised. If you are not
an intended recipient, please notify the sender of this email 
immediately. You should not copy, use or disseminate the 
information contained in the email.
Any views expressed in this message are those of the individual
sender, except where the sender specifically states them to be
the views of Capco.

http://www.capco.com
***********************************************************************


--
To unsubscribe, e-mail:   <mailto:commons-dev-unsubscribe@jakarta.apache.org>
For additional commands, e-mail: <mailto:commons-dev-help@jakarta.apache.org>


Mime
View raw message