当然也可以结合工厂模式来创建AbstractSpoon实例
在Java中Prototype模式变成clone()方法的使用由于Java的纯洁的面向对象特性
使得在Java中使用设计模式变得很自然两者已经几乎是浑然一体了这反映在很多模式上如Interator遍历模式
/***/
创建模式Builder
Builder模式定义:
将一个复杂对象的构建与它的表示分离使得同样的构建过程可以创建不同的表示
Builder模式是一步一步创建一个复杂的对象它允许用户可以只通过指定复杂对象的类型和内容就可以构建它们
用户不知道内部的具体构建细节Builder模式是非常类似抽象工厂模式
细微的区别大概只有在反复使用中才能体会到
为何使用?
是为了将构建复杂对象的过程和它的部件解耦注意: 是解耦过程和部件
因为一个复杂的对象不但有很多大量组成部分如汽车有很多部件:车轮 方向盘 发动机还有各种小零件等等
部件很多但远不止这些如何将这些部件装配成一辆汽车这个装配过程也很复杂(需要很好的组装技术)
Builder模式就是为了将部件和组装过程分开
如何使用?
首先假设一个复杂对象是由多个部件组成的Builder模式是把复杂对象的创建和部件的创建分别开来
分别用Builder类和Director类来表示
首先需要一个接口它定义如何创建复杂对象的各个部件:
public interface Builder {
//创建部件A比如创建汽车车轮
void buildPartA();
//创建部件B 比如创建汽车方向盘
void buildPartB();
//创建部件C 比如创建汽车发动机
void buildPartC();
//返回最后组装成品结果 (返回最后装配好的汽车)
//成品的组装过程不在这里进行而是转移到下面的Director类中进行
//从而实现了解耦过程和部件
Product getResult();
}
用Director构建最后的复杂对象而在上面Builder接口中封装的是如何创建一个个部件
(复杂对象是由这些部件组成的)也就是说Director的内容是如何将部件最后组装成成品:
public class Director {
private Builder builder;
public Director( Builder builder ) {
thisbuilder = builder;
}
// 将部件partA partB partC最后组成复杂对象
//这里是将车轮 方向盘和发动机组装成汽车的过程
public void construct() {
builderbuildPartA();
builderbuildPartB();
builderbuildPartC();
}
}
Builder的具体实现ConcreteBuilder:
通过具体完成接口Builder来构建或装配产品的部件;
定义并明确它所要创建的是什么具体东西;
提供一个可以重新获取产品的接口:
public class ConcreteBuilder implements Builder {
Part partA partB partC;
public void buildPartA() {
//这里是具体如何构建partA的代码
};
public void buildPartB() {
//这里是具体如何构建partB的代码
};
public void buildPartC() {
//这里是具体如何构建partB的代码
};
public Product getResult() {
//返回最后组装成品结果
};
}
复杂对象:产品Product:
public interface Product { }
复杂对象的部件:
public interface Part { }
我们看看如何调用Builder模式:
ConcreteBuilder builder = new ConcreteBuilder();
Director director = new Director( builder );
nstruct();
Product product = buildergetResult();
Builder模式的应用
在Java实际使用中我们经常用到池(Pool)的概念当资源提供者无法提供足够的资源
并且这些资源需要被很多用户反复共享时就需要使用池
池实际是一段内存当池中有一些复杂的资源的断肢(比如数据库的连接池也许有时一个连接会中断)
如果循环再利用这些断肢将提高内存使用效率提高池的性能
修改Builder模式中Director类使之能诊断断肢断在哪个部件上再修复这个部件
/***/
创建模式Singleton
定义:
Singleton模式主要作用是保证在Java应用程序中一个类Class只有一个实例存在
在很多操作中比如建立目录 数据库连接都需要这样的单线程操作
还有 singleton能够被状态化; 这样多个单态类在一起就可以作为一个状态仓库一样向外提供服务比如你要论坛中的帖子计数器每次浏览一次需要计数单态类能否保持住这个计数并且能synchronize的安全自动加如果你要把这个数字永久保存到数据库你可以在不修改单态接口的情况下方便的做到
另外方面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; }
}
使用SingletongetInstance()可以访问单态类
上面第二中形式是lazy initialization也就是说第一次调用时初始Singleton以后就不用再生成了
注意到lazy initialization形式中的synchronized这个synchronized很重要如果没有synchronized那么使用 getInstance()是有可能得到多个Singleton实例关于lazy initialization的Singleton有很多涉及doublechecked locking (DCL)的讨论有兴趣者进一步研究
一般认为第一种形式要更加安全些
使用Singleton注意事项
有时在某些情况下使用Singleton并不能达到Singleton的目的如有多个Singleton对象同时被不同的类装入器装载在EJB这样的分布式系统中使用也要注意这种情况因为EJB是跨服务器跨JVM的
我们以SUN公司的宠物店源码(Pet Store )的ServiceLocator为例稍微分析一下
在Pet Store中ServiceLocator有两种一个是EJB目录下一个是WEB目录下我们检查这两个ServiceLocator会发现内容差不多都是提供EJB的查询定位服务可是为什么要分开呢?仔细研究对这两种ServiceLocator才发现区别在WEB中的 ServiceLocator的采取Singleton模式ServiceLocator属于资源定位理所当然应该使用Singleton模式但是在EJB中Singleton模式已经失去作用所以ServiceLocator才分成两种一种面向WEB服务的一种是面向EJB服务的
Singleton模式看起来简单使用方法也很方便但是真正用好是非常不容易需要对Java的类 线程 内存等概念有相当的了解
/***/
结构模式Facade
Facade的定义: 为子系统中的一组接口提供一个一致的界面
Facade一个典型应用就是数据库JDBC的应用如下例对数据库的操作:
public class DBCompare {
Connection conn = null;
PreparedStatement prep = null;
ResultSet rset = null;
try {
ClassforName( <driver> )newInstance();
conn = DriverManagergetConnection( <database> );
String sql = SELECT * FROM <table> WHERE <column name> = ?;
prep = connprepareStatement( sql );
prepsetString( <column value> );
rset = prepexecuteQuery();
if( rsetnext() ) {
Systemoutprintln( rsetgetString( <column name ) );
}
} catch( SException e ) {
eprintStackTrace();
} finally {
rsetclose();
prepclose();
connclose();
}
}
上例是Jsp中最通常的对数据库操作办法
在应用中经常需要对数据库操作每次都写上述一段代码肯定比较麻烦需要将其中不变的部分提炼出来做成一个接口这就引入了facade外观对象如果以后我们更换ClassforName中的<driver>也非常方便比如从Mysql数据库换到Oracle数据库只要更换 facade接口中的driver就可以
我们做成了一个Facade接口使用该接口上例中的程序就可以更改如下:
public class DBCompare {
String sql = SELECT * FROM <table> WHERE <column name> = ?;
try {
Mysql msql=new mysql(sql);
prepsetString( <column value> );
rset = prepexecuteQuery();
if( rsetnext() ) {
Systemoutprintln( rsetgetString( <column name ) );
}
} catch( SException e ) {
eprintStackTrace();
} finally {
mysqlclose();
mysql=null;
}
}
可见非常简单所有程序对数据库访问都是使用改接口降低系统的复杂性增加了灵活性
如果我们要使用连接池也只要针对facade接口修改就可以
facade实际上是个理顺系统间关系降低系统间耦合度的一个常用的办法
也许你已经不知不觉在使用尽管不知道它就是facade
/***/
结构模式Proxy
Proxy是比较有用途的一种模式而且变种较多应用场合覆盖从小结构到整个系统的大结构
Proxy是代理的意思我们也许有代理服务器等概念
代理概念可以解释为:在出发点到目的地之间有一道中间层意为代理
设计模式中定义: 为其他对象提供一种代理以控制对这个对象的访问
为什么要使用Proxy?
授权机制 不同级别的用户对同一对象拥有不同的访问权利
如Jive论坛系统中就使用Proxy进行授权机制控制
访问论坛有两种人:注册用户和游客(未注册用户)
Jive中就通过类似ForumProxy这样的代理来控制这两种用户对论坛的访问权限
某个客户端不能直接操作到某个对象但又必须和那个对象有所互动
举例两个具体情况:
()如果那个对象是一个是很大的图片需要花费很长时间才能显示出来
那么当这个图片包含在文档中时使用编辑器或浏览器打开这个文档打开文档必须很迅速
不能等待大图片处理完成这时需要做个图片Proxy来代替真正的图片
()如果那个对象在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 = ;
/** Permission to administer the entire sytem*/
public static final int SYSTEM_ADMIN = ;
/** Permission to administer a particular forum*/
public static final int FORUM_ADMIN = ;
/** Permission to administer a particular user*/
public static final int USER_ADMIN = ;
/** Permission to administer a particular group*/
public static final int GROUP_ADMIN = ;
/** Permission to moderate threads*/
public static final int MODERATE_THREADS = ;
/** Permission to create a new thread*/
public static final int CREATE_THREAD = ;
/** Permission to create a new message*/
public static final int CREATE_MESSAGE = ;
/** Permission to moderate messages*/
public static final int MODERATE_MESSAGES = ;
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;
thisauthorization = authorization;
public ForumProxy(Forum forum Authorization authorization
ForumPermissions permissions)
{
thisforum = forum;
thisauthorization = authorization;
thispermissions = permissions;
}
public void setName(String name) throws UnauthorizedException
ForumAlreadyExistsException
{
//只有是系统或论坛管理者才可以修改名称
if (permissionsisSystemOrForumAdmin()) {
forumsetName(name);
}
else {
throw new UnauthorizedException();
}
}
}
而DbForum才是接口Forum的真正实现以修改论坛名称为例:
public class DbForum implements Forum Cacheable {
public void setName(String name) throws ForumAlreadyExistsException {
thisname = name;
//这里真正将新名称保存到数据库中
saveToDb();
}
}
凡是涉及到对论坛名称修改这一事件其他程序都首先得和ForumProxy打交道
由ForumProxy决定是否有权限做某一样事情ForumProxy是个名副其实的网关安全代理系统
在平时应用中无可避免总要涉及到系统的授权或安全体系不管你有无意识的使用Proxy实际你已经在使用Proxy了