﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-每日一得-随笔分类-design</title><link>http://www.blogjava.net/alex/category/13261.html</link><description>不求多得,只求一得
about java,hibernate,spring,design,database,linux,etc.
&lt;br/&gt;&lt;br/&gt;
最近关心的内容关键字:web快速开发方案，建模,workshop studio,Ajax
</description><language>zh-cn</language><lastBuildDate>Tue, 27 Feb 2007 14:53:46 GMT</lastBuildDate><pubDate>Tue, 27 Feb 2007 14:53:46 GMT</pubDate><ttl>60</ttl><item><title>谈谈 Facade与Proxy的联系与区别</title><link>http://www.blogjava.net/alex/archive/2006/10/10/74241.html</link><dc:creator>Alex</dc:creator><author>Alex</author><pubDate>Tue, 10 Oct 2006 03:09:00 GMT</pubDate><guid>http://www.blogjava.net/alex/archive/2006/10/10/74241.html</guid><wfw:comment>http://www.blogjava.net/alex/comments/74241.html</wfw:comment><comments>http://www.blogjava.net/alex/archive/2006/10/10/74241.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/alex/comments/commentRss/74241.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/alex/services/trackbacks/74241.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;&nbsp;<a href='http://www.blogjava.net/alex/archive/2006/10/10/74241.html'>阅读全文</a><img src ="http://www.blogjava.net/alex/aggbug/74241.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/alex/" target="_blank">Alex</a> 2006-10-10 11:09 <a href="http://www.blogjava.net/alex/archive/2006/10/10/74241.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于Facade的应用</title><link>http://www.blogjava.net/alex/archive/2006/10/09/74184.html</link><dc:creator>Alex</dc:creator><author>Alex</author><pubDate>Mon, 09 Oct 2006 14:27:00 GMT</pubDate><guid>http://www.blogjava.net/alex/archive/2006/10/09/74184.html</guid><wfw:comment>http://www.blogjava.net/alex/comments/74184.html</wfw:comment><comments>http://www.blogjava.net/alex/archive/2006/10/09/74184.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/alex/comments/commentRss/74184.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/alex/services/trackbacks/74184.html</trackback:ping><description><![CDATA[Facade用的非常的广了，以前刚接触的时候有个误解，总觉得Facade是简单的，而它后面的支撑服务是复杂的，对于客户来说却是简单的，现在来看，不完全对，或者说只是说对了一半，因为有时候恰恰是Facade是复杂的.<br /><br />我们举一个例子，比如发送短信，我们一般就定义一个MessageService的服务类，里面只提供一个方法就行了，sendToUser(String phone,String content)<br />但是到了客户端的时候有了自己的 "方言",比如它不是关心一个抽象的用户，它只知道向教师发送短信，或者向学生发送短信，或者向家长发送短信。<br /><br />示例如下:<br /><br /><img src="http://www.blogjava.net/images/blogjava_net/alex/images/facade.png" alt="facade.png" border="0" height="277" width="250" /><br />由图中可以看到，Facade的内容非常丰富，而支撑它的服务类却很简单，在开发过程中我们一般先实现通用的ServiceA,然后根据进一步的需求做一个面向具体复杂的Facade.<br /><br /><br /><br />在Spring提供的sample里发现一个小技巧，就是Facade和ServiceA都是接口，然后提供一个实现二者的支撑类:<br /><br /><br /><div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">class</span><span style="color: rgb(0, 0, 0);"> PetStoreAnnotationImpl </span><span style="color: rgb(0, 0, 255);">implements</span><span style="color: rgb(0, 0, 0);"> PetStoreFacade, OrderService {<br /><br />    </span><span style="color: rgb(0, 0, 255);">private</span><span style="color: rgb(0, 0, 0);"> AccountDao accountDao;<br /><br />    </span><span style="color: rgb(0, 0, 255);">private</span><span style="color: rgb(0, 0, 0);"> CategoryDao categoryDao;<br /><br />    </span><span style="color: rgb(0, 0, 255);">private</span><span style="color: rgb(0, 0, 0);"> ProductDao productDao;<br /><br />    </span><span style="color: rgb(0, 0, 255);">private</span><span style="color: rgb(0, 0, 0);"> ItemDao itemDao;<br /><br />    </span><span style="color: rgb(0, 0, 255);">private</span><span style="color: rgb(0, 0, 0);"> OrderDao orderDao;<br /><br /><br />    </span><span style="color: rgb(0, 128, 0);">//</span><span style="color: rgb(0, 128, 0);">-------------------------------------------------------------------------<br />    </span><span style="color: rgb(0, 128, 0);">//</span><span style="color: rgb(0, 128, 0);"> Setter methods for dependency injection<br />    </span><span style="color: rgb(0, 128, 0);">//</span><span style="color: rgb(0, 128, 0);">-------------------------------------------------------------------------</span><span style="color: rgb(0, 128, 0);"><br /></span><span style="color: rgb(0, 0, 0);"><br />    </span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">void</span><span style="color: rgb(0, 0, 0);"> setAccountDao(AccountDao accountDao) {<br />        </span><span style="color: rgb(0, 0, 255);">this</span><span style="color: rgb(0, 0, 0);">.accountDao </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> accountDao;<br />    }<br /><br />    </span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">void</span><span style="color: rgb(0, 0, 0);"> setCategoryDao(CategoryDao categoryDao) {<br />        </span><span style="color: rgb(0, 0, 255);">this</span><span style="color: rgb(0, 0, 0);">.categoryDao </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> categoryDao;<br />    }<br /><br />    </span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">void</span><span style="color: rgb(0, 0, 0);"> setProductDao(ProductDao productDao) {<br />        </span><span style="color: rgb(0, 0, 255);">this</span><span style="color: rgb(0, 0, 0);">.productDao </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> productDao;<br />    }<br /><br />    </span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">void</span><span style="color: rgb(0, 0, 0);"> setItemDao(ItemDao itemDao) {<br />        </span><span style="color: rgb(0, 0, 255);">this</span><span style="color: rgb(0, 0, 0);">.itemDao </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> itemDao;<br />    }<br /><br />    </span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">void</span><span style="color: rgb(0, 0, 0);"> setOrderDao(OrderDao orderDao) {<br />        </span><span style="color: rgb(0, 0, 255);">this</span><span style="color: rgb(0, 0, 0);">.orderDao </span><span style="color: rgb(0, 0, 0);">=</span><span style="color: rgb(0, 0, 0);"> orderDao;<br />    }<br /><br /><br />    </span><span style="color: rgb(0, 128, 0);">//</span><span style="color: rgb(0, 128, 0);">-------------------------------------------------------------------------<br />    </span><span style="color: rgb(0, 128, 0);">//</span><span style="color: rgb(0, 128, 0);"> Operation methods, implementing the PetStoreFacade interface<br />    </span><span style="color: rgb(0, 128, 0);">//</span><span style="color: rgb(0, 128, 0);">-------------------------------------------------------------------------</span><span style="color: rgb(0, 128, 0);"><br /></span><span style="color: rgb(0, 0, 0);"><br />    </span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);"> Account getAccount(String username) {<br />        </span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">this</span><span style="color: rgb(0, 0, 0);">.accountDao.getAccount(username);<br />    }<br /><br />    </span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);"> Account getAccount(String username, String password) {<br />        </span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">this</span><span style="color: rgb(0, 0, 0);">.accountDao.getAccount(username, password);<br />    }<br /><br />    </span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">void</span><span style="color: rgb(0, 0, 0);"> insertAccount(Account account) {<br />        </span><span style="color: rgb(0, 0, 255);">this</span><span style="color: rgb(0, 0, 0);">.accountDao.insertAccount(account);<br />    }<br /><br />    </span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">void</span><span style="color: rgb(0, 0, 0);"> updateAccount(Account account) {<br />        </span><span style="color: rgb(0, 0, 255);">this</span><span style="color: rgb(0, 0, 0);">.accountDao.updateAccount(account);<br />    }<br /><br />    </span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);"> List getUsernameList() {<br />        </span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">this</span><span style="color: rgb(0, 0, 0);">.accountDao.getUsernameList();<br />    }<br /><br />    </span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);"> List getCategoryList() {<br />        </span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">this</span><span style="color: rgb(0, 0, 0);">.categoryDao.getCategoryList();<br />    }<br /><br />    </span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);"> Category getCategory(String categoryId) {<br />        </span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">this</span><span style="color: rgb(0, 0, 0);">.categoryDao.getCategory(categoryId);<br />    }<br /><br />    </span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);"> List getProductListByCategory(String categoryId) {<br />        </span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">this</span><span style="color: rgb(0, 0, 0);">.productDao.getProductListByCategory(categoryId);<br />    }<br /><br />    </span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);"> List searchProductList(String keywords) {<br />        </span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">this</span><span style="color: rgb(0, 0, 0);">.productDao.searchProductList(keywords);<br />    }<br /><br />    </span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);"> Product getProduct(String productId) {<br />        </span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">this</span><span style="color: rgb(0, 0, 0);">.productDao.getProduct(productId);<br />    }<br /><br />    </span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);"> List getItemListByProduct(String productId) {<br />        </span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">this</span><span style="color: rgb(0, 0, 0);">.itemDao.getItemListByProduct(productId);<br />    }<br /><br />    </span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);"> Item getItem(String itemId) {<br />        </span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">this</span><span style="color: rgb(0, 0, 0);">.itemDao.getItem(itemId);<br />    }<br /><br />    </span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">boolean</span><span style="color: rgb(0, 0, 0);"> isItemInStock(String itemId) {<br />        </span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">this</span><span style="color: rgb(0, 0, 0);">.itemDao.isItemInStock(itemId);<br />    }<br /><br />    </span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">void</span><span style="color: rgb(0, 0, 0);"> insertOrder(Order order) {<br />        </span><span style="color: rgb(0, 0, 255);">this</span><span style="color: rgb(0, 0, 0);">.orderDao.insertOrder(order);<br />        </span><span style="color: rgb(0, 0, 255);">this</span><span style="color: rgb(0, 0, 0);">.itemDao.updateQuantity(order);<br />    }<br /><br />    </span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);"> Order getOrder(</span><span style="color: rgb(0, 0, 255);">int</span><span style="color: rgb(0, 0, 0);"> orderId) {<br />        </span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">this</span><span style="color: rgb(0, 0, 0);">.orderDao.getOrder(orderId);<br />    }<br /><br />    </span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);"> List getOrdersByUsername(String username) {<br />        </span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">this</span><span style="color: rgb(0, 0, 0);">.orderDao.getOrdersByUsername(username);<br />    }<br /><br />}</span></div><br /><br />看起来似乎不错，不过仔细想想个人认为还是不是太好,总的感觉就是层次不清晰，因为很多时候Facade和Service之间是被服务与服务的关系，所以理当分开。 同时，这个类有点倾向于"万能类"了，能分还是分开好.这和我们以前提到的dao又背离过来了（以前我们提倡一个业务一个dao,现在觉得只用一个通用的dao更合适）,这个并不矛盾，具体问题具体看待.<br /><br />欢迎各位拍砖.<br /><img src ="http://www.blogjava.net/alex/aggbug/74184.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/alex/" target="_blank">Alex</a> 2006-10-09 22:27 <a href="http://www.blogjava.net/alex/archive/2006/10/09/74184.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[zt]设计模式之Visitor 访问者模式</title><link>http://www.blogjava.net/alex/archive/2006/09/28/72457.html</link><dc:creator>Alex</dc:creator><author>Alex</author><pubDate>Thu, 28 Sep 2006 01:30:00 GMT</pubDate><guid>http://www.blogjava.net/alex/archive/2006/09/28/72457.html</guid><wfw:comment>http://www.blogjava.net/alex/comments/72457.html</wfw:comment><comments>http://www.blogjava.net/alex/archive/2006/09/28/72457.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/alex/comments/commentRss/72457.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/alex/services/trackbacks/72457.html</trackback:ping><description><![CDATA[
		<h3 align="center">设计模式之Visitor</h3>
		<p align="center">
				<a href="http://www.jdon.com/aboutme.htm">板桥里人</a> http://www.jdon.com
                    2002/05/05（转载请保留）</p>
		<p align="center">
				<a href="http://www.jdon.com/mybook/index.htm" target="_blank">
						<strong>模式实战书籍《Java实用系统开发指南》</strong>
				</a>
		</p>
		<p>
				<b>Visitor访问者模式定义</b>
				<br />
                    作用于某个对象群中各个对象的操作. 它可以使你在不改变这些对象本身的情况下,定义作用于这些对象的新操作.</p>
		<p>在Java中,Visitor模式实际上是分离了collection结构中的元素和对这些元素进行操作的行为.</p>
		<p>
				<b>为何使用Visitor?</b>
				<br />
