Page 1 of 3 123 LastLast
Results 1 to 10 of 24

Thread: Ext-GWT: stop the insanity

  1. #1
    Ext User
    Join Date
    Jul 2008
    Posts
    5
    Vote Rating
    0
      0  

    Default Ext-GWT: stop the insanity

    Generics are a tool and like any tool they can be used well or abused and, after some extensive use of Ext-GWT, I'm sorry to say that this is (mostly) a

    case of abused Generics. I'll highlight some of the reasons why.

    Firstly however a note about the positive. The widgets themselves look great and theres a good selection (although, oddly, theres no Image Button;

    why?). That is primarily what I want from a UI library. The API in a lot of ways is secondary and this is teh case here too. Secondary doesn't mean

    its unimportant however.

    I don't know the history of GWT-Ext other than it has its roots in MyGWT (which I liked) and ExtJS (which I've never used). The idea to use generics is

    good but generics aren't simple. You only need to see a declaration like "<X> X max(Collection<? super X> collection)" to realize that and they need to

    be used properly. For better or worse this takes a fair bit of knowhow. Its why the Java Generics FAQ is as large as it is and why it takes someone of

    the calibre of Josh Bloch to explain it.

    1. Model

    Put simply: all models are Maps. Some might disagree with me but that is (imho) not a good thing. For instance, the ModelData interface has a method:

    public <X> X get(String property);

    This is obviously so you can do things like:

    Date date = model.get("startDate")
    int pageCount = model.get("pageCount") // auto-unboxing to boot

    While this might be convenient in that you don't have to explicitly cast but because of type erasure, you're not actually gaining anything. This is

    perfectly legal code:

    model.put("date", "testing 123");
    Date d = model.get('date");

    The result is of course a ClassCastException. Josh Bloch, just to give one example, suggests the use of heteregenous containers eg:

    container.put(String.class, "blah");

    In Swing, models are basically just POJOs with relevant adapters like TableModel and frankly instead of:

    model.get("dateOfBirth")
    model.set("dependants", 2)

    I prefer:

    model.getDateOfBirth()
    model.setDependants(2)

    I realize some will disagree with this but remember that a strongly typed model like this doesn't preclude the use of Maps whereas the use of Maps makes

    strongly typed accessors and mutators little more than syntactic placebos. Granted also that GWT doesn't have access to reflection, which is potentially

    limiting.

    2. Model Events

    BaseModel doesn't fire a lot of events. Basically the only one is Events.Update. Even removing a property fires an Update event where the new value is

    null, which--from an event perspective--is indistinguishable from actually setting the property to null (even though the latter leaves the property in

    the map and the former does not). This is a problem.

    Shouldn't adding a new property trigger an Add event and rmeoving an existing property trigger a Remove event?

    This particular code highlights some issues that are worth pointing out as examples of suboptimal Java code:

    From BaseModelData:

    public void setProperties(Map<String, Object> properties) {
    for (String property : properties.keySet()) {
    set(property, properties.get(property));
    }
    }

    should be:

    public void setProperties(Map<String, Object> properties) {
    for (Map.Entry<String, Object> entry : properties.entrySet()) {
    set(entry.getKey(), entry.getValue());
    }
    }

    and this:

    protected <X> X getNestedValue(ModelData model, List<String> paths) {
    if (paths.size() == 1) {
    return (X) model.get(paths.get(0));
    } else {
    Object obj = model.get(paths.get(0));
    if (obj != null && obj instanceof ModelData) {
    ArrayList<String> tmp = new ArrayList<String>(paths);
    tmp.remove(0);
    return (X) getNestedValue((ModelData) obj, tmp);
    }
    }
    return null;
    }

    has two faults:

    1. The cast to X on line 3 highlights the issue described earlier regarding get() (ie unchecked cast); and

    2. Line 6 can be simplified to:

    if (obj instanceof ModelData) {

    If obj is null then that returns false.

    3. Nested Properties

    For those unfamiliar with it, the BaseModel implementation has a feature called nested properties. Basically if you do this:

    BaseModel a = new BaseModel();
    BaseModel b = new BaseModel();
    a.set("count", 3);
    b.set("blah", a);

    then you can do this:

    b.set("blah.count", 5);

    Interestingly, this will potentially trigger TWO Update events: one on "blah.count" in b and one on "count" in a.

    4. Model Collections

    Models are flat except, as noted above, for nested properties. While this is potentially useful it needs to go further. Specifically I'm talking about

    property values that are collections (namely lists). I've tried various ways of implementing some kind of ListModel but all the implementations are

    pretty ugly because the Model interface is so geared towards Maps. A Model implementation isn't exactly what you want either because then it can

    potentially have its own event listeners. If its not a Model a caller could get access to the List and modify it without triggering any events.

    BaseTreeModel sort of does this but its also limited in that theres only one type of child (more on trees below).

    Collection properties are a fairly compelling and common requirement (eg for items in a combo box) so I think this is something really lacking. About

    the cleanest way to handle this would be to use an observer approach. Ignoring nested properties for simplicity, that would mean something like:

    public class BaseModel ... {
    class ModelList<T> extends AbstractList<T> {
    private final List<T> delegate;

    private ModelList(List<T> delegate) {
    if (delegate == null) {
    throw new NullPointerException("delegate is null");
    }
    this.delegate = delegate;
    }

    public int size() {
    return delegate.size();
    }

    public T get(int index) {
    return delegate.get(index);
    }

    public T set(int index, T element) {
    // Update event at index
    return delegate.set(index, element);
    }

    public void add(int index, T element) {
    // fire Add event at index
    delegate.add(index, element);
    }

    public T remove(int index) {
    // fire Remove event at index
    return delegate.remove(index);
    }
    }
    }

    Note: the above is just a rough guide.

    Various methods in BaseModel and BaseModelData would need to be modified to handle collection properties.

    Additionally, nested properties should support syntax like:

    set("names[3]", "John Smith");

    5. Tree Model

    I've posted on this previously but for completeness I'll mention it here too. Basically the TreeModel interface is a good example of the abuse of

    generics and bad design. Why? Because, TreeModel is an invasive interface. It forces every item in your tree model to implement TreeModel and leads to

    silly declarations like this:

    BaseTreeModel<BaseTreeModel> ...

    This needs a far cleaner interface. The basic concept of a container (which TreeModel is or at least should be) is that it can contain anything. Look

    no further than the Java Collections API and you'll see things like:

    List<T> ...

    not

    List<T extends ListItem> ...

    A good solid example of trees and tree models can be found in the swing JTree class.

    The naming isn't clear either. TreeModel is really a node, not the whole tree model. A rough starting poitn for a good TreeModel interface would be:

    interface TreeNode<T> {
    TreeNode<? extends T> getParent();
    List<TreeNode<? extends T>> getChildren();
    T getValue();
    TreeNode<T> insert(int index, T item);
    T remove(int index);
    void removeAll();
    T setValue(T newValue);
    }

    The list of children should either be a defensive copy of the children or unmodifiable.

    A TreeNode implementation like this will give you the ability to have async loads of children and so forth, something thats currently handled by a

    confusing mix of loaders, binders and stores.

    Oddly, TreeModel is generic in the current code but TreeItem isn't. If an internal UI-level tree is needed to mirror TreeNode (eg to contain styles and

    ohter tree-implementation-specific state) it should be generic.

    6. Events and Listeners

    Events are an area desperately in need of review in Ext-GWT. The events themselves are integer constants when they should be an enum. You might argue

    that this isn't extendible but thats not true. You simply have:

    interface EventType {
    String getName();
    int getNumber();
    }

    enum BaseEvents implements EventType {
    ...
    }

    enum ExtendedEvents implements EventType {
    ...
    }

    and so on and then rather than the enums, the interface is used.

    Events themselves are a concrete type with public fields. This should be an interface with a largely immutable implementation. For example:

    interface Event {
    EventType getEventType();
    Object getSource();
    }

    Also, the generic Listener interface is just painful. addListener() says nothing about what events the model or widget is firing. This is perfectly valid:

    textField.addListener(Events.BeforeAdd, new Listener ... )

    Listener is generic but not in a useful way. This has led to code like:

    tabPanel.addListener(Events.Remove, new Listener<ContainerEvent<TabPanel, TabItem>>() {
    ...
    });

    This is crazy. I'd much rather have correctly typed listeners and explicit add...Listener methods rather than addListener. For example:

    public interface Model ... {
    void addChangeListener(ChangeListener changeListener);
    }

    public interface ChangeListener {
    void propertyChanged(PropertyChangeEvent event); // curently ChangeEvent is passed in and you need to cast it
    }

    7. Inconsistent Generics

    Theres an awful lot of code that partially uses generics. For example, BaseTreeModel.adopt() has a parameter of type TreeModel, not TreeModel<something>. You'll find examples like this and assigning generic arguments to non-generic data members all over the place.

    8. Conclusion

    Theres some pretty scary stuff in here and I know I've only scratched the surface. The current release is 1.0rc2 and I really have to consider this a political 1.0 release. Its nowhere near ready for primetime. Once 1.0 is release this bizarre API is basically set in stone. Hell, its almost set in stone by the RC designation.

    On the project I'm working on we were using MyGWT and quite liked it. It was a natural move to Ext-GWT but we're being forced to abandon it entirely as we learn more about it in favour of vanilla GWT. Sure we won't have ContentPanels that way but we won't have BaseTreeModel<BaseTreeModel> either. A non-generic interface is preferable to a badly designed and inconsistent generic interface and thats what Ext-GWT is.

  2. #2
    Ext User sheesh-kebab's Avatar
    Join Date
    Jan 2008
    Location
    Washington, DC/Falls Church, VA
    Posts
    40
    Vote Rating
    0
      0  

    Default

    disregarding some rants in the post, I agree with the points - I've also noticed some generics abuse. My guess is that some of the things happened due to attempts to approximate extjs javascript api using java, which is rather badly suited for the task. More explicitly defined setters, getters, addxxxListener type api, mimiking Swing would probably look a bit more readable to a java developer.

  3. #3
    Ext User
    Join Date
    Jun 2008
    Posts
    15
    Vote Rating
    0
      0  

    Default

    I agree as well.
    Sometimes, there was so much generics use, I was confused as hell.

    Initially i thought, "wow that is a good way of using generics",
    but then I realized I had no idea what was going on.

    Please make it like Swing, its what Java developers are best with

  4. #4
    Ext User
    Join Date
    Jun 2008
    Posts
    6
    Vote Rating
    0
      0  

    Default

    Wholeheartedly agree. I previously raised a query about the Tree design, which was promptly ignored. GXT in it's current form is a massive opportunity wasted. It's quite possibly the most painful library I have ever had to work with. Every day I say to my guys, "have we found an alternative to GXT yet?" The closed open-source model (no access to subversion, private forums - how sad) and the RC status of the current codebase gives me little hope that GXT can be saved.

    Regards
    Craig

  5. #5
    Ext User
    Join Date
    Jun 2008
    Posts
    25
    Vote Rating
    0
      0  

    Default

    No doubt the Tree design completely baffled me. And when I was looking at how Google implemented it, it just made so much more sense to do it that way. Why was the wheel reinvented for something that already works so well?

    Apart from that, the rest is fairly easy to follow. Though I do wish there were more straightforward tutorials vs just samples of codes - the Tree source code did not help at all.

  6. #6
    Ext GWT Premium Member
    Join Date
    Jul 2008
    Posts
    29
    Vote Rating
    0
      0  

    Default

    I agree as well.
    Although GXT widgets look good enough but what is under the hood sometimes scares me away from adopting the framework.

  7. #7
    Ext User
    Join Date
    Apr 2008
    Posts
    376
    Vote Rating
    0
      0  

    Default

    Wow, this a is a really (too?) huge subject. I think you could have opened 7 threads instead of just one. Actually, some threads are already opened for some points. I give you the links.

    public <X> X get(String property);

    This is obviously so you can do things like:

    Date date = model.get("startDate")
    int pageCount = model.get("pageCount") // auto-unboxing to boot

    While this might be convenient in that you don't have to explicitly cast but because of type erasure, you're not actually gaining anything.
    +1. Let's follow on existing thread : http://extjs.com/forum/showthread.php?t=40670

    In Swing, models are basically just POJOs with relevant adapters like TableModel and frankly instead of:

    model.get("dateOfBirth")
    model.set("dependants", 2)

    I prefer:

    model.getDateOfBirth()
    model.setDependants(2)

    I realize some will disagree with this but remember that a strongly typed model like this doesn't preclude the use of Maps whereas the use of Maps makes strongly typed accessors and mutators little more than syntactic placebos. Granted also that GWT doesn't have access to reflection, which is potentially limiting
    I guess the lack of reflection is the reason we have these maps, but I also prefer strongly typed models. Here again, there are already other threads on that point but I'm glad you relaunch the discussion as this is an important point to me Maybe follow the discussion on http://extjs.com/forum/showthread.php?t=38371

    Even removing a property fires an Update event where the new value is null, which--from an event perspective--is indistinguishable from actually setting the property to null (even though the latter leaves the property in the map and the former does not). This is a problem.
    I have no real opinion about that, for now I have no need about this Update/Remove event

    3. Nested Properties
    Also never used because I try to keep strongly typed models and I don't use the get/set methods (except for things like displayProperty).

    4. Model Collections
    I think I don't understand what you say and don't see the interest (maybe because of my bad english level or maybe because I don't use the get/set methods as I said above).

    5. Tree Model
    Let's follow on existing thread : http://extjs.com/forum/showthread.php?t=40165

    6. Events and Listeners
    Let's follow on existing thread : http://extjs.com/forum/showthread.php?t=38213


    7. Inconsistent Generics

    Theres an awful lot of code that partially uses generics. For example, BaseTreeModel.adopt() has a parameter of type TreeModel, not TreeModel<something>. You'll find examples like this and assigning generic arguments to non-generic data members all over the place.
    It would be nice to open a new thread for that point. About BaseTreeModel.adopt() method, it is a private method so we don't care about it, but like you I think there are some places where there are inconsistent use of generics (but not so numerous places).

    8. Conclusion

    Theres some pretty scary stuff in here and I know I've only scratched the surface. The current release is 1.0rc2 and I really have to consider this a political 1.0 release. Its nowhere near ready for primetime. Once 1.0 is release this bizarre API is basically set in stone. Hell, its almost set in stone by the RC designation.
    Mmm, I would say yes and no It is true that some parts of code need (IMHO) to be refactored but the features are ready and that's what my team leader (and my customer) expect at first from the API, not the consistent use of generics . Actually, I really don't think the API is set in stone. I have used the API for more than 2 months, and most of the times the community suggest enhancement on the API, Darell (the core developer) integrate it. I really believe it will follow this way.
    By the way, when you say "I've only scratched the surface", you seem to believe there are a lot of other critical bad points in the API. I can say you that after 2 months of use, I haven't see other critical bad points, but I agree the API is enhanceable and I'll continue to contribute.

    mimiking Swing would probably look a bit more readable to a java developer
    I also like Swing. Isn't GXT mimiking SWT/JFace (also a nice API, I think) ?

  8. #8
    Ext User
    Join Date
    May 2008
    Posts
    105
    Vote Rating
    0
      0  

    Default

    Thanks for taking the time to write up your thoughts on Ext-GWT architecture, there are many valid points.

    I have a few comments.

    ...
    Date date = model.get("startDate")
    int pageCount = model.get("pageCount") // auto-unboxing to boot

    While this might be convenient in that you don't have to explicitly cast but because of type erasure, you're not actually gaining anything. This is perfectly legal code:
    In the context of "generics provide compile time type safety", you are absolutely correct, there is no gain. But still, since this construction makes a cast optional there is less noise in all the getters on your model object, for what it's worth...

    In Swing, models are basically just POJOs with relevant adapters like TableModel and frankly instead of:

    model.get("dateOfBirth")
    model.set("dependants", 2)

    I prefer:

    model.getDateOfBirth()
    model.setDependants(2)
    Well who doesn't? But isn't the real question how to support POJO models in a databinding? As you said:

    Granted also that GWT doesn't have access to reflection, which is potentially limiting.
    That's an understatement. ExtGWT has adopted a lightweight approach to provide introspection under the GWT JRE using Java Maps, and which has limitations like you point out.

    Another and IMHO very elegant approach was taken by the Gwittir guys. They actually wrote a Java beans style Introspector for GWT, look here. In their solution model objects just have to implement a marker interface (Bindable), and all necessary introspection code to hook up your POJO getters/setters is generated during compile time.
    Admittedly I haven't had time to testdrive their stuff, and they do point out that you should keep number of bindable objects to a minimal since their approach generates significant amounts of binding infrastructure code for your bound components.

    Interestingly, while poking around trying to determine licensing and overall readiness of their library I noticed this post. I believe Cameron Braid is a ExtJS employee, or at least one of the architects behind the ExtGWT generics?

    2. Model Events
    I believe your comments concerning model events are just minor bugs, shouldn't be a problem to fix.

    3. Nested Properties
    I am not 100% sure but I think you're not correct. Isn't cascading of update events from child to parent only applicable for children of a BaseTreeModel object (as in myTreeModel.add(new MyThreeModelChild) )


    4. Model Collections
    ExtGWT uses the Store approach for model collections required by combos and lists.

    As for your proposal, it resembles the Gwittir approach mentioned earlier. In Gwittir you can bind to any child/field on your model class using standard object notation in the binding classifier.


    5. Tree Model
    I agree most of your comments.

    Also there are, or at least was, issues with the GWT RPC/compiler when using plain BaseModel objects that return collection of other BaseModel objects. I ended up converting all my models to TreeModels to get around this, hence lots of model classes specifying themselves in the generic type specifier ...

    A TreeNode implementation like this will give you the ability to have async loads of children and so forth, something thats currently handled by a confusing mix of loaders, binders and stores.
    I found the mix of loaders, binders and stores confusing at first. But after making a sketch on how they are tied together as well as using them on paged tables I kinda like them, they offer reasonable flexibility. You can quite easily drop in your own DataProxy if needed. It was also straightforward to make a memory paged custom data store. But then, if you don't like the Store/Model stuff that might not be relevant.

    6. Events and Listeners
    Agree, my AppEvents file reminds of old days doing Windows programming....
    There were some other discussions on this, more specifically a proposal for how enum based events could be done. I'm not aware of status.


    tabPanel.addListener(Events.Remove, new Listener<ContainerEvent<TabPanel, TabItem>>() {
    ...
    });

    This is crazy. I'd much rather have correctly typed listeners and explicit add...Listener methods rather than addListener. For example:
    Yes, another example on why many people are put off by Java generics...

    It sure looks kinda ugly. Maybe it's just me but after getting used to generics your example line reads perfectly well. "Create a listener for removals of TabItems from the TabPanel, the Listener receives a ContainerEvent wich has access to the tabpanel itself as well as the removed tabitem.

    Do you mean that instead of this you want convenience methods for all possible listener s? , like tabPanel.addRemoveItemListener(TabItem i) ?

    public interface Model ... {
    void addChangeListener(ChangeListener changeListener);
    }

    public interface ChangeListener {
    void propertyChanged(PropertyChangeEvent event);
    }
    This is for model objects and all well, looks like the Gwittir stuff. But I don't see how this applies to your generic example above.

    7. Inconsistent Generics

    Theres an awful lot of code that partially uses generics. For example, BaseTreeModel.adopt() has a parameter of type TreeModel, not TreeModel<something>. You'll find examples like this and assigning generic arguments to non-generic data members all over the place.
    Agreed, a clean-up up is necessary. Also, there are still classes with public properties sticking around.

    8. Conclusion

    Theres some pretty scary stuff in here and I know I've only scratched the surface. The current release is 1.0rc2 and I really have to consider this a political 1.0 release. Its nowhere near ready for primetime. Once 1.0 is release this bizarre API is basically set in stone. Hell, its almost set in stone by the RC designation.
    I totally disagree, there is nothing scary here. On the contrary, everything is transparent and IMHO things are working pretty darn well.

    On a side note, ExtGWT has generally lower performance compared to plain GWT. As far as I understand GWT wraps pure HTML constructs. So I believe standard GWT widgets will always be bound by HTML and never be as advanced as ExtGWT widgets unless GWT departs from this principle, right?

    On the project I'm working on we were using MyGWT and quite liked it. It was a natural move to Ext-GWT but we're being forced to abandon it entirely as we learn more about it in favour of vanilla GWT. Sure we won't have ContentPanels that way but we won't have BaseTreeModel<BaseTreeModel> either. A non-generic interface is preferable to a badly designed and inconsistent generic interface and thats what Ext-GWT is.
    Falling back to plain GWT? Good luck writing your own binding stuff, advanced tables, MVC classes, and all the other stuff missing from GWT. Many development projects did just that during two 1-2 years of GWT existence, with a great cost.
    Gwittir is definitely not ready for prime time despite its slick binding stuff, bindings are only implemented for a couple of widgets and the whole site resembles a proof-of-concept thing, albeit a powerful proof. It's also GPL without a commercial offering so far.
    Then there is Ext-JS with great widgets, but being a JavaScript wrapper I assume any binding and MVC stuff is gone, and no tracing of GUI code in hosted mode...

    Plain GWT would sure be a lot nicer if Google included a data binding mechanism like Gwittir's , data loading abstractions, and advanced tables and lists.

    I am curious what, except for speed wich you haven't mentioned, was better in MyGWT? MyGWT also used the Model stuff, although it wasn't a interface at that time.
    The binding mechanism in MyGWT was a JFace implementation, I assume that is what you are referring to? But as far as I remember that mechanism also relied on the Map based introspection provided by the BaseModel objects, similar like today... ?

    cheers!

  9. #9
    Ext User Cameron Braid's Avatar
    Join Date
    Mar 2008
    Posts
    6
    Vote Rating
    0
      0  

    Default

    You raise lots of good points.

    FYI I am not an Ext employee, nor am I contracted by them. I am a sole trader.

    I use GXT in a web application and I am try to help Darrell make GXT better.

    Regards

    Cameron Braid

  10. #10
    Sencha User darrellmeyer's Avatar
    Join Date
    May 2007
    Location
    Washington, DC
    Posts
    2,242
    Vote Rating
    2
      0  

    Default

    In Swing, models are basically just POJOs with relevant adapters like TableModel and frankly instead of:

    model.get("dateOfBirth")
    model.set("dependants", 2)

    I prefer:

    model.getDateOfBirth()
    model.setDependants(2)
    The get and set methods are not designed to be called directly. The intent is for you to create model subtypes that have the appropriate getters and setters. The callers do not need to be aware that the values are stored in a map. The library uses the set and get methods internally. You are free to use your typed getters and setters in your application. If you follow this model, it really does not matter how generics are used internally.

Page 1 of 3 123 LastLast

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •