Monday, October 16, 2006

设计模式之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模式的应用.

c#的proxy模式



using System;

namespace DoFactory.GangOfFour.Proxy.Structural
{

// MainApp test application

class MainApp
{
static void Main()
{
// Create proxy and request a service
Proxy proxy = new Proxy();
proxy.Request();

// Wait for user
Console.Read();
}
}

// "Subject"

abstract class Subject
{
public abstract void Request();
}

// "RealSubject"

class RealSubject : Subject
{
public override void Request()
{
Console.WriteLine("Called RealSubject.Request()");
}
}

// "Proxy"

class Proxy : Subject
{
RealSubject realSubject;

public override void Request()
{
// Use 'lazy initialization'
if (realSubject == null)
{
realSubject = new RealSubject();
}

realSubject.Request();
}
}
}
------------------------------------------------
In computer programming, a proxy pattern is a software design pattern.

A proxy, in its most general form, is a class functioning as an interface to another thing. The other thing could be anything, a network connection, a large object in memory, a file, or other resource that is expensive or impossible to duplicate.

A well-known example of the proxy pattern is a reference counting pointer object, also known as an auto pointer.

The proxy pattern can be used in situations where multiple copies of a complex object must exist. In order to reduce the application's memory footprint in such situations, one instance of the complex object is created, and multiple proxy objects are created, all of which contain a reference to the single original complex object. Any operations performed on the proxies are forwarded to the original object. Once all instances of the proxy are out of scope, the complex object's memory may be deallocated.

Types of Proxy Pattern:
Remote Proxy: Provides a reference to an object located in a different address space on the same or different machine.
Virtual Proxy: Allows the creation of a memory intensive object on demand. The object will not be created until it is really needed.
Copy-On-Write Proxy: Defers copying (cloning) a target object until required by client actions. Really a form of virtual proxy.
Protection (Access) Proxy: Provides different clients with different levels of access to a target object.
Cache Proxy: Provides temporary storage of the results of expensive target operations so that multiple clients can share the results.
Firewall Proxy: Protects targets from bad clients (or vice versa).
Synchronization Proxy: Provides multiple accesses to a target object.
Smart Reference Proxy: Provides additional actions whenever a target object is referenced, such as counting the number of references to the object.

Examples
[edit]

Java

The following Java example illustrates the "Virtual Proxy" type explained above. The output is:
Loading HiRes_10MB_Photo1
Displaying HiRes_10MB_Photo1
Loading HiRes_10MB_Photo2
Displaying HiRes_10MB_Photo2
Displaying HiRes_10MB_Photo1
import java.util.*;

interface Image {
public void displayImage();
}

class RealImage implements Image {
private String filename;
public RealImage(String filename) {
this.filename = filename;
System.out.println("Loading "+filename);
}
public void displayImage() { System.out.println("Displaying "+filename); }
}

class ProxyImage implements Image {
private String filename;
private RealImage image;

public ProxyImage(String filename) { this.filename = filename; }
public void displayImage() {
if (image == null) {
image = new RealImage(filename); // load only on demand
}
image.displayImage();
}
}

class ProxyExample {
public static void main(String[] args) {
ArrayList<> images = new ArrayList<>();
images.add( new ProxyImage("HiRes_10MB_Photo1") );
images.add( new ProxyImage("HiRes_10MB_Photo2") );
images.add( new ProxyImage("HiRes_10MB_Photo3") );

images.get(0).displayImage(); // loading necessary
images.get(1).displayImage(); // loading necessary
images.get(0).displayImage(); // no loading necessary; already done
// the third image will never be loaded - time saved!
}
}

C#
[edit]

Virtual Proxy Example
using System;
using System.Collections;

namespace ConsoleApplicationTest.FundamentalPatterns.VirtualProxyPattern
{
public interface IImage{
void DisplayImage();
}

public class RealImage : IImage{
private string filename;
public RealImage(string filename)
{
this.filename = filename;
Console.WriteLine("Loading "+filename);
}
public void DisplayImage() { Console.WriteLine("Displaying "+filename); }

}

public class ProxyImage : IImage {
private string filename;
private RealImage image;

public ProxyImage(string filename) { this.filename = filename; }
public void DisplayImage(){
if (image == null) image = new RealImage(filename); // load only on demand
image.DisplayImage();
}
}

class ProxyExample
{
[STAThread]
public static void Main(string[] args)
{
ArrayList images = new ArrayList();
images.Add( new ProxyImage("HiRes_10MB_Photo1") );
images.Add( new ProxyImage("HiRes_10MB_Photo2") );
images.Add( new ProxyImage("HiRes_10MB_Photo3") );

((IImage)images[0]).DisplayImage(); // loading necessary
((IImage)images[1]).DisplayImage(); // loading necessary
((IImage)images[0]).DisplayImage(); // no loading necessary; already done
// the third image will never be loaded - time saved!

Console.Read();
}
}

}
[edit]

