Java-设计模式之建造模式

建造模式

引子

前几天陪朋友去装机店攒了一台电脑,看着装机工在那里熟练的装配着机器,不禁想起来了培训时讲到的建造模式。作为装机工,他们不用管你用的 CPU 是 Intel 还是 AMD,也不管你的显卡是 2000 千大元还是白送的,都能三下五除二的装配在一起——一台 PC 就诞生了!当然对于客户来说,你也不知道太多关于 PC 组装的细节。这和建造模式是多么的相像啊!

今天就来探讨一下建造模式

定义与结构

GOF 给建造模式的定义为:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。这句话说得很抽象,不好理解,其实它的意思可以理解为:将构造复杂对象的过程和组成对象的部件解耦就像攒电脑一样,不管什么品牌的配件,只要兼容就可以装上;同样,一样的配件,可以有好多组装的方式。这是对降低耦合、提高可复用性精神的一种贯彻。

当要生成的产品有复杂的内部结构——比如由多个对象组成;而系统中对此产品的需求将来可能要改变产品对象的内部结构的构成,比如说产品的一些属性现在由一个小对象组成,而更改后的型号可能需要 N 个小对象组成;而且不能将产品的内部构造完全暴露给客户程序,一是为了可用性,二是为了安全等因素。满足上面的设计环境就可以考虑使用建造模式来搭建框架了。

来看看建造模式的组成吧。

  1. 抽象建造者角色:这个角色用来规范产品对象的各个组成成分的建造。一般而言,此角色独立于应用程序的业务逻辑。

  2. 具体建造者角色:担任这个角色的是于应用程序紧密相关的类,它们在指导者的调用下创建产品实例。这个角色在实现抽象建造者角色提供的方法的前提下,达到完成产品组装,提供成品的功能。

  3. 指导者角色:调用具体建造者角色以创建产品对象。指导者并没有产品类的具体知识,真正拥有产品类的具体知识的是具体建造者对象。

  4. 产品角色:建造中的复杂对象。它要包含那些定义组件的类,包括将这些组件装配成产品的接口。

来看下这些角色组成的类图:
在这里插入图片描述

首先客户程序创建一个指导者对象,一个建造者角色,并将建造者角色传入指导者对象进行配置。然后,指导者按照步骤调用建造者的方法创建产品。最后客户程序从建造者或者指导者那里得到产品。

从建造模式的工作流程来看,建造模式将产品的组装“外部化”到了建造者角色中来。这是和任何正规的工厂模式不一样的——产品的创建是在产品类中完成的。

实现

我们知道媒体可以存在不同的表达形式,比如书籍、杂志和网络。这个例子表示不同形式的媒体构造的步骤是相似的,所以可以被提取到指导者角色中去。

import java.util.*;
import junit.framework.*;
//不同的媒体形式:
class Media extends ArrayList {
    
    };
class Book extends Media {
    
    };
class Magazine extends Media {
    
    };
class WebSite extends Media {
    
    };

// 进而包含不同的媒体组成元素:
class MediaItem {
    
    
    private String s;
    public MediaItem(String s) {
    
     this.s = s; }
    public String toString() {
    
     return s; }
}
class Chapter extends MediaItem {
    
    
    public Chapter(String s) {
    
     super(s); }
}
class Article extends MediaItem {
    
    
    public Article(String s) {
    
     super(s); }
}
class WebItem extends MediaItem {
    
    
    public WebItem(String s) {
    
     super(s); }
}

// 抽象建造者角色,它规范了所有媒体建造的步骤:
class MediaBuilder {
    
    
    public void buildBase() {
    
    }
    public void addMediaItem(MediaItem item) {
    
    }
    public Media getFinishedMedia() {
    
     return null; }
}

