Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
Clear All
new posts

    Simple Pub/Sub EventBus for loosley coupled component wiring

    Hello, I have tried out an extremely simple (stripped down) EventBus design for allowing UI components to interact with each other in a loosely coupled way.
    The design is influenced by the excellent EventBus project by Michael Bushe.

    Here is a simple scenario...

    A DynamicForm reads keywords from a "Keywords" TextItem when it Search Button is clicked. It then performs a search via a DataSource using the keywords as query param for the datasource.dataUrl. It then creates a ListGrid that displays the results and published the ListGrid as an Event to a topic named "SearchResults" on a new EventBus class.

    Elsewhere in the UI a TabSet is subscribed to the EventBus for the "SearchResults" topic. Whenever a ListGrid is published on the "SearchResults" topic it receives the event on onEvent listener method and adds the ListGrid as a Tab within itself.

    The DynamicForm (publisher) and TabSet (Subscriber) do not know about each other (are loosely coupled and communicate via the EventBus as an intermediary.

    Here is the simple code for the EventBus and above example. I would appreciate thoughts on this. It does work BTW:

    Here is the EventBus class itself:

    Code:
    /**
     * A very simple EventBus for loosely coupled event driven coupling between components.
     * There is no buffering and events are lost if no listeneres are registered when event is published.
     * Events are relayed serially to Listener in order that they were added.
     *
     * @author najmi
     */
    public class EventBus {
        static Map<String, List<TopicSubscriber>> topicSubscribersMap = new HashMap<String, List<TopicSubscriber>>();
    
        /**
         * Publish an Object to a topic.
         *
         * @param topic the topic being published to
         * @param o the event object being published
         */
        public static void publish(String topic, Object o) {
            System.err.println("Publish entered");
            List<TopicSubscriber> topicSubscribers = topicSubscribersMap.get(topic);
            if (topicSubscribers != null) {
                for (TopicSubscriber topicSubscriber : topicSubscribers) {
                    System.err.println("Sending event to subscriber: " + topicSubscriber);
                    topicSubscriber.onEvent(topic, o);
                }
            }
        }
    
        public static void subscribe(String topic, TopicSubscriber listener) {
            System.err.println("Subscribe entered");
            List<TopicSubscriber> topicListeners = topicSubscribersMap.get(topic);
            if (topicListeners == null) {
                topicListeners = new ArrayList<TopicSubscriber>();
                topicSubscribersMap.put(topic, topicListeners);
            }
    
            topicListeners.add(listener);
        }
    }
    Here is the TopicSubscriber interface that is implemented by subscriber (TabSet in example):

    Code:
    public interface TopicSubscriber<T> {
        /**
         * Listener that gets notified of an event on a topic.
         *
         * @param topic the topic on which event occured
         * @param o the event object
         */
        public void onEvent(String topic, T o);
    }
    Here is the form Button's onClickHandler that publishes ListGrid to EventBus as an Event publisher:
    Code:
            searchButtonItem.addClickHandler(new ClickHandler() {
    		public void onClick(ClickEvent event) {
                        ListGrid resultsGrid = createResultsGrid();
                        EventBus.publish("SearchResults", resultsGrid);
                    }
            });
    Here is the code for the customized TabSet sub-class which is the TopicSubscriber and receives events:
    Code:
    public class MainTabSet extends TabSet implements TopicSubscriber<Canvas> {
    
        ...
        public void onEvent(String topic, Canvas canvas) {
            if (topic.equals("SearchResults")) {
                Tab tab = new Tab("Result " + c.getID());
                tab.setCanClose(true);
                tab.setPane(c);
                this.addTab(tab);
            }
        }
    }
    Please share any thoughts on this extremely light-weight and simple design for loosely coupled pub/sub communication and wiring between components in GWT/SmartGWT.

    Are there pitfalls that I am not seeing?
    Last edited by farrukh_najmi; 5 Feb 2009, 12:59.

    #2
    I think it misses the unsubscribe part, but i'll give it a try anyway, there is still no support for this in smartGWT.

    Comment


      #3
      Ok, just added an unsubscribe method on your solution, so now we have 3 classes instead of 2:
      EventBus
      TopicSubscriber
      Subscription

      the updated code is:
      Code:
      import java.util.ArrayList;
      import java.util.HashMap;
      import java.util.List;
      import java.util.Map;
      
      /**
       * A very simple EventBus for loosely coupled event driven coupling between
       * components. There is no buffering and events are lost if no listeners are
       * registered when event is published. Events are relayed to Listeners in the
       * order that they were added.
       * 
       * @author najmi, mihai007
       */
      @SuppressWarnings("unchecked")
      public class EventBus {
      
      	static Map<String, List<TopicSubscriber>> topicSubscribersMap = new HashMap<String, List<TopicSubscriber>>();
      
      	/**
      	 * Publish an Object to a topic.
      	 * 
      	 * @param topic
      	 *            the topic being published to
      	 * @param o
      	 *            the event object being published
      	 */
      	public static void publish(String topic, Object o) {
      		List<TopicSubscriber> topicSubscribers = topicSubscribersMap.get(topic);
      		if (topicSubscribers != null) {
      			for (TopicSubscriber topicSubscriber : topicSubscribers) {
      				topicSubscriber.onEvent(topic, o);
      			}
      		}
      	}
      
      	public static Subscription subscribe(String topic, TopicSubscriber listener) {
      		List<TopicSubscriber> topicListeners = topicSubscribersMap.get(topic);
      		if (topicListeners == null) {
      			topicListeners = new ArrayList<TopicSubscriber>();
      			topicSubscribersMap.put(topic, topicListeners);
      		}
      		topicListeners.add(listener);
      		return new Subscription(topic, listener);
      	}
      
      	public static void unsubscribe(Subscription subscription) {
      		String topic = subscription.getTopic();
      		TopicSubscriber<?> listener = subscription.getListener();
      		List<TopicSubscriber> topicListeners = topicSubscribersMap.get(topic);
      		if (topicListeners != null) {
      			topicListeners.remove(listener);
      			if (topicListeners.isEmpty()) {
      				topicSubscribersMap.remove(topic);
      			}
      		}
      	}
      }
      Code:
      public interface TopicSubscriber<T> {
      	
      	/**
      	 * Listener that gets notified of an event on a topic.
      	 * 
      	 * @param topic
      	 *            the topic on which event occurred
      	 * @param o
      	 *            the event object
      	 */
      	public void onEvent(String topic, T o);
      }
      Code:
      /**
       * Class that represents a subscription to a given topic with the given listener
       * 
       * @author mihai007
       * 
       */
      public class Subscription {
      
      	private final String topic;
      	private final TopicSubscriber<?> listener;
      
      	public Subscription(String topic, TopicSubscriber<?> listener) {
      		this.topic = topic;
      		this.listener = listener;
      	}
      
      	public String getTopic() {
      		return topic;
      	}
      
      	public TopicSubscriber<?> getListener() {
      		return listener;
      	}
      
      }
      Comments are welcome!

      Comment


        #4
        Ok, from the topic's visits it seems that there is some interest in this, so I thought I should share back the minor improvements I did over the previous version.

        Now it's possible to remove the subscription when you received the message without creating a global Subscription variable, avoid problems when unsubscribing while the publish is still working on the same topic, documented the methods.

        I am currently using this version of the EventBus:

        EventBus:
        Code:
        import java.util.ArrayList;
        import java.util.HashMap;
        import java.util.List;
        import java.util.Map;
        
        /**
         * A very simple EventBus for loosely coupled event driven coupling between
         * components. There is no buffering and events are lost if no listeners are
         * registered when event is published. Events are relayed to Listeners in the
         * order that they were added.
         * 
         * @author najmi, mihai007
         */
        public class EventBus {
        
        	static Map<String, List<Subscription>> topicSubscribersMap = new HashMap<String, List<Subscription>>();
        
        	/**
        	 * Publish an Object to a topic if the topic exists
        	 * 
        	 * @param topic
        	 *            the topic being published to
        	 * @param o
        	 *            the event object being published
        	 */
        	@SuppressWarnings("unchecked")
        	public static void publish(String topic, Object o) {
        		List<Subscription> topicSubscribers = topicSubscribersMap.get(topic);
        		if (topicSubscribers != null) {
        			ArrayList<Subscription> tmpList = new ArrayList<Subscription>();
        			for (Subscription subscription : topicSubscribers) {
        				tmpList.add(subscription);
        			}
        			for (Subscription subscriber : tmpList) {
        				TopicSubscriber listener = subscriber.getListener();
        				listener.onEvent(subscriber, o);
        			}
        		}
        	}
        
        	/**
        	 * Creates a subscription to a given topic, if the topic doesen't exists a
        	 * new one will be created
        	 * 
        	 * @param topic
        	 *            the topic for the subscription
        	 * @param listener
        	 *            the listener to notify when receiving messages
        	 * @return a {@link Subscription} that represents this subscription, to be
        	 *         used later by {@link #unsubscribe(Subscription)}
        	 */
        	public static Subscription subscribe(String topic,
        			TopicSubscriber<?> listener) {
        		Subscription subscription = new Subscription(topic, listener);
        		List<Subscription> topicSubscribers = topicSubscribersMap.get(topic);
        		if (topicSubscribers == null) {
        			topicSubscribers = new ArrayList<Subscription>();
        			topicSubscribersMap.put(topic, topicSubscribers);
        		}
        		topicSubscribers.add(subscription);
        		return subscription;
        	}
        
        	/**
        	 * Removes a subscription to the topic indicated by it if topic still exists
        	 * (if this subscription is the last one in the topic, the topic itself will
        	 * be removed from the {@link EventBus})
        	 * 
        	 * @param subscription
        	 *            the subscription to be removed
        	 */
        	public static void unsubscribe(Subscription subscription) {
        		String topic = subscription.getTopic();
        		List<Subscription> topicSubscribers = topicSubscribersMap.get(topic);
        		if (topicSubscribers != null) {
        			topicSubscribers.remove(subscription);
        			if (topicSubscribers.isEmpty()) {
        				topicSubscribersMap.remove(topic);
        			}
        		}
        	}
        }
        Subscription:
        Code:
        /**
         * Class that represents a subscription to a given topic with the given listener
         * 
         * @author mihai007
         * 
         */
        public class Subscription {
        
        	private final String topic;
        	private final TopicSubscriber<?> listener;
        
        	/**
        	 * Creates a new subscription reference given the topic and it's listener
        	 * 
        	 * @param topic
        	 *            the topic for the subscription
        	 * @param listener
        	 *            the listener associated to it
        	 */
        	protected Subscription(String topic, TopicSubscriber<?> listener) {
        		this.topic = topic;
        		this.listener = listener;
        	}
        
        	/**
        	 * Returns the topic for the subscription
        	 * 
        	 * @return a {@link String} containing the topic for the subscription
        	 */
        	public String getTopic() {
        		return topic;
        	}
        
        	/**
        	 * Returns the listener associated with the subscription
        	 * 
        	 * @return a {@link TopicSubscriber} containing the listener for the subscription
        	 */
        	public TopicSubscriber<?> getListener() {
        		return listener;
        	}
        
        }
        TopicSubscriber:
        Code:
        public interface TopicSubscriber<T> {
        
        	/**
        	 * Listener that gets notified of an event on a topic.
        	 * 
        	 * @param subscription
        	 *            the subscription that generated the event
        	 * @param event
        	 *            the event object
        	 */
        	public void onEvent(Subscription subscription, T event);
        }
        So a usecase for it:
        Code:
        Subscription subscription = EventBus.subscribe("test", new TopicSubscriber<DataSource>() {
        			public void onEvent(Subscription subscription, DataSource event) {
        				EventBus.unsubscribe(subscription); //unsubscribe after receiving the first DataSource message
        				// TODO process datasource
        			}
        		});
        
        
        EventBus.publish("test", ds);

        Comment


          #5
          Hi mihai007,

          Thanks for your valuable improvements and sorry for being silent for some time.
          I have added my initial code for EventBus to the newly created smartgwt-ext project.
          I would be grateful if you could resubmit your changes as patches to this initial code base in svn in project at:

          https://smartgwt-extensions.googlecode.com/svn/trunk

          You probably should also request Sanjiv for committer status in smartgwt-ext project so we can improve this together. Thanks.

          You can reach me off-list at farrukh-at-wellfleetsoftware.com as needed. Thanks again.

          Comment


            #6
            Hello. Just posted changes in revision r16. I used this version since February without any problems so far, hope it will be the same for the rest of the users.

            EDIT: and with this, my #100 post on this forum :)
            Last edited by mihai007; 25 Mar 2009, 07:52.

            Comment


              #7
              Good Stuff

              Thank you for the good work on event pub/sub. This will help out in detaching UI code as much as possible.

              Once again , Thank you.

              Comment


                #8
                Hi ,
                I am new to gwt and also to the eventbus.
                I am trying to implement the eventbus and i am not sure how to get the implement event subscriber.So this is what i am doing.

                I want the login displayed on the lower part of the panel when user clicks on "GO" link.
                So in HeaderMenu class, onClick method , i fire the event
                In SearchResultPanel, i want to listen to that event and display the name as "Welcome: blah blah blah"

                2 things here which i am having questions
                Question 1. When i implement TopicSubscriber<AppUser>, i have to implement the following method

                public void onEvent(Subscription subscription, AppUser event) {
                // TODO Auto-generated method stub
                userName = event.getUserName();
                SC.say("This event is not getting fired :"+event.getUserName());
                }

                This event doesnot get fired.Why is that?

                Question 2.
                If i add this as a part of the constructor, it does get called but the userName on th searchPanel still remains null
                public SearchResultPanel(){

                Subscription subscription = EventBus.subscribe("authenticateUser", new TopicSubscriber<AppUser>() {

                public void onEvent(Subscription subscription, AppUser user) {
                // TODO Auto-generated method stub
                userName = user.getUserName();
                SC.say("event called at the time of constructor:"+user.getUserName());
                }

                });



                }


                ********************
                This is my Subscriber class
                ********************
                package com.myapp.web.gwt.client.search;

                import com.myapp.web.gwt.client.eventbus.EventBus;
                import com.myapp.web.gwt.client.eventbus.Subscription;
                import com.myapp.web.gwt.client.eventbus.TopicSubscriber;
                import com.myapp.web.gwt.shared.vo.authentication.AppUser ;
                import com.smartgwt.client.util.SC;
                import com.smartgwt.client.widgets.Canvas;
                import com.smartgwt.client.widgets.Label;
                import com.smartgwt.client.widgets.layout.VLayout;
                import com.smartgwt.client.widgets.tab.Tab;
                import com.smartgwt.client.widgets.tab.TabSet;

                public class SearchResultPanel implements TopicSubscriber<AppUser>{

                static String userName ;
                public SearchResultPanel(){

                Subscription subscription = EventBus.subscribe("authenticateUser", new TopicSubscriber<AppUser>() {

                public void onEvent(Subscription subscription, AppUser user) {
                // TODO Auto-generated method stub
                userName = user.getUserName();
                SC.say("event called at the time of constructor:"+user.getUserName());
                }

                });



                }

                public Canvas getBasicSearchResults() {

                VLayout testLayout = new VLayout();
                Label welcome =null;
                welcome =new Label("Welcome "+userName);
                TabSet tabs = new TabSet();
                Tab tab = new Tab("Current Status " );
                tab.setCanClose(false);
                tabs.addTab(tab);
                tabs.setWidth("100%");
                tabs.setHeight("100%");
                testLayout .addMember(welcome);
                testLayout .addMember(tabs);
                return testLayout ;
                }

                public void onEvent(Subscription subscription, AppUser event) {
                // TODO Auto-generated method stub
                userName = event.getUserName();
                SC.say("This event is not getting fired :"+event.getUserName());
                }

                }


                **********************************
                This is the MainClass
                **********************************
                package com.myapp.web.gwt.client;

                import com.google.gwt.core.client.EntryPoint;
                import com.google.gwt.core.client.GWT;
                import com.myapp.web.gwt.client.header.HeaderMenu;
                import com.myapp.web.gwt.client.search.SearchResultPanel;
                import com.smartgwt.client.widgets.layout.VLayout;

                /**
                * Entry point classes define <code>onModuleLoad()</code>.
                */
                public class MyappMain implements EntryPoint {
                /**
                * The message displayed to the user when the server cannot be reached or
                * returns an error.
                */
                private static final String SERVER_ERROR = "An error occurred while "
                + "attempting to contact the server. Please check your network "
                + "connection and try again.";

                /**
                * Create a remote service proxy to talk to the server-side Myapp service.
                */
                private final MyappServiceGWTWrapperAsync myappService = GWT.create(MyappServiceGWTWrapper.class);

                /**
                * This is the entry point method.
                */
                public void onModuleLoad() {
                VLayout fullPage= new VLayout();
                fullPage.setBackgroundColor("#D8D8D8");
                fullPage.setWidth("100%");
                fullPage.setHeight("100%");
                fullPage.addChild(HeaderMenu.getHeaderMenuPage());
                SearchResultPanel searchResultPanel = new SearchResultPanel();
                fullPage.addChild(searchResultPanel.getBasicSearch Results());
                fullPage.draw();
                }

                // Create a handler for the sendButton and nameField
                }

                **********************************
                This is the Class who is publishing the event
                **********************************
                package com.myapp.web.gwt.client.header;

                import com.myapp.web.gwt.client.eventbus.EventBus;
                import com.myapp.web.gwt.shared.vo.authentication.AppUser ;
                import com.smartgwt.client.types.Alignment;
                import com.smartgwt.client.util.SC;
                import com.smartgwt.client.widgets.Canvas;
                import com.smartgwt.client.widgets.Img;
                import com.smartgwt.client.widgets.form.DynamicForm;
                import com.smartgwt.client.widgets.form.fields.LinkItem;
                import com.smartgwt.client.widgets.form.fields.PasswordIt em;
                import com.smartgwt.client.widgets.form.fields.SpacerItem ;
                import com.smartgwt.client.widgets.form.fields.TextItem;
                import com.smartgwt.client.widgets.form.fields.events.Cli ckEvent;
                import com.smartgwt.client.widgets.form.fields.events.Cli ckHandler;
                import com.smartgwt.client.widgets.layout.HLayout;
                import com.smartgwt.client.widgets.layout.LayoutSpacer;

                public class HeaderMenu {

                public static HLayout getHeaderMenuPage(){
                HLayout headerMenu = new HLayout();
                headerMenu.setWidth("100%");
                headerMenu.setHeight("5%");

                headerMenu.setBackgroundColor("white");
                Img myLogo = new Img();
                myLogo .setSrc("http://www.abc.com/img/LOGO.JPG");
                myLogo .setHeight("100%");


                LayoutSpacer layoutSpacer = new LayoutSpacer();
                layoutSpacer .setWidth("75%");
                layoutSpacer.setBackgroundColor("black");
                Canvas loginCanvas= getLogin();

                headerMenu.addMember(myLogo);
                headerMenu.addMember(layoutSpacer);
                headerMenu.addMember(loginCanvas);
                return headerMenu;

                }


                public static HLayout getLogin() {
                HLayout loginLayout = new HLayout();
                final DynamicForm loginForm = new DynamicForm();

                final TextItem loginText = new TextItem("loginId");
                loginText .setTitle("Login");
                loginText.setValue("sdss");
                final PasswordItem passwordItem = new PasswordItem ("password");
                passwordItem .setTitle("Password");

                LinkItem submitItem = new LinkItem ("");
                //submitItem .setTitle("LinkItem");
                submitItem .setName("");
                submitItem .setLinkTitle("Go");
                submitItem .setShouldSaveValue(false);
                submitItem .addClickHandler(new ClickHandler(){

                public void onClick(ClickEvent event) {
                // TODO Auto-generated method stub
                if (loginForm.getValue("loginId")!=null && loginForm.getValue("loginId").toString().length()> 0 &&
                loginForm.getValue("password")!=null && loginForm.getValue("password").toString().length() >0 ){

                AppUser user = new AppUser ();
                System.out.println("im here "+loginForm.getValue("loginId").toString());
                user .setUserName(loginForm.getValue("loginId").toStrin g());
                user .setPassword(loginForm.getValue("password").toStri ng());
                EventBus.publish("authenticateUser", user);
                } else if(loginForm.getValue("loginId")== null || loginForm.getValue("loginId").toString().trim().le ngth() ==0){
                SC.warn("Login Id is required");

                }else if(loginForm.getValue("password")== null || loginForm.getValue("password").toString().trim().l ength() ==0){
                SC.warn("Password is required");
                }
                }});

                loginForm.setBackgroundColor("");
                loginForm.setNumCols(8);


                loginForm.setID("loginForm");
                SpacerItem spItem = new SpacerItem();
                spItem .setWidth(1);
                loginForm.setItems(loginText,passwordItem,spItem ,submitItem);
                loginForm.setAlign(Alignment.RIGHT);
                loginLayout.addChild(loginForm);
                loginLayout.setWidth("100%");
                return loginLayout;
                }
                }

                Comment


                  #9
                  SmartGWT Admins:

                  Is this unofficially or otherwise accepted as a "good" way to decouple logic using SmartGWT?

                  Anyone:

                  Are there any working examples for this? Code snippets would be great.

                  Thanks,

                  Comment


                    #10
                    You keep paraphrasing this same question - but the answer will keep being the same: the best strategy for modularity / encapsulation / de-coupling is application-specific.

                    Like any other pattern, an Event Bus applies in some situations and obfuscates others.

                    Anyone who tries to give you a single pattern for all applications is simply inexperienced - has not seen enough different kinds of systems to realize there is no single "holy grail" pattern.

                    Comment


                      #11
                      Our team is attempting to learn the SmartGWT API while forming an approach to coding with it that will lend itself to maintainability, etc.. As we do this we come across several approaches.

                      These questions are necessary as we learn how to use SmartGWT. We have short timelines and many requirements.

                      Most of my posts (in the MVP or Not thread) illustrate a general sense of what we need to do:

                      We have decided to:
                      1: Use the datasources and leave all fetching, etc. to them and not try to re-invent the wheel with a pattern (of our own, or in the community)

                      2: Determine the best way to source and listen for events that will make DMI calls that have lilttle or nothing to do with datasources.

                      3: Determine the best way to update UI components based on actions made within other components without creating an environment in which the components are tightly coupled.

                      I hope this helps clarify our goal(s) with this line of questioning.

                      Thanks,

                      Comment


                        #12
                        As an example of functionality our application requires:

                        Component A exposes a button btnFoo
                        When btnFoo is clicked, it's handler needs to:

                        1: Make a dmi call
                        2: Update state on some other component (disable interaction in Components B and C until DMI operation is successful)

                        We are hoping to use the EventBus to broadcast and handle events to assist with the de-coupling of the application code.

                        Additionally, we would like to leverage the EventBus to broadcast events to components when certain messages are received using the Real-time Messaging module.

                        E.g. a status message is received and two instances of Component A and one instance of Component C need to update certain display members.

                        Thanks for the continued support
                        Last edited by ls3674; 3 Jan 2012, 13:46.

                        Comment


                          #13
                          Once again, explaining the use case as "making a DMI and updating some other component" does not give information that would allow any kind of recommendation as to how to organize your code.

                          Realize this translates to "call the server and change something in the UI". All web applications do this. Different web applications should organize their code very differently.

                          In this post, we showed an example of the right way to think about code organization - in terms of requirements for functionality and resilience against specific future customizations and changes.

                          If it helps, then please consider the advice to be: leave the DMI call in the button click handler, or wherever is most expedient, until you have understood a reason why it shouldn't be there.

                          As far as 'broadcast':

                          1. if what's being broadcast is a change to a DataSource record, use DataSource.updateCaches()

                          2. if it's something else, the EventBus / Publish-Subscribe pattern may apply, however, it would generally be incorrect to use this pattern for every server call or every user event.

                          Comment


                            #14
                            Originally posted by ls3674
                            As an example of functionality our application requires:

                            We are hoping to use the EventBus to broadcast and handle events to assist with the de-coupling of the application code.

                            Additionally, we would like to leverage the EventBus to broadcast events to components when certain messages are received using the Real-time Messaging module.

                            E.g. a status message is received and two instances of Component A and one instance of Component C need to update certain display members.

                            Thanks for the continued support
                            I had intended this portion of the post to explain the use case that goes beyond

                            Realize this translates to "call the server and change something in the UI..."
                            Apologies for the unclear definition/communciation

                            What I was attempting to communicate in the post is that there are a myriad of status messages that our web interface needs to update regulary. These status values will be communicated using the ISCMessageDispatcher on the server and the Messaging class on the client. Is it a good practice to use the event bus on the client to send update all of the client labels, buttons, icons (which are not data driven) with the EventBus.

                            A more specific reason for this question is that we have noticed that there appears to be a siginificant performance hit when several messages are sent to the browser when using the Real-time Messaging Module- the user interface interaction slows down considerably, and we would like to leverage the EventBus to alleviate this.

                            Additionally, we would like to know wheather using the EventBus for this specific use case is a good or a bad idea.

                            Our question seems repetitive, we recognize this, however there are sublte differences each time we ask, for exmple, in the MVP thread, it concerned MVP and GIN. We appreciate your advice specifically with regards to encapsulation and we fully intend to use that. As a follow-on we would like to know if you think (given our use case) if using said encapsulation and the EventBus is something you would recommend.

                            With continued thanks,

                            Comment


                              #15
                              Re: performance - EventBus will not have an impact one way or another on performance. There's no significant performance hit from Messaging-as-such, but the *actions* you take in response to the messages may take some time. For example, in the Messaging samples in the Showcase, one sample continuously regenerates a chart. This is obviously quite a bit faster in eg Firefox or Chrome than IE.

                              So, if you are ending up sending several messages in a row that all cause UI updates, and this means you update UI components so fast that the user doesn't even see the intervening changes (eg, >30 frames a second, or even updating twice while handling the same Messaging callback, so that the intervening change is never even drawn), then you should correct this, but you would do so by throttling updates or combining updates that have the same visual effect or reverse each other's visual effect. This again would be unrelated to EventBus, it's domain-specific logic.

                              On the right pattern to use for real-time updates, this new explanation unfortunately does not contain any new information about feature requirements or future changes you want to be resilient against, which again are what drives code organization.

                              So all we can do is give another example of good code organization for a use case that *is* well-described in terms of requirements. Let's say that, similar to the RTM example that shows a chart, you've got charts and other components showing visual status that are being fed from a real-time data stream, and let's say that *there can be N such charts or status indicators* and the *end-user can create and configure them on the fly* so there's no way to know the full set of charts or indicators in advance.

                              Here, you want an EventBus / PubSub pattern where the charts and indicators can subscribe to get updates. The key information that leads to this code organization structure is that the charts and indicators can be created on the fly. This means that you couldn't just write code like myStatusIndicator.setNewState() because there's a dynamic set of indicators. So you *must* use a PubSub approach.

                              These requirements say nothing about whether a button that initiates a DMI call should or shouldn't just have the DMI call right in the click handler. Other requirements, as discussed in the MVP thread, might lead to wanting to move the code out of the click handler.

                              Comment

                              Working...
                              X