您好,欢迎来到九壹网。
搜索
您的当前位置:首页Android常用的8种设计模式

Android常用的8种设计模式

来源:九壹网
Android常用的8种设计模式

一般来说,常用的android设计模式有以下8种:适配器、工厂、单例、观察者、代理、命令、组合、访问者。

设计模式之Adapter(适配器模式)

定义:将两个不兼容的类纠合在一起使用,属于结构型模式,需要有Adaptee(被适配者)和Adaptor(适配器)两个身份.

为何使用:我们经常碰到要将两个没有关系的类组合在一起使用,第一解决方案是:修改各自类的接口,但是如果我们没有源代码,或者,我们不愿意为了一个应用而修改各自的接口。怎么办:使用Adapter,在这两种接口之间创建一个混合接口(混血儿).

如何使用:实现Adapter方式,其实\"think in Java\"的\"类再生\"一节中已经提到,有两种方式:组合(composition)和继承(inheritance).

假设我们要打桩,有两种类:方形桩 圆形桩. public class SquarePeg{

public void insert(String str){

System.out.println(\"SquarePeg insert():\"+str); } }

public class RoundPeg{

public void insertIntohole(String msg){

System.out.println(\"RoundPeg insertIntoHole():\"+msg); } }

现在有一个应用,需要既打方形桩,又打圆形桩.那么我们需要将这两个没有关系的类综合应用.假设RoundPeg我们没有源代码,或源代码我们不想修改,那么我们使用Adapter来实现这个应用:

public class PegAdapter extends SquarePeg{

private RoundPeg roundPeg;

public PegAdapter(RoundPeg peg)(this.roundPeg=peg;)

public void insert(String str){ roundPeg.insertIntoHole(str);} }

在上面代码中,RoundPeg属于Adaptee,是被适配者.PegAdapter是Adapter,将Adaptee(被适配者RoundPeg)和Target(目标SquarePeg)进行适配.实际上这是将组合方法(composition)和继承(inheritance)方法综合运用.

PegAdapter首先继承SquarePeg,然后使用new的组合生成对象方式,生成RoundPeg的对象roundPeg,再重载父类insert()方法。从这里,你也了解使用

new生成对象和使用extends继承生成对象的不同,前者无需对原来的类修改,甚至无需要知道其内部结构和源代码.

如果你有些Java使用的经验,已经发现,这种模式经常使用。

进一步使用上面的PegAdapter是继承了SquarePeg,如果我们需要两边继承,即继承SquarePeg 又继承RoundPeg,因为Java中不允许多继承,但是我们可以实现(implements)两个接口(interface) public interface IRoundPeg{

public void insertIntoHole(String msg); }

public interface ISquarePeg{

public void insert(String str); }

下面是新的RoundPeg 和SquarePeg, 除了实现接口这一区别,和上面的没什么区别。

public class SquarePeg implements ISquarePeg{ public void insert(String str){

System.out.println(\"SquarePeg insert():\"+str); } }

public class RoundPeg implements IRoundPeg{ public void insertIntohole(String msg){

System.out.println(\"RoundPeg insertIntoHole():\"+msg); } }

下面是新的PegAdapter,叫做two-way adapter:

public class PegAdapter implements IRoundPeg,ISquarePeg{

private RoundPeg roundPeg; private SquarePeg squarePeg;

// 构造方法

public PegAdapter(RoundPeg peg){this.roundPeg=peg;} // 构造方法

public PegAdapter(SquarePeg peg)(this.squarePeg=peg;)

public void insert(String str){ roundPeg.insertIntoHole(str);} }

还有一种叫Pluggable Adapters,可以动态的获取几个adapters中一个。使用Reflection技术,可以动态的发现类中的Public方法。

设计模式之Factory(工厂模式)

定义:提供创建对象的接口. 为何使用:工厂模式是我们最常用的模式了,著名的Jive论坛 ,就大量使用了工厂模式,工厂模式在Java程序系统可以说是随处可见。

为什么工厂模式是如此常用:因为工厂模式就相当于创建实例对象的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 hissample=new HisSample();

随着项目的深入,Sample可能还会\"生出很多儿子出来\那么我们要对这些儿子一个个实例化,更糟糕的是,可能还要对以前的代码进行修改:加入后来生出儿子的实例.这在传统程序中是无法避免的.

但如果你一开始就有意识使用了工厂模式,这些麻烦就没有了. 工厂方法

你会建立一个专门生产Sample实例的工厂:

public class Factory{

public static Sample creator(int which){

//getClass 产生Sample 一般可使用动态类装载装入类。 if (which==1)

return new SampleA(); else if (which==2)

return new SampleB();

} }

那么在你的程序中,如果要实例化Sample时.就使用 Sample sampleA=Factory.creator(1);

这样,在整个就不涉及到Sample的具体子类,达到封装效果,也就减少错误修改的机会,这个原理可以用很通俗的话来比喻:就是具体事情做得越多,越容易范错误.这每个做过具体工作的人都深有体会,相反,官做得越高,说出的话越抽象越笼统,范错误可能性就越少.好象我们从编程序中也能悟出人生道理?呵呵.

使用工厂方法 要注意几个角色,首先你要定义产品接口,如上面的Sample,产品接口下有Sample接口的实现类,如SampleA,其次要有一个factory类,用来生成产品Sample,如下图,最右边是生产的对象Sample:

进一步稍微复杂一点,就是在工厂类上进行拓展,工厂类也有继承它的实现类concreteFactory了。 抽象工厂

工厂模式中有: 工厂方法(Factory Method) 抽象工厂(Abstract Factory). 这两个模式区别在于需要创建对象的复杂程度上。如果我们创建对象的方法变得复杂了,如上面工厂方法中是创建一个对象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(){ .........

return new SampleA }

public Sample2 creator(String name){ .........

return new Sample2A

} }

public class BombFactory extends Factory{ public Sample creator(){ ......

return new SampleB }

public Sample2 creator(String name){ ......

return new Sample2B } }

从上面看到两个工厂各自生产出一套Sample和Sample2,也许你会疑问,为什么我不可以使用两个工厂方法来分别生产Sample和Sample2?

抽象工厂还有另外一个关键要点,是因为 SimpleFactory内,生产Sample和生产Sample2的方法之间有一定联系,所以才要将这两个方法捆绑在一个类中,这个工厂类有其本身特征,也许制造过程是统一的,比如:制造工艺比较简单,所以名称叫SimpleFactory。 在实际应用中,工厂方法用得比较多一些,而且是和动态类装入器组合在一起应用, 举例

我们以Jive的ForumFactory为例,这个例子在前面的Singleton模式中我们讨论过,现在再讨论其工厂模式:

public abstract class ForumFactory {

private static Object initLock = new Object(); private static String className = \"com.jivesoftware.forum.database.DbForumFactory\"; private static ForumFactory factory = null;

public static ForumFactory getInstance(Authorization authorization) { //If no valid authorization passed in, return null. if (authorization == null) { return null; }

//以下使用了Singleton 单态模式 if (factory == null) {

synchronized(initLock) { if (factory == null) { ...... try {

//动态转载类

Class c = Class.forName(className);

factory = (ForumFactory)c.newInstance(); }

catch (Exception e) { return null; } } } }

//Now, 返回 proxy.用来授权对forum的访问

return new ForumFactoryProxy(authorization, factory, factory.getPermissions(authorization)); }

//真正创建forum的方法由继承forumfactory的子类去完成.

public abstract Forum createForum(String name, String description) throws UnauthorizedException, ForumAlreadyExistsException; .... }

因为现在的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模式。 * 具体子类定义是在J2EE的部署描述器中。 */

public static CatalogDAO getDAO() throws CatalogDAOSysException { CatalogDAO catDao = null; try {

InitialContext ic = new InitialContext(); //动态装入CATALOG_DAO_CLASS

//可以定义自己的CATALOG_DAO_CLASS,从而在无需变更太多代码 //的前提下,完成系统的巨大变更。

String className =(String) ic.lookup(JNDINames.CATALOG_DAO_CLASS); catDao = (CatalogDAO) Class.forName(className).newInstance(); } catch (NamingException ne) {

throw new CatalogDAOSysException(\"

CatalogDAOFactory.getDAO: NamingException while getting DAO type : \\n\" + ne.getMessage()); } catch (Exception se) {

throw new CatalogDAOSysException(\"

CatalogDAOFactory.getDAO: Exception while getting DAO type : \\n\" + se.getMessage()); }

return catDao; } }

CatalogDAOFactory是典型的工厂方法,catDao是通过动态类装入器className获得CatalogDAOFactory具体实现子类,这个实现子类在Java宠物店是用来操作catalog数据库,用户可以根据数据库的类型不同,定制自己的具体实现子类,将自己的子类名给与CATALOG_DAO_CLASS变量就可以。 由此可见,工厂方法确实为系统结构提供了非常灵活强大的动态扩展机制,只要我们更换一下具体的工厂方法,系统其他地方无需一点变换,就有可能将系统功能进行改头换面的变化。

设计模式之Singleton(单例模式)

定义:Singleton模式主要作用是保证在Java应用程序中,一个类Class只有一个实例存在。在很多操作中,比如建立目录 数据库连接都需要这样的单线程操作。还有, singleton能够被状态化; 这样,多个单态类在一起就可以作为一个状态仓库一样向外提供服务,比如,你要论坛中的帖子计数器,每次浏览一次需要计数,单态类能否保持住这个计数,并且能synchronize的安全自动加1,如果你要把这个数字永久保存到数据库,你可以在不修改单态接口的情况下方便的做到。

另外方面,Singleton也能够被无状态化。提供工具性质的功能,

Singleton模式就为我们提供了这样实现的可能。使用Singleton的好处还在于可以节省内存,因为它了实例的个数,有利于Java垃圾回收(garbage collection)。

我们常常看到工厂模式中类装入器(class loader)中也用Singleton模式实现的,因为被装入的类实际也属于资源。 如何使用?

一般Singleton模式通常有几种形式:

public class Singleton { private Singleton(){}

//在自己内部定义自己一个实例,是不是很奇怪? //注意这是private 只供内部调用

private static Singleton instance = new Singleton();

//这里提供了一个供外部访问本class的静态方法,可以直接访问

public static Singleton getInstance() { return instance; } }

第二种形式:

public class Singleton {

private static Singleton instance = null;

public static synchronized Singleton getInstance() {

//这个方法比上面有所改进,不用每次都进行生成对象,只是第一次 //使用时生成实例,提高了效率! if (instance==null)

instance=new Singleton(); return instance; } }

使用Singleton.getInstance()可以访问单态类。

上面第二中形式是lazy initialization,也就是说第一次调用时初始Singleton,以后就不用再生成了。

注意到lazy initialization形式中的synchronized,这个synchronized很重要,如果没有synchronized,那么使用getInstance()是有可能得到多个Singleton实例。关于lazy initialization的Singleton有很多涉及double-checked locking (DCL)的讨论,有兴趣者进一步研究。 一般认为第一种形式要更加安全些。 使用Singleton注意事项:

有时在某些情况下,使用Singleton并不能达到Singleton的目的,如有多个Singleton对象同时被不同的类装入器装载;在EJB这样的分布式系统中使用也要注意这种情况,因为EJB是跨服务器,跨JVM的。 我们以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的类 线程 内存等概念有相当的了解。 进一步深入可参考:

Double-checked locking and the Singleton pattern When is a singleton not a singleton?

设计模式之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才能真正使用它: 1.提供Add/Delete observer的方法;

2.提供通知(notisfy) 所有observer的方法;

//产品类 可供Jsp直接使用UseBean调用 该类主要执行产品数据库插入 更新

public class product extends Observable{ private String name;

}

private float price;

public String getName(){ return name;} public void setName(){ this.name=name; //设置变化点 setChanged();

notifyObservers(name); }

public float getPrice(){ return price;} public void setPrice(){ this.price=price; //设置变化点 setChanged();

notifyObservers(new Float(price)); }

//以下可以是数据库更新 插入命令. public void saveToDb(){ .....................

我们注意到,在product类中 的setXXX方法中,我们设置了 notify(通知)方法, 当Jsp表单调用setXXX(如何调用见我的另外一篇文章),实际上就触发了notisfyObservers方法,这将通知相应观察者应该采取行动了. 下面看看这些观察者的代码,他们究竟采取了什么行动:

//观察者NameObserver主要用来对产品名称(name)进行观察的 public class NameObserver implements Observer{ private String name=null;

public void update(Observable obj,Object arg){

if (arg instanceof String){ name=(String)arg;

//产品名称改变值在name中

System.out.println(\"NameObserver :name changet to \"+name); } } }

//观察者PriceObserver主要用来对产品价格(price)进行观察的 public class PriceObserver implements Observer{ private float price=0;

public void update(Observable obj,Object arg){

if (arg instanceof Float){

}

price=((Float)arg).floatValue();

System.out.println(\"PriceObserver :price changet to \"+price); } }

Jsp中我们可以来正式执行这段观察者程序:

<%

if (request.getParameter(\"save\")!=null) {

product.saveToDb();

out.println(\"产品数据变动 保存! 并已经自动通知客户\"); }else{

//加入观察者

product.addObserver(nameobs);

product.addObserver(priceobs); %>

//request.getRequestURI()是产生本jsp的程序名,就是自己调用自己

\" method=post> 产品名称: 产品价格:
<% } %>

执行改Jsp程序,会出现一个表单录入界面, 需要输入产品名称 产品价格, 点按Submit后,还是执行该jsp的

if (request.getParameter(\"save\")!=null)之间的代码.

由于这里使用了数据javabeans的自动赋值概念,实际程序自动执行了setName setPrice语句.你会在服务器控制台中发现下面信息::

NameObserver :name changet to (Jsp表单中输入的产品名称)

PriceObserver :price changet to (Jsp表单中输入的产品价格); 这说明观察者已经在行动了.!!

同时你会在执行jsp的浏览器端得到信息:

产品数据变动 保存! 并已经自动通知客户

上文由于使用jsp概念,隐含很多自动动作,现将调用观察者的Java代码写如下: public class Test {

public static void main(String args[]){ Product product=new Product();

NameObserver nameobs=new NameObserver(); PriceObserver priceobs=new PriceObserver(); //加入观察者

product.addObserver(nameobs); product.addObserver(priceobs); product.setName(\"橘子红了\"); product.setPrice(9.22f); } }

你会在发现下面信息::

NameObserver :name changet to 橘子红了

PriceObserver :price changet to 9.22 这说明观察者在行动了.!!

设计模式之Proxy(代理模式)

理解并使用设计模式,能够培养我们良好的面向对象编程习惯,同时在实际应用中,可以如鱼得水,享受游刃有余的乐趣.

Proxy是比较有用途的一种模式,而且变种较多,应用场合覆盖从小结构到整个系统的大结构,Proxy是代理的意思,我们也许有代理服务器等概念,代理概念可以解释为:在出发点到目的地之间有一道中间层,意为代理.

设计模式中定义: 为其他对象提供一种代理以控制对这个对象的访问. 为什么要使用Proxy?

1.授权机制 不同级别的用户对同一对象拥有不同的访问权利,如Jive论坛系统

中,就使用Proxy进行授权机制控制,访问论坛有两种人:注册用户和游客(未注册用户),Jive中就通过类似ForumProxy这样的代理来控制这两种用户对论坛的访问权限.

2.某个客户端不能直接操作到某个对象,但又必须和那个对象有所互动. 举例两个具体情况:

(1)如果那个对象是一个是很大的图片,需要花费很长时间才能显示出来,那么当这个图片包含在文档中时,使用编辑器或浏览器打开这个文档,打开文档必须很迅速,不能等待大图片处理完成,这时需要做个图片Proxy来代替真正的图片.

(2)如果那个对象在Internet的某个远端服务器上,直接操作这个对象因为网络速度原因可能比较慢,那我们可以先用Proxy来代替那个对象.

总之原则是,对于开销很大的对象,只有在使用它时才创建,这个原则可以为我们节省很多宝贵的Java内存. 所以,有些人认为Java耗费资源内存,我以为这和程序编制思路也有一定的关系. 如何使用Proxy?

以Jive论坛系统为例,访问论坛系统的用户有多种类型:注册普通用户 论坛管理者 系统管理者 游客,注册普通用户才能发言;论坛管理者可以管理他被授权的论坛;系统管理者可以管理所有事务等,这些权限划分和管理是使用Proxy完成的.

Forum是Jive的核心接口,在Forum中陈列了有关论坛操作的主要行为,如论坛名称 论坛描述的获取和修改,帖子发表删除编辑等. 在ForumPermissions中定义了各种级别权限的用户: public class ForumPermissions implements Cacheable { /**

* Permission to read object. */

public static final int READ = 0; /**

* Permission to administer the entire sytem. */

public static final int SYSTEM_ADMIN = 1; /**

* Permission to administer a particular forum. */

public static final int FORUM_ADMIN = 2; /**

* Permission to administer a particular user. */

public static final int USER_ADMIN = 3; /**

* Permission to administer a particular group. */

public static final int GROUP_ADMIN = 4; /**

* Permission to moderate threads. */

public static final int MODERATE_THREADS = 5; /**

* Permission to create a new thread. */

public static final int CREATE_THREAD = 6; /**

* Permission to create a new message. */

public static final int CREATE_MESSAGE = 7; /**

* Permission to moderate messages. */

public static final int MODERATE_MESSAGES = 8; .....

public boolean isSystemOrForumAdmin() { return (values[FORUM_ADMIN] values[SYSTEM_ADMIN]); } ..... }

||

因此,Forum中各种操作权限是和ForumPermissions定义的用户级别有关系的,作为接口Forum的实现:ForumProxy正是将这种对应关系联系起来.比如,修改Forum的名称,只有论坛管理者或系统管理者可以修改,代码如下: public class ForumProxy implements Forum { private ForumPermissions permissions; private Forum forum;

this.authorization = authorization;

public ForumProxy(Forum forum, Authorization authorization,

ForumPermissions permissions) {

this.forum = forum;

this.authorization = authorization; this.permissions = permissions; }

..... public void setName(String name) throws UnauthorizedException,

ForumAlreadyExistsException

{

//只有是系统或论坛管理者才可以修改名称 if (permissions.isSystemOrForumAdmin()) { forum.setName(name); }

else {

throw new UnauthorizedException(); } } ... }

而DbForum才是接口Forum的真正实现,以修改论坛名称为例: public class DbForum implements Forum, Cacheable { ... public void setName(String name) throws ForumAlreadyExistsException {

....

this.name = name;

//这里真正将新名称保存到数据库中 saveToDb(); .... } ... }

凡是涉及到对论坛名称修改这一事件,其他程序都首先得和ForumProxy打交道,由ForumProxy决定是否有权限做某一样事情,ForumProxy是个名副其实的\"网关\安全代理系统\".

在平时应用中,无可避免总要涉及到系统的授权或安全体系,不管你有无意识的使用Proxy,实际你已经在使用Proxy了.

我们继续结合Jive谈入深一点,下面要涉及到工厂模式了,如果你不了解工厂模式,请看我的另外一篇文章:设计模式之Factory

我们已经知道,使用Forum需要通过ForumProxy,Jive中创建一个Forum是使用Factory模式,有一个总的抽象类ForumFactory,在这个抽象类中,调用ForumFactory是通过getInstance()方法实现,这里使用了Singleton(也是设计模式之一,由于介绍文章很多,我就不写了,看这里),getInstance()返回的是ForumFactoryProxy.

为什么不返回ForumFactory,而返回ForumFactory的实现ForumFactoryProxy? 原因是明显的,需要通过代理确定是否有权限创建forum. 在ForumFactoryProxy中我们看到代码如下:

public class ForumFactoryProxy extends ForumFactory {

protected ForumFactory factory;

protected Authorization authorization; protected ForumPermissions permissions;

public ForumFactoryProxy(Authorization authorization, ForumFactory factory,

ForumPermissions permissions) {

this.factory = factory;

this.authorization = authorization; this.permissions = permissions; }

public Forum createForum(String name, String description) throws UnauthorizedException, ForumAlreadyExistsException {

//只有系统管理者才可以创建forum

if (permissions.get(ForumPermissions.SYSTEM_ADMIN)) { Forum newForum = factory.createForum(name, description);

return new ForumProxy(newForum, authorization, permissions); }

else {

throw new UnauthorizedException(); } }

方法createForum返回的也是ForumProxy, Proxy就象一道墙,其他程序只能和Proxy交互操作.

注意到这里有两个Proxy:ForumProxy和ForumFactoryProxy. 代表两个不同的职责:使用Forum和创建Forum;

至于为什么将使用对象和创建对象分开,这也是为什么使用Factory模式的原因所在:是为了\"封装\" \"分派\";换句话说,尽可能功能单一化,方便维护修改. Jive论坛系统中其他如帖子的创建和使用,都是按照Forum这个思路而来的. 以上我们讨论了如何使用Proxy进行授权机制的访问,Proxy还可以对用户隐藏另外一种称为copy-on-write的优化方式.拷贝一个庞大而复杂的对象是一个开销很大的操作,如果拷贝过程中,没有对原来的对象有所修改,那么这样的拷贝开销就没有必要.用代理延迟这一拷贝过程.

比如:我们有一个很大的Collection,具体如hashtable,有很多客户端会并发同时访问它.其中一个特别的客户端要进行连续的数据获取,此时要求其他客户端不能再向hashtable中增加或删除 东东.

最直接的解决方案是:使用collection的lock,让这特别的客户端获得这个lock,进行连续的数据获取,然后再释放lock. public void foFetches(Hashtable ht){ synchronized(ht){

//具体的连续数据获取动作.. } }

但是这一办法可能锁住Collection会很长时间,这段时间,其他客户端就不能访问该Collection了.

第二个解决方案是clone这个Collection,然后让连续的数据获取针对clone出来的那个Collection操作.这个方案前提是,这个Collection是可clone的,而且必须有提供深度clone的方法.Hashtable就提供了对自己的clone方法,但不是Key和value对象的clone,关于Clone含义可以参考专门文章. public void foFetches(Hashtable ht){

Hashttable newht=(Hashtable)ht.clone(); }

问题又来了,由于是针对clone出来的对象操作,如果原来的母体被其他客户端操作修改了, 那么对clone出来的对象操作就没有意义了.

最后解决方案:我们可以等其他客户端修改完成后再进行clone,也就是说,这个特别的客户端先通过调用一个叫clone的方法来进行一系列数据获取操作.但实际上没有真正的进行对象拷贝,直至有其他客户端修改了这个对象Collection. 使用Proxy实现这个方案.这就是copy-on-write操作.

Proxy应用范围很广,现在流行的分布计算方式RMI和Corba等都是Proxy模式的应用. 更多Proxy应用,见http://www.research.umbc.edu/~tarr/cs491/lectures/Proxy.pdf

Sun公司的 Explore the Dynamic Proxy API Dynamic Proxy Classes

设计模式之Command(命令模式)

Command模式是最让我疑惑的一个模式,我在阅读了很多代码后,才感觉隐约掌握其大概原理,我认为理解设计模式最主要是掌握起原理构造,这样才对自己实际编程有指导作用.Command模式实际上不是个很具体,规定很多的模式,正是这个灵活性,让人有些confuse.

Command定义:不少Command模式的代码都是针对图形界面的,它实际就是菜单命令,我们在一个下拉菜单选择一个命令时,然后会执行一些动作.

将这些命令封装成在一个类中,然后用户(调用者)再对这个类进行操作,这就是Command模式,换句话说,本来用户(调用者)是直接调用这些命令的,如菜单上打开文档(调用者),就直接指向打开文档的代码,使用Command模式,就是在这两者之间增加一个中间者,将这种直接关系拗断,同时两者之间都隔离,基本没有关系了.

显然这样做的好处是符合封装的特性,降低耦合度,Command是将对行为进行封装的典型模式,Factory是将创建进行封装的模式,

从Command模式,我也发现设计模式一个\"通病\":好象喜欢将简单的问题复杂化, 喜欢在不同类中增加第三者,当然这样做有利于代码的健壮性 可维护性 还有复用性.

如何使用?

具体的Command模式代码各式各样,因为如何封装命令,不同系统,有不同的做法.下面事例是将命令封装在一个Collection的List中,任何对象一旦加入List中,实际上装入了一个封闭的黑盒中,对象的特性消失了,只有取出时,才有可能模糊的分辨出:

典型的Command模式需要有一个接口.接口中有一个统一的方法,这就是\"将命令/请求封装为对象\":

public interface Command {

public abstract void execute ( ); }

具体不同命令/请求代码是实现接口Command,下面有三个具体命令 public class Engineer implements Command {

public void execute( ) { //do Engineer's command } }

public class Programmer implements Command {

public void execute( ) {

//do programmer's command } }

public class Politician implements Command {

public void execute( ) {

//do Politician's command } }

按照通常做法,我们就可以直接调用这三个Command,但是使用Command模式,我们要将他们封装起来,扔到黑盒子List里去: public class producer{

public static List produceRequests() { List queue = new ArrayList();

queue.add( new DomesticEngineer() ); queue.add( new Politician() ); queue.add( new Programmer() ); return queue; } }

这三个命令进入List中后,已经失去了其外表特征,以后再取出,也可能无法分辨出谁是Engineer 谁是Programmer了,看下面如何调用Command模式:

public class TestCommand {

public static void main(String[] args) {

List queue = Producer.produceRequests();

for (Iterator it = queue.iterator(); it.hasNext(); ) //取出List中东东,其他特征都不能确定,只能保证一个特征是100%正确,

// 他们至少是接口Command的\"儿子\".所以强制转换类型为接口Command

((Command)it.next()).execute(); } }

由此可见,调用者基本只和接口打交道,不合具体实现交互,这也体现了一个原则,面向接口编程,这样,以后增加第四个具体命令时,就不必修改调用者TestCommand中的代码了.

理解了上面的代码的核心原理,在使用中,就应该各人有自己方法了,特别是在如何分离调用者和具体命令上,有很多实现方法,上面的代码是使用\"从List过一遍\"的做法.这种做法只是为了演示.

使用Command模式的一个好理由还因为它能实现Undo功能.每个具体命令都可以记住它刚刚执行的动作,并且在需要时恢复.

Command模式在界面设计中应用广泛.Java的Swing中菜单命令都是使用Command模式,由于Java在界面设计的性能上还有欠缺,因此界面设计具体代码我们就不讨论,网络上有很多这样的示例.

参考:http://www.patterndepot.com/put/8/command.pdf

设计模式之Composite(组合模式)

Composite定义:将对象以树形结构组织起来,以达成“部分-整体” 的层次结构,使得客户端对单个对象和组合对象的使用具有一致性.

Composite比较容易理解,想到Composite就应该想到树形结构图。组合体内这些对象都有共同接口,当组合体一个对象的方法被调用执行时,Composite将遍历(Iterator)整个树形结构,寻找同样包含这个方法的对象并实现调用执行。可以用牵一动百来形容。

所以Composite模式使用到Iterator模式,和Chain of Responsibility模式类似。

Composite好处:

1.使客户端调用简单,客户端可以一致的使用组合结构或其中单个对象,用户就不必关系自己处理的是单个对象还是整个组合结构,这就简化了客户端代码。 2.更容易在组合体内加入对象部件. 客户端不必因为加入了新的对象部件而更改代码。

如何使用Composite?

首先定义一个接口或抽象类,这是设计模式通用方式了,其他设计模式对接口内部定义不多,Composite却有个规定,那就是要在接口内部定义一个用于访问和管理Composite组合体的对象们(或称部件Component). 下面的代码是以抽象类定义,一般尽量用接口interface,

public abstract class Equipment {

private String name; //网络价格

public abstract double netPrice(); //折扣价格

public abstract double discountPrice(); //增加部件方法

public boolean add(Equipment equipment) { return false; } //删除部件方法

public boolean remove(Equipment equipment) { return false; } //注意这里,这里就提供一种用于访问组合体类的部件方法。 public Iterator iter() { return null; }

public Equipment(final String name) { this.name=name; } }

抽象类Equipment就是Component定义,代表着组合体类的对象们,Equipment中定义几个共同的方法。

public class Disk extends Equipment {

public Disk(String name) { super(name); } //定义Disk网络价格为1

public double netPrice() { return 1.; } //定义了disk折扣价格是0.5 对折。

public double discountPrice() { return .5; } }

Disk是组合体内的一个对象,或称一个部件,这个部件是个单独元素( Primitive)。

还有一种可能是,一个部件也是一个组合体,就是说这个部件下面还有'儿子',这是树形结构中通常的情况,应该比较容易理解。现在我们先要定义这个组合体: abstract class CompositeEquipment extends Equipment {

private int i=0;

//定义一个Vector 用来存放'儿子'

private Lsit equipment=new ArrayList();

public CompositeEquipment(String name) { super(name); }

public boolean add(Equipment equipment) { this.equipment.add(equipment); return true; }

public double netPrice() {

double netPrice=0.;

Iterator iter=equipment.iterator(); for(iter.hasNext())

netPrice+=((Equipment)iter.next()).netPrice(); return netPrice; }

public double discountPrice() {

double discountPrice=0.;

Iterator iter=equipment.iterator(); for(iter.hasNext())

discountPrice+=((Equipment)iter.next()).discountPrice(); return discountPrice; }

//注意这里,这里就提供用于访问自己组合体内的部件方法。 //上面dIsk 之所以没有,是因为Disk是个单独(Primitive)的元素.

public Iterator iter() {

return equipment.iterator() ; {

//重载Iterator方法

public boolean hasNext() { return iif(hasNext())

return equipment.elementAt(i++); else

throw new NoSuchElementException(); } }

上面CompositeEquipment继承了Equipment,同时为自己里面的对象们提供了外部访问的方法,重载了Iterator,Iterator是Java的Collection的一个接口,是Iterator模式的实现.

我们再看看CompositeEquipment的两个具体类:盘盒Chassis和箱子Cabinet,箱子里面可以放很多东西,如底板,电源盒,硬盘盒等;盘盒里面可以放一些小设备,如硬盘 软驱等。无疑这两个都是属于组合体性质的。

public class Chassis extends CompositeEquipment {

public Chassis(String name) { super(name); }

public double netPrice() { return 1.+super.netPrice(); } public double discountPrice() { return .5+super.discountPrice(); } }

public class Cabinet extends CompositeEquipment {

public Cabinet(String name) { super(name); }

public double netPrice() { return 1.+super.netPrice(); } public double discountPrice() { return .5+super.discountPrice(); } }

至此我们完成了整个Composite模式的架构。 我们可以看看客户端调用Composote代码:

Cabinet cabinet=new Cabinet(\"Tower\");

Chassis chassis=new Chassis(\"PC Chassis\"); //将PC Chassis装到Tower中 (将盘盒装到箱子里) cabinet.add(chassis);

//将一个10GB的硬盘装到 PC Chassis (将硬盘装到盘盒里) chassis.add(new Disk(\"10 GB\"));

//调用 netPrice()方法;

System.out.println(\"netPrice=\"+cabinet.netPrice());

System.out.println(\"discountPrice=\"+cabinet.discountPrice());

上面调用的方法netPrice()或discountPrice(),实际上Composite使用Iterator遍历了整个树形结构,寻找同样包含这个方法的对象并实现调用执行. Composite是个很巧妙体现智慧的模式,在实际应用中,如果碰到树形结构,我们就可以尝试是否可以使用这个模式。

以论坛为例,一个版(forum)中有很多帖子(message),这些帖子有原始贴,有对原始贴的回应贴,是个典型的树形结构,那么当然可以使用Composite模式,那么我们进入Jive中看看,是如何实现的. Jive解剖

在Jive中 ForumThread是ForumMessages的容器container(组合体).也就是说,ForumThread类似我们上例中的 CompositeEquipment.它和messages的关

系如图: [thread]

|- [message] |- [message]

|- [message] |- [message]

|- [message]

我们在ForumThread看到如下代码:

public interface ForumThread { ....

public void addMessage(ForumMessage parentMessage, ForumMessage newMessage)

throws UnauthorizedException;

public void deleteMessage(ForumMessage message) throws UnauthorizedException;

public Iterator messages(); .... }

类似CompositeEquipment, 提供用于访问自己组合体内的部件方法: 增加 删除 遍历.

结合我的其他模式中对Jive的分析,我们已经基本大体理解了Jive论坛体系的框架,如果你之前不理解设计模式,而直接去看Jive源代码,你肯定无法看懂。

设计模式之Visitor(访问者模式)

Visitor定义:作用于某个对象群中各个对象的操作. 它可以使你在不改变这些对象本身的情况下,定义作用于这些对象的新操作.

在Java中,Visitor模式实际上是分离了collection结构中的元素和对这些元素进行操作的行为.

为何使用Visitor:Java的Collection(包括Vector和Hashtable)是我们最经常使用的技术,可是Collection好象是个黑色大染缸,本来有各种鲜明类型特征的对象一旦放入后,再取出时,这些类型就消失了.那么我们势必要用If来判断,如:

Iterator iterator = collection.iterator() while (iterator.hasNext()) { Object o = iterator.next(); if (o instanceof Collection)

messyPrintCollection((Collection)o); else if (o instanceof String)

System.out.println(\"'\"+o.toString()+\"'\"); else if (o instanceof Float)

System.out.println(o.toString()+\"f\"); else

System.out.println(o.toString()); }

在上例中,我们使用了 instanceof来判断 o的类型.

很显然,这样做的缺点代码If else if 很繁琐.我们就可以使用Visitor模式解决它.

如何使用Visitor?

针对上例,我们设计一个接口visitor访问者:

public interface Visitor {

public void visitCollection(Collection collection); public void visitString(String string); public void visitFloat(Float float); }

在这个接口中,将我们认为Collection有可能的类的类型放入其中.

有了访问者,我们需要被访问者,被访问者就是我们Collection的每个元素Element,我们要为这些Element定义一个可以接受访问的接口(访问和被访问是互动的,只有访问者,被访问者如果表示不欢迎,访问者就不能访问),

我们定义这个接口叫Visitable,用来定义一个Accept操作,也就是说让Collection每个元素具备可访问性.

public interface Visitable {

public void accept(Visitor visitor); }

好了,有了两个接口,我们就要定义他们的具体实现(Concrete class): public class ConcreteElement implements Visitable {

private String value;

public ConcreteElement(String string) { value = string; }

//定义accept的具体内容 这里是很简单的一句调用 public void accept(Visitor visitor) { visitor.visitString(this); } }

再看看访问者的Concrete实现:

public class ConcreteVisitor implements Visitor {

//在本方法中,我们实现了对Collection的元素的成功访问 public void visitCollection(Collection collection) {

}

Iterator iterator = collection.iterator() while (iterator.hasNext()) { Object o = iterator.next(); if (o instanceof Visitable)

((Visitable)o).accept(this); }

public void visitString(String string) { System.out.println(\"'\"+string+\"'\"); }

public void visitFloat(Float float) {

System.out.println(float.toString()+\"f\"); }

在上面的visitCollection我们实现了对Collection每个元素访问,只使用了一个判断语句,只要判断其是否可以访问. 至此,我们完成了Visitor模式基本架构. 使用Visitor模式的前提

对象群结构中(Collection) 中的对象类型很少改变,也就是说访问者的身份类型很少改变,如上面中Visitor中的类型很少改变,如果需要增加新的操作,比如上例中我们在ConcreteElement具体实现外,还需要新的ConcreteElement2 ConcreteElement3.

可见使用Visitor模式是有前提的,在两个接口Visitor和Visitable中,确保Visitor很少变化,变化的是Visitable,这样使用Visitor最方便.

如果Visitor也经常变化, 也就是说,对象群中的对象类型经常改变,一般建议是,不如在这些对象类中逐个定义操作.但是Java的Reflect技术解决了这个问题.

Reflect技术是在运行期间动态获取对象类型和方法的一种技术,具体实现参考Javaworld的英文原文.

注:本文可使用文档结构图模式,方便浏览。Android中常用的8种设计模式,先介绍到这里。以后有时间会陆续整理出一共21一种Android中的会用到的设计模式。

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- 91gzw.com 版权所有 湘ICP备2023023988号-2

违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务