//具体建造者角色
class BookBuilder extends MediaBuilder {
    
    
    private Book b;
    public void buildBase() {
    
    
        System.out.println("Building book framework");
        b = new Book();
    }
    public void addMediaItem(MediaItem chapter) {
    
    
        System.out.println("Adding chapter " + chapter);
        b.add(chapter);
    }
    public Media getFinishedMedia() {
    
     return b; }
}
class MagazineBuilder extends MediaBuilder {
    
    
    private Magazine m;
    public void buildBase() {
    
    
        System.out.println("Building magazine framework");
        m = new Magazine();
    }
    public void addMediaItem(MediaItem article) {
    
    
        System.out.println("Adding article " + article);
        m.add(article);
    }
    public Media getFinishedMedia() {
    
     return m; }
}
class WebSiteBuilder extends MediaBuilder {
    
    
    private WebSite w;
    public void buildBase() {
    
    
        System.out.println("Building web site framework");
        w = new WebSite();
    }public void addMediaItem(MediaItem webItem) {
    
    
        System.out.println("Adding web item " + webItem);
        w.add(webItem);
    }
    public Media getFinishedMedia() {
    
     return w; }
}
//指导者角色,也叫上下文
class MediaDirector {
    
    
    private MediaBuilder mb;
    public MediaDirector(MediaBuilder mb) {
    
    
        this.mb = mb; //具有策略模式相似特征的
    }
    public Media produceMedia(List input) {
    
    
        mb.buildBase();
        for(Iterator it = input.iterator(); it.hasNext();)
            mb.addMediaItem((MediaItem)it.next());
        return mb.getFinishedMedia();
    }
}
//测试程序——客户程序角色
public class BuildMedia extends TestCase {
    
    
    private List input = Arrays.asList(new MediaItem[] {
    
    
            new MediaItem("item1"), new MediaItem("item2"),
            new MediaItem("item3"), new MediaItem("item4"),
    });
    public void testBook() {
    
    
        MediaDirector buildBook = new MediaDirector(new BookBuilder());
        Media book = buildBook.produceMedia(input);
        String result = "book: " + book;
        System.out.println(result);
        assertEquals(result, "book: [item1, item2, item3, item4]");
    }
    public void testMagazine() {
    
    
        MediaDirector buildMagazine = new MediaDirector(new MagazineBuilder());
        Media magazine = buildMagazine.produceMedia(input);
        String result = "magazine: " + magazine;
        System.out.println(result);
        assertEquals(result, "magazine: [item1, item2, item3, item4]");
    }
    public void testWebSite() {
    
    
        MediaDirector buildWebSite = new MediaDirector(new WebSiteBuilder());
        Media webSite = buildWebSite.produceMedia(input);
        String result = "web site: " + webSite;System.out.println(result);
        assertEquals(result, "web site: [item1, item2, item3, item4]");
    }
    public static void main(String[] args) {
    
    
        junit.textui.TestRunner.run(BuildMedia.class);
    }
}

应用优点

建造模式可以使得产品内部的表象独立变化。在原来的工厂方法模式中,产品内部的表象是由产品自身来决定的;而在建造模式中则是“外部化”为由建造者来负责。这样定义一个新的具体建造者角色就可以改变产品的内部表象,符合“开闭原则”。

建造模式使得客户不需要知道太多产品内部的细节。它将复杂对象的组建和表示方式封装在一个具体的建造角色中,而且由指导者来协调建造者角色来得到具体的产品实例。

每一个具体建造者角色是毫无关系的。

建造模式可以对复杂产品的创建进行更加精细的控制。产品的组成是由指导者角色调用具体建造者角色来逐步完成的,所以比起其它创建型模式能更好的反映产品的构造过程。

扩展

建造模式中很可能要用到组成成品的各种组件类,对于这些类的创建可以考虑使用工厂方法或者原型模式来实现,在必要的时候也可以加上单例模式来控制类实例的产生。但是要坚持一个大前提就是要使引入的模式给你的系统带来好处,而不是臃肿的结构。

建造模式在得到复杂产品的时候可能要引用多个不同的组件,在这一点上来看,建造模式和抽象工厂模式是相似的。可以从以下两点来区分两者:创建模式着重于逐步将组件装配成一个成品并向外提供成品,而抽象工厂模式着重于得到产品族中相关的多个产品对象;抽象工厂模式的应用是受限于产品族的,建造模式则不会。

由于建造模式和抽象工厂模式在实现功能上相似,所以两者使用的环境都比较复杂并且需要更多的灵活性。

组合模式中的树枝构件角色(Composite)往往是由多个树叶构件角色(Leaf)组成,因此树枝构件角色的产生可以由建造模式来担当。

猜你喜欢

转载自blog.csdn.net/clearlxj/article/details/121357909
今日推荐