Protection Proxy Example
The real client stores an Account Number. Only the users who know the valid password can access this Account Number.
Real client is protected by a proxy who knows the password.
If a user wants to get an Account Number, first the proxy asks him/here to authenticate itself. If the user
entered a correct password the proxy will call the real client and pass the Account Number to the user.

using System;

namespace ConsoleApplicationTest.FundamentalPatterns.ProtectionProxyPattern
{
public interface IClient{
string GetAccountNo();
}

public class RealClient : IClient{
private string accountNo="AB-111111";
public RealClient(){
Console.WriteLine("RealClient: Initialized");
}
public string GetAccountNo(){
Console.WriteLine("RealClient's AccountNo: " + accountNo);
return accountNo;
}
}


public class ProtectionProxy : IClient
{
private string password=null; //password to get secret
RealClient client=null;

public ProtectionProxy(string pwd)
{
Console.WriteLine("ProtectionProxy: Initialized");
password=pwd;
client=new RealClient();
}

///
/// Authenticate the user and return the Account Number
///

///
public String GetAccountNo()
{
Console.WriteLine("Password: ");
string tmpPwd= Console.ReadLine();

if(tmpPwd == password)
{
return client.GetAccountNo();
}
else
{
Console.WriteLine("ProtectionProxy: Illegal password!");
return "";
}
}
}

class ProtectionProxyExample
{
[STAThread]
public static void Main(string[] args){

IClient client =new ProtectionProxy("thePassword");
Console.WriteLine("main received: "+client.GetAccountNo());
Console.WriteLine("main received: "+client.GetAccountNo());

Console.Read();
}
}
}
设计模式之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方法。

c#的Adapter模式
using System;

namespace DoFactory.GangOfFour.Adapter.Structural
{

// Mainapp test application

class MainApp
{
static void Main()
{
// Create adapter and place a request
Target target = new Adapter();
target.Request();

// Wait for user
Console.Read();
}
}

// "Target"

class Target
{
public virtual void Request()
{
Console.WriteLine("Called Target Request()");
}
}

// "Adapter"

class Adapter : Target
{
private Adaptee adaptee = new Adaptee();

public override void Request()
{
// Possibly do some other work
// and then call SpecificRequest
adaptee.SpecificRequest();
}
}

// "Adaptee"

class Adaptee
{
public void SpecificRequest()
{
Console.WriteLine("Called SpecificRequest()");
}
}
}

---------------------------------------------------------------------------------------------

In computer programming, the adapter design pattern (sometimes referred to as the wrapper pattern or simply a wrapper) 'adapts' one interface for a class into one that a client expects. An adapter allows classes to work together that normally could not because of incompatible interfaces by wrapping its own interface around that of an already existing class.
There are two types of adapter patterns:
(本人英语也不怎么样,翻译了还不如不翻译呢 呵呵)
The Object Adapter pattern - In this type of adapter pattern the adapter contains an instance of the class it wraps. In this situation the adapter makes calls to a physical instance of the wrapped object.

The Class Adapter pattern - This type of adapter uses multiple inheritance to achieve its goal. The adapter is created inheriting interfaces from both the interface that is expected and the interface that is pre-existing. The Object Adapter pattern is more often used as some popular languages, such as Java, do not support true multiple inheritance as the designers of these languages consider it a dangerous practice.


The adapter pattern is useful in situations where an already existing class provides some or all of the services you need but does not use the interface you need. A good real life example is an adapter that converts the interface of a Document Object Model of an XML document into a tree structure that can be displayed. A link to a tutorial that uses the adapter design pattern is listed in the links below.
[edit]

Sample - Object Adapter
/**
* Java code sample
*/

interface Stack
{
void push (Object o);
Object pop ();
Object top ();
}

