|
以文本方式查看主题 - 中文XML论坛 - 专业的XML技术讨论区 (http://bbs.xml.org.cn/index.asp) -- 『 Java/Eclipse 』 (http://bbs.xml.org.cn/list.asp?boardid=41) ---- 设计模式之Interpreter(解释器) (http://bbs.xml.org.cn/dispbbs.asp?boardid=41&rootid=&id=15592) |
|
-- 作者:npuhetao -- 发布时间:3/15/2005 10:00:00 AM -- 设计模式之Interpreter(解释器) Interpreter解释器模式定义: 定义语言的文法 ,并且建立一个解释器来解释该语言中的句子. Interpreter似乎使用面不是很广,它描述了一个语言解释器是如何构成的,在实际应用中,我们可能很少去构造一个语言的文法.我们还是来简单的了解一下: 首先要建立一个接口,用来描述共同的操作. public interface AbstractExpression { 再看看包含解释器之外的一些全局信息 public interface Context { } AbstractExpression的具体实现分两种:终结符表达式和非终结符表达式: public class TerminalExpression implements AbstractExpression { 对于文法中没一条规则,非终结符表达式都必须的: public AbstractExpression getSuccessor() { public void interpret( Context context ) { } |
|
-- 作者:npuhetao -- 发布时间:3/15/2005 10:01:00 AM -- 设计模式之Visitor Visitor访问者模式定义 作用于某个对象群中各个对象的操作. 它可以使你在不改变这些对象本身的情况下,定义作用于这些对象的新操作. 在Java中,Visitor模式实际上是分离了collection结构中的元素和对这些元素进行操作的行为. 为何使用Visitor? 很显然,这样做的缺点代码If else if 很繁琐.我们就可以使用Visitor模式解决它. 如何使用Visitor? 被访问者是我们Collection的每个元素Element,我们要为这些Element定义一个可以接受访问的接口(访问和被访问是互动的,只有访问者,被访问者如果表示不欢迎,访问者就不能访问),取名为Visitable,也可取名为Element。 public interface Visitable 被访问的具体元素继承这个新的接口Visitable: public class StringElement implements Visitable public String getValue(){
上面是被访问者是字符串类型,下面再建立一个Float类型的: public class FloatElement implements Visitable public Float getValue(){
我们设计一个接口visitor访问者,在这个接口中,有一些访问操作,这些访问操作是专门访问对象集合Collection中有可能的所有类,目前我们假定有三个行为:访问对象集合中的字符串类型;访问对象集合中的Float类型;访问对象集合中的对象集合类型。注意最后一个类型是集合嵌套,通过这个嵌套实现可以看出使用访问模式的一个优点。 接口visitor访问者如下: public interface Visitor public void visitString(StringElement stringE); }
访问者的实现: public class ConcreteVisitor implements Visitor public void visitString(StringElement stringE) { } 在上面的visitCollection我们实现了对Collection每个元素访问,只使用了一个判断语句,只要判断其是否可以访问. StringElement只是一个实现,可以拓展为更多的实现,整个核心奥妙在accept方法中,在遍历Collection时,通过相应的accept方法调用具体类型的被访问者。这一步确定了被访问者类型, 如果是StringElement,而StringElement则回调访问者的visiteString方法,这一步实现了行为操作方法。 客户端代码: Visitor visitor = new ConcreteVisitor(); StringElement stringE = new StringElement("I am a String"); Collection list = new ArrayList(); 客户端代码中的list对象集合中放置了多种数据类型,对对象集合中的访问不必象一开始那样,使用instance of逐个判断,而是通过访问者模式巧妙实现了。 至此,我们完成了Visitor模式基本结构. 使用Visitor模式的前提 在两个接口Visitor和Visitable中,确保Visitable很少变化,也就是说,确保不能老有新的Element元素类型加进来,可以变化的是访问者行为或操作,也就是Visitor的不同子类可以有多种,这样使用访问者模式最方便. 如果对象集合中的对象集合经常有变化, 那么不但Visitor实现要变化,Visistable也要增加相应行为,GOF建议是,不如在这些对象类中直接逐个定义操作,无需使用访问者设计模式。 但是在Java中,Java的Reflect技术解决了这个问题,因此结合reflect反射机制,可以使得访问者模式适用范围更广了。 |
|
-- 作者:npuhetao -- 发布时间:3/15/2005 10:01:00 AM -- 设计模式之Mediator(中介者) Mediator中介者模式定义: 用一个中介对象来封装一系列关于对象交互行为. 为何使用Mediator? 如何使用? 首先 有一个接口,用来定义成员对象之间的交互联系方式: public interface Mediator { } Meiator具体实现,真正实现交互操作的内容: public class ConcreteMediator implements Mediator { //假设当前有两个成员. ... } 再看看另外一个参与者:成员,因为是交互行为,都需要双方提供一些共同接口,这种要求在Visitor Observer等模式中都是相同的. public class Colleague { public void setMediator( Mediator mediator ) { public class ConcreteColleague1 { } public class ConcreteColleague2 { } 每个成员都必须知道Mediator,并且和 Mediator联系,而不是和其他成员联系. 至此,Mediator模式框架完成,可以发现Mediator模式规定不是很多,大体框架也比较简单,但实际使用起来就非常灵活. Mediator模式在事件驱动类应用中比较多,例如界面设计GUI.;聊天,消息传递等,在聊天应用中,需要有一个MessageMediator,专门负责request/reponse之间任务的调节. MVC是J2EE的一个基本模式,View Controller是一种Mediator,它是Jsp和服务器上应用程序间的Mediator. |
|
-- 作者:npuhetao -- 发布时间:3/15/2005 10:02:00 AM -- 设计模式之Strategy(策略) Strategy策略模式是属于设计模式中 对象行为型模式,主要是定义一系列的算法,把这些算法一个个封装成单独的类. Stratrgy应用比较广泛,比如, 公司经营业务变化图, 可能有两种实现方式,一个是线条曲线,一个是框图(bar),这是两种算法,可以使用Strategy实现. 这里以字符串替代为例, 有一个文件,我们需要读取后,希望替代其中相应的变量,然后输出.关于替代其中变量的方法可能有多种方法,这取决于用户的要求,所以我们要准备几套变量字符替代方案.
首先,我们建立一个抽象类RepTempRule 定义一些公用变量和方法: public abstract class RepTempRule{ protected String oldString=""; protected String newString="";
public abstract void replace() throws Exception; 在RepTempRule中 有一个抽象方法abstract需要继承明确,这个replace里其实是替代的具体方法. 对应的类分别是RepTempRuleOne RepTempRuleTwo public class RepTempRuleOne extends RepTempRule{ //replaceFirst是jdk1.4新特性 newString=oldString.replaceFirst("aaa", "ccc") 第二步:我们要建立一个算法解决类,用来提供客户端可以自由选择算法。 public class RepTempRuleSolve { public RepTempRuleSolve(RepTempRule rule){ public String getNewContext(Site site,String oldString) { public void changeAlgorithm(RepTempRule newAlgorithm) { }
调用如下: public class test{ ...... public void testReplace(){ //使用第一套替代方案 //使用第二套 solver=new RepTempRuleSolve(new RepTempRuleTwo()); ..... } 我们达到了在运行期间,可以自由切换算法的目的。 实际整个Strategy的核心部分就是抽象类的使用,使用Strategy模式可以在用户需要变化时,修改量很少,而且快速. Strategy和Factory有一定的类似,Strategy相对简单容易理解,并且可以在运行时刻自由切换。Factory重点是用来创建对象。 Strategy适合下列场合: 1.以不同的格式保存文件; 2.以不同的算法压缩文件; 3.以不同的算法截获图象; 4.以不同的格式输出同样数据的图形,比如曲线 或框图bar等 |
|
-- 作者:npuhetao -- 发布时间:3/15/2005 10:02:00 AM -- 设计模式之State State模式的定义: 不同的状态,不同的行为;或者说,每个状态有着相应的行为. 何时使用? 不只是根据状态,也有根据属性.如果某个对象的属性不同,对象的行为就不一样,这点在数据库系统中出现频率比较高,我们经常会在一个数据表的尾部,加上property属性含义的字段,用以标识记录中一些特殊性质的记录,这种属性的改变(切换)又是随时可能发生的,就有可能要使用State. 这里要阐述的是"开关切换状态" 和" 一般的状态判断"是有一些区别的, " 一般的状态判断"也是有 if..elseif结构,例如: if (which==1) state="hello"; if (state.euqals("bye")) state="hello"; 如果单纯有上面一种将"hello"-->"hi"-->"bye"-->"hello"这一个方向切换,也不一定需要使用State模式,因为State模式会建立很多子类,复杂化,但是如果又发生另外一个行为:将上面的切换方向反过来切换,或者需要任意切换,就需要State了. 请看下例: public class Context{ private Color state=null; public void push(){ //如果当前red状态 就切换到blue //如果当前blue状态 就切换到green //如果当前black状态 就切换到red //如果当前green状态 就切换到black public void pull(){ //与push状态切换正好相反 if (state==Color.green) state=Color.blue; Sample2 sample2=new Sample2(state); } 在上例中,我们有两个动作push推和pull拉,这两个开关动作,改变了Context颜色,至此,我们就需要使用State模式优化它. 另外注意:但就上例,state的变化,只是简单的颜色赋值,这个具体行为是很简单的,State适合巨大的具体行为,因此在,就本例,实际使用中也不一定非要使用State模式,这会增加子类的数目,简单的变复杂. 例如: 银行帐户, 经常会在Open 状态和Close状态间转换. 例如: 经典的TcpConnection, Tcp的状态有创建 侦听 关闭三个,并且反复转换,其创建 侦听 关闭的具体行为不是简单一两句就能完成的,适合使用State 例如:信箱POP帐号, 会有四种状态, start HaveUsername Authorized quit,每个状态对应的行为应该是比较大的.适合使用State 例如:在工具箱挑选不同工具,可以看成在不同工具中切换,适合使用State.如 具体绘图程序,用户可以选择不同工具绘制方框 直线 曲线,这种状态切换可以使用State. 如何使用 1.state manager 状态管理器 ,就是开关 ,如上面例子的Context实际就是一个state manager, 在state manager中有对状态的切换动作. 以上面的Context为例.我们要修改它,建立两个类型的实体. public abstract class State{ public abstract void handlepush(Context c); } 父类中的方法要对应state manager中的开关行为,在state manager中 本例就是Context中,有两个开关动作push推和pull拉.那么在状态父类中就要有具体处理这两个动作:handlepush() handlepull(); 同时还需要一个获取push或pull结果的方法getcolor() 下面是具体子类的实现: public class BlueState extends State{ public void handlepush(Context c){ } //根据pull方法"如果是blue状态的切换到red" ; } public abstract void getcolor(){ return (Color.blue)} } 同样 其他状态的子类实现如blue一样. 第二步: 要重新改写State manager 也就是本例的Context: public class Context{ private Sate state=null; //我们将原来的 Color state 改成了新建的State state; //setState是用来改变state的状态 使用setState实现状态的切换 this.state=state; } public void push(){ //状态的切换的细节部分,在本例中是颜色的变化,已经封装在子类的handlepush中实现,这里无需关心 }
public void pull(){ state.handlepull(this); } } 至此,我们也就实现了State的refactorying过程. 以上只是相当简单的一个实例,在实际应用中,handlepush或handelpull的处理是复杂的. 状态模式优点: 状态模式在工作流或游戏等各种系统中有大量使用,甚至是这些系统的核心功能设计,例如政府OA中,一个批文的状态有多种:未办;正在办理;正在批示;正在审核;已经完成等各种状态,使用状态机可以封装这个状态的变化规则,从而达到扩充状态时,不必涉及到状态的使用者。 在网络游戏中,一个游戏活动存在开始;开玩;正在玩;输赢等各种状态,使用状态模式就可以实现游戏状态的总控,而游戏状态决定了游戏的各个方面,使用状态模式可以对整个游戏架构功能实现起到决定的主导作用。 状态模式实质: 使用状态模式后,客户端外界可以直接使用事件Event实现,根本不必关心该事件导致如何状态变化,这些是由状态机等内部实现。 这是一种Event-condition-State,状态模式封装了condition-State部分。 每个状态形成一个子类,每个状态只关心它的下一个可能状态,从而无形中形成了状态转换的规则。如果新的状态加入,只涉及它的前一个状态修改和定义。 状态转换有几个方法实现:一个在每个状态实现next(),指定下一个状态;还有一种方法,设定一个StateOwner,在StateOwner设定stateEnter状态进入和stateExit状态退出行为。 状态从一个方面说明了流程,流程是随时间而改变,状态是截取流程某个时间片。 |
|
-- 作者:npuhetao -- 发布时间:3/15/2005 10:03:00 AM -- 设计模式之Command Command模式是最让我疑惑的一个模式,我在阅读了很多代码后,才感觉隐约掌握其大概原理,我认为理解设计模式最主要是掌握起原理构造,这样才对自己实际编程有指导作用.Command模式实际上不是个很具体,规定很多的模式,正是这个灵活性,让人有些confuse. Command定义 这是一种两台机器之间通讯联系性质的模式,类似传统过程语 言的 CallBack功能。 优点: 不少Command模式的代码都是针对图形界面的,它实际就是菜单命令,我们在一个下拉菜单选择一个命令时,然后会执行一些动作. 将这些命令封装成在一个类中,然后用户(调用者)再对这个类进行操作,这就是Command模式,换句话说,本来用户(调用者)是直接调用这些命令的,如菜单上打开文档(调用者),就直接指向打开文档的代码,使用Command模式,就是在这两者之间增加一个中间者,将这种直接关系拗断,同时两者之间都隔离,基本没有关系了. 显然这样做的好处是符合封装的特性,降低耦合度,Command是将对行为进行封装的典型模式,Factory是将创建进行封装的模式, 如何使用? 典型的Command模式需要有一个接口.接口中有一个统一的方法,这就是"将命令/请求封装为对象": 具体不同命令/请求代码是实现接口Command,下面有三个具体命令 public void execute( ) { public void execute( ) { public class Politician implements Command { public void execute( ) { }
这三个命令进入List中后,已经失去了其外表特征,以后再取出,也可能无法分辨出谁是Engineer 谁是Programmer了,看下面客户端如何调用Command模式: } 由此可见,调用者基本只和接口打交道,不合具体实现交互,这也体现了一个原则,面向接口编程,这样,以后增加第四个具体命令时,就不必修改调用者TestCommand中的代码了. 理解了上面的代码的核心原理,在使用中,就应该各人有自己方法了,特别是在如何分离调用者和具体命令上,有很多实现方法,上面的代码是使用"从List过一遍"的做法.这种做法只是为了演示. Command模式在界面设计中应用广泛.Java的Swing中菜单命令都是使用Command模式,由于Java在界面设计的性能上还有欠缺,因此界面设计具体代码我们就不讨论,网络上有很多这样的示例. |
|
-- 作者:npuhetao -- 发布时间:3/15/2005 10:04:00 AM -- 设计模式之Chain of Responsibility(职责链) Chain of Responsibility定义 Chain of Responsibility(CoR) 是用一系列类(classes)试图处理一个请求request,这些类之间是一个松散的耦合,唯一共同点是在他们之间传递request. 也就是说,来了一个请求,A类先处理,如果没有处理,就传递到B类处理,如果没有处理,就传递到C类处理,就这样象一个链条(chain)一样传递下去。 如何使用? 有一个Handler接口: public interface Handler{ 这是一个处理request的事例, 如果有多种request,比如 请求帮助 请求打印 或请求格式化: 最先想到的解决方案是:在接口中增加多个请求: } 具体是一段实现接口Handler代码: public ConcreteHandler(Handler successor){ public void handleHelp(){ public void handlePrint(){ } 虽然思路简单明了,但是有一个扩展问题,如果我们需要再增加一个请求request种类,需要修改接口及其每一个实现。 第二方案:将每种request都变成一个接口,因此我们有以下代码 : public interface HelpHandler{ public interface PrintHandler{ public interface FormatHandler{ public class ConcreteHandler public ConcreteHandler(HelpHandler helpSuccessor,PrintHandler printSuccessor,FormatHandler formatSuccessor) public void handleHelp(){ public void handlePrint(){this.printSuccessor=printSuccessor;} public void handleFormat(){this.formatSuccessor=formatSuccessor;} } 这个办法在增加新的请求request情况下,只是节省了接口的修改量,接口实现ConcreteHandler还需要修改。而且代码显然不简单美丽。 解决方案3: 在Handler接口中只使用一个参数化方法: public ConcreteHandler(Handler successor){ public void handleRequest(String request){ } } public Request(String type){this.type=type;} public String getType(){return type;} public void execute(){ public ConcreteHandler(Handler successor){ public void handleRequest(Request request){ } } 这个解决方案就是CoR, 在一个链上,都有相应职责的类,因此叫Chain of Responsibility. CoR的优点: 缺点是效率低,因为一个请求的完成可能要遍历到最后才可能完成,当然也可以用树的概念优化。 在Java AWT1.0中,对于鼠标按键事情的处理就是使用CoR,到Java.1.1以后,就使用Observer代替CoR 扩展性差,因为在CoR中,一定要有一个统一的接口Handler.局限性就在这里。 与Command模式区别: Command 模式需要事先协商客户端和服务器端的调用关系,比如 1 代表 start 2 代表 move 等,这些 都是封装在 request 中,到达服务器端再分解。 CoR 模式就无需这种事先约定,服务器端可以使用 CoR 模式进行客户端请求的猜测,一个个猜测 试验。 |
|
-- 作者:npuhetao -- 发布时间:3/15/2005 10:04:00 AM -- 设计模式之Observer Java深入到一定程度,就不可避免的碰到设计模式(design pattern)这一概念,了解设计模式,将使自己对java中的接口或抽象类应用有更深的理解.设计模式在java的中型系统中应用广泛,遵循一定的编程模式,才能使自己的代码便于理解,易于交流,Observer(观察者)模式是比较常用的一个模式,尤其在界面设计中应用广泛,而本站所关注的是Java在电子商务系统中应用,因此想从电子商务实例中分析Observer的应用. 虽然网上商店形式多样,每个站点有自己的特色,但也有其一般的共性,单就"商品的变化,以便及时通知订户"这一点,是很多网上商店共有的模式,这一模式类似Observer patern观察者模式. 具体的说,如果网上商店中商品在名称 价格等方面有变化,如果系统能自动通知会员,将是网上商店区别传统商店的一大特色.这就需要在商品product中加入Observer这样角色,以便product细节发生变化时,Observer能自动观察到这种变化,并能进行及时的update或notify动作.
Java的API还为为我们提供现成的Observer接口Java.util.Observer.我们只要直接使用它就可以. 我们必须extends Java.util.Observer才能真正使用它: public String getName(){ return name;} } public float getPrice(){ return price;} } //以下可以是数据库更新 插入命令. 下面看看这些观察者的代码,他们究竟采取了什么行动: //观察者NameObserver主要用来对产品名称(name)进行观察的 private String name=null; public void update(Observable obj,Object arg){ if (arg instanceof String){ name=(String)arg; } } //观察者PriceObserver主要用来对产品价格(price)进行观察的 private float price=0; public void update(Observable obj,Object arg){ if (arg instanceof Float){ price=((Float)arg).floatValue(); } } <jsp:useBean id="product" scope="session" class="Product" /> <jsp:useBean id="nameobs" scope="session" class="NameObserver" /> <jsp:useBean id="priceobs" scope="session" class="PriceObserver" /> <% if (request.getParameter("save")!=null) }else{ //加入观察者 product.addObserver(priceobs); %> //request.getRequestURI()是产生本jsp的程序名,就是自己调用自己 <input type=hidden name="save" value="1"> </form> <% } %>
执行改Jsp程序,会出现一个表单录入界面, 需要输入产品名称 产品价格, 点按Submit后,还是执行该jsp的 NameObserver :name changet to ?????(Jsp表单中输入的产品名称) PriceObserver :price changet to ???(Jsp表单中输入的产品价格); 这说明观察者已经在行动了.!! 产品数据变动 保存! 并已经自动通知客户
上文由于使用jsp概念,隐含很多自动动作,现将调用观察者的Java代码写如下:
public class Test { public static void main(String args[]){ Product product=new Product(); NameObserver nameobs=new NameObserver(); //加入观察者 product.setName("橘子红了"); } }
你会在发现下面信息:: NameObserver :name changet to 橘子红了 PriceObserver :price changet to 9.22 这说明观察者在行动了.!! |
|
-- 作者:npuhetao -- 发布时间:3/15/2005 10:05:00 AM -- 设计模式之Memento(备忘机制) Memento备望录模式定义: Memento模式相对也比较好理解,我们看下列代码: public class Originator { private int number; public Originator(){} // 创建一个Memento // 恢复到原始值 } 我们再看看Memento类: private class Memento implements java.io.Serializable{ private int number; private File file = null; public Memento( Originator o){ number = o.number; } }
可见 Memento中保存了Originator中的number和file的值. 通过调用Originator中number和file值改变的话,通过调用setMemento()方法可以恢复. Memento模式的缺点是耗费大,如果内部状态很多,再保存一份,无意要浪费大量内存. Memento模式在Jsp+Javabean中的应用 如下图中 First Name是用户已经输入,Last Name没有输入,我们则提示红字警告.:
这种技术的实现,就是利用了Javabean的scope="request"或scope="session"特性,也就是Memento模式. |
|
-- 作者:npuhetao -- 发布时间:3/15/2005 10:05:00 AM -- 设计模式之Template Template模板模式定义: 定义一个操作中算法的骨架,将一些步骤的执行延迟到其子类中. 使用Java的抽象类时,就经常会使用到Template模式,因此Template模式使用很普遍.而且很容易理解和使用。
public abstract class Benchmark for (int i = 0; i < count; i++) long stopTime = System.currentTimeMillis();
在上例中,我们希望重复执行benchmark()操作,但是对benchmark()的具体内容没有说明,而是延迟到其子类中描述: public class MethodBenchmark extends Benchmark for (int i = 0; i < Integer.MAX_VALUE; i++){ 至此,Template模式已经完成,是不是很简单? 我们称repeat方法为模板方法, 它其中的benchmark()实现被延迟到子类MethodBenchmark中实现了, 看看如何使用: Benchmark operation = new MethodBenchmark();
也许你以前还疑惑抽象类有什么用,现在你应该彻底明白了吧? 至于这样做的好处,很显然啊,扩展性强,以后Benchmark内容变化,我只要再做一个继承子类就可以,不必修改其他应用代码. |
|
-- 作者:npuhetao -- 发布时间:3/15/2005 10:06:00 AM -- 设计模式之Bridge Bridge模式定义 : 将抽象和行为划分开来,各自独立,但能动态的结合。 任何事物对象都有抽象和行为之分,例如人,人是一种抽象,人分男人和女人等;人有行为,行为也有各种具体表现,所以,“人”与“人的行为”两个概念也反映了抽象和行为之分。 在面向对象设计的基本概念中,对象这个概念实际是由属性和行为两个部分组成的,属性我们可以认为是一种静止的,是一种抽象,一般情况下,行为是包含在一个对象中,但是,在有的情况下,我们需要将这些行为也进行归类,形成一个总的行为接口,这就是桥模式的用处。 为什么使用? 如果一个抽象类或接口有多个具体实现(子类、concrete subclass),这些子类之间关系可能有以下两种情况: 2.这多个子类之中有内容概念上重叠.那么需要我们把抽象共同部分和行为共同部分各自独立开来,原来是准备放在一个接口里,现在需要设计两个接口:抽象接口和行为接口,分别放置抽象和行为. 例如,一杯咖啡为例,子类实现类为四个:中杯加奶、大杯加奶、 中杯不加奶、大杯不加奶。 但是,我们注意到:上面四个子类中有概念重叠,可从另外一个角度进行考虑,这四个类实际是两个角色的组合:抽象 和行为,其中抽象为:中杯和大杯;行为为:加奶 不加奶(如加橙汁 加苹果汁). 实现四个子类在抽象和行为之间发生了固定的绑定关系,如果以后动态增加加葡萄汁的行为,就必须再增加两个类:中杯加葡萄汁和大杯加葡萄汁。显然混乱,扩展性极差。 那我们从分离抽象和行为的角度,使用Bridge模式来实现。 如何实现? 先看看抽象部分的接口代码: public void setCoffeeImp() { public CoffeeImp getCoffeeImp() {return this.CoffeeImp;} public abstract void pourCoffee(); 其中CoffeeImp 是加不加奶的行为接口,看其代码如下: public abstract class CoffeeImp 现在我们有了两个抽象类,下面我们分别对其进行继承,实现concrete class: //中杯 public void pourCoffee() coffeeImp.pourCoffeeImp(); public void pourCoffee() coffeeImp.pourCoffeeImp(); 上面分别是中杯和大杯的具体实现.下面再对行为CoffeeImp进行继承: //加奶 public void pourCoffeeImp() public void pourCoffeeImp() Bridge模式的基本框架我们已经搭好了,别忘记定义中还有一句:动态结合,我们现在可以喝到至少四种咖啡: 看看是如何动态结合的,在使用之前,我们做个准备工作,设计一个单态类(Singleton)用来hold当前的CoffeeImp: public class CoffeeImpSingleton public CoffeeImpSingleton(CoffeeImp coffeeImpIn) public static CoffeeImp getTheCoffeeImp() 看看中杯加奶 和大杯加奶 是怎么出来的: //拿出牛奶 //中杯加奶 //大杯加奶 注意: Bridge模式的执行类如CoffeeImp和Coffee是一对一的关系, 正确创建CoffeeImp是该模式的关键, Bridge模式在EJB中的应用 1.Business Object (类似Coffee) 实现一些抽象的商业操作:如寻找一个用户下所有的订单 涉及数据库操作都使用DAOImplementor. 2.Data Access Object (类似CoffeeImp) 一些抽象的对数据库资源操作 3.DAOImplementor 如OrderDAOCS, OrderDAOOracle, OrderDAOSybase(类似MilkCoffeeImp FragrantCoffeeImp) 具体的数据库操作,如"INSERT INTO "等语句,OrderDAOOracle是Oracle OrderDAOSybase是Sybase数据库. 4.数据库 (Cloudscape, Oracle, or Sybase database via JDBC API) |
|
-- 作者:npuhetao -- 发布时间:3/15/2005 10:06:00 AM -- 设计模式之Decorator(油漆工) 装饰模式:Decorator常被翻译成"装饰",我觉得翻译成"油漆工"更形象点,油漆工(decorator)是用来刷油漆的,那么被刷油漆的对象我们称decoratee.这两种实体在Decorator模式中是必须的. 使用Decorator的理由是:这些功能需要由用户动态决定加入的方式和时机.Decorator提供了"即插即用"的方法,在运行期间决定何时增加何种功能. 如何使用? 我们先建立一个接口: public interface Work }
接口Work有一个具体实现:插入方形桩或圆形桩,这两个区别对Decorator是无所谓.我们以插入方形桩为例: public class SquarePeg implements Work{
现在有一个应用:需要在桩打入前,挖坑,在打入后,在桩上钉木板,这些额外的功能是动态,可能随意增加调整修改,比如,可能又需要在打桩之后钉架子(只是比喻). 那么我们使用Decorator模式,这里方形桩SquarePeg是decoratee(被刷油漆者),我们需要在decoratee上刷些"油漆",这些油漆就是那些额外的功能. public class Decorator implements Work{ private Work work; //在构造器中使用组合new方式,引入Work对象; others.add("钉木板"); public void insert(){ newMethod(); }
在上例中,我们把挖坑和钉木板都排在了打桩insert前面,这里只是举例说明额外功能次序可以任意安排. 好了,Decorator模式出来了,我们看如何调用: Work squarePeg = new SquarePeg(); 如果你细心,会发现,上面调用类似我们读取文件时的调用: FileReader fr = new FileReader(filename); 实际上Java 的I/O API就是使用Decorator实现的,I/O变种很多,如果都采取继承方法,将会产生很多子类,显然相当繁琐. Jive中的Decorator实现 在IBM Java专栏中专门谈Jive的文章中,有谈及Jive中ForumMessageFilter.java使用了Decorator模式,其实,该程序并没有真正使用Decorator,而是提示说:针对特别论坛可以设计额外增加的过滤功能,那么就可以重组ForumMessageFilter作为Decorator模式了. 所以,我们在分辨是否真正是Decorator模式,以及会真正使用Decorator模式,一定要把握好Decorator模式的定义,以及其中参与的角色(Decoratee 和Decorator). |
|
-- 作者:npuhetao -- 发布时间:3/15/2005 10:07:00 AM -- 设计模式之Proxy(代理) 理解并使用设计模式,能够培养我们良好的面向对象编程习惯,同时在实际应用中,可以如鱼得水,享受游刃有余的乐趣. 代理模式是比较有用途的一种模式,而且变种较多,应用场合覆盖从小结构到整个系统的大结构,Proxy是代理的意思,我们也许有代理服务器等概念,代理概念可以解释为:在出发点到目的地之间有一道中间层,意为代理. 设计模式中定义: 为其他对象提供一种代理以控制对这个对象的访问. 为什么要使用Proxy? 2.某个客户端不能直接操作到某个对象,但又必须和那个对象有所互动. (2)如果那个对象在Internet的某个远端服务器上,直接操作这个对象因为网络速度原因可能比较慢,那我们可以先用Proxy来代替那个对象. 如何使用Proxy? Forum是Jive的核心接口,在Forum中陈列了有关论坛操作的主要行为,如论坛名称 论坛描述的获取和修改,帖子发表删除编辑等. 在ForumPermissions中定义了各种级别权限的用户: public class ForumPermissions implements Cacheable { /** /** /** /** /** /** /** /** ..... public boolean isSystemOrForumAdmin() { ..... } 因此,Forum中各种操作权限是和ForumPermissions定义的用户级别有关系的,作为接口Forum的实现:ForumProxy正是将这种对应关系联系起来.比如,修改Forum的名称,只有论坛管理者或系统管理者可以修改,代码如下: public class ForumProxy implements Forum { private ForumPermissions permissions; public ForumProxy(Forum forum, Authorization authorization, ..... public void setName(String name) throws UnauthorizedException, ... }
而DbForum才是接口Forum的真正实现,以修改论坛名称为例: public class DbForum implements Forum, Cacheable { public void setName(String name) throws ForumAlreadyExistsException { .... this.name = name; ....
... } 凡是涉及到对论坛名称修改这一事件,其他程序都首先得和ForumProxy打交道,由ForumProxy决定是否有权限做某一样事情,ForumProxy是个名副其实的"网关","安全代理系统". 在平时应用中,无可避免总要涉及到系统的授权或安全体系,不管你有无意识的使用Proxy,实际你已经在使用Proxy了. 我们继续结合Jive谈入深一点,下面要涉及到工厂模式了,如果你不了解工厂模式,请看我的另外一篇文章:设计模式之Factory 我们已经知道,使用Forum需要通过ForumProxy,Jive中创建一个Forum是使用Factory模式,有一个总的抽象类ForumFactory,在这个抽象类中,调用ForumFactory是通过getInstance()方法实现,这里使用了Singleton(也是设计模式之一,由于介绍文章很多,我就不写了,看这里),getInstance()返回的是ForumFactoryProxy. 为什么不返回ForumFactory,而返回ForumFactory的实现ForumFactoryProxy? 在ForumFactoryProxy中我们看到代码如下: public class ForumFactoryProxy extends ForumFactory { public ForumFactoryProxy(Authorization authorization, ForumFactory factory, public Forum createForum(String name, String description)
方法createForum返回的也是ForumProxy, Proxy就象一道墙,其他程序只能和Proxy交互操作. 注意到这里有两个Proxy:ForumProxy和ForumFactoryProxy. 代表两个不同的职责:使用Forum和创建Forum; Jive论坛系统中其他如帖子的创建和使用,都是按照Forum这个思路而来的. 以上我们讨论了如何使用Proxy进行授权机制的访问,Proxy还可以对用户隐藏另外一种称为copy-on-write的优化方式.拷贝一个庞大而复杂的对象是一个开销很大的操作,如果拷贝过程中,没有对原来的对象有所修改,那么这样的拷贝开销就没有必要.用代理延迟这一拷贝过程. 比如:我们有一个很大的Collection,具体如hashtable,有很多客户端会并发同时访问它.其中一个特别的客户端要进行连续的数据获取,此时要求其他客户端不能再向hashtable中增加或删除 东东. 最直接的解决方案是:使用collection的lock,让这特别的客户端获得这个lock,进行连续的数据获取,然后再释放lock. } 第二个解决方案是clone这个Collection,然后让连续的数据获取针对clone出来的那个Collection操作.这个方案前提是,这个Collection是可clone的,而且必须有提供深度clone的方法.Hashtable就提供了对自己的clone方法,但不是Key和value对象的clone,关于Clone含义可以参考专门文章. } 问题又来了,由于是针对clone出来的对象操作,如果原来的母体被其他客户端操作修改了, 那么对clone出来的对象操作就没有意义了. 最后解决方案:我们可以等其他客户端修改完成后再进行clone,也就是说,这个特别的客户端先通过调用一个叫clone的方法来进行一系列数据获取操作.但实际上没有真正的进行对象拷贝,直至有其他客户端修改了这个对象Collection. 使用Proxy实现这个方案.这就是copy-on-write操作. Proxy应用范围很广,现在流行的分布计算方式RMI和Corba等都是Proxy模式的应用. |
|
-- 作者:npuhetao -- 发布时间:3/15/2005 10:08:00 AM -- 设计模式之Facade(外观 总管 Manager) Facade模式的定义: 为子系统中的一组接口提供一个一致的界面. Facade一个典型应用就是数据库JDBC的应用,如下例对数据库的操作: public class DBCompare {
上例是Jsp中最通常的对数据库操作办法. 在应用中,经常需要对数据库操作,每次都写上述一段代码肯定比较麻烦,需要将其中不变的部分提炼出来,做成一个接口,这就引入了facade外观对象.如果以后我们更换Class.forName中的<driver>也非常方便,比如从Mysql数据库换到Oracle数据库,只要更换facade接口中的driver就可以. 我们做成了一个Facade接口,使用该接口,上例中的程序就可以更改如下: public class DBCompare { String sql = "SELECT * FROM <table> WHERE <column name> = ?"; try { 可见非常简单,所有程序对数据库访问都是使用改接口,降低系统的复杂性,增加了灵活性. 如果我们要使用连接池,也只要针对facade接口修改就可以.
由上图可以看出, facade实际上是个理顺系统间关系,降低系统间耦合度的一个常用的办法,也许你已经不知不觉在使用,尽管不知道它就是facade. |
|
-- 作者:npuhetao -- 发布时间:3/15/2005 10:08:00 AM -- 设计模式之Singleton(单态) 单态定义: 在很多操作中,比如建立目录 数据库连接都需要这样的单线程操作。 还有, singleton能够被状态化; 这样,多个单态类在一起就可以作为一个状态仓库一样向外提供服务,比如,你要论坛中的帖子计数器,每次浏览一次需要计数,单态类能否保持住这个计数,并且能synchronize的安全自动加1,如果你要把这个数字永久保存到数据库,你可以在不修改单态接口的情况下方便的做到。 另外方面,Singleton也能够被无状态化。提供工具性质的功能, Singleton模式就为我们提供了这样实现的可能。使用Singleton的好处还在于可以节省内存,因为它限制了实例的个数,有利于Java垃圾回收(garbage collection)。 我们常常看到工厂模式中类装入器(class loader)中也用Singleton模式实现的,因为被装入的类实际也属于资源。 public class Singleton { private Singleton(){} //在自己内部定义自己一个实例,是不是很奇怪? private static Singleton instance = new Singleton(); //这里提供了一个供外部访问本class的静态方法,可以直接访问 第二种形式: public class Singleton { public static synchronized Singleton getInstance() { //这个方法比上面有所改进,不用每次都进行生成对象,只是第一次 } 使用Singleton.getInstance()可以访问单态类。 上面第二中形式是lazy initialization,也就是说第一次调用时初始Singleton,以后就不用再生成了。 注意到lazy initialization形式中的synchronized,这个synchronized很重要,如果没有synchronized,那么使用getInstance()是有可能得到多个Singleton实例。关于lazy initialization的Singleton有很多涉及double-checked locking (DCL)的讨论,有兴趣者进一步研究。 一般认为第一种形式要更加安全些。 我们以SUN公司的宠物店源码(Pet Store 1.3.1)的ServiceLocator为例稍微分析一下: 在Pet Store中ServiceLocator有两种,一个是EJB目录下;一个是WEB目录下,我们检查这两个ServiceLocator会发现内容差不多,都是提供EJB的查询定位服务,可是为什么要分开呢?仔细研究对这两种ServiceLocator才发现区别:在WEB中的ServiceLocator的采取Singleton模式,ServiceLocator属于资源定位,理所当然应该使用Singleton模式。但是在EJB中,Singleton模式已经失去作用,所以ServiceLocator才分成两种,一种面向WEB服务的,一种是面向EJB服务的。 Singleton模式看起来简单,使用方法也很方便,但是真正用好,是非常不容易,需要对Java的类 线程 内存等概念有相当的了解。 总之:如果你的应用基于容器,那么Singleton模式少用或者不用,可以使用相关替代技术。 |
|
-- 作者:npuhetao -- 发布时间:3/15/2005 10:08:00 AM -- 设计模式之Builder Builder模式定义: Builder模式是一步一步创建一个复杂的对象,它允许用户可以只通过指定复杂对象的类型和内容就可以构建它们.用户不知道内部的具体构建细节.Builder模式是非常类似抽象工厂模式,细微的区别大概只有在反复使用中才能体会到. 为何使用? 因为一个复杂的对象,不但有很多大量组成部分,如汽车,有很多部件:车轮 方向盘 发动机还有各种小零件等等,部件很多,但远不止这些,如何将这些部件装配成一辆汽车,这个装配过程也很复杂(需要很好的组装技术),Builder模式就是为了将部件和组装过程分开. public interface Builder { //创建部件A 比如创建汽车车轮 //返回最后组装成品结果 (返回最后装配好的汽车) } 用Director构建最后的复杂对象,而在上面Builder接口中封装的是如何创建一个个部件(复杂对象是由这些部件组成的),也就是说Director的内容是如何将部件最后组装成成品: public class Director { private Builder builder; public Director( Builder builder ) { } } Builder的具体实现ConcreteBuilder: public class ConcreteBuilder implements Builder { Part partA, partB, partC; }; 复杂对象:产品Product: 复杂对象的部件: public interface Part { } director.construct(); Builder模式的应用 "池"实际是一段内存,当池中有一些复杂的资源的"断肢"(比如数据库的连接池,也许有时一个连接会中断),如果循环再利用这些"断肢",将提高内存使用效率,提高池的性能.修改Builder模式中Director类使之能诊断"断肢"断在哪个部件上,再修复这个部件. |
|
-- 作者:npuhetao -- 发布时间:3/15/2005 10:09:00 AM -- 设计模式之Prototype(原型) 原型模式定义: Prototype模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节,工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建。 如何使用? 以勺子为例: public abstract class AbstractSpoon implements Cloneable public void setSpoonName(String spoonName) {this.spoonName = spoonName;} public Object clone() 有个具体实现(ConcretePrototype): public class SoupSpoon extends AbstractSpoon 调用Prototype模式很简单: AbstractSpoon spoon = new SoupSpoon(); 当然也可以结合工厂模式来创建AbstractSpoon实例。 在Java中Prototype模式变成clone()方法的使用,由于Java的纯洁的面向对象特性,使得在Java中使用设计模式变得很自然,两者已经几乎是浑然一体了。这反映在很多模式上,如Interator遍历模式。 |
|
-- 作者:npuhetao -- 发布时间:3/15/2005 10:09:00 AM -- 设计模式之Factory 工厂模式定义:提供创建对象的接口. 为何使用? 为什么工厂模式是如此常用?因为工厂模式就相当于创建实例对象的new,我们经常要根据类Class生成实例对象,如A a=new A() 工厂模式也是用来创建实例对象的,所以以后new时就要多个心眼,是否可以考虑实用工厂模式,虽然这样做,可能多做一些工作,但会给你系统带来更大的可扩展性和尽量少的修改量。 我们以类Sample为例, 如果我们要创建Sample的实例对象: Sample sample=new Sample(); 可是,实际情况是,通常我们都要在创建sample实例时做点初始化的工作,比如赋值 查询数据库等。 首先,我们想到的是,可以使用Sample的构造函数,这样生成实例就写成: Sample sample=new Sample(参数); 但是,如果创建sample实例时所做的初始化工作不是象赋值这样简单的事,可能是很长一段代码,如果也写入构造函数中,那你的代码很难看了(就需要Refactor重整)。 为什么说代码很难看,初学者可能没有这种感觉,我们分析如下,初始化工作如果是很长一段代码,说明要做的工作很多,将很多工作装入一个方法中,相当于将很多鸡蛋放在一个篮子里,是很危险的,这也是有背于Java面向对象的原则,面向对象的封装(Encapsulation)和分派(Delegation)告诉我们,尽量将长的代码分派“切割”成每段,将每段再“封装”起来(减少段和段之间偶合联系性),这样,就会将风险分散,以后如果需要修改,只要更改每段,不会再发生牵一动百的事情。 在本例中,首先,我们需要将创建实例的工作与使用实例的工作分开, 也就是说,让创建实例所需要的大量初始化工作从Sample的构造函数中分离出去。 这时我们就需要Factory工厂模式来生成对象了,不能再用上面简单new Sample(参数)。还有,如果Sample有个继承如MySample, 按照面向接口编程,我们需要将Sample抽象成一个接口.现在Sample是接口,有两个子类MySample 和HisSample .我们要实例化他们时,如下: Sample mysample=new MySample(); 随着项目的深入,Sample可能还会"生出很多儿子出来", 那么我们要对这些儿子一个个实例化,更糟糕的是,可能还要对以前的代码进行修改:加入后来生出儿子的实例.这在传统程序中是无法避免的. 但如果你一开始就有意识使用了工厂模式,这些麻烦就没有了. 工厂方法 public class Factory{ public static Sample creator(int which){ //getClass 产生Sample 一般可使用动态类装载装入类。 } } 那么在你的程序中,如果要实例化Sample时.就使用 Sample sampleA=Factory.creator(1); 这样,在整个就不涉及到Sample的具体子类,达到封装效果,也就减少错误修改的机会,这个原理可以用很通俗的话来比喻:就是具体事情做得越多,越容易范错误.这每个做过具体工作的人都深有体会,相反,官做得越高,说出的话越抽象越笼统,范错误可能性就越少.好象我们从编程序中也能悟出人生道理?呵呵. 使用工厂方法 要注意几个角色,首先你要定义产品接口,如上面的Sample,产品接口下有Sample接口的实现类,如SampleA,其次要有一个factory类,用来生成产品Sample,如下图,最右边是生产的对象Sample:
进一步稍微复杂一点,就是在工厂类上进行拓展,工厂类也有继承它的实现类concreteFactory了。 抽象工厂 这两个模式区别在于需要创建对象的复杂程度上。如果我们创建对象的方法变得复杂了,如上面工厂方法中是创建一个对象Sample,如果我们还有新的产品接口Sample2. 这里假设:Sample有两个concrete类SampleA和SamleB,而Sample2也有两个concrete类Sample2A和SampleB2 那么,我们就将上例中Factory变成抽象类,将共同部分封装在抽象类中,不同部分使用子类实现,下面就是将上例中的Factory拓展成抽象工厂: public abstract class Factory{ public abstract Sample creator(); public abstract Sample2 creator(String name); } public class SimpleFactory extends Factory{ public Sample creator(){ public Sample2 creator(String name){ } public class BombFactory extends Factory{ public Sample creator(){ public Sample2 creator(String name){ } 从上面看到两个工厂各自生产出一套Sample和Sample2,也许你会疑问,为什么我不可以使用两个工厂方法来分别生产Sample和Sample2? 抽象工厂还有另外一个关键要点,是因为 SimpleFactory内,生产Sample和生产Sample2的方法之间有一定联系,所以才要将这两个方法捆绑在一个类中,这个工厂类有其本身特征,也许制造过程是统一的,比如:制造工艺比较简单,所以名称叫SimpleFactory。 举例 我们以Jive的ForumFactory为例,这个例子在前面的Singleton模式中我们讨论过,现在再讨论其工厂模式: public abstract class ForumFactory { private static Object initLock = new Object(); public static ForumFactory getInstance(Authorization authorization) { try { //Now, 返回 proxy.用来限制授权对forum的访问 //真正创建forum的方法由继承forumfactory的子类去完成. .... }
因为现在的Jive是通过数据库系统存放论坛帖子等内容数据,如果希望更改为通过文件系统实现,这个工厂方法ForumFactory就提供了提供动态接口: private static String className = "com.jivesoftware.forum.database.DbForumFactory"; 你可以使用自己开发的创建forum的方法代替com.jivesoftware.forum.database.DbForumFactory就可以. 在上面的一段代码中一共用了三种模式,除了工厂模式外,还有Singleton单态模式,以及proxy模式,proxy模式主要用来授权用户对forum的访问,因为访问forum有两种人:一个是注册用户 一个是游客guest,那么那么相应的权限就不一样,而且这个权限是贯穿整个系统的,因此建立一个proxy,类似网关的概念,可以很好的达到这个效果. 看看Java宠物店中的CatalogDAOFactory: public class CatalogDAOFactory { /** * 本方法制定一个特别的子类来实现DAO模式。 public static CatalogDAO getDAO() throws CatalogDAOSysException { CatalogDAO catDao = null; try { InitialContext ic = new InitialContext(); String className =(String) ic.lookup(JNDINames.CATALOG_DAO_CLASS); catDao = (CatalogDAO) Class.forName(className).newInstance(); } catch (NamingException ne) { throw new CatalogDAOSysException(" } catch (Exception se) { throw new CatalogDAOSysException(" } return catDao; } }
由此可见,工厂方法确实为系统结构提供了非常灵活强大的动态扩展机制,只要我们更换一下具体的工厂方法,系统其他地方无需一点变换,就有可能将系统功能进行改头换面的变化。 |
|
W 3 C h i n a ( since 2003 ) 旗 下 站 点 苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》 |
218.750ms |