当前位置: 首页 > 工具软件 > GWT INjection > 使用案例 >

关于GWT-Presenter的简介

全兴运
2023-12-01

原文出自GWT-Presenter项目的创建者对该项目的简要介绍:gwt-presenter explained

 

NOTE: Feb 12, 2010: We have upgraded gwt-presenter to 1.1.1 from replace-branch, which has heavy impact on how we handle places. After reading this post, go to gwt-presenter upgrade and read about the place changes.

 

It took me quite some time browsing source code and examples to fully understand how gwt-presenter works. This will be a short walkthrough of how the framework works with some references to its source code. I will make references to source code classes that you can browse online here. I really want to give the developers of gwt-presenter big thumbs up for a great job. It is a great implementation and there seems to be even more improvements on the way.

 

Okey. So what is gwt-presenter really? It is a GWT implementation of the MVP pattern as described by Ray Ryan at Google I/O 2009. It also integrates support for history handling, event handling and GIN integration. GIN is a GWT framework for dependency injection which we will use in this project as well.

 

If you're not familiar with MVP, have a look here

 

Some good gwt-presenter guides/examples: gwt-presenter Getting Started , Apache HUPA project and TurboManage blog

 

We will take a look at the following concepts: Presenter, Display, Places and EventBus.

 

Let's have a quick look at how the Presenter and Display interfaces look.

 

public interface Presenter {
    void bind();
    void unbind();
    Display getDisplay();
    void refreshDisplay();
    void revealDisplay();
}

public interface Display {
    void startProcessing();
    void stopProcessing();
}

 

(注:该实现基于gwt-presenter1.0 版本)

 

When you implement your presenter, you can implement the Presenter interface directly but you will likely want to inherit from BasicPresenter or WidgetPresenter. WidgetPresenter inherits from BasicPresenter and only adds a Display interface via WidgetDisplay. But let's focus on BasicPresenter now since this is where the logic is. BasicPresenter adds the following abstract methods that you must implement:

 

protected abstract void onBind();
protected abstract void onUnbind();
public abstract Place getPlace();
protected abstract void onPlaceRequest( PlaceRequest request );

 

BasicPresenter implements bind(), unbind(), getDisplay() and revealDisplay() from Presenter interface, so you don't generally need to bother with those. Let's go over a typical scenario where we subclass BasicPresenter.

 

When you create your presenter, you must provide BasicPresenter with references to a Display implementation and a reference to the EventBus. The Display implementation will be your View. The EventBus is a gwt-presenter class that inherits from GWT HandlerManager, which is how GWT handles events. The eventbus has an important role in the MVP patterns as it makes it possible for us to really enforce loose coupling in our app. Our presenters and views that reside on the same abstraction layer should have no knowledge of each other. The only thing they care about are events. Who triggers the event and who handles it is not their concern.

 

Once you have created your presenter, you will want to call bind() on it before you use it in any way. This will give you a callback to onBind() in your presenter, where you typically setup event handlers etc for any user interaction that your corresponding view offers. BasicPresenter will also register a PlaceRequestHandler if you have defined a Place in your presenter. Typically you will do this for most of your presenters. The place string you provide is bascially the history token that will trigger this presenter to activate. You can however also trigger this manually via a PlaceRequestEvent, which is useful. You can also define parameters for your place. For example, if you have a ShowPersonPresenter you probably want to handle a Person with a specific id. You can then add id as a parameter and gwt-presenter will handle that for you.

 

When a place event is triggered that matches your presenter place token, you will get a callback in your presenter's onPlaceRequest(PlaceRequest request). This is where you typically do the real work by fetching the model data and updating the view with that data. Another nifty thing that happens when this event is handled is that revealDisplay() is called on your presenter. Unless you override this method, BasicPresenter will fire a PresenterRevealedEvent with your presenter as parameter. Once you start layering your app, you will have nested presenters. We will look at this later, but basically your parent presenter will get a callback that this presenter is active and should be displayed. It's all very automagic  .

 

When you are "done" with the presenter, you call unbind() on it and BasicPresenter will remove all event handlers and give you a callback to onUnbind() where you basically undo what you did in onBind().

 

Okey. So let's have a look at how the Display interface interacts with your presenters and views. Remember earlier that we talked about WidgetPresenter and WidgetDisplay? These are the classes you will want to inherit from for your widget presenters/views. WidgetDisplay extends Display interface and adds a method asWidget(). This is necessary to actually use your widgets outside of the view class. So let's assume now that we are writing widget stuff and inherit from those classes.

 

You define all your presenter-view interaction in the Display interface, which you define in your presenter like this:

 