/* DoubleLinkedList */
class DList
{
public void insert (DNode pos, Object o) { ... }
public void remove (DNode pos, Object o) { ... }

public void insertHead (Object o) { ... }
public void insertTail (Object o) { ... }

public Object removeHead () { ... }
public Object removeTail () { ... }

public Object getHead () { ... }
public Object getTail () { ... }
}

/* Adapt DList class to Stack interface */
class DListStack implements Stack
{
private DList _dlist;

public DListStack() { _dlist = new DList(); }
public void push (Object o) {
_dlist.insertTail (o);
}

public Object pop () {
return _dlist.removeTail ();
}

public Object top () {
return _dlist.getTail ();
}
}
[edit]

Sample - Class Adaptor1
/**
* Java code sample
*/

interface Stack
{
void push (Object);
Object pop ();
Object top ();
}

/* DoubleLinkedList */
class DList
{
public void insert (DNode pos, Object o) { ... }
public void remove (DNode pos, Object o) { ... }

public void insertHead (Object o) { ... }
public void insertTail (Object o) { ... }

public Object removeHead () { ... }
public Object removeTail () { ... }

public Object getHead () { ... }
public Object getTail () { ... }
}

/* Adapt DList class to Stack interface */
class DListImpStack extends DList implements Stack
{
public void push (Object o) {
insertTail (o);
}

public Object pop () {
return removeTail ();
}

public Object top () {
return getTail ();
}
}

Sunday, October 15, 2006

设计模式之Prototype(原型)

一 java Prototupe

原型模式定义:
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象.

Prototype模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节,工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建。

如何使用?
因为Java中的提供clone()方法来实现对象的克隆,所以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;
  }
}


有个具体实现(ConcretePrototype):
public class SoupSpoon extends AbstractSpoon
{
  public SoupSpoon()
  {
    setSpoonName("Soup Spoon");
  }
}




调用Prototype模式很简单:

AbstractSpoon spoon = new SoupSpoon();
AbstractSpoon spoon2 = spoon.clone();

当然也可以结合工厂模式来创建AbstractSpoon实例。

在Java中Prototype模式变成clone()方法的使用,由于Java的纯洁的面向对象特性,使得在Java中使用设计模式变得很自然,两者已经几乎是浑然一体了。这反映在很多模式上,如Interator遍历模式。

二 c# Prototype



using System;

namespace DoFactory.GangOfFour.Prototype.Structural
{

// MainApp test application

class MainApp
{

static void Main()
{
// Create two instances and clone each

ConcretePrototype1 p1 = new ConcretePrototype1("I");
ConcretePrototype1 c1 = (ConcretePrototype1)p1.Clone();
Console.WriteLine ("Cloned: {0}", c1.Id);

ConcretePrototype2 p2 = new ConcretePrototype2("II");
ConcretePrototype2 c2 = (ConcretePrototype2)p2.Clone();
Console.WriteLine ("Cloned: {0}", c2.Id);

// Wait for user
Console.Read();
}
}

// "Prototype"

abstract class Prototype
{
private string id;

// Constructor
public Prototype(string id)
{
this.id = id;
}

// Property
public string Id
{
get{ return id; }
}

public abstract Prototype Clone();
}

// "ConcretePrototype1"

class ConcretePrototype1 : Prototype
{
// Constructor
public ConcretePrototype1(string id) : base(id)
{
}

public override Prototype Clone()
{
// Shallow copy
return (Prototype)this.MemberwiseClone();
}
}

// "ConcretePrototype2"

class ConcretePrototype2 : Prototype
{
// Constructor
public ConcretePrototype2(string id) : base(id)
{
}

public override Prototype Clone()
{
// Shallow copy
return (Prototype)this.MemberwiseClone();
}
}
}
设计模式之Builder

Builder模式定义:
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示.
Builder模式是一步一步创建一个复杂的对象,它允许用户可以只通过指定复杂对象的类型和内容就可以构建它们.用户不知道内部的具体构建细节.Builder模式是非常类似抽象工厂模式,细微的区别大概只有在反复使用中才能体会到.
为何使用?
是为了将构建复杂对象的过程和它的部件解耦.注意: 是解耦过程和部件.

因为一个复杂的对象,不但有很多大量组成部分,如汽车,有很多部件:车轮 方向盘 发动机还有各种小零件等等,部件很多,但远不止这些,如何将这些部件装配成一辆汽车,这个装配过程也很复杂(需要很好的组装技术),Builder模式就是为了将部件和组装过程分开.