Java的Collection(包括Vector和Hashtable)是我们最经常使用的技术,可是Collection好象是个黑色大染缸,本来有
各种鲜明类型特征的对象一旦放入后,再取出时,这些类型就消失了.那么我们势必要用If来判断,如:</p>
		<p>
				<br />
                    Iterator iterator = collection.iterator()<br />
                    while (iterator.hasNext()) {<br />
                    　　 Object o = iterator.next();<br />
                    　　 if (o instanceof Collection)<br />
                    　　 　　 messyPrintCollection((Collection)o);<br />
                    　　 else if (o instanceof String)<br />
                    　　 　　 System.out.println("'"+o.toString()+"'");<br />
                    　　 else if (o instanceof Float)<br />
                    　　 　　 System.out.println(o.toString()+"f");<br />
                    　　 else<br />
                    　　 　　 System.out.println(o.toString());<br />
                    }<br />
                    在上例中,我们使用了 instanceof来判断 o的类型.</p>
		<p>很显然,这样做的缺点代码If else if 很繁琐.我们就可以使用Visitor模式解决它.</p>
		<p>
				<b>如何使用Visitor?</b>
				<br />
                    针对上例,定义接口叫Visitable,用来定义一个Accept操作,也就是说让Collection每个元素具备可访问性.</p>
		<p>被访问者是我们Collection的每个元素Element,我们要为这些Element定义一个可以接受访问的接口(访问和被访问是互动的,只有访问者,被访问者如果表示不欢迎,访问者就不能访问),取名为Visitable，也可取名为Element。</p>
		<table border="0" cellpadding="3" cellspacing="3" width="80%">
				<tbody>
						<tr>
								<td bgcolor="#cccccc">public interface Visitable<br />
                        {<br />
                        　　 public void accept(Visitor visitor);<br />
                        }</td>
						</tr>
				</tbody>
		</table>
		<p>被访问的具体元素继承这个新的接口Visitable：</p>
		<table border="0" cellpadding="3" cellspacing="3" width="80%">
				<tbody>
						<tr>
								<td bgcolor="#cccccc">
										<p>public class StringElement implements 
                          Visitable<br />
                          {<br />
                          　　 private String value;<br />
                          　　 public StringElement(String string) {<br />
                          　　 　　 value = string;<br />
                          　　 }</p>
										<p>　　 public String getValue(){<br />
                          　　 　　 return value;<br />
                          　　 }</p>
										<p>
												<br />
                          　　 //定义accept的具体内容 这里是很简单的一句调用<br />
                          　　 public void accept(Visitor visitor) {<br />
                          　　 　　 visitor.visitString(this);<br />
                          　　 }<br />
                          }</p>
										<p>
												<br />
										</p>
								</td>
						</tr>
				</tbody>
		</table>
		<p>上面是被访问者是字符串类型，下面再建立一个Float类型的：</p>
		<table border="0" cellpadding="3" cellspacing="3" width="80%">
				<tbody>
						<tr>
								<td bgcolor="#cccccc">
										<p>public class FloatElement implements 
                          Visitable<br />
                          {<br />
                          　　 private Float value;<br />
                          　　 public FloatElement(Float value) {<br />
                          　　 　　 this.value = value;<br />
                          　　 }</p>
										<p>　　 public Float getValue(){<br />
                          　　 　　 return value;<br />
                          　　 }</p>
										<p>
												<br />
                          　　 //定义accept的具体内容 这里是很简单的一句调用<br />
                          　　 public void accept(Visitor visitor) {<br />
                          　　 　　 visitor.visitFloat(this);<br />
                          　　 }<br />
                          }</p>
										<p>
												<br />
										</p>
								</td>
						</tr>
				</tbody>
		</table>
		<p>我
