Patterns in Java
23个模式,被分类为创建型模式(Creational Patterns),结构型模式(Structural Patterns)和行为模式(Behavioral Patterns)
1. 创建模式
1.Singleton(单列模式)
基本概念
Singleton 是一种创建性模型,它用来确保只产生一个实例,并提供一个访问它的全局访问点.对一些类来说,保证只有一个实例是很重要的,比如有的时候,数据库连接或 Socket 连接要受到一定的限制,必须保持同一时间只能有一个连接的存在.再举个例子,集合中的 set 中不能包含重复的元素,添加到set里的对象必须是唯一的,如果重复的值添加到 set,它只接受一个实例.JDK中正式运用了Singleton模式来实现 set 的这一特性,大家可以查看java.util.Collections里的内部静态类SingletonSet的原代码.其实Singleton是最简单但也是应用最广泛的模式之一,在 JDK 中随处可见。
简单分析
为了实现 Singleton 模式,我们需要的是一个静态的变量,能够在不创建对象的情况下记忆是否已经产生过实例了.静态变量或静态方法都可以在不产生具体实例的情况下直接调用, 这样的变量或方法不会因为类的实例化而有所改变。
具体实现
1. 静态方法实现
1.1. 直观式
publicclass Singleton {
privatestatic Singleton instance = new Singleton();
private Singleton(){}
//在自己内部定义自己一个实例,是不是很奇怪?
//注意这是private 只供内部调用
//这里提供了一个供外部访问本class的静态方法,可以直接访问
publicstatic Singleton getInstance() {
return instance;
}
}
|
或者
Publicclass Singleton {
Privatestatic Singleton instance;
Static{
try {
instance = new MyFactory();
} catch (IOException e) {
throw new RuntimeException("Darn, an error's occurred!", e);
}
}
private Singleton(){}
publicstatic Singleton getInstance() {
return instance;
}
}
1.2. Lazy initialization
publicclass Singleton {
privatestatic Singleton instance = null;
publicstaticsynchronized Singleton getInstance() {
//这个方法比上面有所改进,不用每次都进行生成对象,只是第一次
//使用时生成实例!
if (instance==null)
instance=new Singleton();
return instance;
}
}
|
2. 静态变量为标志实现
class SingletonException extends RuntimeException {
public SingletonException(String s) {
super(s);
}
}
Public class Singleton {
static boolean instance_flag = false; // true if 1 instance
public Singleton() {
if (instance_flag)
throw new SingletonException("Only one instance allowed");
else
instance_flag = true; // set flag for 1 instance
}
}
|
3. 注册器机制实现
首先用集合中的Hashtable 和Enumeration来实现addItem(Object key, Object value),getItem(Object key), ,removeItem(Object key)等方法实现一个管理器,将key和value一一关联起来,客户程序员创建实例前首先用addItem方法进行注册,再用getItem方法获取实例.Hashtable中的key是唯一的,从而保证创建的实例是唯一的,具体实现限于篇幅不再细说,在Prototype模型的应用一文中我将会给出一个实现注册器的代码.用注册器机制来创建 Singleton模式的好处是易于管理,可以同时控制多个不同类型的Singleton 实例。
注意事项
Singleton模式看起来简单,使用方法也很方便,但是真正用好,是非常不容易,需要对Java的类,线程,内存等概念有相当的了解。如果你的应用基于容器,那么Singleton模式少用或者不用,可以使用相关替代技术。
有时在某些情况下,使用Singleton并不能达到Singleton的目的,如有多个Singleton对象同时被不同的类装入器装载;在EJB这样的分布式系统中使用也要注意这种情况,因为EJB是跨服务器,跨JVM的。
注意到在静态方法实现singleton时,lazy initialization形式中的synchronized,这个synchronized很重要,如果没有synchronized,那么使用getInstance()是有可能得到多个Singleton实例。一般认为第一种形式要更加安全些。关于lazy initialization的Singleton有很多涉及double-checked locking (DCL) 同步开销,如下:
public class MyFactory {
private static MyFactory instance;
Public synchronized static MyFactory getFactory() {
if (instance == null)
instance = new MyFactory();
return instance;
}
}
|
上面的例子是完全正确的,考虑到所有的Read操作也需要同步,为了避免昂贵的同步开销
public class MyBrokenFactory {
private static MyFactory instance;
private int field1, field2 ;
public static MyBrokenFactory getFactory() {
if (instance == null) {
synchronized (MyBrokenFactory.class)
{
instance = new MyFactory();
}
}
return instance;
}
private MyBrokenFactory() {
field1 = ...;
field2 = ...;
}
}
|
上面的做法是不正确的,考虑2个线程同时调用MyBrokenFactory.getFactory(),线程2在线程1完成对象创建到该对象初始化之间就可能得到了对象的引用(Thread 2 gets in just when Thread 1 has written the object reference to memory, but before it has written all the fields.)。可以采用静态方法直接式来避免这个问题,但是要不是去lazy策略,可以采用javaEE5 方式:
public class MyFactory {
private static volatile MyFactory instance;
public static MyFactory getInstance(Connection conn) throws IOException { if (instance == null) { //first check synchronized (MyFactory.class) { if (instance == null) // second check instance = new MyFactory(conn);
}
}
return instance; }
private MyFactory(Connection conn) throws IOException { // init factory using the database connection passed in
}
}
JAVA5以后,访问一个volatile的变量具有synchronized 的语义。换句话说,JAVA5保证unsycnrhonized volatile read 会在写之后发生。
|
2.Prototype(原型模式)
基本概念
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象.
简单分析
Prototype模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节, 工作原理:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建。
具体实现
public abstract class AbstractSpoon implements Cloneable { String spoonName; public void setSpoonName(String spoonName) {this.spoonName = spoonName;} public String getSpoonName() {return this.spoonName;} public Object clone() { Object object = null; try { object = super.clone(); } catch (CloneNotSupportedException exception) { System.err.println("AbstractSpoon is not Cloneable"); } return object; } } |
public class SoupSpoon extends AbstractSpoon { public SoupSpoon() { setSpoonName("Soup Spoon"); } } |
调用Prototype模式很简单:
AbstractSpoon spoon = new SoupSpoon();
AbstractSpoon spoon2 = spoon.clone();
在Java中Prototype模式变成clone()方法的使用,由于Java的纯洁的面向对象特性,使得在Java中使用设计模式变得很自然,两者已经几乎是浑然一体了。
3. Factory Method(工厂方法模式)-----工厂模式1
基本概念:
提供创建对象的接口
简单分析:
工厂模式相当于创建对象的new,考虑用它主要是考虑为你的系统带来更大的可扩展性和尽量少的修改量。假如在创建对象时,需要初始化许多工作,当然可以在构造函数中通过传值的方式来实现,但是这样的话,你的构造函数会非常臃肿,不宜于阅读,还有初始化工作复杂,如果在同一个方法中,势必会带来危险,不要将很多鸡蛋放在一个篮子里面,这也有悖于java面向对象的原则(封装)。
具体实现
Public class MethodFactory {
Public static Sample creator(int flag)
{
If(flag ==1) return new SampleA();
Else if (flag ==2) return new SampleB();
//…这里可能更适合使用class.forName(),通过反射机制来创建对象
}
}
|
Sample |
SampleA |
SampleB |
Factory |
GetClass |
Param |
Sample |
4. Abstract Factory(抽象工厂模式)-----工厂模式2
基本概念:
基于工厂方法,如果创建对象的过程更复杂。
简单分析:
如上述的Sample 下有SampleA, SampleB 产品,如果在需要此上 还有Demo 接口下还有DemoA , DemoB产品,并且同中接口下的不同产品或许存在某种特定关系,那应该就考虑到用抽象工厂了。那我们把相同部分封装在抽象工厂了,那不同部分就在具体的工厂里面实现。
具体实现:
Public abstract class Factory{
Public abstract Sample createSample ();
Public abstract Demo createDemo ();
}
|
Public class SampleFactory extends Factory{
Public abstract Sample createSample(){
Return new SampleA();
}
Public abstract Demo createDemo(){
Return new DemoA();
}
}
|
Public class DemoFactory extends Factory{
Public abstract Sample createSample(){
Return new SampleB();
}
Public abstract Demo createDemo(){
Return new DemoB();
}
}
|
使用:
Factory samplefactory =new SampleFactory();
Sample sampleA = Samplefactory. createSample();
Demo demoA = Samplefactory. createDemo();
Factory demofactory =new DemoFactory();
Sample sampleB = demofactory. createSample();
Demo demoB = demofactory. createDemo();
|
SampleB |
Sample |
SampleA |
Factory |
GetClass |
Param |
Sample |
Demo |
DemoA |
DemoB |
SampleFactory |
DemoFactory |
Demo |
5. Builder(建造模式)
基本概念:
将一个复杂的对象的构建与他的表示分离,使得同样的构建过程可以创建不同的表示。
简单分析:
此模式是一步步创建一个复杂的对象,它允许用户可以通过指定复杂对象的类型和内容就可以构建他们,拥护不知道内部的具体实现细节,非常类似抽象工厂模式。但是有细微差别。为了将构建对象的复杂过程和他的部件解藕。
具体实现:
比如在汽车的装配过程,需要将许许多多的部件创建,然后组装。
1.构建的具体过程抽象
Public interface Builder {
Void buildPartA(); //创建部件A
Void buildPartB(); //创建部件B
Void buildPartC(); //创建部件C
Void buildPartD(); //创建部件D
Product getProduct(); //组装汽车。
}
|
2.组装过程抽象
Public class Director{
Private Builder builder;
Public Director(Builder builder)
{
This. Builder = builder;
}
Public void construct()
{
builder.buildPartA(); //创建部件A
builder.buildPartB(); //创建部件B
builder.buildPartC(); //创建部件C
builder.buildPartD(); //创建部件D
}
}
|
3.构建的具体实现
Public class ConreteBuilder implements Builder{
Private Product product;
Private Part A,B,C,D;
Void buildPartA(){this .A = new A();}
Void buildPartB(){this .B = new B();}
Void buildPartC(){this .C = new C();}
Void buildPartD(){this .D = new D();}
Product getProduct ()
{
This .product = new Product(A,B,C,D);
Return this.product;
}
}
|
4. 使用:
ConreteBuilder builder = new ConreteBuilder();
Director director = new Director(builder);
Director. Construct();
Product product = Builder. getProduct();
|
2. 结构型模式
1. 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);
}
}
|
1.组合继承使用
public class PegAdapter extends SquarePeg{
private RoundPeg roundPeg;
public PegAdapter(RoundPeg peg)(this.roundPeg=peg;)
public void insert(String str){ roundPeg.insertIntoHole(str);}
}
|
2. 高级使用(实用于多继承)
public interface IRoundPeg{
public void insertIntoHole(String msg);
}
public interface ISquarePeg{
public void insert(String str);
}
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);
}
}
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);}
}
|
2. Bridge (桥接模式)
基本概念:
将抽象和行为划分开来,各自独立,但能动态的结合。
简单分析:
任何事物对象都有抽象和行为之分,例如人,人是一种抽象,人分男人和女人等;人有行为,行为也有各种具体表现,所以,“人”与“人的行为”两个概念也反映了抽象和行为之分。
在面向对象设计的基本概念中,对象这个概念实际是由属性和行为两个部分组成的,属性我们可以认为是一种静止的,是一种抽象,一般情况下,行为是包含在一个对象中,但是,在有的情况下,我们需要将这些行为也进行归类,形成一个总的行为接口,这就是桥模式的用处。
应用场景:
Business Object >> Java DAO
具体实现:
这多个子类之间概念是并列的,如前面举例,打桩,有两个concrete class:方形桩和圆形桩;这两个形状上的桩是并列的,没有概念上的重复。2.这多个子类之中有内容概念上重叠.那么需要我们把抽象共同部分和行为共同部分各自独立开来,原来是准备放在一个接口里,现在需要设计两个接口:抽象接口和行为接口,分别放置抽象和行为. 例如,一杯咖啡为例,子类实现类为四个:中杯加奶、大杯加奶、中杯不加奶、大杯不加奶。
抽象接口:咖啡
public abstract class Coffee {
CoffeeAction coffeeAction;
public void setCoffeeAction () {
this.coffeeAction = CoffeeActionSingleton.getTheCoffeeAction ();
}
public CoffeeImp getCoffeeAction() {
return this. coffeeAction;
}
public abstract void pourCoffee();
}
|
行为接口:附加动作
public abstract class CoffeeAction {
public abstract void pourCoffeeAction();
}
|
具体抽象接口实现类:大杯,中杯
Public MediumCoffee extends Coffee
{
Public MediumCoffee(){setCoffeeAction ()}
public void pourCoffee()
{
for(int i=0;i<2;i++)//2次为小杯
coffeeAction.pourCoffeeAction();
}
}
Public BigCoffee extends Coffee
{
Public MediumCoffee(){setCoffeeAction ()}
public void pourCoffee()
{
for(int i=0;i<5;i++)//5次为小杯
coffeeAction.pourCoffeeAction();
}
}
|
具体行为接口实现类:加奶,不加奶。。。
//加奶
public class MilkCoffeeAction extends CoffeeAction {
MilkCoffeeAction () {}
public void pourCoffeeAction()
{
System.out.println("加了美味的牛奶");
}
}
//不加奶
public class FragrantCoffeeAction extends CoffeeAction {
FragrantCoffeeAction () {}
public void pourCoffeeAction()
{
System.out.println("什么也没加,清");
}
}
|
public class CoffeeImpSingleton
{
private static CoffeeImp coffeeImp;
public CoffeeImpSingleton(CoffeeImp coffeeImpIn)
{this.coffeeImp = coffeeImpIn;}
public static CoffeeImp getTheCoffeeImp()
{ return coffeeImp; }
}
|
//拿出牛奶
CoffeeImpSingleton coffeeImpSingleton = new CoffeeImpSingleton(new MilkCoffeeImp());
//中杯加奶
MediumCoffee mediumCoffee = new MediumCoffee(); mediumCoffee.pourCoffee();
//大杯加奶
SuperSizeCoffee superSizeCoffee = new SuperSizeCoffee(); superSizeCoffee.pourCoffee();
|
3. Composite (合成模式)
基本概念:
将对象以树形结构组织起来,以达成“部分-整体” 的层次结构,使得客户端对单个对象和组合对象的使用具有一致性.
简单分析:
具体实现:
4. Decorator (门面模式,装饰模式)
基本概念:
动态给一个对象添加一些额外的职责, 就象在墙上刷油漆.使用Decorator模式相比用生成子类方式达到功能的扩充显得更为灵活.
简单分析:
我们通常可以使用继承来实现功能的拓展,如果这些需要拓展的功能的种类很繁多,那么势必生成很多子类,增加系统的复杂性,同时,使用继承实现功能拓展,我们必须可预见这些拓展功能,这些功能是编译时就确定了,是静态的.
使用Decorator的理由是:这些功能需要由用户动态决定加入的方式和时机.Decorator提供了"即插即用"的方法,在运行期间决定何时增加何种功能.
应用场景:
Java 文件读写 I/O
FileReader fr = new FileReader(filename);
BufferedReader br = new BufferedReader(fr);
具体实现:
举Adapter中的打桩示例,在Adapter中有两种类:方形桩圆形桩,Adapter模式展示如何综合使用这两个类,在Decorator模式中,我们是要在打桩时增加一些额外功能,比如,挖坑,在桩上钉木板等,不关心如何使用两个不相关的类.
//具体工作
public interface Work {
public void insert();
}
|
//打桩
public class SquarePeg implements Work{
public void insert(){
System.out.println("方形桩插入");
}
}
|
//装饰
public class Decorator implements Work{
private Work work;
//额外增加的功能被打包在这个List中
private ArrayList others = new ArrayList();
//在构造器中使用组合new方式,引入Work对象;
public Decorator(Work work) {
this.work=work;
others.add("挖坑");
others.add("钉木板");
}
public void insert(){
newMethod();
}
//在新方法中,我们在insert之前增加其他方法,这里次序先后是用户灵活指定的
public void newMethod() {
otherMethod();
work.insert();
}
public void otherMethod() {
ListIterator listIterator = others.listIterator();
while (listIterator.hasNext()) {
System.out.println(((String)(listIterator.next()))
+ " 正在进行"); } }
}
}
|
使用:
Work squarePeg = new SquarePeg();
Work decorator = new Decorator(squarePeg);
decorator.insert();
|
5. Façade (外观模式)
基本概念:
为子系统中的一组接口提供一个一致的界面.
简单分析:
典型应用就是数据库JDBC的应用,
具体实现:
public class DBCompare {
Connection conn = null;
PreparedStatement prep = null;
ResultSet rset = null;
try {
Class.forName( "<driver>" ).newInstance();
conn = DriverManager.getConnection( "<database>" );
String sql = "SELECT * FROM <table> WHERE <column name> = ?";
prep = conn.prepareStatement( sql );
prep.setString( 1, "<column value>" );
rset = prep.executeQuery();
if( rset.next() ) {
System.out.println( rset.getString( "<column name" ) );
}
} catch( SException e ) {
e.printStackTrace();
} finally {
rset.close();
prep.close();
conn.close();
}
|
6. Flyweight (亨元模式)
基本概念:
避免拥有大量有相同内容的小类的开销,使大家共享一个类(元类)
简单分析:
面向对象语言的原则一切皆对象,但是有时对象的数量可能相当庞大,比如字处理软件,几千个字就是几千的对象,这样内存耗费相当巨大,那我们采用“求同存异”,找出共同点作为一个对象,封装可以被共享得类,即为元类。Flyweight有两个重要概念,外部状态,内部状态。
具体实现:
它常常会结合工厂模式一起来实现复杂的过程。
Public interface Flyweight{
Public void operator(ExstrinsicState extState);
}
|
Public interface ExstrinsicState {
//外部状态,自行实现
}
|
Public Class ConcreteFlyweight extends Flyweight {
Private InstrinsicState instrState;
//内部状态,共享数据,也可以没有共享数据
Public void operator(ExstrinsicState extState){
//外部状态,自行实现
}
}
|
Public Class FlyweightFactory {
private Hashtable flyweights = new Hashtable();
public Flyweight getFlyweight( Object key ) {
Flyweight flyweight = (Flyweight) flyweights.get(key);
if( flyweight == null ) {
//产生新的ConcreteFlyweight
flyweight = new ConcreteFlyweight();
flyweights.put( key, flyweight );
}
return flyweight;
}
}
|
使用
FlyweightFactory factory = new FlyweightFactory();
Flyweight fly1 = factory.getFlyweight( "Fred" );
Flyweight fly2 = factory.getFlyweight( "Wilma" );
|
7. Proxy (代理模式)
基本概念:
为其他对象提供一种代理以控制对这个对象的访问。
简单分析:
代理模式是比较有用途的一种模式,而且变种较多,应用场合覆盖从小结构到整个系统的大结构,Proxy是代理的意思,我们也许有代理服务器等概念,代理概念可以解释为:在出发点到目的地之间有一道中间层,意为代理.
应用场景:
1.授权机制
2. 提高系统性能,特别是进行大文件处理,远程网络访问
3.额外为对象添加一些功能。
具体实现:
1. 代理授权
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();
}
}
|
2. 动态代理
|
3. 行为型模式
1. Chain of responsibility
基本概念:
Chain of Responsibility(CoR) 是用一系列类(classes)试图处理一个请求request
简单分析:
Chain of Responsibility(CoR) 是用一系列类(classes)试图处理一个请求request,这些类之间是一个松散的耦合,唯一共同点是在他们之间传递request. 也就是说,来了一个请求,A类先处理,如果没有处理,就传递到B类处理,如果没有处理,就传递到C类处理,就这样象一个链条(chain)一样传递下去。
具体实现:
public interface Handler
{
public void handleRequest(Request request);
}
|
public class Request{
private String type;
public Request(String type){this.type=type;}
public String getType(){return type;}
public void execute(){
//request真正具体行为代码
}
}
|
public class ConcreteHandler implements Handler
{
private Handler successor;
public ConcreteHandler(Handler successor)
{
this.successor=successor;
}
public void handleRequest(Request request){
if (request instanceof HelpRequest){
//这里是处理Help的具体代码
}
else if (request instanceof PrintRequst)
{
request.execute();
}else
//传递到下一个
successor.handle(request);
}
}
}
|
2. Command (命令模式)
基本概念:
将来自客户端的请求传入一个对象,无需了解这个请求激活的动作或有关接受这个请求的处理细节。
这是一种两台机器之间通讯联系性质的模式,类似传统过程语言的 CallBack功能。
简单分析:
解耦了发送者和接受者之间联系。 发送者调用一个操作,接受者接受请求执行相应的动作,因为使用Command模式解耦,发送者无需知道接受者任何接口。
不少Command模式的代码都是针对图形界面的,它实际就是菜单命令,我们在一个下拉菜单选择一个命令时,然后会执行一些动作.
将这些命令封装成在一个类中,然后用户(调用者)再对这个类进行操作,这就是Command模式,换句话说,本来用户(调用者)是直接调用这些命令的,如菜单上打开文档(调用者),就直接指向打开文档的代码,使用Command模式,就是在这两者之间增加一个中间者,将这种直接关系拗断,同时两者之间都隔离,基本没有关系了.
显然这样做的好处是符合封装的特性,降低耦合度,Command是将对行为进行封装的典型模式,Factory是将创建进行封装的模式,
具体实现:
public interface Command {
public abstract void execute ( );
}
|
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 }
}
|
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;
}
}
|
public class TestCommand {
public static void main(String[] args) {
List queue = Producer.produceRequests();
for (Iterator it = queue.iterator(); it.hasNext(); )
//客户端直接调用execute方法,无需知道被调用者的其它更多类的方法名。
(Command)it.next()).execute();
|
2. Iterator (迭代模式)
基本概念:
Iterator模式可以顺序的访问一个聚集中的元素而不必暴露聚集的内部情况。
简单分析:
Iterator模式的优点是当(ConcreteTeacher)对象中有变化是,比如说同学出勤集合中有加入了新的同学,或减少同学时,这种改动对客户端是没有影响的。
具体实现:
public interface Iterator {
void first(); //第一个
void next(); //下一个
boolean isDone(); //是否点名完毕
Object currentItem(); //当前同学的出勤情况
}
|
一般都是使用匿名类来实现
public class ConcreteIterator implements Iterator{
private ConcreteTeacher teacher;
private int index = 0;
private int size = 0;
public ConcreteIterator(ConcreteTeacher teacher)
{
this.teacher = teacher;
size = teacher.getSize(); //得到同学的数目
index = 0;
}
public void first(){ //第一个
index = 0;
}
public void next(){ //下一个
if(index<size){
index++;
}
}
public boolean isDone(){ //是否点名完毕
return (index>=size);
}
public Object currentItem(){ //当前同学的出勤情况
return teacher.getElement(index);
}
}
|
public interface Teacher {
public Iterator createIterator(); //点名
}
|
public class ConcreteTeacher implements Teacher{
private Object[] present = {"张三来了","李四来了","王五没来"};
//同学出勤集合
public Iterator createIterator(){
return new ConcreteIterator(this); //新的点名
}
public Object getElement(int index){ //得到当前同学的出勤情况
if(index<present.length){
return present[index];
}
else{
return null;
}
}
public int getSize(){
return present.length; //得到同学出勤集合的大小,也就是说要知道班上有多少人
}
}
|
测试:
public class Test {
private Iterator it;
private Teacher teacher = new ConcreteTeacher();
public void operation(){
it = teacher.createIterator(); //老师开始点名
while(!it.isDone()){ //如果没点完
System.out.println(it.currentItem().toString()); //获得被点到同学的情况
it.next(); //点下一个
}
}
public static void main(String agrs[]){
Test test = new Test();
test.operation();
}
}
|
4.Interpreter (翻译模式)
基本概念:
定义语言的文法,并且建立一个解释器来解释该语言中的句子.
简单分析:
Interpreter似乎使用面不是很广,它描述了一个语言解释器是如何构成的,在实际应用中,我们可能很少去构造一个语言的文法
具体实现:
public interface AbstractExpression {
void interpret( Context context );
}
// AbstractExpression的具体实现分两种:终结符表达式和非终结符表达式: |
public interface AbstractExpression {
void interpret( Context context );
}
public interface Context {
}
public class NonterminalExpression implements AbstractExpression {
private AbstractExpression successor;
public void setSuccessor( AbstractExpression successor )
{ this.successor = successor; }
public AbstractExpression getSuccessor()
{return successor; }
public void interpret( Context context ) { } }
|
5.Mediator (中介者模式)
基本概念:
用一个中介对象来封装一系列关于对象交互行为.
简单分析:
各个对象之间的交互操作非常多;每个对象的行为操作都依赖彼此对方,修改一个对象的行为,同时会涉及到修改很多其他对象的行为,如果使用Mediator模式,可以使各个对象间的耦合松散,只需关心和Mediator的关系,使多对多的关系变成了一对多的关系,可以降低系统的复杂性,提高可修改扩展性.
Mediator模式在事件驱动类应用中比较多,例如界面设计GUI.;聊天,消息传递等,在聊天应用中,需要有一个MessageMediator,专门负责request/reponse之间任务的调节.
MVC是J2EE的一个基本模式,View Controller是一种Mediator,它是Jsp和服务器上应用程序间的Mediator.
具体实现:
public interface Mediator { }
|
public class ConcreteMediator implements Mediator
{
//假设当前有两个成员.
private ConcreteColleague1 colleague1 = new ConcreteColleague1(); private ConcreteColleague2 colleague2 = new ConcreteColleague2();
...
}
|
public class Colleague {
private Mediator mediator;
public Mediator getMediator() { return mediator; }
public void setMediator( Mediator mediator )
{
this.mediator = mediator;
}
}
public class ConcreteColleague1 { }
public class ConcreteColleague2 { }
|
6.Memento (备忘录模式)
基本概念:
memento是一个保存另外一个对象内部状态拷贝的对象.这样以后就可以将该对象恢复到原先保存的状态.
简单分析:
Jsp+Javabean中的应用,在Jsp应用中,我们通常有很多表单要求用户输入,比如用户注册,需要输入姓名和Email等, 如果一些表项用户没有填写或者填写错误,我们希望在用户按"提交Submit"后,通过Jsp程序检查,发现确实有未填写项目,则在该项目下红字显示警告或错误,同时,还要显示用户刚才已经输入的表项.
具体实现:
public class Originator
{
private int number;
private File file = null;
public Originator(){}
// 创建一个Memento
public Memento getMemento(){ return new Memento(this); }
// 恢复到原始值
public void setMemento(Memento m){
number = m.number;
file = m.file;
}
}
private class Memento implements java.io.Serializable{
private int number;
private File file = null;
public Memento( Originator o){
number = o.number;
file = o.file;
}
}
|
7.Observer (观察者模式)
基本概念:
对象发生变化,会立即通知订阅者。
简单分析:
Observer(观察者)模式是比较常用的一个模式,尤其在界面设计中应用广泛,而本站所关注的是Java在电子商务系统中应用,因此想从电子商务实例中分析Observer的应用.
虽然网上商店形式多样,每个站点有自己的特色,但也有其一般的共性,单就"商品的变化,以便及时通知订户"这一点,是很多网上商店共有的模式,这一模式类似Observer patern观察者模式.
具体的说,如果网上商店中商品在名称价格等方面有变化,如果系统能自动通知会员,将是网上商店区别传统商店的一大特色.这就需要在商品product中加入Observer这样角色,以便product细节发生变化时,Observer能自动观察到这种变化,并能进行及时的update或notify动作.
具体实现:
Java的API还为为我们提供现成的Observer接口Java.util.Observer. java.util.Observable
我们只要直接使用它就可以.
public class product extends Observable{
private String name;
private float price;
public String getName(){ return name;}
public void setName(String name)
{ this.name=name; //设置变化点
setChanged();
notifyObservers(name);
}
public float getPrice(){ return price;}
public void setPrice(float price){
this.price=price; //设置变化点
setChanged();
notifyObservers(new Float(price));
} //以下可以是数据库更新插入命令.
public void saveToDb(){//…..}
}
|
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);
}
}
}
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中我们可以来正式执行这段观察者程序
<jsp:useBean id="product" scope="session" class="Product" /> <jsp:setProperty name="product" property="*" />
<jsp:useBean id="nameobs" scope="session" class="NameObserver" /> <jsp:setProperty name="product" property="*" />
<jsp:useBean id="priceobs" scope="session" class="PriceObserver" /> <jsp:setProperty name="product" property="*" />
<% if (request.getParameter("save")!=null)
{
product.saveToDb();
out.println("产品数据变动保存! 并已经自动通知客户");
}
else{
//加入观察者
product.addObserver(nameobs);
product.addObserver(priceobs);
%>
//request.getRequestURI()是产生本jsp的程序名,就是自己调用自己 <form action="<%=request.getRequestURI()%>" method=post>
<input type=hidden name="save" value="1"> 产品名称:<input type=text name="name" > 产品价格:<input type=text name="price"> <input type=submit>
</form>
<%
}
%>
|
观察者的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);
}
}
|
8.State (状态模式)
基本概念:
不同的状态,不同的行为;或者说,每个状态有着相应的行为.
简单分析:
State模式在实际使用中比较多,适合"状态的切换".因为我们经常会使用If elseif else 进行状态切换, 如果针对状态的这样判断切换反复出现,我们就要联想到是否可以采取State模式了.
不只是根据状态,也有根据属性.如果某个对象的属性不同,对象的行为就不一样,这点在数据库系统中出现频率比较高,我们经常会在一个数据表的尾部,加上property属性含义的字段,用以标识记录中一些特殊性质的记录,这种属性的改变(切换)又是随时可能发生的,就有可能要使用State.
应用场景:
状态模式优点:
(1)封装转换过程,也就是转换规则
(2)枚举可能的状态,因此,需要事先确定状态种类。
1.银行帐户, 经常会在Open 状态和Close状态间转换.
1.经典的TcpConnection, Tcp的状态有创建侦听关闭三个,并且反复转换, 其创建侦听 关闭的具体行为不是简单一两句就能完成的,适合使用State
3.信箱POP帐号, 会有四种状态, start HaveUsername Authorized quit, 每个状态对应的行为应该是比较大的.适合使用State
4. 政府OA中,一个批文的状态有多种:未办;正在办理;正在批示;正在审核;已经完成等各种状态,使用状态机可以封装这个状态的变化规则,从而达到扩充状态时,不必涉及到状态的使用者。
具体实现:
State需要两种类型实体参与:
1.state manager 状态管理器, 就是开关, 如上面例子的Context实际就是一个state manager, 在state manager中有对状态的切换动作.
2. 用抽象类或接口实现的父类, 不同状态就是继承这个父类的不同子类.
状态抽象
public abstract class State
{
public abstract void handlepush(Context c);
public abstract void handlepull(Context c);
public abstract void getcolor();
}
|
状态具体实现
public class BlueState extends State
{
public void handlepush(Context c){
//根据push方法"如果是blue状态的切换到green" ;
c.setState(new GreenState());
}
public void handlepull(Context c){
//根据pull方法"如果是blue状态的切换到red" ;
c.setState(new RedState());
}
public abstract void getcolor(){ return (Color.blue)}
}
|
State manager
public class Context{
private Sate state=null;
//我们将原来的 Color state 改成了新建的State state;
//setState是用来改变state的状态使用setState实现状态的切换
pulic void setState(State state){ this.state=state;}
public void push(){
//状态的切换的细节部分,在本例中是颜色的变化,已经封装在子类的handlepush中实现,这里无需关心
state.handlepush(this);
//因为sample要使用state中的一个切换结果,使用getColor() Sample sample=new
Sample(state.getColor());
sample.operate();
}
public void pull(){
state.handlepull(this);
Sample2 sample2=new Sample2(state.getColor());
sample2.operate();
}
}
|
9.Strategy (策略模式)
基本概念:
Strategy策略模式是属于设计模式中对象行为型模式,主要是定义一系列的算法,把这些算法一个个封装成单独的类.
简单分析:
Stratrgy应用比较广泛,比如, 公司经营业务变化图, 可能有两种实现方式,一个是线条曲线,一个是框图(bar),这是两种算法,可以使用Strategy实现.
这里以字符串替代为例, 有一个文件,我们需要读取后,希望替代其中相应的变量,然后输出.关于替代其中变量的方法可能有多种方法,这取决于用户的要求,所以我们要准备几套变量字符替代方案.
应用场景:
Strategy适合下列场合:
1.以不同的格式保存文件;
2.以不同的算法压缩文件;
3.以不同的算法截获图象;
4.以不同的格式输出同样数据的图形,比如曲线或框图bar等
具体实现:
决策基类
public abstract class RepTempRule{
protected String oldString="";
public void setOldString(String oldString)
{ this.oldString=oldString; }
protected String newString="";
public String getNewString(){ return newString; }
public abstract void replace() throws Exception;
}
|
决策具体类:
public class RepTempRuleOne extends RepTempRule{
public void replace() throws Exception{
//replaceFirst是jdk1.4新特性
newString=oldString.replaceFirst("aaa", "bbbb")
System.out.println("this is replace one");
}
}
public class RepTempRuleTwo extends RepTempRule{
public void replace() throws Exception{
//replaceFirst是jdk1.4新特性
newString=oldString.replaceFirst("aaa", "cccc")
System.out.println("this is replace two");
}
}
|
算法解决类
public class RepTempRuleSolve
{
private RepTempRule strategy;
public RepTempRuleSolve(RepTempRule rule)
{ this.strategy=rule; }
public String getNewContext(Site site,String oldString)
{ return strategy.replace(site,oldString); }
public void changeAlgorithm(RepTempRule newAlgorithm)
{ strategy = newAlgorithm; }
}
|
测试代码:
public class test{
public void testReplace()
{
//使用第一套替代方案
RepTempRuleSolve solver=new RepTempRuleSolve(new RepTempRuleSimple()); solver.getNewContext(site,context);
//使用第二套
solver=new RepTempRuleSolve(new RepTempRuleTwo());
solver.getNewContext(site,context);
}
|
10.Template (模板模式)
基本概念:
定义一个操作中算法的骨架,将一些步骤的执行延迟到其子类中.
简单分析:
使用Java的抽象类时,就经常会使用到Template模式,因此Template模式使用很普遍.而且很容易理解和使用。
具体实现:
public abstract class Benchmark {
/** * 下面操作是我们希望在子类中完成 */
public abstract void benchmark();
/** * 重复执行benchmark次数 */
public final long repeat (int count) {
if (count <= 0) return 0;
else {
long startTime = System.currentTimeMillis();
for (int i = 0; i < count; i++)
benchmark();
long stopTime = System.currentTimeMillis();
return stopTime - startTime; }
}
}
|
public class MethodBenchmark extends Benchmark {
/** * 真正定义benchmark内容 */
public void benchmark() {
for (int i = 0; i < Integer.MAX_VALUE;i++){
System.out.printtln("i="+i); } } }
|
11.Visitor (访问者模式)
基本概念:
作用于某个对象群中各个对象的操作. 它可以使你在不改变这些对象本身的情况下,定义作用于这些对象的新操作.
在Java中,Visitor模式实际上是分离了collection结构中的元素和对这些元素进行操作的行为.
简单分析:
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());
}
|
这样做的缺点代码If else if 很繁琐.我们就可以使用Visitor模式解决它.
使用Visitor模式的前提使用访问者模式是对象群结构中(Collection) 中的对象类型很少改变。
在两个接口Visitor和Visitable中,确保Visitable很少变化,也就是说,确保不能老有新的Element元素类型加进来,可以变化的是访问者行为或操作,也就是Visitor的不同子类可以有多种,这样使用访问者模式最方便.
如果对象集合中的对象集合经常有变化, 那么不但Visitor实现要变化,Visistable也要增加相应行为,GOF建议是,不如在这些对象类中直接逐个定义操作,无需使用访问者设计模式。
具体实现:
Element定义一个可以接受访问的接口
public interface Visitable
{
public void accept(Visitor visitor);
}
|
接口具体实现
public class StringElement implements Visitable {
private String value;
public StringElement(String string) { value = string; }
public String getValue(){ return value; }
//定义accept的具体内容这里是很简单的一句调用
public void accept(Visitor visitor)
{
visitor.visitString(this);
}
}
public class FloatElement implements Visitable {
private Float value;
public FloatElement(Float value) { this.value = value; }
public Float getValue(){ return value; }
//定义accept的具体内容这里是很简单的一句调用
public void accept(Visitor visitor)
{
visitor.visitFloat(this);
}
}
|
访问者接口
public interface Visitor
{
public void visitString(StringElement stringE);
public void visitFloat(FloatElement floatE);
public void visitCollection(Collection collection);
}
|
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(StringElement stringE) {
System.out.println("'"+stringE.getValue()+"'");
stringE. accept(this);
}
public void visitFloat(FloatElement floatE){
System.out.println(floatE.getValue().toString()+"f");
floatE .accept(this);
}
}
|
测试
Visitor visitor = new ConcreteVisitor();
StringElement stringE = new StringElement("I am a String");
visitor.visitString(stringE);
Collection list = new ArrayList();
list.add(new StringElement("I am a String1"));
list.add(new StringElement("I am a String2"));
list.add(new FloatElement(new Float(12)));
list.add(new StringElement("I am a String3"));
visitor.visitCollection(list);
|