如何使用?
首先假设一个复杂对象是由多个部件组成的,Builder模式是把复杂对象的创建和部件的创建分别开来,分别用Builder类和Director类来表示.
如何使用?
首先假设一个复杂对象是由多个部件组成的,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 ) {
    this.builder = builder;
  }
  // 将部件partA partB partC最后组成复杂对象
  //这里是将车轮 方向盘和发动机组装成汽车的过程
  public void construct() {
    builder.buildPartA();
    builder.buildPartB();
    builder.buildPartC();

  }

}


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 );

director.construct();
Product product = builder.getResult();

Builder模式的应用
在Java实际使用中,我们经常用到"池"(Pool)的概念,当资源提供者无法提供足够的资源,并且这些资源需要被很多用户反复共享时,就需要使用池.
"池"实际是一段内存,当池中有一些复杂的资源的"断肢"(比如数据库的连接池,也许有时一个连接会中断),如果循环再利用这些"断肢",将提高内存使用效率,提高池的性能.修改Builder模式中Director类使之能诊断"断肢"断在哪个部件上,再修复这个部件.

下面是c#中的Builder模式




using System;
using System.Collections;

namespace DoFactory.GangOfFour.Builder.Structural
{
// MainApp test application

public class MainApp
{
public static void Main()
{
// Create director and builders
Director director = new Director();

Builder b1 = new ConcreteBuilder1();
Builder b2 = new ConcreteBuilder2();

// Construct two products
director.Construct(b1);
Product p1 = b1.GetResult();
p1.Show();

director.Construct(b2);
Product p2 = b2.GetResult();
p2.Show();

// Wait for user
Console.Read();
}
}

// "Director"

class Director
{
// Builder uses a complex series of steps
public void Construct(Builder builder)
{
builder.BuildPartA();
builder.BuildPartB();
}
}

// "Builder"

abstract class Builder
{
public abstract void BuildPartA();
public abstract void BuildPartB();
public abstract Product GetResult();
}

// "ConcreteBuilder1"

class ConcreteBuilder1 : Builder
{
private Product product = new Product();

public override void BuildPartA()
{
product.Add("PartA");
}

public override void BuildPartB()
{
product.Add("PartB");
}

public override Product GetResult()
{
return product;
}
}

// "ConcreteBuilder2"

class ConcreteBuilder2 : Builder
{
private Product product = new Product();

public override void BuildPartA()
{
product.Add("PartX");
}

public override void BuildPartB()
{
product.Add("PartY");
}

public override Product GetResult()
{
return product;
}
}

// "Product"

class Product
{
ArrayList parts = new ArrayList();

public void Add(string part)
{
parts.Add(part);
}

public void Show()
{
Console.WriteLine("\nProduct Parts -------");
foreach (string part in parts)
Console.WriteLine(part);
}
}
}

Friday, September 29, 2006

先来学习学习webwork吧呵呵
这是从webwork官方抄来的图

小弟浅见,webwork的特点在几个方面
1 将webwork和xwork分离
2 有拦截器
3 有容器的东西,现在可以把容器的工作放到spring中解决了,在这个架构中的所有对象(action,result,interceptor等等)都是通过ObjectFactory创建的.ObjectFactory是可插入,因此可以和Spring,Pico这样的框架整合的很好.如果需要,Webwork中的对象也可以由你自己提供的ObjectFactory来创建.
4 webwork中的model是双向的,然后通过spring来注入就更灵活了

现在,搞不清楚的问题有webwork和xwork分离的问题,它们是着么分离的,它们个干点什么事情,将webwork应用到
桌面应用该怎么作?
拦截器的实现怎么搞?

Thursday, September 28, 2006

RP指数--.前两天刚换工作,马上开始加班.55,好累啊! 什么样的工作才能让人舒服一点.
闲话少说,开始学习.
一来公司就接触到了一个复杂的架构.开始学习新的架构,Sitemesh+FreeMark+webwork+Spring+Hibernate


sitemesh-web布局器,FreeMark-面板语言,webwork-web框架,Sping-轻量级容器,Hibernate-数据持久层(ORM).

现抛开sitemesh,FreeMark不讲.我们从其他三个开始学习,嘿嘿!!
webwork分为webwork和xwork现在2.2以后默认的容器已经是Spring了,我们用Spring来做webwork的action的管理和服务类的,Hibernate的Session和事务的管理.