们设计一个接口visitor访问者，在这个接口中,有一些访问操作，这些访问操作是专门访问对象集合Collection中有可能的所有类，目前我们假
定有三个行为：访问对象集合中的字符串类型；访问对象集合中的Float类型；访问对象集合中的对象集合类型。注意最后一个类型是集合嵌套，通过这个嵌套
实现可以看出使用访问模式的一个优点。</p>
		<p>接口visitor访问者如下：</p>
		<table border="0" cellpadding="3" cellspacing="3" width="80%">
				<tbody>
						<tr>
								<td bgcolor="#cccccc">
										<p>public interface Visitor<br />
                          {<br /><br />
                          　　 public void visitString(StringElement stringE);<br />
                          　　 public void visitFloat(FloatElement floatE);<br />
                          　　 public void visitCollection(Collection collection); 
                          <br /><br />
                          }<br /></p>
								</td>
						</tr>
				</tbody>
		</table>
		<p>访问者的实现:</p>
		<table border="0" cellpadding="3" cellspacing="3" width="80%">
				<tbody>
						<tr>
								<td bgcolor="#cccccc">
										<p>public class ConcreteVisitor implements 
                          Visitor<br />
                          {<br />
                          　　 //在本方法中,我们实现了对Collection的元素的成功访问<br />
                          　　 public void visitCollection(Collection collection) 
                          {<br />
                          　　 　　 Iterator iterator = collection.iterator()<br />
                          　　 　　 while (iterator.hasNext()) {<br />
                          　　 　　 　　 Object o = iterator.next();<br />
                          　　 　　 　　 if (o instanceof Visitable)<br />
                          　　 　　 　　 　　 ((Visitable)o).accept(this);<br />
                          　　 　　 } <br />
                          　　 ｝</p>
										<p>　　 public void visitString(StringElement stringE) {<br />
                          　　 　　 System.out.println("'"+stringE.getValue()+"'");<br />
                          　　 } <br />
                          　　 public void visitFloat(FloatElement floatE){<br />
                          　　 　　 System.out.println(floatE.getValue().toString()+"f");<br />
                          　　 } </p>
										<p> }</p>
								</td>
						</tr>
				</tbody>
		</table>
		<p>在上面的visitCollection我们实现了对Collection每个元素访问,只使用了一个判断语句,只要判断其是否可以访问.</p>
		<p>StringElement只是一个实现，可以拓展为更多的实现，整个核心奥妙在accept方法中，在遍历Collection时，通过相应的accept方法调用具体类型的被访问者。这一步确定了被访问者类型，</p>
		<p>如果是StringElement，而StringElement则回调访问者的visiteString方法，这一步实现了行为操作方法。</p>
		<p>客户端代码：</p>
		<table bgcolor="#cccccc" border="0" cellpadding="2" cellspacing="2" width="80%">
				<tbody>
						<tr>
								<td>
										<p>Visitor visitor = new ConcreteVisitor();<br /><br />
                          StringElement stringE = new StringElement("I am 
                          a String");<br />
                          visitor.visitString(stringE);</p>
										<p>Collection list = new ArrayList();<br />
                          list.add(new StringElement("I am a String1")); 
                          <br />
                          list.add(new StringElement("I am a String2")); 
                          <br />
                          list.add(new FloatElement(new Float(12))); <br />
                          list.add(new StringElement("I am a String3")); 
                          <br />
                          visitor.visitCollection(list);</p>
								</td>
						</tr>
				</tbody>
		</table>
		<p>客户端代码中的list对象集合中放置了多种数据类型，对对象集合中的访问不必象一开始那样，使用instance of逐个判断，而是通过访问者模式巧妙实现了。</p>
		<p>至此,我们完成了Visitor模式基本结构.</p>
		<p>
				<b>使用Visitor模式的前提</b>
				<br />
                    使用访问者模式是对象群结构中(Collection) 中的对象类型很少改变。</p>
		<p>在两个接口Visitor和Visitable中,确保Visitable很少变化,也就是说，确保不能老有新的Element元素类型加进来，可以变化的是访问者行为或操作，也就是Visitor的不同子类可以有多种,这样使用访问者模式最方便.</p>
		<p>如果对象集合中的对象集合经常有变化, 那么不但Visitor实现要变化，Visistable也要增加相应行为，GOF建议是,不如在这些对象类中直接逐个定义操作，无需使用访问者设计模式。</p>
		<p>但是在Java中，Java的Reflect技术解决了这个问题，因此结合reflect反射机制，可以使得访问者模式适用范围更广了。</p>
                  Reflect技术是在运行期间动态获取对象类型和方法的一种技术,具体实现参考Javaworld的<a href="http://www.javaworld.com/javaworld/javatips/jw-javatip98.html" target="_blank">英文原文</a>.<img src ="http://www.blogjava.net/alex/aggbug/72457.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/alex/" target="_blank">Alex</a> 2006-09-28 09:30 <a href="http://www.blogjava.net/alex/archive/2006/09/28/72457.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[zt]一个软件设计的全过程(基于UML)</title><link>http://www.blogjava.net/alex/archive/2006/09/22/71297.html</link><dc:creator>Alex</dc:creator><author>Alex</author><pubDate>Fri, 22 Sep 2006 05:16:00 GMT</pubDate><guid>http://www.blogjava.net/alex/archive/2006/09/22/71297.html</guid><wfw:comment>http://www.blogjava.net/alex/comments/71297.html</wfw:comment><comments>http://www.blogjava.net/alex/archive/2006/09/22/71297.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/alex/comments/commentRss/71297.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/alex/services/trackbacks/71297.html</trackback:ping><description><![CDATA[key words:软件设计 建模 UML<br /><br />这篇文章以前就看到了，后来再想看的时候居然找不到了，感觉写的不错，作为想把软件开发往深里整地朋友有借鉴作用。<br /><br />转自<a href="http://meyegg.cnblogs.com/archive/2006/04/13/374128.html">这里</a><br /><br />前段时间把一个界面框架完成了，今天基于这个框架开发一个小模块，在这里把这个模块设计的全过程记录下来，希望大家讨论并指正。<br /><br />一、起因<br /><br />公
司交给我一个任务，为测试员写一个手机模拟界面，以方便她们的手机短信测试。过去她们都是用MC4J直接调用公司服务器的MBean服务来模拟进行测试，
以验证我们整个系统平台。这种测试主要是检查收发短信是否正常，而我的要做的工作就是，让她们在测试的时候更方便更直观。<br /><br />二、需求<br /><br />我和测试员陈MM（也就是软件的使用者）约定了一个时间，大家一起来讨论这个软件的需求。<br /><br />1、首先，我大概了解了一下她们的测试工作，知道我要做个什么东东。<br /><br />2、然后我回去思考了一下，再次找她详细了解其测试的具体步骤，并在一张<strong>白纸</strong>上以UML用例图的方式，记录下需求的功能。<strong>用例是什么？用例就是需求，就是你的软件应该具有的功能</strong>，当然用例图只是概括性的对功能进行了描述。<br /><br />3、最后，我坐在我的电脑前开始用MagicDraw UML来画用例图（我不喜欢用Rose，那玩意太笨重了，界面友好性也不好）。在画用例图的时候，我发现了一些隐含的功能，这些是陈MM在和我做需求时没有考虑到的<strong>（注：开发者应该为用户挖掘隐含需求）</strong>。我和陈MM一一确定了这些我新发现的需求，最后得到如下的用例图。<br /><br />（1）手机前台测试操作的用例图（说明：include是指某用例<strong>包含(include)</strong>子用例）<br /><br /><img style="width: 623px; height: 330px;" alt="[用例]手机.jpg" src="../../images/blogjava_net/chengang/others/%5B%E7%94%A8%E4%BE%8B%5D%E6%89%8B%E6%9C%BA.jpg" border="0" height="410" width="1429" /><br /> （2）后台管理<br /><img alt="[用例]后台管理.jpg" src="../../images/blogjava_net/chengang/others/%5B%E7%94%A8%E4%BE%8B%5D%E5%90%8E%E5%8F%B0%E7%AE%A1%E7%90%86.jpg" border="0" height="258" width="392" /><br /><br /><br />三、界面设计<br /><br />接下来是界面设计。既然是手机模拟，我很自然就拿我的motorola手机的操作界面来做参考。不过这里应该注意到，手机操作环境和电脑操作环境不尽相同（比如说电脑有鼠标，还有键盘可以输入文字），所以没有必要唯妙唯肖的完全模枋，还是以使用者操作方便为主。<br /><br />界
面设计是很重要的一步，不要一上来就写程序，一定要先做到心中有个大概，否则返工的可能性就很大。而且，把界面拿出来给客户看，客户也就能做到心中有数，
还能尽早提出一些新需求和意见来。千万不要等到软件做完了再拿给客户看，到时客户看了如果要修改，那就做太多白费工了。<br /><br />由于软件界面相对简单，陈ＭＭ基本没有提修改意见，但这不是个好兆头。不过极限编程就是要拥抱变化不是^_^。咱不怕她改，只要大致的界面她能定下来就行了。<br /><br />界面我喜欢用Visio来画，当然也听说有人喜欢用ＶＢ来快速构建界面原型的，看个人喜好了。整个界面如下：<br /><img alt="[界面设计]手机.jpg" src="../../images/blogjava_net/chengang/others/%5B%E7%95%8C%E9%9D%A2%E8%AE%BE%E8%AE%A1%5D%E6%89%8B%E6%9C%BA.jpg" border="0" height="611" width="556" /><br /><br /><br /><br />这个是后台管理界面<br /><img alt="[界面设计]号码管理.jpg" src="../../images/blogjava_net/chengang/others/%5B%E7%95%8C%E9%9D%A2%E8%AE%BE%E8%AE%A1%5D%E5%8F%B7%E7%A0%81%E7%AE%A1%E7%90%86.jpg" border="0" height="480" width="478" /><br /><br /><br /><br />四、类图<br /><br />类图反映了软件的数据模型。在设计数据模型，我参考了界面设计图和用例图，找出一个个的类。然后参照用例图的一个个功能，设计出了各类的属性和方法。设计初始的类图当然不可能很详细，但至少应该看到个大概。有错误不要紧，后期可以慢慢修正，但大体关系就算定下来了。<br /><br /><strong>Neil（公司ＣＴＯ，一个40岁左右的真正的资深程序员）说：看一个软件的设计主要看两个类：类图和时序图。类图确定了软件数据模型的静态关型，时序图则是数据模型的动态关系。</strong><br /><br />类图如下，看英文大致可以知道类／属性／方法的含义和作用了，就不一一介绍了。<br /><br /><br /><img alt="[类图].jpg" src="../../images/blogjava_net/chengang/others/%5B%E7%B1%BB%E5%9B%BE%5D.jpg" border="0" height="562" width="625" /><br /><br /><br /><br />五、时序图<br /><br />时序图是本文最后一个图，时序图表明了<strong>用例图</strong>中各功能的实现方案，同时也反应了<strong>类图</strong>中各类的交互关系。以后程序的逻辑和时序图基本一致。不过，有些人会去画得很详细的时序图，详细到都快赶上伪代码级别了，我觉得这没必要。我把时序图看做反映自己思路的大概过程，所以也就画个大概。<br /><br />我认为时序图要简洁易懂，这样以后你的后继维护者，拿到这个软件的时序图（当然也包括用例图、类图），就能明白你的大概设计思路。另外，画时序图也能整理自己的思路，同时还可以对类图的设计进行验证。在画这个时序图的过程中，我就纠正了在类图中的几处考虑不周的地方。<br /><br />总结：时序图可以（１）整理思路（２）验证类的设计（３）是很好的软件文档，对维护者理解代码很有帮助。<br /><br />这里仅给出其中几个时序图（实际上我也没有把用例都画完，有些类似的简单的，就忽略了）<br /><br />（１）新增一个手机号码<br /><img alt="[时序图]add phone number.jpg" src="../../images/blogjava_net/chengang/others/%5B%E6%97%B6%E5%BA%8F%E5%9B%BE%5Dadd%20phone%20number.jpg" border="0" height="327" width="583" /><br /><br />（２）关机<br /><img alt="[时序图]power off.jpg" src="../../images/blogjava_net/chengang/others/%5B%E6%97%B6%E5%BA%8F%E5%9B%BE%5Dpower%20off.jpg" border="0" height="255" width="293" /><br />（３）开机<br /><img alt="[时序图]power on.jpg" src="../../images/blogjava_net/chengang/others/%5B%E6%97%B6%E5%BA%8F%E5%9B%BE%5Dpower%20on.jpg" border="0" height="337" width="407" /><br />（４）发送短信<br /><img alt="[时序图]send message.jpg" src="../../images/blogjava_net/chengang/others/%5B%E6%97%B6%E5%BA%8F%E5%9B%BE%5Dsend%20message.jpg" border="0" height="381" width="405" /><br /><br /><br /><br />到这里设计阶段就完成了，用时一天。下一步是编码，将应用ＴＤＤ先写测试代码的方式来写代码，下次再介绍了。<br /><br /><br /><h5><font size="4">作者简介</font></h5><p>陈刚，广西桂林人，著作有《Eclipse从入门到精通》<br />您可以通过其博客了解更多信息和文章：<a href="http://www.chengang.com.cn/" target="_blank" &#111;nfocus="this.blur()"><font color="#000033">http://www.ChenGang.com.cn</font></a><br />版权声明：本博客所有文章仅适用于非商业性转载，并请在转载时注明出处及作者的署名。</p><img src ="http://www.blogjava.net/alex/aggbug/71297.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/alex/" target="_blank">Alex</a> 2006-09-22 13:16 <a href="http://www.blogjava.net/alex/archive/2006/09/22/71297.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[zt]成为软件架构师</title><link>http://www.blogjava.net/alex/archive/2006/09/22/71296.html</link><dc:creator>Alex</dc:creator><author>Alex</author><pubDate>Fri, 22 Sep 2006 05:14:00 GMT</pubDate><guid>http://www.blogjava.net/alex/archive/2006/09/22/71296.html</guid><wfw:comment>http://www.blogjava.net/alex/comments/71296.html</wfw:comment><comments>http://www.blogjava.net/alex/archive/2006/09/22/71296.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/alex/comments/commentRss/71296.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/alex/services/trackbacks/71296.html</trackback:ping><description><![CDATA[key words:软件架构师<br />转自<a href="/chengang/archive/2006/09/16/70067.html">here</a><br /><br />现在软件架构师满天飞，是个写代码的都称自己为软件架构师，就象开个公司管上四五号人就给自己按个CEO头衔一样，着实让人好笑。于是到网上GOOGLE了一下看看软件构架师具体是个啥东东，有想做货真价实的构架师，就朝着那方向努力吧。网摘如下：<br /><br /><span style="color: rgb(0, 0, 0);">软件架构师的职责：将客户的需求转换为规范的开发计划及文本，并制定这个项目的总体架构，指导整个开发团队完成这个计划。<br /><br /><strong>软件架构师的具体工作：<br /></strong>    (</span><span style="color: rgb(0, 0, 0);">1</span><span style="color: rgb(0, 0, 0);">)在需求阶段，软件架构师主要负责理解和管理非功能性系统需求，比如软件的可维护性、性能、复用性、可靠性、有效性和可测试性等等，此外，架构师还要经常审查和客户及市场人员所提出的需求，确认开发团队所提出的设计；<br />    (</span><span style="color: rgb(0, 0, 0);">2</span><span style="color: rgb(0, 0, 0);">)在需求越来越明确后，架构师的关注点开始转移到组织开发团队成员和开发过程定义上；<br />    (</span><span style="color: rgb(0, 0, 0);">3</span><span style="color: rgb(0, 0, 0);">)在软件设计阶段，架构师负责对整个软件体系结构、关键构件、接口和开发政策的设计；<br />    (</span><span style="color: rgb(0, 0, 0);">4</span><span style="color: rgb(0, 0, 0);">)在编码阶段，架构师则成为详细设计者和代码编写者的顾问，并且经常性地要举行一些技术研讨会、技术培训班等；<br />    (</span><span style="color: rgb(0, 0, 0);">5</span><span style="color: rgb(0, 0, 0);">)随着软件开始测试、集成和交付，集成和测试支持将成为软件架构师的工作重点；<br />    (</span><span style="color: rgb(0, 0, 0);">6</span><span style="color: rgb(0, 0, 0);">)在软件维护开始时，软件架构师就开始为下一版本的产品是否应该增加新的功能模块进行决策。<br /> <br /><strong>软件架构师的要求</strong><br />      (</span><span style="color: rgb(0, 0, 0);">1</span><span style="color: rgb(0, 0, 0);">)必须对开发技术非常了解，具有丰富的软件设计与开发经验，关键时候能对技术的选择作出及时、有效的决定。<br />      (</span><span style="color: rgb(0, 0, 0);">2</span><span style="color: rgb(0, 0, 0);">)有良好的组织管理能力：沟通、领导、团队协作<br />      (</span><span style="color: rgb(0, 0, 0);">3</span><span style="color: rgb(0, 0, 0);">)构件通信机制方面的知识:远程调用、JAVARMI、CORBA、COM</span><span style="color: rgb(0, 0, 0);">/</span><span style="color: rgb(0, 0, 0);">DCOM、各种标准的通信协议、网络服务、面对对象数据库、关系数据库等等<br /><br /><strong>成长为软件架构师的几个阶段：<br /></strong>     <font color="#ff0000"> </font><font color="#0000ff">(</font></span><font color="#0000ff"><span style="color: rgb(0, 0, 0);">1</span><span style="color: rgb(0, 0, 0);">)构架师胚胎（程序员）：语言基础、设计基础、通信基础等，内容包括java、c、c</span><span style="color: rgb(0, 0, 0);">++</span><span style="color: rgb(0, 0, 0);">、uml、RUP、XML、socket通信（通信协议）<br />      (</span><span style="color: rgb(0, 0, 0);">2</span><span style="color: rgb(0, 0, 0);">)构架师萌芽（高级程序员）：分布式系统组建等内容，包括分布式系统原理、ejb、corba、com</span><span style="color: rgb(0, 0, 0);">/</span><span style="color: rgb(0, 0, 0);">com</span><span style="color: rgb(0, 0, 0);">+</span><span style="color: rgb(0, 0, 0);">、webservice、网络计算机、高性能并发处理等<br />      (</span><span style="color: rgb(0, 0, 0);">3</span><span style="color: rgb(0, 0, 0);">)构架师幼苗（设计师）：透彻掌握设计模式，包括设计模式（c</span><span style="color: rgb(0, 0, 0);">++</span><span style="color: rgb(0, 0, 0);">版本、java版本）、ejb设计模式、J2EE构架、UDDI、软件设计模式等。此期间，最好能够了解软件工程在实际项目中的应用以及小组开发、团队管理</span></font><img src ="http://www.blogjava.net/alex/aggbug/71296.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/alex/" target="_blank">Alex</a> 2006-09-22 13:14 <a href="http://www.blogjava.net/alex/archive/2006/09/22/71296.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>DAO模式演变</title><link>http://www.blogjava.net/alex/archive/2006/09/21/71123.html</link><dc:creator>Alex</dc:creator><author>Alex</author><pubDate>Thu, 21 Sep 2006 08:05:00 GMT</pubDate><guid>http://www.blogjava.net/alex/archive/2006/09/21/71123.html</guid><wfw:comment>http://www.blogjava.net/alex/comments/71123.html</wfw:comment><comments>http://www.blogjava.net/alex/archive/2006/09/21/71123.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/alex/comments/commentRss/71123.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/alex/services/trackbacks/71123.html</trackback:ping><description><![CDATA[key words: DAO模式<br /><br />今天在看一篇文章时提到了DAO，这个东西以前也经常接触，突然想回顾一下，于是打开Appfuse里看看dao模式（记忆中appfuse里就是很多的dao）<br /><br />截图如下:<br /><img src="http://www.blogjava.net/images/blogjava_net/alex/images/appfusedao.png" alt="appfusedao.png" border="0" height="382" width="776" /><br /><br />很清楚，左边的部分是基础模块，原意是想让右边的DAO和实现能够重用左边的，可是我找了半天也没看到需要重用左边的东西，因为在client调用的所有方法中都是明确的getUser或removeUser,就是没有getObject或者removeObject,那么不禁要问，左边的基础dao和它的实现还有什么意义呢？所以我的第一想法就是把左边的去掉得了，还好，果然有支持我想法的做法，打开springside,我们看到如下的结构:<br />截图2<br /><img src="http://www.blogjava.net/images/blogjava_net/alex/images/springside.png" alt="springside.png" border="0" height="331" width="834" /><br />这里的做法更厉害，连interface也不要了，不过效果确实是很简洁，在bookmanager里完全重用了左边的基本方法 ：<br /><div style="border: 1px solid rgb(204, 204, 204); padding: 4px 5px 4px 4px; background-color: rgb(238, 238, 238); font-size: 13px; width: 98%;"><!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);"> Book get(Integer id) {<br />        </span><span style="color: rgb(0, 0, 255);">return</span><span style="color: rgb(0, 0, 0);"> (Book) </span><span style="color: rgb(0, 0, 255);">super</span><span style="color: rgb(0, 0, 0);">.get(id);<br />    }<br /><br />    </span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">void</span><span style="color: rgb(0, 0, 0);"> save(Book book) {<br />        </span><span style="color: rgb(0, 0, 255);">super</span><span style="color: rgb(0, 0, 0);">.save(book);<br />        logger.info(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">保存图书.图书详情:</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">+</span><span style="color: rgb(0, 0, 0);"> book.toString());<br />    }<br /><br />    </span><span style="color: rgb(0, 0, 255);">public</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 255);">void</span><span style="color: rgb(0, 0, 0);"> remove(Integer id) {<br />        </span><span style="color: rgb(0, 0, 255);">super</span><span style="color: rgb(0, 0, 0);">.remove(id);<br />        logger.info(</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);">删除图书.图书ID:</span><span style="color: rgb(0, 0, 0);">"</span><span style="color: rgb(0, 0, 0);"> </span><span style="color: rgb(0, 0, 0);">+</span><span style="color: rgb(0, 0, 0);"> id);<br />    }</span></div><br />这是一个更务实的做法，如果你的项目并不是那么那么的复杂完全可以这么做，当然要说其有什么缺点显然和没有了interface的天生属性决定了的，不可强求，若你对测试隔离面向接口以及你能想到的所有关于interface的好处，那就用你自己的方式吧。<br /><br />现在我在想一个问题，难道appfuse里的继承的基本关于object的做法就没有地方可用了么？ <br /><br /><hr size="2" width="100%" />其时正好碰到java视线这一篇文章有点相关，你可以参考一下: <br /><a target="_blank" href="http://www.javaeye.com/topic/20216"><span style="color: rgb(0, 102, 153); font-weight: bold; font-size: 10pt;">用DAO模式有什么好处？</span></a><br /><hr size="2" width="100%" /><br /><font color="#ff0000">ps:<br />以前是一个基本的dao,然后n个业务dao继承于这个基本dao,现在提供一个通用dao,每个要用到的地方直接继承用就是了，更务实了!<br />不过，有一个小小的瑕疵，就是对于service中类似getUserByName或者getPeopleByEmail方法中需要提供给dao一个sql语句，从mvc的角度看，在service中看到了db层，有点不雅，不过综合来看这个还是可以或略，不要专牛角尖嘛  :)</font><br /><img src ="http://www.blogjava.net/alex/aggbug/71123.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/alex/" target="_blank">Alex</a> 2006-09-21 16:05 <a href="http://www.blogjava.net/alex/archive/2006/09/21/71123.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[zt]做设计的步骤</title><link>http://www.blogjava.net/alex/archive/2006/09/20/70678.html</link><dc:creator>Alex</dc:creator><author>Alex</author><pubDate>Wed, 20 Sep 2006 01:02:00 GMT</pubDate><guid>http://www.blogjava.net/alex/archive/2006/09/20/70678.html</guid><wfw:comment>http://www.blogjava.net/alex/comments/70678.html</wfw:comment><comments>http://www.blogjava.net/alex/archive/2006/09/20/70678.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/alex/comments/commentRss/70678.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/alex/services/trackbacks/70678.html</trackback:ping><description><![CDATA[key words: 如何做设计 设计步骤<br />转自<a href="http://www.javaeye.com/topic/1464?page=3">robin</a><br /><br />一。需求分析(抽象Use case + 分析Use case之间的关系)<br /><p>分析软件需求，以用户的角度来使用软件，找出发生的scenerio，<font color="#ff1493">抽象成为一个一个Use Case，分析出Use Case之间的关系</font>，这一步是非常重要的，这一步做好了，设计就成功了一半。Use Case的抽象有一些可以遵循的原则，这里不详细谈。</p><p>然后用语言描述每一个Use Case，描述用户使用一个Use Case发生的主事件流以及异常流。</p><p>这样就完成了需求分析阶段。</p><p>二。概要设计(找出实体 + 分析实体类之间的关系 + 提取控制类 + 画序列图)<br /></p><p>接下来做概要设计，针对每个Use Case，读Use Case的描述，<font color="#ff1493">看事件流，找出所有的实体类</font>，这也有一些可以遵循的原则，例如<b>找出所有的名词，画表格排除</b>等等方法。</p><p><font color="#0000ff">然后分析实体类之间的关系，是包含，聚合还是依赖，是1：1，还是1：n，还是其他</font>....，根据这些关系，就可以得出实体类和别的实体类想关联的属性，然后再找出每个实体类本身重要的属性。</p><p>然后再次分析Use
Case的事件流，一方面check实体类的设计是否合理，另一方面你可以找出动词，<font color="#ff1493">分析对实体类的控制逻辑，这样就可以可以设计出业务控制类</font>，一般你可
以一个实体类一个控制类，也可以业务逻辑相关的实体类由一个Facade Session
Bean(非EJB含义)来统一控制，这里面的控制类的颗粒度就由你自己来掌握了。<font color="#0000ff">一般来说先可以设计一些细颗粒度的控制类</font>，然后再按照模块，用粗粒度封
装细颗粒度的控制类，提供给Web层一个Facade。</p><p><font color="#0000ff">然后你可以画序列图，就是用序列图来表达事件流</font>，在这个过程中，你需要不断回到类图，给控制类添加方法，而序列图就是控制类的方法调用。</p><p>至此，你已经在Rose里面完成了概要设计，当然你不可能一次设计完善，会有很多次迭代，因此你不能一开始把类设计的太详细，只抓住主要的属性和方法，特别需要注意的是，是抽象的设计，不要用具体的编程语言来表达类。</p><p>三。实施(结合xdoclet和Schema工具自动生成代码)<br /></p><p>然后你就可以抛开Rose了，转到Eclipse+Togehter里面，根据那些类，规划一下package层次，然后在Together里面进行类的详细设计，所有需要的属性一一写上，当然你还是不可能一下把所有的属性方法写全，不过没有关系，把重要的写好就行了。</p><p>然后类框架已经生成好了，<font color="#ff1493">给所有的实体类加上xdoclet，然后生成hbm，然后用Hibernate的ExportScheme生成DDL，</font>运行一遍自动创建好所有的表。这样所有的实体相关类全部做好了。</p><p>你现在就集中精力把控制类那些方法里面的代码填写上就OK了，在这个过程，你会发现有些实体类缺属性，没有关系，加上属性，然后写好xdoclet，运行一遍，自动生成hbm，自动创建好表，然后继续写你的方法，也有可能你发现控制类缺方法，那么就加上。</p><p>基本上实体类就是getter/setter，和少量的实体相关方法，所有的控制逻辑都写在控制类里面。</p><p>最后你的软件就基本写好了，用Eclipse生成好一堆你的testCase运行测试，反复修改，除bug。</p><p>看看使用OOAD的设计思路，是多么的爽的事情阿！<b>你只需要把精力放到Use
Case的抽象</b>，实体类的关系总结，控制类的归纳。而当你使用Eclipse＋Together之后，你所需要写的代码只不过是控制类的方法实现代码，其
他的都已经生成好了。另外可能需要写少量工具类。</p><br /><img src ="http://www.blogjava.net/alex/aggbug/70678.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/alex/" target="_blank">Alex</a> 2006-09-20 09:02 <a href="http://www.blogjava.net/alex/archive/2006/09/20/70678.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[zt]Effective Java读书笔记</title><link>http://www.blogjava.net/alex/archive/2006/09/11/69002.html</link><dc:creator>Alex</dc:creator><author>Alex</author><pubDate>Mon, 11 Sep 2006 10:14:00 GMT</pubDate><guid>http://www.blogjava.net/alex/archive/2006/09/11/69002.html</guid><wfw:comment>http://www.blogjava.net/alex/comments/69002.html</wfw:comment><comments>http://www.blogjava.net/alex/archive/2006/09/11/69002.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/alex/comments/commentRss/69002.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/alex/services/trackbacks/69002.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 终于翻开这本James都称赞的java经典书籍了，发现比一般的英语书籍要难懂一些。但是里面的Item都是非常实用的，是java程序员应该理解的。 Creating and Destroying ObjectItem 1:考虑用静态工厂方法替代构造器例如：public static Boolean valueOf(boolean b)     {          return (b?Boolean...&nbsp;&nbsp;<a href='http://www.blogjava.net/alex/archive/2006/09/11/69002.html'>阅读全文</a><img src ="http://www.blogjava.net/alex/aggbug/69002.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/alex/" target="_blank">Alex</a> 2006-09-11 18:14 <a href="http://www.blogjava.net/alex/archive/2006/09/11/69002.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[zt]从工作流状态机实践中总结状态模式使用心得</title><link>http://www.blogjava.net/alex/archive/2006/09/09/68672.html</link><dc:creator>Alex</dc:creator><author>Alex</author><pubDate>Sat, 09 Sep 2006 02:43:00 GMT</pubDate><guid>http://www.blogjava.net/alex/archive/2006/09/09/68672.html</guid><wfw:comment>http://www.blogjava.net/alex/comments/68672.html</wfw:comment><comments>http://www.blogjava.net/alex/archive/2006/09/09/68672.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/alex/comments/commentRss/68672.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/alex/services/trackbacks/68672.html</trackback:ping><description><![CDATA[
		<h3 align="center">从工作流状态机实践中总结状态模式使用心得</h3>
		<p align="center">
				<a href="http://www.jdon.com/jive/profile.jsp?user=2" title="彭晨阳(网名: 板桥里人)">
						<b>banq</b>
				</a> http://www.jdon.com Dec 7, 2003 12:10 AM
                <a href="http://www.jdon.com/jive/post.jsp?forum=91&amp;thread=10981&amp;message=4743784&amp;reply=true"><img src="http://www.jdon.com/jive/images/reply.gif" alt="回复此消息" border="0" height="17" hspace="3" width="17" /></a><font><a href="http://www.jdon.com/jive/post.jsp?forum=91&amp;thread=10981&amp;message=4743784&amp;reply=true" title="回复此消息">回复</a></font></p>
                
状态模式好像是很简单的模式，正因为状态好像是个简单的对象，想复杂化实现设计模式就不是容易，误用情况很多。<br /><br />我个人曾经设计过一个大型游戏系统的游戏状态机，游戏状态可以说是游戏设计的主要架构，但是由于系统过分复杂<br />和时间仓促，并没有真正实现状态模式。<br /><br />目前在实现一个电子政务项目中，需要进行流程状态变化，在电子政务设计中，我发现，如果一开始完全按照工作流<br />规范开发，难度很大，它和具体项目实践结合无法把握，而且工作流规范现在有wfmc，还有bpml，选择也比较难。因<br />此，我决定走自创的中间道路。<br /><br />因为，我需要做一个状态机API，或者说状态机框架，供具体系统调用：类如公文流转应用或信息发报送应用等。<br /><br />好的状态模式必须做到两点：<br />1. 状态变化必须从外界其它逻辑划分出来。<br />2. 状态必须可方便拓展，对其它代码影响非常小。<br /><br />要做到这两点，必须先明确状态变化机制，状态变化实际是由Event事件驱动的，可以认为是Event-condition-State，<br />在MVC模式一般是Event-condition-Action实现。状态模式需要封装的是Event-condition-State中的condition-State<br />部分。<br /><br />清晰理解状态和流程的关系也非常重要，因为状态不是孤立的，可以说和流程是点和线的关系，状态从一个方面说明<br />了流程，流程是随时间而改变，状态是截取流程某个时间片。因此，必须明白使用状态模式实现状态机实际是为了更<br />好地表达和说明流程。<br /><br />状态和流程以及事件的关系如下：<br /><br /><br />                 |Event<br />___currentState__|______newState___<br /><br /><br /><br /><br />图中表示了是事件改变了流程的状态，在业务逻辑中，经常发生的是事件，如果不使用状态模式，需要在很多业务逻<br />辑处实现事件到状态判定和转换，这有很多危险性。<br /><br />最大的危险是系统没有一个一抓就灵的主体结构，以那个游戏系统为例，在没有状态模式对状态提炼的情况下，状态<br />改变由每个程序员想当然实现，导致每个程序员开发的功能在整合时就无法调试，因为这个程序员可能不知道那个程<br />序员的代码在什么运行条件下改变了游戏状态，结果导致自己的代码无法运行。<br /><br />这种现象实际上拒绝了项目管理的协作性，大大地拖延项目进度（程序员之间要反复商量讨论对方代码设计）。从这<br />一点也说明，一个好的架构设计是一个项目快速成功完成的基础技术保证，没有这个技术基础，再先进的项目管理手<br />段也是没有效率的，或者是笨拙的。<br /><br />状态模式对于很多系统来说，确实是架构组成一个重要部分。<br /><br />下面继续讨论如何实现一个好的状态模式，为了实现好的状态模式，必须在状态模式中封装下面两个部分：<br />1. 状态转换规则（行为）<br />2. 状态属性（属性）<br /><br />状态转换行为有两种划分标准：<br />1. run和next两个行为，run是当前状态运行行为，next是指在Event参与下，几种可能转向的下一个状态。<br />2. stateEnter和stateExit， 状态进入和状态退出。<br /><br />如果用进入一个个房间来表示状态流程的话， 第一种分析是只重视着“在房间里”和“如何转入下一个房间”，这两种行<br />为一旦确定，可以被反复使用，进而一个流程的状态切换可以全部表达出来。<br /><br />第二中分析方法有所区别，只重视进入房间和离开房间这个两个行为，同样，这种模型也可以被反复利用在其它房间，<br />一个流程的状态切换也可以全部表达出来。<br /><br />具体选择取决于你的需求，比如，如果你在进入一个状态开始，要做很多初始化工作，那么第二种模型就很适合。<br /><br />状态变化都离不开一个主体对象，主体对象可以说包含状态变化（行为）和状态属性（属性），假设为StateOwner，<br />StateOwner有两个部分组成：Task/事情和状态。任何一个Task/事情都会对应一个状态。<br /><br />这样，我们已经抽象出两个主要对象：状态State和StateOwner。<br /><br />为了封装状态变化细节，我们可以抽象出一个状态机StateMachine来专门实现状态根据事情实现转换。<br /><br />这样，客户端外界通过状态机可访问状态模式这个匣子。在实践中，外界客户端需要和状态机实现数据交换，我们把<br />它也分为两种：属性和行为。<br /><br />其中属性可能需要外界告诉状态状态变化的主体对象Task，解决状态的主人问题，是谁的问题；行为可能是需要持久<br />化当前这个主体对象的状态到数据库。<br /><br />这两种数据交换可以分别通过StateOwner和StateMachine与整个状态机实现数据交换，这样，具体状态和状态切换也<br />和外界实现了解耦隔离。<br /><br />因此好的状态模式实现必须有下列步骤：<br />（1）将每个状态变成State的子类，实现每个状态对象化。<br />（2）在每个状态中，封装着进入下一个状态可能规则，这些规则是状态变化的核心，换句话说，统一了状态转换的规则。<br />     具体可采取run和next这样的转换行为。<br /><br />下面是一个子状态代码：<br /><br /><br /><b>public</b><b>class</b> Running <b>extends</b> StateT{<br /><br /><font color="#0000aa">//</font><font color="black"><br /><b>public</b><b>void</b> run(StateOwner stateOwner){<br />     stateOwner.setCurrentState(<b>this</b>);<br />  }<br /><br /></font><font color="#0000aa">//转换到下一个状态的规则</font><font color="black"><br /></font><font color="#0000aa">//当前是Running状态，下一个状态可能是暂停、结束或者强制退出等</font><font color="black"><br /></font><font color="#0000aa">//状态，但是绝对不会是Not_Started这样的状态</font><font color="black"><br /></font><font color="#0000aa">//转换规则在这里得到了体现。</font><font color="black"><br /><b>public</b> State next(Event e) {<br /><b>if</b>(transitions == <b>null</b>){<br />       addEventState(<b>new</b> EventImp(</font><font color="#00bb00">"PAUSE"</font><font color="black">), <b>new</b> Suspended());<br />       addEventState(<b>new</b> EventImp(</font><font color="#00bb00">"END"</font><font color="black">), <b>new</b> Completed());<br />       addEventState(<b>new</b> EventImp(</font><font color="#00bb00">"STOP"</font><font color="black">), <b>new</b> Aborted());<br />    }<br /><b>return</b><b>super</b>.next(e);<br />  }<br /><br />}     <br /><br /><br /><br />外界直接调用 StateMachine的关键方法transition；实行状态的自动转变。  <br /><br /><br /><b>public</b><b>class</b> StateMachine {<br /><br /></font><font color="#0000aa">/**<br />   * 状态切换<br />   * 根据Event参数，运行相应的状态。<br />   * 1. 获得当前状态<br />   * 2. 使用当前状态的next()转换<br />   *                  |Event<br />   * ___currentState__|______newState___<br />   *<br />   * @param inputs<br />   */</font><font color="black"><br /><b>public</b><b>final</b><b>void</b> transition(String taskid, Event e) throws Exception {<br />    State currentState = readCurrentState(taskid); </font><font color="#0000aa">//从数据库获得当前状态</font><font color="black"><br />    StateOwner stateOwner = <b>new</b> StateOwner(taskid, currentState);<br /></font><font color="#0000aa">//转换状态</font><font color="black"><br />    currentState = currentState.next(e);<br /><b>if</b> (currentState != <b>null</b>) {<br />      currentState.run(stateOwner);<br />      saveCurrentState(stateOwner); </font><font color="#0000aa">//保存当前状态</font><font color="black"><br />    }<br />  }<br /><br />} </font><img src ="http://www.blogjava.net/alex/aggbug/68672.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/alex/" target="_blank">Alex</a> 2006-09-09 10:43 <a href="http://www.blogjava.net/alex/archive/2006/09/09/68672.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[zt]设计模式之state 状态模式</title><link>http://www.blogjava.net/alex/archive/2006/09/09/68671.html</link><dc:creator>Alex</dc:creator><author>Alex</author><pubDate>Sat, 09 Sep 2006 02:42:00 GMT</pubDate><guid>http://www.blogjava.net/alex/archive/2006/09/09/68671.html</guid><wfw:comment>http://www.blogjava.net/alex/comments/68671.html</wfw:comment><comments>http://www.blogjava.net/alex/archive/2006/09/09/68671.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/alex/comments/commentRss/68671.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/alex/services/trackbacks/68671.html</trackback:ping><description><![CDATA[
		<h3 align="center">设计模式之State</h3>
		<p align="center">
				<a href="http://www.jdon.com/aboutme.htm">板桥里人</a> http://www.jdon.com 2002/4/6/</p>
		<p align="center">
				<a href="http://www.jdon.com/mybook/index.htm" target="_blank">
						<strong>模式实战书籍《Java实用系统开发指南》</strong>
				</a>
		</p>
		<p>
				<i>
						<b>State模式的定义</b>
				</i>: 不同的状态,不同的行为;或者说,每个状态有着相应的行为.</p>
		<p>
				<i>
						<b>何时使用</b>
				</i>?<br />
                    State模式在实际使用中比较多,适合"状态的切换".因为我们经常会使用If elseif else 
                    进行状态切换, 如果针对状态的这样判断切换反复出现,我们就要联想到是否可以采取State模式了.</p>
		<p>不
只是根据状态,也有根据属性.如果某个对象的属性不同,对象的行为就不一样,这点在数据库系统中出现频率比较高,我们经常会在一个数据表的尾部,加上
property属性含义的字段,用以标识记录中一些特殊性质的记录,这种属性的改变(切换)又是随时可能发生的,就有可能要使用State.<br /></p>
		<p>
				<i>
						<b>是否使用?</b>
				</i>
				<br />
                    在实际使用,类似开关一样的状态切换是很多的,但有时并不是那么明显,取决于你的经验和对系统的理解深度.</p>
		<p>这里要阐述的是"开关切换状态" 和" 一般的状态判断"是有一些区别的, " 
                    一般的状态判断"也是有 if..elseif结构,例如:</p>
		<p>　　　　if (which==1) state="hello";<br />
                    　　　　else if (which==2) state="hi";<br />
                    　　　　else if (which==3) state="bye";<br /></p>
		<p>这是一个 " 一般的状态判断",state值的不同是根据which变量来决定的,which和state没有关系.如果改成:</p>
		<p>　　　　if (state.euqals("bye")) state="hello";<br />
                    　　　　else if (state.euqals("hello")) state="hi";<br />
                    　　　　else if (state.euqals("hi")) state="bye";<br /></p>
		<p>这就是 "开关切换状态",是将state的状态从"hello"切换到"hi",再切换到""bye";在切换到"hello",好象一个旋转开关,这种状态改变就可以使用State模式了.</p>
		<p>如
果单纯有上面一种将"hello"--&gt;"hi"--&gt;"bye"--&gt;"hello"这一个方向切换,也不一定需要使用State模
式,因为State模式会建立很多子类,复杂化,但是如果又发生另外一个行为:将上面的切换方向反过来切换,或者需要任意切换,就需要State了.</p>
		<p>请看下例:</p>
		<table border="0" cellpadding="3" cellspacing="3" width="80%">
				<tbody>
						<tr>
								<td bgcolor="#cccccc">
										<p>public class Context{</p>
										<p>　　private Color state=null;</p>
										<p>　　public void push(){</p>
										<p>　　　　//如果当前red状态 就切换到blue<br />
                          　　　　if (state==Color.red) state=Color.blue;<br /><br />
                          　　　　//如果当前blue状态 就切换到green<br />
                          　　　　else if (state==Color.blue) state=Color.green;<br /><br />
                          　　　　//如果当前black状态 就切换到red<br />
                          　　　　else if (state==Color.black) state=Color.red;<br /><br />
                          　　　　//如果当前green状态 就切换到black<br />
                          　　　　else if (state==Color.green) state=Color.black;<br />
                          　　　　<br />
                          　　　　Sample sample=new Sample(state);<br />
                          　　　　sample.operate();<br />
                          　　}</p>
										<p>　　public void pull(){<br /><br />
                          　　　　//与push状态切换正好相反</p>
										<p>　　　　if (state==Color.green) state=Color.blue;<br />
                          　　　　else if (state==Color.black) state=Color.green;<br />
                          　　　　else if (state==Color.blue) state=Color.red;<br />
                          　　　　else if (state==Color.red) state=Color.black;<br /><br />
                          　　　　Sample2 sample2=new Sample2(state);<br />
                          　　　　sample2.operate(); <br />
                          　　}</p>
										<p>}</p>
								</td>
						</tr>
				</tbody>
		</table>
		<p>在上例中,我们有两个动作push推和pull拉,这两个开关动作,改变了Context颜色,至此,我们就需要使用State模式优化它.</p>
		<p>另外注意:但就上例,state的变化,只是简单的颜色赋值,这个具体行为是很简单的,State适合巨大的具体行为,因此在,就本例,实际使用中也不一定非要使用State模式,这会增加子类的数目,简单的变复杂.</p>
		<p>例如: 银行帐户, 经常会在Open 状态和Close状态间转换.</p>
		<p>例如: 经典的TcpConnection, Tcp的状态有创建 侦听 关闭三个,并且反复转换,其创建 侦听 关闭的具体行为不是简单一两句就能完成的,适合使用State</p>
		<p>例如:信箱POP帐号, 会有四种状态, start HaveUsername Authorized quit,每个状态对应的行为应该是比较大的.适合使用State</p>
		<p>例如:在工具箱挑选不同工具,可以看成在不同工具中切换,适合使用State.如 具体绘图程序,用户可以选择不同工具绘制方框 
                    直线 曲线,这种状态切换可以使用State.</p>
		<p>
				<i>
						<b>如何使用</b>
				</i>
				<br />
                    State需要两种类型实体参与:</p>
		<p>1.state manager 状态管理器 ,就是开关 ,如上面例子的Context实际就是一个state manager, 
                    在state manager中有对状态的切换动作.<br />
                    2.用抽象类或接口实现的父类,,不同状态就是继承这个父类的不同子类.</p>
		<p>以上面的Context为例.我们要修改它,建立两个类型的实体.<br /><b>第一步: 首先建立一个父类:</b></p>
		<table bgcolor="#ffffff" border="0" cellpadding="3" cellspacing="3" width="80%">
				<tbody>
						<tr>
								<td bgcolor="#cccccc">
										<p>public abstract class State{</p>
										<p>　　public abstract void handlepush(Context c);<br />
                          　　public abstract void handlepull(Context c);<br />
                          　　public abstract void getcolor();</p>
										<p>}</p>
								</td>
						</tr>
				</tbody>
		</table>
		<p>父类中的方法要对应state manager中的开关行为,在state manager中 本例就是Context中,有两个开关动作push推和pull拉.那么在状态父类中就要有具体处理这两个动作:handlepush() 
                    handlepull(); 同时还需要一个获取push或pull结果的方法getcolor()</p>
		<p>下面是具体子类的实现:</p>
		<table border="0" cellpadding="3" cellspacing="3" width="80%">
				<tbody>
						<tr>
								<td bgcolor="#cccccc">
										<p>public class BlueState extends 
                          State{</p>
										<p>　　public void handlepush(Context c){<br />
                          　　　　 //根据push方法"如果是blue状态的切换到green" ;<br />
                          　　　　 c.setState(new GreenState());</p>
										<p>　　}<br />
                          　　public void handlepull(Context c){</p>
										<p>　　　　 //根据pull方法"如果是blue状态的切换到red" ;<br />
                          　　　　c.setState(new RedState());</p>
										<p>　　}</p>
										<p>　　public abstract void getcolor(){ return (Color.blue)}</p>
										<p>}</p>
										<p> </p>
								</td>
						</tr>
				</tbody>
		</table>
		<p>同样 其他状态的子类实现如blue一样.</p>
		<p>
				<b>第二步: 要重新改写State manager 也就是本例的Context:</b>
		</p>
		<table border="0" cellpadding="3" cellspacing="3" width="97%">
				<tbody>
						<tr>
								<td bgcolor="#cccccc">
										<p>public class Context{</p>
										<p>　　private Sate state=null; //我们将原来的 Color state 改成了新建的State 
                          state;</p>
										<p>　　//setState是用来改变state的状态 使用setState实现状态的切换<br />
                          　　pulic void setState(State state){<br /><br />
                          　　　　this.state=state;</p>
										<p>　　}</p>
										<p>　　public void push(){</p>
										<p>　　　　//状态的切换的细节部分,在本例中是颜色的变化,已经封装在子类的handlepush中实现,这里无需关心<br />
                          　　　　state.handlepush(this);<br />
                          　　　　<br />
                          　　　　//因为sample要使用state中的一个切换结果,使用getColor()<br />
                          　　　　Sample sample=new Sample(state.getColor());<br />
                          　　　　sample.operate(); </p>
										<p>　　}</p>
										<p> </p>
										<p>　　public void pull(){</p>
										<p>　　　　state.handlepull(this);<br />
                          　　　　<br />
                          　　　　Sample2 sample2=new Sample2(state.getColor());<br />
                          　　　　sample2.operate(); </p>
										<p>　　}</p>
										<p>}</p>
										<p> </p>
								</td>
						</tr>
				</tbody>
		</table>
		<p>至此,我们也就实现了State的refactorying过程.</p>
		<p>以上只是相当简单的一个实例,在实际应用中,handlepush或handelpull的处理是复杂的.</p>
		<p>状态模式优点：<br />
                    （1） 封装转换过程，也就是转换规则<br />
                    （2） 枚举可能的状态，因此，需要事先确定状态种类。<br /></p>
		<p>状态模式可以允许客户端改变状态的转换行为，而状态机则是能够自动改变状态，状态机是一个比较独立的而且复杂的机制，具体可参考一个状态机开源项目：<a href="http://sourceforge.net/projects/smframework/" target="_blank">http://sourceforge.net/projects/smframework/</a></p>
		<p>状态模式在工作流或游戏等各种系统中有大量使用，甚至是这些系统的核心功能设计，例如政府OA中，一个批文的状态有多种：未办；正在办理；正在批示；正在审核；已经完成等各种状态，使用状态机可以封装这个状态的变化规则，从而达到扩充状态时，不必涉及到状态的使用者。</p>
		<p>在网络游戏中，一个游戏活动存在开始；开玩；正在玩；输赢等各种状态，使用状态模式就可以实现游戏状态的总控，而游戏状态决定了游戏的各个方面，使用状态模式可以对整个游戏架构功能实现起到决定的主导作用。</p>
		<p>
				<strong>状态模式实质</strong>：<br />
                    使用状态模式前，客户端外界需要介入改变状态，而状态改变的实现是琐碎或复杂的。</p>
		<p>使用状态模式后，客户端外界可以直接使用事件Event实现，根本不必关心该事件导致如何状态变化，这些是由状态机等内部实现。</p>
		<p>这是一种Event-condition-State，状态模式封装了condition-State部分。</p>
		<p>每个状态形成一个子类，每个状态只关心它的下一个可能状态，从而无形中形成了状态转换的规则。如果新的状态加入，只涉及它的前一个状态修改和定义。</p>
		<p>状态转换有几个方法实现：一个在每个状态实现next()，指定下一个状态；还有一种方法，设定一个StateOwner，在StateOwner设定stateEnter状态进入和stateExit状态退出行为。</p>
		<p>状态从一个方面说明了流程，流程是随时间而改变，状态是截取流程某个时间片。</p>
		<p>
				<br />
                    相关文章：</p>
		<p>
				<a href="http://www.jdon.com/jive/article.jsp?forum=91&amp;thread=10981" target="_blank">从工作流状态机实践中总结状态模式使用心得</a>
				<br />
		</p>
		<p>参考资源:<br /><a href="http://www.research.umbc.edu/%7Etarr/cs491/lectures/StateStrategy.pdf" target="_blank">the 
                    State and Stategy</a><br /><a href="http://www.javaworld.com/javaworld/jw-08-1997/jw-08-stated.html" target="_blank">How 
                    to implement state-dependent behavior</a><br /><a href="http://www.patterndepot.com/put/8/state.pdf" target="_blank">The 
                    state patterns</a></p>
		<p> </p>
<img src ="http://www.blogjava.net/alex/aggbug/68671.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/alex/" target="_blank">Alex</a> 2006-09-09 10:42 <a href="http://www.blogjava.net/alex/archive/2006/09/09/68671.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[zt]设计模式之复合模式 -------.Net中的设计模式——Composite模式</title><link>http://www.blogjava.net/alex/archive/2006/09/09/68634.html</link><dc:creator>Alex</dc:creator><author>Alex</author><pubDate>Fri, 08 Sep 2006 16:51:00 GMT</pubDate><guid>http://www.blogjava.net/alex/archive/2006/09/09/68634.html</guid><wfw:comment>http://www.blogjava.net/alex/comments/68634.html</wfw:comment><comments>http://www.blogjava.net/alex/archive/2006/09/09/68634.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/alex/comments/commentRss/68634.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/alex/services/trackbacks/68634.html</trackback:ping><description><![CDATA[
		<div class="storycontent">
				<p>转自 <br /></p>
				<h3 class="storytitle">
						<a href="http://www.brucezhang.com/?p=57" rel="bookmark">.Net中的设计模式——Composite模式</a>
				</h3>
				<i>前言：google到一篇关于复合模式的文章，虽然是关于 .NET的，但是对于开发java同样有借鉴意义.<br /></i>
				<br />
				<p>一、模式概述</p>
				<p>描述Composite模式的最佳方式莫过于树形图。从抽象类或接口为根节点开始，然后生枝发芽，以形成树枝节点和叶结点。因此，
Composite模式通常用来描述部分与整体之间的关系，而通过根节点对该结构的抽象，使得客户端可以将单元素节点与复合元素节点作为相同的对象来看
待。</p>
				<p>由于Composite模式模糊了单元素和复合元素的区别，就使得我们为这些元素提供相关的操作时，可以有一个统一的接口。例如，我们要编写一个字
处理软件，该软件能够处理文字，对文章进行排版、预览、打印等功能。那么，这个工具需要处理的对象，就应该包括单个的文字、以及由文字组成的段落，乃至整
篇文档。这些对象从软件处理的角度来看，对外的接口应该是一致的，例如改变文字的字体，改变文字的位置使其居中或者右对齐，也可以显示对象的内容，或者打
印。而从内部实现来看，我们对段落或者文档进行操作，实质上也是对文字进行操作。从结构来看，段落包含了文字，文档又包含了段落，是一个典型的树形结构。
而其根节点正是我们可以抽象出来暴露在外的统一接口，例如接口IElement：</p>
				<p align="center">
						<img alt="composite1.GIF" src="http://wayfarer.cnblogs.com/images/cnblogs_com/wayfarer/patterns/composite1.GIF" border="0" height="129" width="181" />
				</p>
				<p>既然文字、段落、文档都具有这些操作，因此它们都可以实现IElement接口：</p>
				<p align="center">
						<img src="http://wayfarer.cnblogs.com/images/cnblogs_com/wayfarer/patterns/composite2.GIF" border="0" height="356" width="603" />
				</p>
				<p>从上图可以看到，对象Word、Paragraph、Document均实现了IElement接口，但Paragraph和Document与
Word对象不同的是，这两者处除了实现了IElement接口，它们还与IElement接口对象之间具有聚合的关系，且是一对多。也就是说
Paragraph与Document对象内可以包含0个到多个IElement对象，这也是与前面对字处理软件分析获得的结果是一致的。</p>
				<p>从整个结构来看，完全符合树形结构的各个要素，接口IElement是根节点，而Paragraph和Document类为枝节点，Word对象为
叶节点。既然作为枝节点，它就具有带叶节点的能力，从上图的聚合关系中我们体现出了这一点。也就是说，Paragraph和Document类除了具有排
版、打印方面的职责外，还能够添加、删除叶节点的操作。那么这些操作应该放在哪里呢？</p>
				<p>
						<font color="#0000ff">管理对子节点的管理，Composite模式提供了两种方式：一个是透明方式</font>，也就是说在根节点中声明所有用来管理子元素的方法，包括Add()、
Remove()等方法。这样一来，实现根节点接口的子节点同时也具备了管理子元素的能力。这种实现策略，最大的好处就是完全消除了叶节点和枝节点对象在
抽象层次的区别，它们具备完全一致的接口。而缺点则是不够安全。由于叶节点本身不具备管理子元素的能力，因此提供的Add()、Remove()方法在实
现层次是无意义的。但客户端调用时，却看不到这一点，从而导致在运行期间有出错的可能。</p>
				<p>
						<font color="#0000ff">另一种策略则是安全方式</font>。与透明方式刚好相反，<font color="#0000ff">它只在枝节点对象里声明管理子元素的方法</font>，<font color="#0000ff">由于叶节点不具备这些方法，当客户端在操作叶节点时，就不会出现前一种方式的安全错误</font>。然而，这种实现方式，却导致了叶节点和枝节点接口的不完全一致，这给客户端操作时带来了不便。</p>
				<p>这两种方式各有优缺点，我们在实现时，应根据具体的情况，作出更加合理的抉择。在字处理软件一例中，我选择了安全方式来实现，因为对于客户端而言，
在调用IElement接口时，通常是将其视为可被排版、打印等操作的对象。至于为Paragraph和Document对象添加、删除子对象，往往是一
种初始化的行为，完全可以放到一个单独的模块中。根据单一职责原则（SRP），我们没有必要让IElement接口负累太重。所以，我们需要对上图作稍许
的修改，在Paragraph和Document对象中增加Add()和Remove()方法：</p>
				<p align="center">
						<img src="http://wayfarer.cnblogs.com/images/cnblogs_com/wayfarer/patterns/composite3.GIF" border="0" height="184" width="425" />
				</p>
				<p>以下是IElement对象结构的实现代码：<br />
public interface IElement<br />
{<br />
      void ChangeFont(Font font);<br />
      void Show();<br />
      //其他方法略;<br />
}<br />
public class Word<br />
{<br />
 public void ChangeFont(Font font)<br />
 {<br />
      this.font = font;<br />
 }<br />
 public void Show()<br />
 {<br />
      Console.WriteLine(this.ToString());<br />
 }<br />
 //其他方法略;<br />
}<br />
public class Paragraph<br />
{<br />
 private ArrayList elements = new ArrayList();<br />
 public void Add(IElement element)<br />
 {<br />
      elements.Add(element);<br />
 }<br />
 public void Remove(IElement element)<br />
 {<br />
      elements.Remove(element);<br />
 }<br />
 public void ChangeFont(Font font)<br />
 {<br />
      foreach (IElement element in elements)<br />
      {<br />
          element.ChangeFont(font);<br />
  }<br />
 }<br />
 public void Show()<br />
 {<br />
      foreach (IElement element in elements)<br />
      {<br />
          element.Show(font);<br />
  }<br />
 }<br />
 //其他方法略;<br />
}<br />
//Document类略；</p>
				<p>
						<font color="#ff0000">实际上，我们在为叶节点实现Add()，Remove()方法时，还需要考虑一些异常情况。例如在Paragraph类中，添加的子元素就不能是
Document对象和Paragraph对象。所以在添加IElement对象时，还需要做一些条件判断，以确定添加行为是否正确，如果错误，应抛出异
常。</font>
				</p>
				<p>采用Composite模式，我们将Word、Paragraph、Document抽象为IElement接口。虽然各自内部的实现并不相同，枝
节点和叶节点的实质也不一样，但对于调用者而言，是没有区别的。例如在类WordProcessor中，包含一个GetSelectedElement
()静态方法，它能够获得当前选择的对象：<br />
public class WordProcessor<br />
{<br />
 public static IElement GetSelectedElement(){……}<br />
}</p>
				<p>对于字处理软件的UI来说，如果要改变选中对象的字体，则可以在命令按钮cmdChangeFont的Click事件中写下如下代码：<br />
public void cmdChangeFont_Click(object sender, EventArgs e)<br />
{<br />
    WordProcessor.GetSelectedElement().ChangeFont(currentFont);<br />
}</p>
				<p>不管当前选中的对象是文字、段落还是整篇文档，对于UI而言，操作都是完全一致的，根本不需要去判断对象的类别。因此，如果在Business
Layer的类库设计时，采用Composite模式，将极大地简化UI表示层的开发工作。此外，应用该模式也较好的支持项目的可扩展性。例如，我们为
IElement接口增加了Sentence类，对于前面的例子而言，只需要修改GetSelectedElement()方法，而
cmdChangeFont命令按钮的Click事件以及Business
Layer类库原有的设计，都不需要做任何改变。这也符合OO的开放-封闭原则（OCP），即对于扩展是开放的(Open for
extension)，对于更改则是封闭的（Closed for modification）。</p>
				<p>二、.Net Framework中的Composite模式</p>
				<p>在.Net中，最能体现Composite模式的莫过于Windows或Web的控件。在这些控件中，有的包含子控件，有的则不包含且不能包含子控
件，这正好符合叶节点和枝节点的含义。所有Web控件的基类为System.Web.UI.Contril类（如果是Windows控件，则基类为
System.Windows.Forms.Control类）。其子类包含有HtmlControl、HtmlContainerControl等。按
照Composite模式的结构，枝节点和叶节点属于根节点的不同分支，同时枝节点与根节点之间应具备一个聚合关系，可以通过Add()、Remove
()方法添加和移除其子节点。设定HtmlControl为叶节点，而HtmlContaiinerControl为枝节点，那么采用透明方式的设计方
法，在.Net中控件类的结构，就应该如下图所示：</p>
				<p align="center">
						<img src="http://wayfarer.cnblogs.com/images/cnblogs_com/wayfarer/patterns/composite4.GIF" border="0" height="255" width="353" />
				</p>
				<p>虽然根据透明方式的Composite模式，HtmlControl类与其父类Control之间也应具备一个聚合关系，但实质上该类并不具备管理
子控件的职责，因此我在类图中忽略了这个关系。此时，HtmlControl类中的Add()、Remove()方法，应该为空，或者抛出一个客户端能够
捕获的异常。</p>
				<p>然而，从具体实现来考虑，由于HtmlControl类和HtmlContainerControl类在实现细节层次，区别仅在于前者不支持子控
件，但从控件本身的功能来看，很多行为是相同或者相近的。例如HtmlControl类的Render()方法，调用了方法RenderBeginTag
()方法：<br />
protected override void Render(HtmlTextWriter writer)<br />
{<br />
      this.RenderBeginTag(writer);<br />
}<br />
protected virtual void RenderBeginTag(HtmlTextWriter writer)<br />
{<br />
      writer.WriteBeginTag(this.TagName);<br />
      this.RenderAttributes(writer);<br />
      writer.Write(’&gt;');<br />
}</p>
				<p>而HtmlContainerControl类也具有Render()方法，在这个方法中也调用了RenderBeginTag()方法，且RenderBeginTag方法的实现和前者完全一致：<br />
protected override void Render(HtmlTextWriter writer)<br />
{<br />
      this.RenderBeginTag(writer);<br />
      this.RenderChildren(writer);<br />
      this.RenderEndTag(writer);<br />
}</p>
				<p>按照上面的结构，由于HtmlControl和HtmlContainerControl之间并无继承关系，这就要求两个类中，都要重复实现
RenderBeginTag()方法，从而导致产生重复代码。根据OO的特点，解决的办法，就是让HtmlContainerControl继承自
HtmlControl类（因为HtmlContainerControl的接口比HtmlControl宽，所以只能令
HtmlContainerControl作为子类），并让RenderBeginTag()方法成为HtmlControl类的protected方
法，子类HtmlContainerControl可以直接调用这个方法。然而与之矛盾的是，HtmlContainerControl却是一个可以包含
子控件的枝节点，而HtmlControl则是不能包含子控件的叶节点，那么这样的继承关系还成立吗？</p>
				<p align="center">
						<img src="http://wayfarer.cnblogs.com/images/cnblogs_com/wayfarer/patterns/composite5.GIF" border="0" height="361" width="271" />
				</p>
				<p>HtmlControl类对Add()方法和Remove()方法的重写后，这两个方法内容为空。由于HtmlContainerControl类
继承HtmlControl类，但我们又要求它的Add()和Remove()方法和Control类保持一致，而父类HtmlControl已经重写这
两个方法，此时是无法直接继承来自父类的方法的。以上是采用透明方式的设计。</p>
				<p>如果采用安全方式，仍然有问题。虽然在HtmlControl类中不再有Add()和Remove()方法，但由于Control类和
HtmlContainerControl类都允许添加子控件，它们包含的Add()、Remove()方法，只能分别实现。这样的设计必然会导致重复代
码。这也是与我们的期望不符的。</p>
				<p>那么在.Net中，Control类究竟是怎样实现的呢？下面，我将根据.Net实现Control控件的源代码，来分析Control控件的真实结构，以及其具体的实现细节。</p>
				<p>三、深入分析.Net中的Composite模式</p>
				<p>首先，我们来剖析Web控件的基类Control类的内部实现：</p>
				<p>public class Control : IComponent, IDisposable, IParserAccessor, IDataBindingsAccessor<br />
{<br />
       // Events;略  <br />
       // Methods<br />
 public Control()<br />
 {<br />
             if (this is INamingContainer)<br />
             {<br />
                    this.flags[0×80] = true;<br />
             }<br />
 }<br />
 public virtual bool HasControls()<br />
 {<br />
              if (this._controls != null)<br />
              {<br />
                    return (this._controls.Count &gt; 0);<br />
              }<br />
              return false;<br />
 }<br />
 public virtual void DataBind()<br />
        {<br />
              this.OnDataBinding(EventArgs.Empty);   <br />
              if (this._controls != null)           <br />
       {                <br />
              string text1 = this._controls.SetCollectionReadOnly(”Parent_collections_readonly”);<br />
                        int num1 = this._controls.Count;<br />
                 for (int num2 = 0; num2 &lt; num1; num2++)                 <br />
   {<br />
                         this._controls[num2].DataBind();<br />
                   }<br />
                   this._controls.SetCollectionReadOnly(text1);<br />
              }<br />
 }<br />
        protected virtual void Render(HtmlTextWriter writer)<br />
 {<br />
              this.RenderChildren(writer);<br />
 }<br />
 protected virtual ControlCollection CreateControlCollection()<br />
 {<br />
              return new ControlCollection(this);<br />
 }<br />
       // Properties<br />
 public virtual ControlCollection Controls<br />
 {<br />
             get<br />
             {<br />
                   if (this._controls == null)<br />
                   {<br />
                         this._controls = this.CreateControlCollection();<br />
                   }<br />
                   return this._controls;<br />
             }<br />
 }<br />
        // Fields<br />
        private ControlCollection _controls;<br />
}</p>
				<p>Control基类中的属性和方法很多，为清晰起见，我只保留了几个与模式有关的关键方法与属性。在上述的源代码中，我们需要注意几点：</p>
				<p>1、Control类不是抽象类，而是具体类。这是因为在设计时，我们可能会创建Control类型的实例。根据这一点来看，这并不符合OOP的要
求。一般而言，作为抽象出来的基类，必须定义为接口或抽象类。不过在实际的设计中，也不应拘泥于这些条条框框，而应审时度势，根据实际的情况来抉择最佳的
设计方案。<br />
2、公共属性Controls为ControlCollection类型，且该属性为virtual属性。也就是说，这个属性可以被它的子类
override。同时，该属性为只读属性，在其get访问器中，调用了方法CreateControlCollection()；这个方法为
protected虚方法，默认的实现是返回一个ControlCollection实例。<br />
3、方法HasControls()，功能为判断Control对象是否有子控件。它判断的依据是根据私有字段_controls（即公共属性
Controls）的Count值。但是需要注意的是，通过HasControls()方法的返回值，并不能决定对象本身属于叶节点，还是枝节点。因为即
使是枝节点其内部仍然可以不包含任何子对象。<br />
4、
方法DataBind()的实现中，首先调用了自身的OnDataBinding()方法，然后又遍历了Controls中的所有控件，并调用其
DataBind()方法。该方法属于控件的共有行为，从这里可以看出不管是作为叶节点的控件，还是作为枝节点的控件，它们都实现统一的接口。对于客户端
调用而言，枝节点和叶节点是没有区别的。<br />
5、 Control类的完整源代码中，并不存在Add()、Remove()等类似的方法，以提供添加和移除子控件的功能。事实上，继承Control类的所有子类均不存在Add()、Remove()等方法。</p>
				<p>显然，在Control类的定义和实现中，值得我们重视的是公共属性Controls的类型ControlCollection。顾名思义，该类必
然是一个集合类型。是否有关子控件的操作，都是在ControlCollection类型中实现呢？我们来分析一下ControlCollection的
代码：<br />
public class ControlCollection : ICollection, IEnumerable<br />
{<br />
       // Methods<br />
 public ControlCollection(Control owner)<br />
 {<br />
               this._readOnlyErrorMsg = null;<br />
               if (owner == null)<br />
               {<br />
                       throw new ArgumentNullException("owner");<br />
               }<br />
               this._owner = owner;<br />
        }<br />
 public virtual void Add(Control child)<br />
        {<br />
               if (child == null)<br />
               {<br />
                throw new ArgumentNullException("child");<br />
               }<br />
               if (this._readOnlyErrorMsg != null)<br />
                   {<br />
                throw new HttpException(HttpRuntime.FormatResourceString(this._readOnlyErrorMsg));<br />
               }<br />
               if (this._controls == null)<br />
               {<br />
                   this._controls = new Control[5];<br />
               }<br />
               else if (this._size &gt;= this._controls.Length)<br />
               {<br />
                   Control[] controlArray1 = new Control[this._controls.Length * 4];<br />
                   Array.Copy(this._controls, controlArray1, this._controls.Length);<br />
                   this._controls = controlArray1;<br />
               }<br />
               int num1 = this._size;<br />
               this._controls[num1] = child;<br />
               this._size++;<br />
               this._version++;<br />
               this._owner.AddedControl(child, num1);<br />
        }<br />
        public virtual void Remove(Control value)<br />
 {<br />
               int num1 = this.IndexOf(value);<br />
               if (num1 &gt;= 0)<br />
               {<br />
                this.RemoveAt(num1);<br />
               }<br />
        }<br />
        // Indexer<br />
 public virtual Control this[int index]<br />
 {<br />
               get<br />
               {<br />
                    if ((index &lt; 0) || (index &gt;= this._size))<br />
                    {<br />
                         throw new ArgumentOutOfRangeException(”index”);<br />
                    }<br />
                    return this._controls[index];<br />
               }<br />
 }<br />
 // Properties    <br />
 public int Count<br />
 {<br />
        get<br />
               {<br />
                    return this._size;<br />
               }<br />
        }<br />
 protected Control Owner<br />
 {<br />
               get<br />
               {<br />
                    return this._owner;<br />
               }<br />
 }<br />
        protected Control Owner { get; }   <br />
        // Fields<br />
        private Control[] _controls;<br />
        private const int _defaultCapacity = 5;<br />
        private const int _growthFactor = 4;<br />
 private Control _owner;    <br />
}</p>
				<p>一目了然，正是ControlCollection的Add()、Remove()方法完成了对子控件的添加和删除。例如：<br />
Control parent = new Control();<br />
Control child = new Child();<br />
//添加子控件child;<br />
parent.Controls.Add(child);<br />
//移除子控件child;<br />
parent.Controls.Remove(child);</p>
				<p>为什么要专门提供ControlCollection类型来管理控件的子控件呢？首先，作为类库使用者，自然希望各种类型的控件具有统一的接口，尤
其是自定义控件的时候，不希望自己重复定义管理子控件的操作；那么采用透明方式自然是最佳方案。然而，在使用控件的时候，安全也是需要重点考虑的，如果不
考虑子控件管理的合法性，一旦使用错误，会导致整个应用程序出现致命错误。从这样的角度考虑，似乎又应采用安全方式。这里就存在一个抉择。故而，.Net
在实现Control类库时，利用了职责分离的原则，将控件对象管理子控件的属性与行为和控件本身分离，并交由单独的ControlCollection
类负责。同时采用聚合而非继承的方式，以一个公共属性Controls，存在于Control类中。这种方式，集保留了透明方式和安全方式的优势，又摒弃
了这两种方式固有的缺陷，因此我名其为“复合方式”。</p>
				<p>“复合方式”的设计，其对安全的保障，不仅仅是去除了Control类关于子控件管理的统一接口，同时还通过异常管理的方式，在ControlCollection类的子类中实现：<br />
public class EmptyControlCollection : ControlCollection<br />
{<br />
        // Methods<br />
 public EmptyControlCollection(Control owner) : base(owner)<br />
 {}<br />
 public override void Add(Control child)<br />
 {<br />
            this.ThrowNotSupportedException();<br />
 }                         <br />
        private void ThrowNotSupportedException()<br />
        {<br />
            throw new
HttpException(HttpRuntime.FormatResourceString(”Control_does_not_allow_children”,
base.Owner.GetType().ToString()));<br />
        }<br />
}</p>
				<p>EmptyControlCollection继承了ControlCollection类，并重写了Add()等添加子控件的方法，使其抛出一个
异常。注意，它并没有重写父类的Remove()方法，这是因为ControlCollection类在实现Remove()方法时，对集合内的数据进行
了非空判断。而在EmptyControlCollection类中，是不可能添加子控件的，直接调用父类的Remove()方法，是不会出现错误的。</p>
				<p>既然管理子控件的职责由ControlCollection类型负责，且Control类中的公共属性Controls即为
ControlCollection类型。所以，对于控件而言，如果是树形结构中的叶节点，它不能包含子控件，它的Controls属性就应为
EmptyControlCollection类型，假如用户调用了Controls的Add()方法，就会抛出异常。如果控件是树形结构中的枝节点，它
支持子控件，那么Controls属性就是ControlCollection类型。究竟是枝节点还是叶节点，决定权在于公共属性Controls：<br />
public virtual ControlCollection Controls<br />
{<br />
      get<br />
      {<br />
             if (this._controls == null)<br />
             {<br />
                   this._controls = this.CreateControlCollection();<br />
             }<br />
             return this._controls;<br />
      }<br />
}</p>
				<p>在属性的get访问器中，调用了protected方法CreateControlCollection()，它创建并返回了一个ControlCollection实例：<br />
protected virtual ControlCollection CreateControlCollection()<br />
{<br />
     return new ControlCollection(this);<br />
}</p>
				<p>很明显，在Control基类实现Controls属性时，采用了Template Method模式,它推迟了ControlCollection的创建，将决定权交给了CreateControlCollection()方法。</p>
				<p>如果我们需要定义一个控件，要求它不能管理子控件，就重写CreateControlCollection()方法，返回EmptyControlCollection对象：</p>
				<p>protected override ControlCollection CreateControlCollection()<br />
{<br />
     return new EmptyControlCollection(this);<br />
}<br />
 <br />
现在再回过头来看HtmlControl和HtmlContainerControl类。根据前面的分析，我们要求
HtmlContainerControl继承HtmlControl类，同时，HtmlContainerControl应为枝节点，能够管理子控件；
HtmlControl则为叶节点，不支持子控件。通过引入ControlCollection类和其子类
EmptyControlCollection，以及Template Method模式后，这些类之间的关系与结构如下所示：</p>
				<p align="center">
						<img src="http://wayfarer.cnblogs.com/images/cnblogs_com/wayfarer/patterns/composite6.GIF" border="0" height="380" width="661" />
				</p>
				<p>HtmlContainerControl继承了HtmlControl类，这两个类都重写了自己父类的protected方法
CreateControlCollection()。HtmlControl类，该方法返回EmptyControlCollection对象，使其成
为了不包含子控件的叶节点；HtmlContainerControl类中，该方法则返回ControlCollection对象，从而被赋予了管理子控
件的能力，成为了枝节点：<br />
public abstract class HtmlControl : Control, IAttributeAccessor<br />
{<br />
        // Methods<br />
 protected override ControlCollection CreateControlCollection()<br />
 {<br />
            return new EmptyControlCollection(this);<br />
 }<br />
}<br />
public abstract class HtmlContainerControl : HtmlControl<br />
{<br />
       // Methods<br />
 protected override ControlCollection CreateControlCollection()<br />
 {<br />
            return new ControlCollection(this);<br />
 }<br />
}</p>
				<p>HtmlControl和HtmlContainerControl类均为抽象类。要定义它们的子类，如果不重写其父类的
CreateControlCollection()方法，那么它们的Controls属性，就与父类完全一致。例如HtmlImage控件继承自
HtmlControl类，该控件不能添加子控件；而HtmlForm控件则继承自HtmlContainerControl类，显然，HtmlForm
控件是支持添加子控件的操作的。</p>
				<p>.Net的控件设计采用Composite模式的“复合方式”，较好地将控件的透明性与安全性结合起来，它的特点是：</p>
				<p>1、在统一接口中消除了Add()、Remove()等子控件的管理方法，而由ControlCollection类实现，同时通过EmptyControlCollection类保障了控件进一步的安全；<br />
2、控件能否管理子控件，不由继承的层次决定；而是通过重写CreateControlCollection()方法，由Controls属性的真正类型来决定。</p>
				<p>如此一来，要定义自己的控件就更加容易。我们可以任意地扩展自己的控件类。不管继承自Control，还是HtmlControl或
HtmlContainerControl，都可以轻松地定义出具有枝节点或叶节点属性的新控件。如果有新的需求要求改变管理子控件的方式，我们还可以定
义继承自ControlCollection的类，并在控件类的方法CreateControlCollection()中创建并返回它的实例。
</p>
		</div>
<img src ="http://www.blogjava.net/alex/aggbug/68634.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/alex/" target="_blank">Alex</a> 2006-09-09 00:51 <a href="http://www.blogjava.net/alex/archive/2006/09/09/68634.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[zt]设计模式之复合模式（from jdon）</title><link>http://www.blogjava.net/alex/archive/2006/09/09/68631.html</link><dc:creator>Alex</dc:creator><author>Alex</author><pubDate>Fri, 08 Sep 2006 16:44:00 GMT</pubDate><guid>http://www.blogjava.net/alex/archive/2006/09/09/68631.html</guid><wfw:comment>http://www.blogjava.net/alex/comments/68631.html</wfw:comment><comments>http://www.blogjava.net/alex/archive/2006/09/09/68631.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/alex/comments/commentRss/68631.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/alex/services/trackbacks/68631.html</trackback:ping><description><![CDATA[
		<div class="postbody">
				<h3 align="center">设计模式之Composite(组合)</h3>
				<p align="center">
						<a href="http://www.jdon.com/aboutme.htm">
								<font color="#002c99">板桥里人</font>
						</a> http://www.jdon.com 2002/04/27（转载请保留）</p>
				<p align="center">
						<a href="http://www.jdon.com/mybook/index.htm" target="_blank">
								<strong>
								</strong>
						</a> </p>
				<p>
						<b>Composite模式定义</b>:<br />将对象以树形结构组织起来,以达成“部分－整体” 的层次结构，使得客户端对单个对象和组合对象的使用具有一致性.</p>
				<p>Composite比较容易理解，<font color="#0000ff">想到Composite就应该想到树形结构图</font>。组合体内这些对象都有共同接口,<font color="#0000ff">当组合体一个对象的方法被调用执行
时，Composite将遍历(Iterator)整个树形结构,寻找同样包含这个方法的对象并实现调用执行。可以用牵一动百来形容。</font></p>
				<p>
						<b>所以Composite模式使用到Iterator模式</b>，和Chain of Responsibility模式类似。</p>
				<p>
						<b>Composite好处</b>:<br />1.使客户端调用简单，客户端可以一致的使用组合结构或其中单个对象，用户就不必关系自己处理的是单个对象还是整个组合结构，这就简化了客户端代码。<br />2.更容易在组合体内加入对象部件. 客户端不必因为加入了新的对象部件而更改代码。</p>
				<p>
						<b>如何使用Composite?</b>
						<br />首先定义一个接口或抽象类，这是设计模式通用方式了，其他设计模式对接口内部定义限制不多，<font color="#ff1493">Composite却有个规定，那就是要在接口内部定义一个用于访问和管理Composite组合体的对象们（或称部件Component）.</font></p>
				<p>下面的代码是以抽象类定义，一般尽量用接口interface,</p>
				<table border="0" cellpadding="3" cellspacing="3" width="80%">
						<tbody>
								<tr>
										<td bgcolor="#cccccc">public abstract class Equipment<br />{<br />　　private String name; <br />　　//实价<br />　　public abstract double netPrice();<br />　　//折扣价格<br />　　public abstract double discountPrice();<br />　　//增加部件方法　　<br />　　public boolean add(Equipment equipment) { return false; }<br />　　//删除部件方法<br />　　public boolean remove(Equipment equipment) { return false; }<br />　　//注意这里，这里就提供一种用于访问组合体类的部件方法。<br />　　public Iterator iter() { return null; }<br />　　<br />　　public Equipment(final String name) { this.name=name; }<br />} </td>
								</tr>
						</tbody>
				</table>
				<p>抽象类Equipment就是Component定义，代表着组合体类的对象们,Equipment中定义几个共同的方法。</p>
				<table border="0" cellpadding="3" cellspacing="3" width="80%">
						<tbody>
								<tr>
										<td bgcolor="#cccccc">public class Disk extends Equipment<br />{<br />　　public Disk(String name) { super(name); }<br />　　//定义Disk实价为1<br />　　public double netPrice() { return 1.; }<br />　　//定义了disk折扣价格是0.5 对折。<br />　　public double discountPrice() { return .5; }<br />}</td>
								</tr>
						</tbody>
				</table>
				<p>Disk是组合体内的一个对象，或称一个部件，这个部件是个单独元素( Primitive)。<br /><i>还有一种可能是，一个部件也是一个组合体，就是说这个部件下面还有'儿子'，这是树形结构中通常的情况</i>，应该比较容易理解。现在我们先要定义这个组合体：</p>
				<table border="0" cellpadding="3" cellspacing="3" width="80%">
						<tbody>
								<tr>
										<td bgcolor="#cccccc">
												<p>abstract class CompositeEquipment extends Equipment<br />{<br />　　private int i=0; <br />　　//定义一个Vector 用来存放'儿子'<br />　　<font color="#ff1493">private Lsit equipment=new ArrayList();</font><br /><br />　　public CompositeEquipment(String name) { super(name); }<br /><br />　　<font color="#0000ff">public boolean add(Equipment equipment) { <br />　　　　 this.equipment.add(equipment); <br />　　　　 return true; <br />　　 }</font><br /><br />　　public double netPrice() <br />　　{<br />　　　　double netPrice=0.;<br />　　　　Iterator iter=equipment.iterator();<br />　　　　for(iter.hasNext())<br />　　　　　　netPrice+=((Equipment)iter.next()).netPrice();<br />　　　　return netPrice;<br />　　}<br /><br />　　public double discountPrice() <br />　　{<br />　　　　double discountPrice=0.;<br />　　　　Iterator iter=equipment.iterator();<br />　　　　for(iter.hasNext())<br />　　　　　　discountPrice+=((Equipment)iter.next()).discountPrice();<br />　　　　return discountPrice;<br />　　}<br />　　<br /></p>
												<p>　　<font color="#ff1493">//注意这里，这里就提供用于访问自己组合体内的部件方法。<br />　　//上面dIsk 之所以没有，是因为Disk是个单独(Primitive)的元素.</font><br />　　public Iterator iter()<br />　　{<br />　　　　return equipment.iterator() ;<br />　　{<br />　　//重载Iterator方法<br />　　 public boolean hasNext() { return i&lt;equipment.size(); }<br />　　//重载Iterator方法<br />　　 public Object next()<br />　　 {<br />　　　　if(hasNext())<br />　　　　　　 return equipment.elementAt(i++);<br />　　　　else <br />　　 　　 　 throw new NoSuchElementException();<br />　　 }<br />　　<br /><br />}</p>
										</td>
								</tr>
						</tbody>
				</table>
				<p>上面CompositeEquipment继承了Equipment,同时为自己里面的对象们提供了外部访问的方法,重载了Iterator,Iterator是Java的Collection的一个接口，是Iterator模式的实现.</p>
				<p>我们再看看CompositeEquipment的两个具体类:盘盒Chassis和箱子Cabinet，箱子里面可以放很多东西，如底板，电源盒，硬盘盒等；盘盒里面可以放一些小设备，如硬盘 软驱等。无疑这两个都是属于组合体性质的。</p>
				<table border="0" cellpadding="3" cellspacing="3" width="80%">
						<tbody>
								<tr>
										<td bgcolor="#cccccc">public class Chassis extends CompositeEquipment<br />{<br />　　 public Chassis(String name) { super(name); }<br />　　 public double netPrice() { return 1.+super.netPrice(); }<br />　　 public double discountPrice() { return .5+super.discountPrice(); }<br />} 
<p>public class Cabinet extends CompositeEquipment<br />{<br />　　 public Cabinet(String name) { super(name); }<br />　　 public double netPrice() { return 1.+super.netPrice(); }<br />　　 public double discountPrice() { return .5+super.discountPrice(); }<br />}</p></td>
								</tr>
						</tbody>
				</table>
				<p>至此我们完成了整个Composite模式的架构。</p>
				<p>我们可以看看客户端调用Composote代码:<br /><br />Cabinet cabinet=new Cabinet("Tower");<br /><br />Chassis chassis=new Chassis("PC Chassis");<br />//将PC Chassis装到Tower中 (将盘盒装到箱子里)<br />cabinet.add(chassis);<br />//将一个10GB的硬盘装到 PC Chassis (将硬盘装到盘盒里)<br />chassis.add(new Disk("10 GB"));<br /><br />//调用 netPrice()方法;<br />System.out.println("netPrice="+cabinet.netPrice());<br />System.out.println("discountPrice="+cabinet.discountPrice());<br /><br /></p>
				<p>上面调用的方法netPrice()或discountPrice()，实际上Composite使用Iterator遍历了整个树形结构,寻找同样包含这个方法的对象并实现调用执行.</p>
				<p>Composite是个很巧妙体现智慧的模式，<font color="#0000ff">在实际应用中，如果碰到树形结构，我们就可以尝试是否可以使用这个模式。</font></p>
				<p>以论坛为例，一个版(forum)中有很多帖子(message),这些帖子有原始贴，有对原始贴的回应贴，是个典型的树形结构，那么当然可以使用Composite模式，那么我们进入Jive中看看，是如何实现的.</p>
				<p>
						<b>Jive解剖</b>
						<br />在Jive中 ForumThread是ForumMessages的容器container(组合体).也就是说，ForumThread类似我们上例中的 CompositeEquipment.它和messages的关系如图：<br />[thread]<br />　　 |- [message]<br />　　 |- [message]<br />　　 　　 |- [message]<br />　　 　　 |- [message]<br />　　 　　 　　 |- [message] </p>
				<p>我们在ForumThread看到如下代码：<br /></p>
				<table border="0" cellpadding="3" cellspacing="3" width="80%">
						<tbody>
								<tr>
										<td bgcolor="#cccccc">public interface ForumThread {<br />　　 .... <br />　　 public void addMessage(ForumMessage parentMessage, ForumMessage newMessage)<br />　　 　　 　　 throws UnauthorizedException; 
<p>　　 public void deleteMessage(ForumMessage message)<br />　　 　　 　　 throws UnauthorizedException;</p><p>　　<br />　　 public Iterator messages();<br />　　 　　 .... </p><p>}</p></td>
								</tr>
						</tbody>
				</table>
				<p>类似CompositeEquipment, 提供用于访问自己组合体内的部件方法: 增加 删除 遍历.</p>
				<p>结合我的其他模式中对Jive的分析，我们已经基本大体理解了Jive论坛体系的框架，如果你之前不理解设计模式，而直接去看Jive源代码，你肯定无法看懂。</p>
				<p>:)</p>
		</div>
<img src ="http://www.blogjava.net/alex/aggbug/68631.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/alex/" target="_blank">Alex</a> 2006-09-09 00:44 <a href="http://www.blogjava.net/alex/archive/2006/09/09/68631.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[zt]设计模式之复合模式</title><link>http://www.blogjava.net/alex/archive/2006/09/08/68627.html</link><dc:creator>Alex</dc:creator><author>Alex</author><pubDate>Fri, 08 Sep 2006 15:59:00 GMT</pubDate><guid>http://www.blogjava.net/alex/archive/2006/09/08/68627.html</guid><wfw:comment>http://www.blogjava.net/alex/comments/68627.html</wfw:comment><comments>http://www.blogjava.net/alex/archive/2006/09/08/68627.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/alex/comments/commentRss/68627.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/alex/services/trackbacks/68627.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 转自: CCIENET		自从J2EE出现以来，就大大简化了在Java下的企业级开发。但是随着J2EE越来越普遍地被应用到各个领域中，开发者们渐渐意识到需要一种方法来标准化应用程序的开发过程，他们采用的方法是标准化应用程序的结构层。在结构层通常封装了一些独立于业务逻辑的复杂技术，以便在业务逻辑和底层的架构之间建立起弱连接。无可否认，J2EE是一个很成功的技术，它为一些基本的任务提供了一...&nbsp;&nbsp;<a href='http://www.blogjava.net/alex/archive/2006/09/08/68627.html'>阅读全文</a><img src ="http://www.blogjava.net/alex/aggbug/68627.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/alex/" target="_blank">Alex</a> 2006-09-08 23:59 <a href="http://www.blogjava.net/alex/archive/2006/09/08/68627.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[zt]Java设计模式之外观模式研究</title><link>http://www.blogjava.net/alex/archive/2006/09/03/67333.html</link><dc:creator>Alex</dc:creator><author>Alex</author><pubDate>Sat, 02 Sep 2006 16:07:00 GMT</pubDate><guid>http://www.blogjava.net/alex/archive/2006/09/03/67333.html</guid><wfw:comment>http://www.blogjava.net/alex/comments/67333.html</wfw:comment><comments>http://www.blogjava.net/alex/archive/2006/09/03/67333.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/alex/comments/commentRss/67333.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/alex/services/trackbacks/67333.html</trackback:ping><description><![CDATA[外观模式（Facade pattern）涉及到子系统的一些类。所谓子系统，是为提供一系列相关的特征（功能）而紧密关联的一组类。例如，一个Account类、Address类和CreditCard类相互关联，成为子系统的一部分，提供在线客户的特征。<br /><br />　
　在真实的应用系统中，一个子系统可能由很多类组成。子系统的客户为了它们的需要，需要和子系统中的一些类进行交互。客户和子系统的类进行直接的交互会导
致客户端对象和子系统（Figure1）之间高度耦合。任何的类似于对子系统中类的接口的修改，会对依赖于它的所有的客户类造成影响。<br /><br /><table align="center" border="0" width="90%"><tbody><tr><td><div align="center"> <img src="http://dev.yesky.com/imagelist/05/11/8k4m329893mg.bmp" /> <br />Figure1: <a href="http://www.yesky.com/key/1880/161880.html" class="bluekey" target="_blank">Client</a> Interaction with Subsystem Classes before Applying the Facade Pattern </div></td></tr></tbody></table><br />　　外观模式（Facade pattern）很适用于在上述情况。外观模式（Facade pattern）为子系统提供了一个更高层次、更简单的接口，从而降低了子系统的复杂度和依赖。这使得子系统更易于使用和管理。<br /><br />　　外观是一个能为子系统和客户提供简单接口的类。当正确的应用外观，客户不再直接和子系统中的类交互，而是与外观交互。外观承担与子系统中类交互的责任。实际上，外观是子系统与客户的接口，这样外观模式降低了子系统和客户的耦合度(Figure2). <br /><br /><table align="center" border="0" width="90%"><tbody><tr><td><div align="center"> <img src="http://dev.yesky.com/imagelist/05/11/54hkko9rj4a6.bmp" /> <br />Figure2: Client Interaction with Subsystem Classes after Applying the Facade Pattern </div></td></tr></tbody></table><br />　　从Figure2中我们可以看到：外观对象隔离了客户和子系统对象，从而降低了耦合度。当子系统中的类进行改变时，客户端不会像以前一样受到影响。<br /><br />　　尽管客户使用由外观提供的简单接口，但是当需要的时候，客户端还是可以视外观不存在，直接访问子系统中的底层次的接口。这种情况下，它们之间的依赖/耦合度和原来一样。 <br /><br />　　例子：<br /><br />　　让我们建立一个应用：<br /><br />　　（1） 接受客户的详细资料（账户、地址和信用卡信息）<br /><br />　　（2） 验证输入的信息<br /><br />　　（3） 保存输入的信息到相应的文件中。<br /><br />　　这个应用有三个类：Account、Address和CreditCard。每一个类都有自己的验证和保存数据的方法。<br /><br />　　Listing1: AccountClass <br /><br /><table align="center" bgcolor="#e3e3e3" border="1" bordercolor="#cccccc" width="90%"><tbody><tr><td>public class Account { <br />　String firstName; <br />　String lastName; <br />　final String ACCOUNT_DATA_FILE = "AccountData.txt"; <br />　public Account(String fname, String lname) { <br />　　firstName = fname; <br />　　lastName = lname; <br />　} <br />　public boolean isValid() { <br />　　/* <br />　　Let's go with simpler validation <br />　　here to keep the example simpler. <br />　　*/ <br />　　… <br />　　… <br />　} <br />　public boolean save() { <br />　　FileUtil futil = new FileUtil(); <br />　　String dataLine = getLastName() + ”," + getFirstName(); <br />　　return futil.writeToFile(ACCOUNT_DATA_FILE, dataLine,true, true); <br />　} <br />　public String getFirstName() { <br />　　return firstName; <br />　} <br />　public String getLastName() { <br />　　return lastName; <br />　} <br />} </td></tr></tbody></table><br />　　Listing2: Address Class <br /><br /><table align="center" bgcolor="#e3e3e3" border="1" bordercolor="#cccccc" width="90%"><tbody><tr><td>public class Address { <br />　String address; <br />　String city; <br />　String state; <br />　final String ADDRESS_DATA_FILE = "Address.txt"; <br />　public Address(String add, String cty, String st) { <br />　　address = add; <br />　　city = cty; <br />　　state = st; <br />　} <br />　public boolean isValid() { <br />　　/* <br />　　The address validation algorithm <br />　　could be complex in <a href="http://www.yesky.com/key/4733/164733.html" class="bluekey" target="_blank">real</a>-world <br />　　applications. <br />　　Let's go with simpler validation <br />　　here to keep the example simpler. <br />　　*/ <br />　　if (getState().<a href="http://www.yesky.com/key/914/160914.html" class="bluekey" target="_blank">trim</a>().length() &lt; 2) <br />　　　return false; <br />　　return true; <br />　} <br />　public boolean save() { <br />　　FileUtil futil = new FileUtil(); <br />　　String dataLine = getAddress() + ”," + getCity() + ”," + getState(); <br />　　return futil.writeToFile(ADDRESS_DATA_FILE, dataLine,true, true); <br />　} <br />　public String getAddress() { <br />　　return address; <br />　} <br />　public String getCity() { <br />　　return city; <br />　} <br />　public String getState() { <br />　　return state; <br />　} <br />} </td></tr></tbody></table><br />　　Listing3: CreditCard Class <br /><br /><table align="center" bgcolor="#e3e3e3" border="1" bordercolor="#cccccc" width="90%"><tbody><tr><td>public class CreditCard { <br />　String cardType; <br />　String cardNumber; <br />　String cardExpDate; <br />　final String CC_DATA_FILE = "CC.txt"; <br />　public CreditCard(String ccType, String ccNumber, <br />　String ccExpDate) { <br />　　cardType = ccType; <br />　　cardNumber = ccNumber; <br />　　cardExpDate = ccExpDate; <br />　} <br />　public boolean isValid() { <br />　　/* <br />　　Let's go with simpler validation <br />　　here to keep the example simpler. <br />　　*/ <br />　　if (getCardType().equals(AccountManager.VISA)) { <br />　　　return (getCardNumber().trim().length() == 16); <br />　　} <br />　　if (getCardType().equals(AccountManager.<a href="http://www.yesky.com/key/81/160081.html" class="bluekey" target="_blank">DISCOVER</a>)) { <br />　　　return (getCardNumber().trim().length() == 15); <br />　　} <br />　　if (getCardType().equals(AccountManager.<a href="http://www.yesky.com/key/2407/162407.html" class="bluekey" target="_blank">MASTER</a>)) { <br />　　　return (getCardNumber().trim().length() == 16); <br />　　} <br />　　return false; <br />　} <br />　public boolean save() { <br />　　FileUtil futil = new FileUtil(); <br />　　String dataLine = getCardType() + ,”" + getCardNumber() + ”," + getCardExpDate(); <br />　　return futil.writeToFile(CC_DATA_FILE, dataLine, true, true); <br />　} <br />　public String getCardType() { <br />　　return cardType; <br />　} <br />　public String getCardNumber() { <br />　　return cardNumber; <br />　} <br />　public String getCardExpDate() { <br />　　return cardExpDate; <br />　} <br />} </td></tr></tbody></table><br /><div align="center"><img src="http://dev.yesky.com/imagelist/05/11/ge36sg91s750.bmp" />  <br />Figure3: Subsystem Classes to Provide the Necessary Functionality to Validate and Save the Customer Data<br /><br /><br /><br /><div align="left">让我们建立一个客户AccountManager，它提供用户输入数据的用户界面。<br /><br />　　Listing4: Client AccountManager Class <br /><br /><table align="center" bgcolor="#e3e3e3" border="1" bordercolor="#cccccc" width="90%"><tbody><tr><td>public class AccountManager extends JFrame { <br />　public static final String newline = "\n"; <br />　public static final String VALIDATE_SAVE = "Validate &amp; Save"; <br />　… <br />　… <br />　public AccountManager() { 