- 浏览: 236096 次
- 性别:
- 来自: 深圳
文章分类
- 全部博客 (101)
- Practice (17)
- Model (15)
- Prototype (2)
- Integration (6)
- GWT (3)
- Android (16)
- Lab (6)
- Ubuntu (4)
- Data Structure(D,S) (1)
- 社会观察员 (1)
- python (14)
- redis (0)
- mysql (9)
- php (0)
- Data Structure(D (1)
- haproxy (2)
- Shell (5)
- Zabbix (1)
- CentOS (1)
- sqlplus (1)
- rlwrap (1)
- Oracle (2)
- schema (2)
- user (1)
- accredit (1)
- Delphi (2)
- nagios (1)
- nginx (0)
最新评论
-
白云飞:
兄弟能不能发一份完整的源码到我邮箱?luochengwei20 ...
【Python真的很强大】程序Log实时监控 -
myreligion:
nice job!
解决一个棘手的bug: java.lang.NoClassDefFoundError: android.os.AsyncTask -
yw9002:
你这个貌似提交的时候整个页面都会刷新。
AjaxAnyWhere+Struts的一个应用(demo/feature crew) -
fkpwolf:
这总结偏向于细节了,流水账
Android app项目和开发总结 -
crazybull:
期待详细总结~~~
Android app项目和开发总结
设计模式在gwt中的应用
本文主要介绍gwt中的模式,或模式的应用。结合近一段时间项目的开发,演示一些示例。部分代码可能不够完整,但尽量保证能够阅读清晰。
按四人组对模式的分类:创建型1(1.1SingleTon,1.2Builder,ProtoType,Abstrct Factory,Factory Method),结构型2(2.1Decorator,2.1Composite,Proxy,Facade,Adapter,2.2Bridge,Flyweight),行为型3(3.1Observer,3.2Command,Strategy,State,Template Method,Visitor,Mediator,Memento,Chain of Responsibility,Interpreter,Iterator)
顺序进行描写。不求一一覆盖,只为抛砖引玉,期望和大家一起交流
问题: 目前项目有一个带分页功能的电影浏览,用户在页面间切换频率很大。造成server traffic.
情景: 点上一页,下一页,都需要到服务器去取数据。
解决方案: 在客户端设置cache,第一次访问后,存储当前页数据。当存储满100页数据时,清掉最早存储的数据,以更新数据和防止memory out.
上述构造和应用一个全局的Cache实例(OrderedMap)。
上述描述了Rpc call 采用 RequestBuilder,以方便统一处理.
在Screen class也有上述类似代码,每个Page属于一个相关Screen, 一个Screen有当前Page,Screen也负责show / hide Page.
充分利用了组合和装饰。
上述的CommandPushButton has-a push command,通过这个桥,就把实现类和DataTableButtonCommand 连接起来了.
该模式是UI中最常见的,凡是xxxListern基本都是.
问题: 在页面数据较多的页面,当用户Forward first,then go back,将被迫再次从server取数据.
情景: 再我们的preShowPage中,写有很多fetch data from server. 这里在go back 时也会执行。
解决方案: hook go back! Add history listern. 当前浏览地址发生变化的时候通知我。
另外利用ChangeListener,ChangeListenerCollection,fireChange 制作一个mvc模式的Validator组件也是必要的.
[list=]3.2 Command[/list]
原型: Interface Command {
public void execute();
}
问题: 可能需要定时任务.
情景: Context help content -tips 需要定时关闭(其他地方也需要定时任务,但任务可能未知:( )
解决方案: 只要任务实现 Command,然后动态传入Command 实例即可.
Trigger既实现了一个对外的公开接口,也提供一个constructor 接口.所以,有时候方便别人也是方便自己.
本文主要介绍gwt中的模式,或模式的应用。结合近一段时间项目的开发,演示一些示例。部分代码可能不够完整,但尽量保证能够阅读清晰。
按四人组对模式的分类:创建型1(1.1SingleTon,1.2Builder,ProtoType,Abstrct Factory,Factory Method),结构型2(2.1Decorator,2.1Composite,Proxy,Facade,Adapter,2.2Bridge,Flyweight),行为型3(3.1Observer,3.2Command,Strategy,State,Template Method,Visitor,Mediator,Memento,Chain of Responsibility,Interpreter,Iterator)
顺序进行描写。不求一一覆盖,只为抛砖引玉,期望和大家一起交流
- 1.1 SingleTon
问题: 目前项目有一个带分页功能的电影浏览,用户在页面间切换频率很大。造成server traffic.
情景: 点上一页,下一页,都需要到服务器去取数据。
解决方案: 在客户端设置cache,第一次访问后,存储当前页数据。当存储满100页数据时,清掉最早存储的数据,以更新数据和防止memory out.
/** * GWT of this version have no class LinkedHashMap,so class OrderedMap * implements much of LinkedHashMap's function. The class be used in cache * data. Attetion,the OrderedMap has only a global instance. * */ public class OrderedMap implements IsSerializable { private static ArrayList keys = new ArrayList(); private static HashMap map = new HashMap(); private static OrderedMap singleton=new OrderedMap(); private OrderedMap(){} public static OrderedMap getInstance(){ return singleton; } public ArrayList getKeys() { return keys; } public Object put(Object key, Object value) { if (keys.contains(key)) { keys.remove(key); } keys.add(key); return map.put(key, value); } public Object remove(Object key) { keys.remove(key); return map.remove(key); } public Object remove(int index) { Object o = keys.get(index); return remove(o); } public void clear(){ keys.clear(); map.clear(); } public Object get(Object key) { return map.get(key); } public boolean containsKey(Object key) { return map.containsKey(key); } public int size() { return map.size(); } } /* <p> * first visit the data, store it.Then second visit,using the cache data. * cached data when setMovieElement,using cache when click "cmdPreviousMovie","cmdNextMovie" * and fetch more page once to reduce server traffic. * attention,the first entry page per genre will not use cache forever! *</p> */ public class MovieGridWithCache extends MovieGrid { // use the symbol to decide to use cache or not protected class Symbol implements Cloneable { private String genreid; // movie grenre id private String currentpage; private String languageid; // moveie language id public String getCurrentpage() { return currentpage; } public void setCurrentpage(String currentpage) { this.currentpage = currentpage; } public String getGenreid() { return genreid; } public void setGenreid(String genreid) { this.genreid = genreid; } public String getLanguageid() { return languageid == null ? "" : languageid; } public void setLanguageid(String languageid) { this.languageid = languageid; } public Symbol(String genreid, String currentpage, String languageid) { this.genreid = genreid; this.currentpage = currentpage; this.languageid = languageid == null ? "" : languageid; } public Symbol() { } public Object clone() { Symbol o = new Symbol(); o.genreid = new String(this.genreid == null ? "" : this.genreid); o.languageid = new String(this.languageid == null ? "" : this.languageid); o.currentpage = new String(this.currentpage == null ? "" : this.currentpage); return o; } public boolean equals(Object o) { if (this == o) return true; if (o == null) return false; if (o instanceof Symbol) { Symbol symbol = (Symbol) o; return (symbol.genreid.equals(this.genreid) && symbol.languageid.equals(this.languageid) && symbol.currentpage .equals(this.currentpage)); } else return false; } // define the hashCode because of using as a Map key,otherwish, the // map.get(key) get the null value. public int hashCode() { int hash = 1; hash = hash * 31 + genreid == null ? 0 : genreid.hashCode(); hash = hash * 29 + languageid == null ? 0 : languageid.hashCode(); hash = hash * 23 + currentpage == null ? 0 : currentpage.hashCode(); return hash; } } protected final static int PAGE_NUMBER=3; // fetch more page to reduce server traffic protected final static int maxCacheSize = 100; // that's to say: store 100 page data. // the map store pageId,genreid,languageid and _movieList protected static OrderedMap cache = OrderedMap.getInstance(); public MovieGridWithCache(ScreenData screenData, String genreid, String movielanguageid) { super(screenData, genreid, movielanguageid); } // it's a Callback,be used in class MovieDataAccess public void setMovieElement(JSONValue jsonMovies) { _movieList = MovieDataAccess.JSON2MovieDTO(jsonMovies); if (_currentPage ==0) showPage(); cachePage(); } public void showPage(int page) { super.showPage(page); } public void showPage() { showPage(0); } /** * Attention! As the first entry of page ,it will not use the cache!! * * @param genreid * @param movielanguageid: * if null or blank,fetch all movielanguage */ public void ShowPageByGenreWithCache(String genreid, String movielanguageid) { _currentPage = 0; this._genreID = genreid; this._movielanguageID = movielanguageid; _movieDataAccess = new MovieDataAccess(this, Integer.toString(_currentPage), Integer .toString(PAGE_SIZE), _genreID, movielanguageid,Integer.toString(PAGE_NUMBER)); //showPage(); } protected void nextPage() { _currentPage++; Symbol symbol = (Symbol) new Symbol(_genreID, _movielanguageID, Integer .toString(_currentPage)).clone(); if (!cache.containsKey(symbol)) { _movieDataAccess = new MovieDataAccess(this, Integer.toString(_currentPage), Integer .toString(PAGE_SIZE), _genreID, _movielanguageID,Integer.toString(PAGE_NUMBER)); } else { _movieList = (ArrayList) cache.get(symbol); cmdPreviousMovie.setLabel("<(" + (_currentPage + 1) + "/" + _totalPage + ")"); cmdNextMovie.setLabel(">(" + (_currentPage + 1) + "/" + _totalPage + ")"); cmdPreviousMovie.setVisible(_currentPage > 0); cmdNextMovie .setVisible((_currentPage + 1) * PAGE_SIZE < _totalSize); } showPage(_currentPage); } protected void prevPage() { // prevent access to -ve item number if (_currentPage > 0) { _currentPage--; Symbol symbol = (Symbol) new Symbol(_genreID, _movielanguageID, Integer.toString(_currentPage)).clone(); if (!cache.containsKey(symbol)) { _movieDataAccess = new MovieDataAccess(this, Integer.toString(_currentPage), Integer .toString(PAGE_SIZE), _genreID, _movielanguageID,Integer.toString(PAGE_NUMBER)); } else { _movieList = (ArrayList) cache.get(symbol); cmdPreviousMovie.setLabel("<(" + (_currentPage + 1) + "/" + _totalPage + ")"); cmdNextMovie.setLabel(">(" + (_currentPage + 1) + "/" + _totalPage + ")"); cmdPreviousMovie.setVisible(_currentPage > 0); cmdNextMovie .setVisible((_currentPage + 1) * PAGE_SIZE < _totalSize); } showPage(_currentPage); } } protected class NextPageCommand implements Command { public void execute() { nextPage(); } } protected class PreviousPageCommand implements Command { public void execute() { prevPage(); } } // save the movielist when have got data,storing much page once! protected void cachePage() { Iterator it = _movieList.iterator(); int iPageNum=_movieList.size()/MovieGrid.PAGE_SIZE; if(_movieList.size()%MovieGrid.PAGE_SIZE>0) iPageNum++; ArrayList[] list = new ArrayList[iPageNum]; for(int i=0;i<list.length;i++) list[i]=new ArrayList(); int iMovieNum=0; int iCurrentPage=_currentPage; int index=0; while (it.hasNext()) { MovieData movieData = (MovieData) ((MovieData) it.next()).clone(); index=iMovieNum /MovieGrid.PAGE_SIZE; iCurrentPage=_currentPage+index; list[index].add(movieData); if (iMovieNum%MovieGrid.PAGE_SIZE==0){ Symbol symbol = (Symbol) new Symbol(_genreID, _movielanguageID, Integer .toString(iCurrentPage)).clone(); //if stored beyond maxCacheSize page data,remove the earliest page. if(cache.size()>=maxCacheSize) cache.remove(0); cache.put(symbol, list[index]); } iMovieNum++; } if (_movieList.size()%MovieGrid.PAGE_SIZE!=0){ Symbol symbol = (Symbol) new Symbol(_genreID, _movielanguageID, Integer .toString(iCurrentPage)).clone(); //if stored beyond maxCacheSize page data,remove the earliest page. if(cache.size()>=maxCacheSize) cache.remove(0); cache.put(symbol, list[index]); } } }
上述构造和应用一个全局的Cache实例(OrderedMap)。
- 1.2 Builder
public void call() { // Establish an Async service call //final ServiceAsync service = (ServiceAsync) GWT.create(Service.class); //ServiceDefTarget target = (ServiceDefTarget) service; //target.setServiceEntryPoint(GWT.getModuleBaseURL()+ROI_SERVICE_URL); //service.call(instr, timeout, dataParameterName, jsonROIParam.toString() , roih ); if ( roiCallback != null ) { roiHandler = new ROIResponseHandler(getProxyInstruction(), roiCallback); RequestBuilder builder = new RequestBuilder( RequestBuilder.POST, GWT.getModuleBaseURL()+ROI_SERVICE_URL); try { String URLParam; // setup the proxy instruction URLParam = ROI.PROXY_INSTRUCTION + "=" + URL.encodeComponent(proxyInstruction) + "&"; URLParam += JSON_REQUEST_DTO + "=" + URL.encodeComponent(jsonROIParam.toString()) + "&"; // setting up necessary parameters builder.setTimeoutMillis(DEFAULT_TIME_OUT); builder.setHeader(HTTP_CONTENT_TYPE, HTTP_CONTENT_TYPE_VALUE); builder.setHeader(HTTP_CONNECTION, HTTP_CONNECTION_TYPE); httpResponse = builder.sendRequest(URLParam, roiHandler); } catch (RequestException e) { // if I have an exception, on client side, there is nothing I can do! // Window.alert("Failed to send the request: " + e.getMessage()); } } // else - callback is not defined, don't do the call }
上述描述了Rpc call 采用 RequestBuilder,以方便统一处理.
- 2.1 Decorator && Composite
/** * Page is the abstract class that handles displaying and validation of a page. * Each page is a singleton object and has an associated PageInfo class. This * PageInfo class is responsible for instantiating each Page. */ public abstract class Page extends Composite implements HasDataListeners { protected String _token = ""; // _token is the associated history token for this page protected Screen _parent = null; // _parent is the screen that contains this page protected ScreenData _data = null; // _data contains all of the server variables for this page protected ArrayList _dataNames = new ArrayList(); // _dataNames is all the names of the maintained variables for this page ... /** * PageInfo is an abstract class that is responsible for accessing each Page * object. Each page should have it's own PageInfo derived class that * implements createInstance. If the page is an entry point for the * associated screen, it should overload isEntryPage(), returning true. For * example, the first step in the SignUpMember screen is the only entry page * for that screen. */ public abstract static class PageInfo { protected Page _instance; protected String _token; // history token display on the browser's address bar protected Screen _parent; // the screen that owns this... public PageInfo(String token, Screen parent) { _token = token; _parent = parent; } public abstract Page createInstance(); public String getToken() { return _token; } public String getFullHistoryToken() { return _parent.getName() + CommonDefn.HTML_SCREEN_SEPERATOR + getToken(); } public final Page getInstance() { if (_instance == null) { _instance = createInstance(); } return _instance; } /** * Indicates whether or not the associated page can be navigated to * directly from another screen. * Override this method for all entry pages. * * Basically, if 'false' it also means that the page is not allowed to be book-marked. */ public boolean isEntryPage() { return false; } } }
在Screen class也有上述类似代码,每个Page属于一个相关Screen, 一个Screen有当前Page,Screen也负责show / hide Page.
充分利用了组合和装饰。
- 2.2 Bridge
public class DataTable extends Composite { private static final String CSS_DATATABLE = "datatable"; private static final String CSS_DATATABLE_ROW_HEADER = "datatable-row-header"; private static final String CSS_DATATABLE_ROW_EVEN = "datatable-row-even"; private static final String CSS_DATATABLE_ROW_ODD = "datatable-row-odd"; public class ColumnInfo { private static final int COL_LABEL = 0; private static final int COL_BUTTON = 1; private String _name; private String _styleName; private String _dataName; private int _colType; private HashMap _dataMap; public ColumnInfo(String name, String styleName, String dataName, int colType, HashMap dataMap) { _name = name; _styleName = styleName; _dataName = dataName; _colType = colType; _dataMap = dataMap; } public String getDataName() { return _dataName; } public String getName() { return _name; } public String getStyleName() { return _styleName; } public String getLabel(String value) { if (_dataMap != null) { return (String)_dataMap.get(value); } return value; } public boolean isButton() { return _colType == COL_BUTTON; } public boolean isLabel() { return _colType == COL_LABEL; } } public interface DataTableButtonCommand extends Command { public DataTableButtonCommand clone(); public String getParam(); public void setParam(String param); } protected int _tableSize = 0; protected ArrayList _columnInfo = null; protected FlexTable _table = null; public DataTable(int tableSize) { _tableSize = tableSize; _columnInfo = new ArrayList(); _table = new FlexTable(); // Insert the header row _table.insertRow(0); _table.getRowFormatter().addStyleName(0, CSS_DATATABLE_ROW_HEADER); _table.setStyleName(CSS_DATATABLE); initWidget(_table); } public void addColInfo(String name, String styleName, String dataName) { addColInfo(name, styleName, dataName, (HashMap)null); } public void addColInfo(String name, String styleName, String dataName, HashMap dataMap) { ColumnInfo colInfo = new ColumnInfo(name, styleName, dataName, ColumnInfo.COL_LABEL, dataMap); _columnInfo.add(colInfo); int newColNum = _table.getCellCount(0); OverflowLabel headerLabel = new OverflowLabel(name, OverflowLabel.LABEL_NONE); _table.setWidget(0, newColNum, headerLabel); String cellStyleName = CSS_DATATABLE + "-" + colInfo.getStyleName(); setStyleName(headerLabel.getElement(), cellStyleName, true); for (int row=1; row<=_tableSize; ++row) { OverflowLabel label = new OverflowLabel("", OverflowLabel.LABEL_ELLIPSES | OverflowLabel.LABEL_POPUP); _table.setWidget(row, newColNum, label); _table.getCellFormatter().addStyleName(row, newColNum, styleName); if (newColNum == 0) { String rowStyle = (row % 2 == 0) ? CSS_DATATABLE_ROW_EVEN : CSS_DATATABLE_ROW_ODD; _table.getRowFormatter().addStyleName(row, rowStyle); label.setHTML(" "); } } } public void addColInfo(String label, String styleName, String dataName, DataTableButtonCommand command) { ColumnInfo colInfo = new ColumnInfo(label, styleName, dataName, ColumnInfo.COL_BUTTON, null); _columnInfo.add(colInfo); int newColNum = _table.getCellCount(0); OverflowLabel headerLabel = new OverflowLabel(label, OverflowLabel.LABEL_NONE); _table.setWidget(0, newColNum, headerLabel); String cellStyleName = CSS_DATATABLE + "-" + colInfo.getStyleName(); setStyleName(headerLabel.getElement(), cellStyleName, true); for (int row=1; row<=_tableSize; ++row) { CommandPushButton button = new CommandPushButton(label); button.setCommand(command.clone()); button.setVisible(false); _table.setWidget(row, newColNum, button); _table.getCellFormatter().addStyleName(row, newColNum, styleName); if (newColNum == 0) { String rowStyle = (row % 2 == 0) ? CSS_DATATABLE_ROW_EVEN : CSS_DATATABLE_ROW_ODD; _table.getRowFormatter().addStyleName(row, rowStyle); } } } protected void populateRow(int rowNum, JSONValue rowValue) { JSONObject row = rowValue.isObject(); assert(row != null); for (int col=0; col<_columnInfo.size(); ++col) { ColumnInfo colInfo = (ColumnInfo)_columnInfo.get(col); String value = JSONHelper.getString(row, colInfo.getDataName()); if (colInfo.isLabel()) { OverflowLabel label = null; label = (OverflowLabel)_table.getWidget(rowNum+1, col); value = colInfo.getLabel(value); label.setHTML(value); } else if (colInfo.isButton()) { CommandPushButton button = null; button = (CommandPushButton)_table.getWidget(rowNum+1, col); button.setVisible(true); DataTableButtonCommand command = null; command = (DataTableButtonCommand)button.getCommand(); command.setParam(value); } else { assert false; } } } public void populateTable(JSONValue rowValues) { JSONArray rows = rowValues.isArray(); assert(rows != null); for (int row=0; row<rows.size() && row<20; ++row) { populateRow(row, rows.get(row)); } clearEmpties(rows.size()); } protected void clearEmpties(int emptyStart) { for (int emptyRow=emptyStart; emptyRow<_tableSize; ++emptyRow) { for (int col=0; col<_columnInfo.size(); ++col) { ColumnInfo colInfo = (ColumnInfo)_columnInfo.get(col); Widget widget = _table.getWidget(emptyRow+1, col); if (colInfo.isButton()) { widget.setVisible(false); } else if (colInfo.isLabel()) { OverflowLabel label = (OverflowLabel)widget; label.setHTML(" "); } } } } }
上述的CommandPushButton has-a push command,通过这个桥,就把实现类和DataTableButtonCommand 连接起来了.
- 3.1 Observer
该模式是UI中最常见的,凡是xxxListern基本都是.
问题: 在页面数据较多的页面,当用户Forward first,then go back,将被迫再次从server取数据.
情景: 再我们的preShowPage中,写有很多fetch data from server. 这里在go back 时也会执行。
解决方案: hook go back! Add history listern. 当前浏览地址发生变化的时候通知我。
public class MovieByGenrePage extends Page implements HistoryListener { private static final String CSS_RENTAL_MOVIEBYGENRE = "rental-moviebygenre"; private static final String CSS_RENTAL_MOVIEBYRENREGRID = "rental-moviebygenregrid"; private MovieGrid _movieGrid = null; private HTML _title; private String _from = ""; //record the html referrer. private static final String FROM = CommonDefn.SCREEN_RENTAL + CommonDefn.HTML_SCREEN_SEPERATOR + CommonDefn.PAGE_MOVIE_GENRE; private static final String CHECKOUT=CommonDefn.SCREEN_RENTAL + CommonDefn.HTML_SCREEN_SEPERATOR +CommonDefn.PAGE_COMPLETED_RENTAL; public static PageInfo init(final Screen parent,final ScreenData data) { return new PageInfo( CommonDefn.PAGE_MOVIE_BY_GENRE, parent ) { public Page createInstance() { return new MovieByGenrePage( CommonDefn.PAGE_MOVIE_BY_GENRE,parent, data ); } }; } public MovieByGenrePage(String token, Screen parent, ScreenData data) { super(token, parent, data); History.addHistoryListener(this); //i want to know this page came from which. // A vertical layout. VerticalPanel pageContentPanel = new VerticalPanel(); // Title { _title = new HTML(""); pageContentPanel.add( _title ); pageContentPanel.setCellHorizontalAlignment( _title, VerticalPanel.ALIGN_CENTER ); _movieGrid = new MovieGrid(data,data.getValue( Rental.DATA_RENTAL_GENRESELECTION_GENREID ),_data.getValue( Rental.DATA_RENTAL_MOVIELANGUAGESELECTION_MOVIELANGUAGEID ) ); _movieGrid.setStyleName( CSS_RENTAL_MOVIEBYRENREGRID ); pageContentPanel.add( _movieGrid ); pageContentPanel.setCellHorizontalAlignment( _movieGrid, VerticalPanel.ALIGN_CENTER ); } initWidget( pageContentPanel ); setStyleName( CSS_RENTAL_MOVIEBYGENRE ); } //just implement the function of the html referrer public void onHistoryChanged( String historyToken ){ // from is the referrer of this page,to is this page! _from=historyToken; //System.out.println("_from="+_from); // if the referrer page is mainmenu or the default screen, reset the genreid. if ( _from.equals(CommonDefn.SCREEN_DEFAULT_STARTUP) || _from.equals(CommonDefn.SCREEN_DEFAULT_STARTUP + CommonDefn.HTML_SCREEN_SEPERATOR) || _from.equals(CommonDefn.SCREEN_MAIN_MENU)) { _data.setValue(Rental.DATA_RENTAL_GENRESELECTION_GENREID,"",null); //System.out.println(_from); } } public void preShowPageExecute() { if ( _data.getValue(Rental.DATA_RENTAL_GENRESELECTION_GENREID).equals("") ) { // the first time I have not gotton it from server yet, that's why I hard-coded the default here... _data.setValue(Rental.DATA_RENTAL_GENRESELECTION_GENREID,CommonDefn.DEFAULT_GENRE_ID,null); } if ( _data.getValue(Rental.DATA_RENTAL_MOVIELANGUAGESELECTION_MOVIELANGUAGEID).equals("") ) { _data.setValue( Rental.DATA_RENTAL_MOVIELANGUAGESELECTION_MOVIELANGUAGEID,CommonDefn.DEFAULT_MOVIE_LANGUAGE_ID,null ); } if ( _from.length()<1 || _from.equals(FROM) || _from.equals(CHECKOUT) || _from.equals(CommonDefn.SCREEN_MAIN_MENU)) { //_from.length()<1,it's screen redire to this page! another flow is from search to this page, otherwise it's goto this page. //for goto we do nothing,just show it's orignal model nor fetch data from server. _movieGrid.updateGenreAndLanguageID( _data.getValue( Rental.DATA_RENTAL_GENRESELECTION_GENREID ),_data.getValue( Rental.DATA_RENTAL_MOVIELANGUAGESELECTION_MOVIELANGUAGEID ) ); } }
另外利用ChangeListener,ChangeListenerCollection,fireChange 制作一个mvc模式的Validator组件也是必要的.
[list=]3.2 Command[/list]
原型: Interface Command {
public void execute();
}
问题: 可能需要定时任务.
情景: Context help content -tips 需要定时关闭(其他地方也需要定时任务,但任务可能未知:( )
解决方案: 只要任务实现 Command,然后动态传入Command 实例即可.
public Trigger extends Timer implements Command{ private int _delay; private Command _command; public Trigger(int delay,Command command){ _delay = delay; _command = command; } public void run() { _command.execute(); } public void execute(){ this.schedule(_delay); } }
Trigger既实现了一个对外的公开接口,也提供一个constructor 接口.所以,有时候方便别人也是方便自己.
相关推荐
10. **CSS和主题**:GWT应用可以通过自定义皮肤和CSS样式来改变外观,提供一致的用户体验。 在学习GWT的过程中,练习StockWatcher可以帮助你掌握GWT的核心组件和工作原理。逐步理解并实践这些知识点,你将能够创建...
在描述中提到的“Coral's Design”可能是指一个基于GWT的项目或者设计模式,但具体的信息没有给出。通常,这样的设计可能涉及到如何优雅地组织GWT代码结构、实现可重用的组件或遵循特定的设计原则,如MVP(Model-...
在这个模式下,开发者可以在 Java 虚拟机中使用 GWT 内置的浏览器模拟器运行未转换的 Java 代码,实现快速的“编码、测试、调试”循环。启动宿主模式可以通过运行 `com.google.gwt.dev.GWTShell` 命令实现。 【Web ...
4. **MVC模式**:GWT和GXT通常遵循Model-View-Controller设计模式,理解模型如何存储和管理数据,视图如何展示数据,以及控制器如何处理用户交互。 5. **API的使用**:在代码中查找GWT和GXT的API用法,例如数据绑定...
**Tutorial-hellomvp-2.1.zip** MVP(Model-View-Presenter)是GWT中推荐的一种设计模式,用于组织和分离业务逻辑和用户界面。这个教程可能专注于介绍如何在GWT中实现MVP模式,帮助开发者理解如何划分模型、视图和...
学习如何分析GWT应用的性能瓶颈,使用开发工具中的Profiler模块进行内存和CPU性能分析。 通过以上介绍,我们可以看到GWT不仅提供了一套完整的开发工具链,还有一系列高级特性和最佳实践,使得开发者能够高效地构建...
这意味着使用 GWT 构建的应用程序可以在多种主流浏览器(如 Internet Explorer、Firefox、Mozilla、Safari 和 Opera)中正常运行,开发者无需过多关注浏览器间的差异性问题。这一点对于提高开发效率至关重要。 **5....
你将学习如何设计和实现一个完整的GWT应用,涵盖登录注册、数据展示、用户交互等常见功能。此外,还将分享一些最佳实践和技巧,帮助你避免常见问题,提高开发效率和代码质量。 在整个学习过程中,教程附带的详细...
在实际项目中,SmartGwt通常与MVC(Model-View-Controller)设计模式结合使用,帮助开发者组织代码,实现良好的模块化和可维护性。同时,SmartGwt也兼容其他流行的开发框架和库,如Spring、Hibernate等,方便整合到...
Model-View-Presenter(MVP)设计模式是GWT推荐的UI组织方式。模型层负责业务逻辑,视图层负责展示,而presenter作为中间人协调两者。通过MVP,可以实现清晰的代码结构和良好的可维护性。 7. **国际化与本地化**:...
1. **分层结构**:为了更好地组织和管理代码,开发框架采用了分层设计模式,将应用程序划分为表示层、业务逻辑层和数据访问层等。 2. **RPC服务扩展框架**:该框架利用GWT提供的远程过程调用(Remote Procedure ...
这个项目可能是为了展示如何在GWT前端应用程序中有效地利用Hibernate来处理后端数据库操作。GWT是一种用于构建高性能、富客户端Web应用的开发工具,而Hibernate则是一个流行的Java对象关系映射(ORM)框架,它简化了...
10. **最佳实践和设计模式**:掌握GWT开发中的常见设计模式和最佳实践,以提高代码质量和可维护性。 通过这个压缩包,你可以系统地学习和实践这些知识点,从而提升你在GWT、Java和Ajax编程方面的能力。
3. **数据访问对象(DAO)模式**:为了更好地组织代码,可以采用数据访问对象(DAO)设计模式。DAO是一个接口,包含对数据库进行特定操作的方法,如查询、插入、更新和删除数据。它的实现类则负责具体的数据库操作。...
Eclipse是一款广泛使用的集成开发环境(IDE),GWT Eclipse插件则为在Eclipse中开发GWT应用提供了便利。 标题中的"GWT eclipse插件4.2版本离线下载包"指的是专门为Eclipse 3.8到4.2版本设计的GWT开发工具包,这个...
1. **GWT入门教程**:对于初学者,资料可能涵盖GWT的基本概念、开发环境搭建(如Eclipse插件配置)、Hello World示例、MVP(Model-View-Presenter)设计模式的介绍,以及如何创建和运行第一个GWT项目。 2. **GWT...