Wednesday, November 19, 2014

Series introduction of jBPM 6.x (1)-Use Email notification of human task

It is a very common use case where the task owner get notified if he does not start task in certain time, or he does not complete it in certain time. BPMS 6 or jBPM 6 provides an out of the box email notification feature. But the docs are very few. Here I use an example to show you how to use it.

PS: application server : jboss as 6.2



Overview

The sample has only one User task. This task must be finished in certain time.


Let's click the Notifications in properties panel.

Here we add 2 notifications: not-started and not-completed. The “Expires At” is the duration to fire the notification event. We input “1m” and “2m”. For test purpose, the notification event will be fired if the task is not started within 1 minute, and fired again if it is not completed within 2 minutes. In real cases, you can user “h” or “d” which means hours and days respectively.
The From, Reply To, and To are user names being used when sending Email. Input your user id in field “To”.
Subject and Body are texts you want for the email. You may use process variables and task variable when setting values for those fields. Here is my example for Body field.
<html>
        <body>
                <b>You have been assigned to a task (task-id ${taskId})</b><br>
                Important technical information that can be of use when working on it<br/>
                - process instance id - ${processInstanceId}<br/>
                - work item id - ${workItemId}<br/>
                
                <hr/>
                Here are some task variables available
                <ul>
                        <li>ActorId = ${doc['ActorId']}</li>
                        <li>GroupId = ${doc['GroupId']}</li>
                        <li>Comment = ${doc['Comment']}</li>
                </ul>
                <hr/>
                Here are all potential owners for this task
                <ul>
                $foreach{orgEntity : owners}
                        <li>Potential owner = ${orgEntity.id}</li>
                $end{}
                </ul>
                
                <i>Regards from jBPM team</i>
        </body>
</html>

Configure default Email session

This notification feature uses default Email session. We need to configure it properly in Jboss standalone.xml (or standalone-full.xml, depends on your favorable one).
Find the mail subsystem and update the jndi-name as follows.
<subsystem xmlns="urn:jboss:domain:mail:1.1">
<mail-session jndi-name="java:/mail/jbpmMailSession">
<smtp-server outbound-socket-binding-ref="mail-smtp"/>
</mail-session>
</subsystem>
Make sure the there is a socket-binding as follows.
<socket-binding-group name="standard-sockets" ... >
...
<outbound-socket-binding name="mail-smtp">
<remote-destination host="localhost" port="25"/>
</outbound-socket-binding>
</socket-binding-group>
If you are using other host for your SMTP server, you need to change the host and port accordingly. Generally, Linux host has a SMTP service on port 25 – that is why the above configuration works in most cases (at least on my RHEL6 machine and Maitai production server).

Configure user info

Case one : Default UserGroupCallBack-JAASUserGourpCallBack

You may wonder what email address the notification is sent to - we only specify the name of user, such as “***”. If you try to input a full email address, such as ***@***.com, an error will happen. I think it should support such case. But unfortunately, jBPM does not support a full email address when sending notifications. Then where does the user information come from? The following file “userinfo.properties” hide the answer.
Path:#{your web application }/WEB-INF/userinfo.properties
Open the file and add following lines to make the example work.
Administrators=ruhan@redhat.com:en-UK:Administrators:[*]
*=*@redhat.com:en-UK:*
maitai-dev-list=maitai-dev-list@redhat.com:en-UK:maitai-dev-list

Case two : Custom UserGroupCallBack

This case, we use usergourpcallback: JAASUserGourpCallBack, if you costume the UserGourpCallBack, You need to follow a few steps:

1 Implementation custom UserGroupCallBack

  Open the beans.xml file in “jboss-home/standalone/deployments/business-central.war/WEB-INF”, you will see:
<alternatives>...
<class>org.jbpm.kie.services.cdi.producer.JAASUserGroupInfoProducer</class>
</alternatives>
This line specify which UserGroupInfoProducer implementation is used. The default is JAASUserGroupInfoProducer.
There is a DefaultUserInfo which is responsible for providing user information. You just need to design your implementation and hook it here. For example,
public class CustomUserGroupInfoProducer extends JAASUserGroupInfoProducer {
  private UserInfo userInfo = new CustomUserInfo(true);
  @Override
  @ApplicationScoped
  @Produces
  public UserInfo produceUserInfo() {
    return userInfo;
  }
}
And then modify the beans.xml as below:
<alternatives>...
<class>your.package.CustomUserGroupInfoProducer</class>
</alternatives>
2 Implementation custom UserInfo
  For example,
public class CustomUserInfo implements UserInfo{
@Override
    public String getDisplayName(OrganizationalEntity entity) {
        String name = entity.getId();
        if (name.trim().equals("Administrator")) {
            return name;
        }
        if (entity instanceof User) {
        //your logic
        }
        return null;
    }

 @Override


        public Iterator<OrganizationalEntity>
    getMembersForGroup(Group group) {

            List<OrganizationalEntity> members = new
    ArrayList<OrganizationalEntity>();


            if (group.getId().equals("Administrators")) {

                List<String> memberList = new ArrayList();

                memberList.add("Administrator");

                members = Lists.transform(memberList, new
    Function<String, OrganizationalEntity>() {

                    @Override
                    public OrganizationalEntity apply(String memberUid)
    {

                        User user =
    TaskModelProvider.getFactory().newUser();

                        ((InternalOrganizationalEntity)
    user).setId(memberUid);
                       return user;

                    }

                });
                return members.iterator();
            }
       //your logic

}
  @Override
        public boolean hasEmail(Group group) {
          //
        }
 @Override


        public String getEmailForEntity(OrganizationalEntity entity) {
            String name = entity.getId();
            if (name.trim().equals("Administrator")) {
                return "email address";
            }
            if (entity instanceof User) {
                //your logic
            }
            return null;
        }

          @Override

       public String getLanguageForEntity(OrganizationalEntity entity)
    {

           return "en-UK";

        }
}
Need to be aware of is group:Administrators and user : Administrator, these entity must be handled,Because of them is default group or user for start process.(version jBPM 6.0.1 and BPMS 6.0.1) .
 
    















Monday, April 16, 2007

工作小结(2)
--- 通用数据推送
首先,介绍一下我通过xml对推送业务的定义。由于,我们的数据下载都是通过对某个具体网站数据的下载,
所以,我们下载的数据基本都是树状结构,如下图所示:

主要面对的问题:
1 树状结构的层数不确定
2 节点数不确定
比如说网站展示某产品,就有公司分类,公司,公司图片,产品分类,产品,产品图片等等。这里每一项都
是一个数据节点,属于不同节点的子节点。对于不同的业务就会有不同的节点数与层数。
首先,我通过对现有业务的分析总结一下每一个节点所要执行的动作。
1 从下载库中读出有效数据
2 插入对应的目标库中
3 查询下一级节点
这些是基本每一个节点所具有的属性(通用),下面是我们定义这些节点的xml。
source id="companies" interval="1000" name="company" istranstion="true" futrueid="ID" ismutheard="false">
这些是我们每个节点的一些设置信息。其中 isTranstion是指我们控制事务的粒度是从
这一级的节点开始控制的。isMuTheard是指,我们时候以这个为线索执行多线程(为了加快一下处理数度)。futrueid和interval是线程的休息,线程id号,线程间隔启动时间。
下面是对节点动作的描述
1 查询此节点的数据
query id="****" name="***" type="page" maxfield="ID">
其中id就是ibatis中对应的函数id(在这里面我们采用ibatis,具体的设置在后面详细描述)。name是来表明这个查询的含义,type和maxField主要是针对数据分页问题解决方法。
因为在下载数据中符合条件的数据经常达到几十万,如果不采取分页技术的话,会导致程序执行缓慢甚至导致崩溃。如果我们type指向page那么说明我的查询要求分页,maxFiled则是指我们是按那个字段来排序的。
2 插入更新操作数据
update id="**" name="**" type="insert" index="1">
这里有两个特殊的地方是type,我分了这么几种insert,update,procedure,transfer。前两就不需要解释了,procedure就是对存储过程的一种,transfer这个比较特殊主要是为了完成文件远程存储。最后这个index是非常关键的因为在对这个节点数据进行插入更新文件传输操作的时候这些动作的执行是有先后顺序的。
由于更新操作的前三种类型是与数据库相关的,主要就是id对应的ibatis中sql id 的调用。但是,transfer就比较特殊了。
update id="sender.transfer" name="transfer" type="transfer"
srcFile="#PHOTO_PATH#" tempFile="d:\\temp\\" targetFile="ftp://hexiaofeng:xiaofeng123@192.168.5.21:21/server/img/#IMAGEROOTPATH#/products/?imageTarget(#PHOTO_SEND_ID#)"
index="3"/>

其中srcfile是指下载文件的路径,#PHOTO_PATH#是指在存储文件路径的字段名,targetFile是指通过一定逻辑生成的目标路径名。

以上这两种动作就是我们所有数据节点的动作(也就是业务)。
下面介绍一下,我们对这些业务动作一些设置功能的配置。
1 fieldref name="WEB_×××" parentref="MAP_ID">
这个主要是针对外键引用的设置。
2 condition type="isNull" property="×××">
这个是条件设置,因为在执行一些动作的时候回需要满足一些条件才可以进行,比如两个指比较大小,判断是否为空等。这个条件嵌入到动作中完成对动作的限制。
3 statusconfig defaultvalue="0" value="1" statusfielder="SHOW_STATUS">
condition type="isChinese" property="COMP_NAME">
condition type="isChinese" property="DESCRIPTION">
statusconfig>
这个配置主要是为了一些特殊要求所设定的,比如说当数据满足一下条件是将SHOW_STATUS设置为0或者设置成1.
以下附上我对某个业务完整的描述。

并以图说明


Sunday, April 15, 2007

工作小结(1)
--- 通用数据推送
目前,在公司的一个重要工作内容就是做sprider和b2b数据内容的推送工作。
简单需求描述 公司主要的业务领域就是b2b的垂直搜索,我所在的数据组主要
负责下载联盟网站的b2b的信息,然后讲下载数据进行一定的处理推送到目标库
(线上运行库)中。
以下就是经过这段时间工作对公司业务理解的一个流程图。



那我主要负责的就是推送以及生成生成信用值。

好了,该说说自己的工作了,数据推送本身很简单,就是把a库中数据推送到b库中。但是由于下载数据本身、的不严谨,以及两个库中表结构页不由差异,同时还要求增量的推送。(还有很多小细节方面的东西,比如图片的推送,针对某些字段的简单处理,对一些标志位的设定等等 总之 由于业务的不同会产生众多的问题)。
如题所述公司要求的通用推送,通用两个字就是我工作的核心内容。
首先来说说我的解决问题的思路。
1 我们通过一个xml文件来定义整个推送的流程
2 然后通过我们的程序来执行定义好的推送流程
这样的话可以将我的java代码和我们面对的业务相分离,业务只和xml相关,做到通用这个字。

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