Author: Anders小明

產(chǎn)品化和客戶化兩個(gè)詞語是一個(gè)對孿生兄弟,一個(gè)沒有強(qiáng)大客戶化能力的軟件是不能稱為產(chǎn)品化的軟件.產(chǎn)品化也就意味著軟件公司的開發(fā)至少分為兩個(gè)不同性質(zhì)的角色:一個(gè)負(fù)責(zé)產(chǎn)品化,一個(gè)負(fù)責(zé)客戶化.除去傳統(tǒng)的參數(shù)化, 繼承以及plugin技術(shù)外,aspectJ為我們提供另一種能力.

如前所述, 公司有兩個(gè)不同角色的團(tuán)隊(duì)在開發(fā)和維護(hù)—研發(fā)組和項(xiàng)目組,更進(jìn)一步,本文所說適應(yīng)如下的開發(fā)團(tuán)隊(duì)結(jié)構(gòu):
一個(gè)研發(fā)組---多個(gè)項(xiàng)目組, 每個(gè)項(xiàng)目組需要定制化的東西各自不盡相同. 為了保證幾個(gè)團(tuán)隊(duì)的工作的相對獨(dú)立性,公司希望項(xiàng)目組在做客戶化過程中不改研發(fā)團(tuán)隊(duì)所開發(fā)的代碼,應(yīng)用了已知的方法:參數(shù)化, 繼承以及plugin技術(shù)外.

然而這些技術(shù)并沒有覆蓋到所有的問題, 問題描述如下
example.JPG

經(jīng)過分析發(fā)現(xiàn)需要客戶化的地方,是需要在Root類里加一個(gè)propertyA, 這個(gè)時(shí)候基于已有的能力,我們可以做的選擇是要么項(xiàng)目組A在修改Root類,要么修改每個(gè)葉子類(LeafA....), 顯然修改root類是一個(gè)不錯的選擇. 然而槽糕的是,項(xiàng)目組B需要是添加另一個(gè)properyB,于是項(xiàng)目組B也這么做了. 對于項(xiàng)目組A和B來說這樣做是沒有太多問題的.
但對于研發(fā)組來說問題卻是很嚴(yán)重的, 由于研發(fā)組需要從項(xiàng)目回收代碼,以便吸取優(yōu)點(diǎn)改進(jìn)軟件,研發(fā)組不得不小心的維護(hù)各個(gè)版本,并處理項(xiàng)目組所做的修改.而同時(shí)項(xiàng)目組獲取了修改研發(fā)組代碼的權(quán)力,處于時(shí)間的壓力,會產(chǎn)生不符合系統(tǒng)結(jié)構(gòu)約束的代碼.
很明顯,這樣的工作方式不利我們的工作,我們需要一個(gè)新的思路.

在正式提出新方法前,我想先介紹一下微軟的DSL Tools以及C# 2.0,各位客官,還請耐心的看一下。
DSL Tools是微軟開發(fā)的Domain Specification Language,目的是用更接近于業(yè)務(wù)的語言模型來描述業(yè)務(wù)問題,并生成運(yùn)行代碼。工具允許其用戶定義各自的Domain Language,目標(biāo)是軟件工廠。DSL Tools面臨的一個(gè)問題(也是公司目前面臨),定義好的DSL以及生成代碼,不可避免的出現(xiàn)不同客戶存在細(xì)微的差異性,DSL Tools必須很好的解決它,事實(shí)上DSL Tools的確做到了——它基于是C#2.0 Partial Class技術(shù)。(有關(guān)于DSL Tools的更多信息可以參見MSDN)。
簡單說的Partial Class的其實(shí)就是一個(gè)類的結(jié)構(gòu)信息可以被存儲在多個(gè)文件中,以下是個(gè)例子:
 
file1.cs(微軟的一個(gè)文件可以定義多個(gè)類,所有其文件名不需要同類名):
public partial class CAgent {
    private String name;
    Property String Name{
        get{
            return name;
        }
        set{
            this.name = name;
        }
    }
    public void doSomethingA(){
        ....
    }
}
 
file2.cs
public partial class CAgent {
    private String code;
    Property String Code{
        get{
            return code;
        }
        set{
            this.code= code;
        }
    }
    public void doSomethingB(){
        ....
    }
}
雖然CAgent類被分解為兩個(gè)文件,不過對于調(diào)用者類說,CAgent具體name以及code兩個(gè)property,以及doSomethingA和doSomethingB兩個(gè)方法。
現(xiàn)在你可以想象DSL Tools是如何解決客戶化這個(gè)問題的了!
DSL Tools生成的所有class都是partial class,當(dāng)對于CAgent(或者其它類)有需要客戶化時(shí),需要做的只是添加一個(gè)新的文件,用同樣的類名加上一個(gè)partial的關(guān)鍵字,
public partial class CAgent 就可以添加新的屬性和方法(如同我展示的那樣,想象一下file1.cs是研發(fā)維護(hù),而file2.cs是項(xiàng)目維護(hù)的)。也就說,DSL的研發(fā)團(tuán)隊(duì)和客戶化團(tuán)隊(duì)的工作時(shí)相對獨(dú)立的。
 
那么你一定會說,這個(gè)微軟的C#,不幸的是公司用的是Java,java的語言規(guī)范不支持這樣的能力。
是的,你說的很對,Java語言本身是不支持,不過我們有這樣的手段——那就是AspectJ。
實(shí)際上AspectJ面世很久了,我接觸它至少有一年半了,不過支持一直沒有想到這樣的用法,直到我研究了DSL Tools(我相信很多道理是相通的,我們目前面臨的問題硅谷的那幫人也一樣面對過)
下面是一個(gè)簡單的例子:
OnType.java:
public class OnType {
 public void doAction(){
  System.out.println("OnType.doAction");
 }
}
 
OnTypeAspect.aj
public aspect OnTypeAspect {
 public String OnType.name = "OnTypeAspect";
 
 public void OnType.doActionAspect(){
  System.out.println("OnType.doActionAspect and name is "+ name);
 }
}
 
Main2.aj
public class Main2 {
 public static void main(String[] args){
  OnType onType = new OnType();
  onType.doActionAspect();
 }
}
 
如你所見,程序是可以完整的運(yùn)行。你的控制臺打出了:OnType.doActionAspect and name is OnTypeAspect
不僅如此,AspectJ可以為已有的類添加新的接口,就像C#的partial class做的那樣。
 
我以為aspectj是實(shí)現(xiàn)我們的目標(biāo)的一個(gè)捷徑,并且它是經(jīng)過實(shí)踐的,我不相信aspectj和微軟的那幫人會閑著沒事來支持這樣的技術(shù)。BTW,我聽說weblogic的jrockit準(zhǔn)備在虛擬機(jī)一級上支持aspectj, 那么或許剩下的工作就是如何管理使用AspectJ了.