public interface Display extends WidgetDisplay {
	void setData(Data data);
	HasClickHandlers getEdit();
	HasClickHandlers getDelete();
}

 

The above is just a typical example of how the interface might look. We have a method setData() to update our view with data, and two methods for getting the HasClickHandlers interface from user interaction elements in the view. These are often buttons which we will add ClickHandlers to in our presenters bind() method. You do something like this:

 

display.getDelete().addClickHandler(new ClickHandler() {
	@Override
	public void onClick(ClickEvent event) {
		eventBus.fireEvent(new DeletePersonEvent("id of Person"));
	}
});

 

Let's move on and have a look at our View, the Display implementation, which typically looks like this:

 

public class OurView extends Composite implements OurPresenter.Display {

	private VerticalPanel mPanel;
	private Button mSaveButton;
	private Button mCancelButton;

	public OurView(){

		mPanel = new VerticalPanel();
		mSaveButton = new Button();
		mCancelButton = new Button();

		mPanel.add(mSaveButton);
		mPanel.add(mCancelButton);

		initWidget(mPanel);
	}

	@Override
	public HasClickHandlers getCancel() {
		return mCancelButton;
	}

	@Override
	public HasClickHandlers getSave() {
		return mSaveButton;
	}

	@Override
	public void setData(Data data) {

		// Present the data
	}

	@Override
	public Widget asWidget() {
		return this;
	}

	@Override
	public void startProcessing() {
	}

	@Override
	public void stopProcessing() {
	}

 

The methods startProcessing and stopProcessing are mainly there for integration with gwt-dispatch. Anyway, the view above is pretty straightforward, so I will move on now.

 

Let's dig into the WidgetContainerPresenter and WidgetContainerDisplay classes. It is very likely that you will want to use these when you start layering your views. A WidgetContainerPresenter holds a list of other presenters. The main feature of this class is how it handles presenter visiblity. In the onBind()-method, it creates a PresenterRevealedHandler for each child presenter that you have added.

 

Remember earlier where we mentioned that BasicPresenter fires a PresenterRevealedEvent as a result of a PlaceRequestEvent? Well, this is where this is caught and handled. It's a really nifty feature, as we don't need to care about a parent presenters at all when we want to present a particular widget. We just place a request event, the widget presenter gets a callback, and then we will get revealDisplay() callbacks all the way to our top level presenter container.

 

For the particular presenter that catches the event, it first calls showPresenter(presenter) on the presenter that should be activated and requests its view to be displayed. Then it calls revealDisplay() which will lead to the same thing on the next level. Forget everything you learned doing Swing development  .

 

WidgetContainerDisplay enforces some expected methods on the view:

 

public interface WidgetContainerDisplay extends WidgetDisplay {
    void addWidget( Widget widget );
    void removeWidget( Widget widget );
    void showWidget( Widget widget );

 

It is up to the view how many widgets it holds and how they will be layout.

 

The library also provides access to DeckPresenter and DeckDisplay which are slight simplied version of the above. It uses a DeckPanel to present widgets, which holds a number of widgets in memory but only displays one at a given time. I would probably recommend using the WidetContainer-variants for more flexibility and control, but these might be useful for certain simple scenarios.

 

If you are using GIN for dependency injection, gwt-presenter has some convenience functions you might want to use. In your GIN client module class, do something like this:

 

// Make sure to inherit from gwt-presenter AbstractPresenterModule
public class YourClientModule extends AbstractPresenterModule {

    @Override
    protected void configure() {
        // Bind presenters and views

        // For generic views, we have to bind displays separately from
        // presenters to avoid binding them several times
        bindDisplay(ShowDataPresenter.Display.class, ShowDataView.class);
        bindDisplay(EditDataPresenter.Display.class, EditDataView.class);
        bindDisplay(ListModelsPresenter.Display.class, ListModelsView.class);

        // Bind presenters only
        bind(ShowPersonPresenter.class).in(Singleton.class);
        bind(EditPersonPresenter.class).in(Singleton.class);
        bind(ListTeamsPresenter.class).in(Singleton.class);
        bind(ListPlayersPresenter.class).in(Singleton.class);

        // For 1-1 mapping of presenter-view, we can use bindPresenter()
        bindPresenter(DynamicContentPresenter.class, DynamicContentPresenter.Display.class, DynamicContentView.class);
        bindPresenter(AppPresenter.class, AppPresenter.Display.class, AppView.class);
    }

}

 

You can then simply add @Inject to your presenter constructors to inject your display implementations.

 

That was it for now. Comment..feedback?

 类似资料: