人在江湖

            BlogJava :: 首頁(yè) :: 聯(lián)系 :: 聚合  :: 管理
            82 Posts :: 10 Stories :: 169 Comments :: 0 Trackbacks

          基本概念

          FactoryMethod是一種創(chuàng)建性模式,它定義了一個(gè)創(chuàng)建對(duì)象的接口,但是卻讓子類來(lái)決定具體實(shí)例化哪一個(gè)類.當(dāng)一個(gè)類無(wú)法預(yù)料要?jiǎng)?chuàng)建哪種類的對(duì)象或是一個(gè)類需要由子類來(lái)指定創(chuàng)建的對(duì)象時(shí)我們就需要用到Factory Method 模式了.簡(jiǎn)單說(shuō)來(lái),Factory Method可以根據(jù)不同的條件產(chǎn)生不同的實(shí)例,當(dāng)然這些不同的實(shí)例通常是屬于相同的類型,具有共同的父類.Factory Method把創(chuàng)建這些實(shí)例的具體過(guò)程封裝起來(lái)了,簡(jiǎn)化了客戶端的應(yīng)用,也改善了程序的擴(kuò)展性,使得將來(lái)可以做最小的改動(dòng)就可以加入新的待創(chuàng)建的類. 通常我們將Factory Method作為一種標(biāo)準(zhǔn)的創(chuàng)建對(duì)象的方法,當(dāng)發(fā)現(xiàn)需要更多的靈活性的時(shí)候,就開(kāi)始考慮向其它創(chuàng)建型模式轉(zhuǎn)化

          簡(jiǎn)單分析

          圖1是Factory Method 模式的結(jié)構(gòu)圖,這里提供了一些術(shù)語(yǔ),讓我們可以進(jìn)行更方便的描述:

          1. Product: 需要?jiǎng)?chuàng)建的產(chǎn)品的抽象類.
          2. ConcreteProduct: Product的子類,一系列具體的產(chǎn)品.
          3. Creator: 抽象創(chuàng)建器接口,聲明返回Product類型對(duì)象的Factory Method.
          4. ConcreteCreator: 具體的創(chuàng)建器,重寫(xiě)Creator中的Factory Method,返回ConcreteProduct類型的實(shí)例.

          圖1: Factory Method 模式結(jié)構(gòu)

           

          由此可以清楚的看出這樣的平行對(duì)應(yīng)關(guān)系: Product <====> Creator ; ConreteProduct <====> ConreteCreator

          抽象產(chǎn)品對(duì)應(yīng)抽象創(chuàng)建器,具體產(chǎn)品對(duì)應(yīng)具體創(chuàng)建器.這樣做的好處是什么呢?為什么我們不直接用具體的產(chǎn)品和具體的創(chuàng)建器完成需求呢?實(shí)際上我們也可以這樣做.但通過(guò)Factory Method模式來(lái)完成,客戶(client)只需引用抽象的Product和Creater,對(duì)具體的ConcreteProduct和ConcreteCreator可以毫不關(guān)心,這樣做我們可以獲得額外的好處:

          • 首先客戶端可以統(tǒng)一從抽象創(chuàng)建器獲取產(chǎn)生的實(shí)例,Creator的作用將client和產(chǎn)品創(chuàng)建過(guò)程分離開(kāi)來(lái),客戶不用操心返回的是那一個(gè)具體的產(chǎn)品,也不用關(guān)心這些產(chǎn)品是如何創(chuàng)建的.同時(shí),ConcreteProduct也被隱藏在Product后面,ConreteProduct繼承了Product的所有屬性,并實(shí)現(xiàn)了Product中定義的抽象方法,按照J(rèn)ava中的對(duì)象造型(cast)原則,通過(guò)ConcreteCreator產(chǎn)生的ConcreteProduct可以自動(dòng)的上溯造型成Product.這樣一來(lái),實(shí)質(zhì)內(nèi)容不同的ConcreteProduct就可以在形式上統(tǒng)一為Product,通過(guò)Creator提供給client來(lái)訪問(wèn).
          • 其次,當(dāng)我們添加一個(gè)新的ConcreteCreator時(shí),由于Creator所提供的接口不變,客戶端程序不會(huì)有絲毫的改動(dòng),不會(huì)帶來(lái)動(dòng)一發(fā)而牽全身的災(zāi)難, 這就是良好封裝性的體現(xiàn).但如果直接用ConcreteProduct和ConcreteCreator兩個(gè)類是無(wú)論如何也做不到這點(diǎn)的. 優(yōu)良的面向?qū)ο笤O(shè)計(jì)鼓勵(lì)使用封裝(encapsulation)和委托(delegation),而Factory Method模式就是使用了封裝和委托的典型例子,這里封裝是通過(guò)抽象創(chuàng)建器Creator來(lái)體現(xiàn)的,而委托則是通過(guò)抽象創(chuàng)建器把創(chuàng)建對(duì)象的責(zé)任完全交給具體創(chuàng)建器ConcreteCreator來(lái)體現(xiàn)的.

          現(xiàn)在,請(qǐng)?jiān)倩仡^看看基本概念中的那段話,開(kāi)始也許覺(jué)得生澀難懂,現(xiàn)在是不是已經(jīng)明朗化了很多.

          下面讓我們看看在 Java 中如何實(shí)現(xiàn)Factory Method模式,進(jìn)一步加深對(duì)它的認(rèn)識(shí).

          具體實(shí)施

          先說(shuō)明一點(diǎn),用Factory Method模式創(chuàng)建對(duì)象并不一定會(huì)讓我們的代碼更短,實(shí)事上往往更長(zhǎng),我們也使用了更多的類,真正的目的在于這樣可以靈活的,有彈性的創(chuàng)建不確定的對(duì)象.而且,代碼的可重用性提高了,客戶端的應(yīng)用簡(jiǎn)化了,客戶程序的代碼會(huì)大大減少,變的更具可讀性.

          1. 標(biāo)準(zhǔn)實(shí)現(xiàn): 這里我采用Bruce Eckel 用來(lái)描述OO思想的經(jīng)典例子 Shape.這樣大家會(huì)比較熟悉一些.我完全按照?qǐng)D1中所定義的結(jié)構(gòu)寫(xiě)了下面的一段演示代碼.這段代碼的作用是創(chuàng)建不同的Shape實(shí)例,每個(gè)實(shí)例完成兩個(gè)操作:draw和erase.具體的創(chuàng)建過(guò)程委托?oShapeFactory來(lái)完成.

            1.a 首先定義一個(gè)抽象類Shape,定義兩個(gè)抽象的方法.

            abstract class Shape {
              // 勾畫(huà)shape
              public abstract void draw();
              // 擦去 shape
              public abstract void erase();
              public String name;
              public Shape(String aName){
                name = aName;
              }
            }

            1.b 定義 Shape的兩個(gè)子類: Circle, Square,實(shí)現(xiàn)Shape中定義的抽象方法

            // 圓形子類
            class Circle extends Shape {
              public void draw() {
                System.out.println("It will draw a circle.");
              }
              public void erase() {
                System.out.println("It will erase a circle."); 
              }
              // 構(gòu)造函數(shù)
              public Circle(String aName){
                super(aName);
              }
            }
            // 方形子類
            class Square extends Shape {
              public void draw() { 
                System.out.println("It will draw a square."); 
              }
              public void erase() { 
                System.out.println("It will erase a square."); 
              }
              // 構(gòu)造函數(shù)
              public Square(String aName){
                super(aName);
              }
            }
            

            1.c 定義抽象的創(chuàng)建器,anOperation調(diào)用factoryMethod創(chuàng)建一個(gè)對(duì)象,并對(duì)該對(duì)象進(jìn)行一系列操作.

            abstract class ShapeFactory {  
              protected abstract Shape factoryMethod(String aName);
              // 在anOperation中定義Shape的一系列行為
            public void anOperation(String aName){
                Shape s = factoryMethod(aName);
                System.out.println("The current shape is: " + s.name);
                s.draw();
                s.erase();
              }
            }

            1.d 定義與circle和square相對(duì)應(yīng)的兩個(gè)具體創(chuàng)建器CircleFactory,SquareFactory,實(shí)現(xiàn)父類的methodFactory方法

            // 定義返回 circle 實(shí)例的 CircleFactory
            class CircleFactory extends ShapeFactory {
              // 重載factoryMethod方法,返回Circle對(duì)象
              protected Shape factoryMethod(String aName) {
                return new Circle(aName + " (created by CircleFactory)");
              }
            }
              
            // 定義返回 Square 實(shí)例的 SquareFactory
            class SquareFactory extends ShapeFactory {
              // 重載factoryMethod方法,返回Square對(duì)象
            protected Shape factoryMethod(String aName) {
                return new Square(aName + " (created by SquareFactory)");
              }
            }
            

            1.e 測(cè)試類:請(qǐng)注意這個(gè)客戶端程序多么簡(jiǎn)潔,既沒(méi)有羅嗦的條件判斷語(yǔ)句,也無(wú)需關(guān)心ConcreteProduct和ConcreteCreator的細(xì)節(jié)(因?yàn)檫@里我用anOperation封裝了Product里的兩個(gè)方法,所以連Product的影子也沒(méi)看見(jiàn),當(dāng)然把Product里方法的具體調(diào)用放到客戶程序中也是不錯(cuò)的).

            class Main {
              public static void main(String[] args){
                ShapeFactory sf1 = new SquareFactory(); 
                ShapeFactory sf2 = new CircleFactory();
                sf1.anOperation("Shape one");
                sf2.anOperation("Shape two");
              }
            }  
            

            運(yùn)行結(jié)果如下:

            The current shape is: Shape one (created by SquareFactory)

            It will draw a square.

            It will erase a square.

            The current shape is: Shape two (created by CircleFactory)

            It will draw a circle.

            It will erase a circle.

          2. 參數(shù)化的Factory Method: 這種方式依靠指定的參數(shù)作為標(biāo)志來(lái)創(chuàng)建對(duì)應(yīng)的實(shí)例,這是很常見(jiàn)的一種辦法.比如JFC中的BorderFactory就是個(gè)很不錯(cuò)的例子. 以下的這個(gè)例子是用字符串作為標(biāo)記來(lái)進(jìn)行判斷的,如果參數(shù)的類型也不一樣,那就可以用到過(guò)載函數(shù)來(lái)解決這個(gè)問(wèn)題,定義一系列參數(shù)和方法體不同的同名函數(shù),這里java.util.Calendar.getInstance()又是個(gè)極好的例子.參數(shù)化的創(chuàng)建方式克服了Factory Method模式一個(gè)最顯著的缺陷,就是當(dāng)具體產(chǎn)品比較多時(shí),我們不得不也建立一系列與之對(duì)應(yīng)的具體構(gòu)造器. 但是在客戶端我們必須指定參數(shù)來(lái)決定要?jiǎng)?chuàng)建哪一個(gè)類.

            2.a 我們?cè)诘谝环N方法的基礎(chǔ)上進(jìn)行修改,首先自定義一個(gè)的異常,這樣當(dāng)傳入不正確的參數(shù)時(shí)可以得到更明顯的報(bào)錯(cuò)信息.

            class NoThisShape extends Exception {
              public NoThisShape(String aName) {
                super(aName);
              }
            }
            

            2.b去掉了ShapeFactory的兩個(gè)子類,改為由ShapeFactory直接負(fù)責(zé)實(shí)例的創(chuàng)建. ShapeFactory自己變成一個(gè)具體的創(chuàng)建器,直接用參數(shù)化的方法實(shí)現(xiàn)factoryMethod返回多種對(duì)象.

            abstract class ShapeFactory {  
              private static Shape s;
              private ShapeFactory() {}
                
              static Shape factoryMethod(String aName, String aType) throws NoThisShape{
                if (aType.compareTo("square")==0)
                  return new Square(aName);
                else if (aType.compareTo("circle")==0)
                  return new Circle(aName);
                else throw new NoThisShape(aType);  
              }
              
              // 在anOperation中定義Shape的一系列行為
              static void anOperation(String aName, String aType) throws NoThisShape{
                s = factoryMethod(aName, aType);
                System.out.println("The current shape is: " + s.name);
                s.draw();
                s.erase();
              }
            }
            

            2.c 測(cè)試類:這里客戶端必須指定參數(shù)來(lái)決定具體創(chuàng)建哪個(gè)類.這個(gè)例子里的anOperation是靜態(tài)函數(shù),可以直接引用.

            class Main {
              public static void main(String[] args) throws NoThisShape{
                ShapeFactory.anOperation("Shape one","circle");
                ShapeFactory.anOperation("Shape two","square");
                ShapeFactory.anOperation("Shape three", "delta");
              }
            }
            

            運(yùn)行結(jié)果如下:

            The current shape is: Shape one
            It will draw a circle.
            It will erase a circle.
            The current shape is: Shape two
            It will draw a square.
            It will erase a square.
            Exception in thread "main" NoThisShape: delta
                    at ShapeFactory.factoryMethod(ShapeFactory.java:10)
                    at ShapeFactory.anOperation(ShapeFactory.java:15)
                    at Main.main(Main.java:5)
            
          3. 動(dòng)態(tài)裝載機(jī)制:

            有的時(shí)候我們會(huì)把ConcreteProduct的實(shí)例傳給創(chuàng)建器作為參數(shù),這種情況下,如果在創(chuàng)建器里完成創(chuàng)建過(guò)程,就必須判斷參數(shù)的具體類型(用instanceof),然后才能產(chǎn)生相應(yīng)的實(shí)例,那么比較好的做法是利用Java的動(dòng)態(tài)裝載機(jī)制來(lái)完成這件事.比如:

            我們得到一個(gè)Shape的子類s,但不知道具體是那個(gè)子類,就可以利用Class類自帶的方法newInstance()得到實(shí)例

            return (Shape)s.getClass().newInstance();

            這種方法有興趣得讀者可以自己嘗試,限于篇幅,不寫(xiě)具體代碼出來(lái)了.

          posted on 2011-02-13 11:33 人在江湖 閱讀(1445) 評(píng)論(0)  編輯  收藏 所屬分類: design pattern
          主站蜘蛛池模板: 阜平县| 湘潭市| 隆昌县| 罗源县| 北海市| 林周县| 呼伦贝尔市| 且末县| 出国| 崇州市| 茂名市| 南京市| 呼伦贝尔市| 喀什市| 枣阳市| 屏东市| 灵川县| 沈阳市| 宜州市| 博客| 莲花县| 全州县| 阜康市| 太仆寺旗| 三河市| 佛冈县| 思南县| 阿勒泰市| 武强县| 抚顺县| 鸡西市| 江西省| 尤溪县| 萍乡市| 长兴县| 新营市| 江华| 平昌县| 河源市| 绥滨县| 环江|