设计模式学习04—建造者模式

一、动机与定义

创建对象时,我们知道可以使用工厂方式来创建,使调用者和具体实现解耦,但是有一种情况,当要创建的多个对象之间重复性较大,只有创建步骤、组装顺序或者内部构件不同时,工厂模式就需要进一步的演化了,如我们去KFC,有很多种套餐,比如套餐1(薯条+可乐+汉堡),套餐2(鸡肉卷+薯条+可乐),这个套餐就是我们要获取的复杂对象,那么程序如何创建出这种对象呢。

我们看到套餐的内容很多是一样的,那么我们是不是可以考虑将创建单个食品(如鸡肉卷、可乐等)方法提取出来,使用单独一个类协调这些食品的组合比较好呢,如下,具体食品创建者:

//食物的抽象创建者
public abstract class FoodBuilder {
    // 套餐
    protected SetMealProduct sm = new SetMealProduct();

    // 创建汉堡
    public abstract void buildHamburg();

    // 创建薯条
    public abstract void buildChips();

    // 创建可乐
    public abstract void buildCola();

    // 创建鸡肉卷
    public abstract void buildChickenRolls();

    public SetMealProduct getResult() {
        return sm ;
    }
}

具体食品创建者:

//具体食品创建者:KFC
//这样未来还可以支持麦当劳
public class KFCFoodBuilder extends FoodBuilder {

    @Override
    public void buildHamburg() {
        sm.getFoodList().add( "汉堡");
    }

    @Override
    public void buildChips() {
        sm.getFoodList().add( "薯条");
    }

    @Override
    public void buildCola() {
        sm.getFoodList().add( "可乐");
    }

    @Override
    public void buildChickenRolls() {
        sm.getFoodList().add( "鸡肉卷" );
    }

}

套餐产品类:

//套餐
public class SetMealProduct {
    // 套餐名称
    private String name;
    // 套餐中的食品集合
    private List<String> foodList = new ArrayList<String>();

    public List<String> getFoodList() {
        return foodList ;
    }

    public void setFoodList(List<String> foodList) {
        this.foodList = foodList;
    }

    public String getName() {
        return name ;
    }

    public void setName(String name) {
        this.name = name;
    }

}

负责生成套餐,组装顺序的类:

//套餐一(薯条+可乐+汉堡)
class FirstDirector {

    private FoodBuilder fb;

    public FirstDirector(FoodBuilder fb) {
        this.fb = fb;
    }

    public SetMealProduct construct() {
        fb.buildChips(); // 薯条
        fb.buildHamburg(); // 汉堡
        fb.buildCola(); // 可乐
        return fb .getResult();
    }
}

// 套餐二(鸡肉卷+薯条+可乐)
class SecondDirector {

    private FoodBuilder fb;

    public SecondDirector(FoodBuilder fb) {
        this.fb = fb;
    }

    public SetMealProduct construct() {
        fb.buildChickenRolls(); // 鸡肉卷
        fb.buildChips(); // 薯条
        fb.buildCola(); // 可乐
        return fb .getResult();
    }
}

调用者:

        FirstDirector fDirector = new FirstDirector( new KFCFoodBuilder());
        SecondDirector sDirector = new SecondDirector( new KFCFoodBuilder());
        fDirector.construct();
        sDirector.construct();

这样的话,调用者不需要关心底层如何创建,如何组装的,直接获得就行了,其实这就是创建者模式的思想。

二、结构与类图

建造者模式主要用于分步骤的创建一些复杂的对象,将这些步骤一个个的独立起来,使用一个指挥者来控制这些步骤的顺序。

建造者模式有4个角色:

1、Builder:抽象建造者,定义了产品的各个部件的创建方法,具体如何构建由子类实现;

2、ConcreteBuilder:具体建造者,实现了各个部件的创建,返回一个创建好的对象;

3、Director:指挥者,也叫导演者,负责各个部件的组装、步骤的执行顺序,调用Builder创建具体产品,它是建造者模式的核心,有两个职责,1个是隔离了用户与产品的生成过程,另一个负责产品的生产过程;

4、Product:具体产品,由各个部件构成的复杂产品。

三、适用场景及效果(优缺点)

建造者模式核心是创建者和指挥者,创建者负责具体创建的部件、零件或步骤,指挥者负责步骤顺序,部件、零件组装顺序,多少等。所以以下场景可以考虑使用建造者模式:

1、产品类非常复杂,产品类的组成部件或每个具体创建的步骤比较固定,需要根据不同的组装方法或执行顺序产生不同的产品结果时,可以考虑使用建造者模式,如开篇的KFC的例子,部件(可乐、鸡肉卷等)稳定,组装方法不稳定,产生不通结果(套餐);有的时候,这种情况也可以考虑使用模版方法,模版方法更简单一些,但没有建造者这么灵活;

2、产品类非常复杂,产品类部件组装方法或创建步骤比较固定,需要根据不同的部件或步骤,产生不同的产品时,即需要用相同的创建过程创建不同的产品时,举个常见的例子,生活中我们自己组装电脑,组装电脑的方法基本固定,将cpu,硬盘,内存,显卡等等插到主板上,连接显示器鼠标键盘,开机,这个步骤就是指挥者干的事,但是具体的cpu是intel还是AMD的,内存是金士顿还是三星的等等是不固定,提供cpu,内存,硬盘等部件就是创建者干的事;

3、产品类非常复杂,具体部件和步骤,还有组装方法,顺序都经常变化,也可以使用这个模式,不过此时最好和其他模式一起来用,比如和模版模式一起使用;

4、生成的产品对象的属性相互依赖,需要指定执行顺序时,可以考虑;

使用后的效果(优点):

1、封装、解耦,这个是创建型设计模式基本的作用,封装底层,调用者不需要知道如何复杂的创建,只需要直接使用即可。

2、扩展方便,可以很方便的增加具体的建造者,用户使用不同建造者就能得到不同的产品。

3、产品组装过程、执行顺序和具体部件、步骤分离,非常灵活,很容易扩展出新的组装方法和执行顺序。

4、建造者模式可以对复杂产品的创建过程进行精细化控制,比起其他创建型模式能更好的反应产品的创建过程,每个建造者的具体步骤都是独立的,因此可以逐步细化,而不对其他模块产生影响。

缺点:如果产品内部变化复杂,可能会导致需要定义很多具体建造者(Builder)和指挥者(Director),导致系统变得非常庞大。

建造者模式一般创建的产品都具有较多的共同点,组成部分也相似,如果产品之间差异较大,则不适合使用建造者模式。

五、模式扩展

1、简化模式,如果变化在于指挥者,组装步骤和执行顺序,建造者只有1个,那么可以省略抽象建造者。再简单的,如果组装方法和步骤稳定,可以省略指挥者,直接将步骤合并到建造者中。

2、你会发现,建造者模式和工厂模式非常相似,其实他俩还是有区别的,建造者主要功能是对部件的组装,步骤的执行顺序进行控制,也就是部件已经定了,我来控制组装方法,而工厂模式的侧重点是在于创建,如何创建具体的部件,至于怎么组装不是它关心的。可以参考上面提到的组装电脑的例子,建造者模式重点在于如何组装,而工厂模式在于如何创建cpu,创建内存等。

工厂模式更像一种思想,将不确定的,易变的东西延迟到子类去实现(或者说留给以后实现),达到某种程度的解耦。抽象工厂也是接口,但它不是因为不确定,而是产品等级,产品族太多,进行了一层封装而已。

建造者模式不是建造模式,有人说改名“导演模式”更能反应它的本质,两者都是一种封装,建造者是过程性的封装,工厂是结构性的封装。

3、建造者模式经常和其他模式混合使用,比如和模版模式混合使用,建造者模式中有一个角色被忽略了,就是部件,部件的创建很多时候使用模版模式就非常合适。还有这些部件的创建使用了工厂模式、原型模式等,甚至有时候结合单例来控制类实例的生成,不论用什么模式,不论怎么组合,最终的目的一定是能给系统带来好处,而不是臃肿的结构,复杂的代码。

4、灵活配置创建顺序,我们可以把建造者模式中的指挥者角色配置到配置文件中,定义好统一标准,读取配置文件来执行创建者的各个方法,这样后续修改指挥者的执行顺序就非常方便了。其实很多模式都可以把步骤提取到配置文件中,虽然灵活,但是代码复杂了,配置文件管理不善也是个问题,这也是一个取舍。

5、建造者模式真的只能创建对象吗?前面我们一直说建造者模式控制部件组装,步骤的执行顺序都是为了创建对象,其实还有更深一个层次,控制步骤的执行顺序不仅仅是创建对象,还有初始化对象工作,甚至对象的执行步骤工作,比如我们使用建造者模式创建一个文档对象,完全可以获取一个已经打开的文档,未打开的文档,已经初始化一些内容的文档等等。

小结,建造者模式其实就是将复杂对象的创建分解成很多精细的小步骤或小部件,然后使用一个指挥者的类来控制这些步骤的执行或部件的组装,从而达到解耦合,易扩展的目的,调用者只需要使用指挥类和建造类,从而达到了对底层对象创建的封装目的。因为建造者是抽象类,不是具体实现,后续扩展也不影响现有代码,符合了开闭原则。

设计模式学习04—建造者模式

时间: 2024-07-28 13:35:47

设计模式学习04—建造者模式的相关文章

设计模式学习笔记-建造者模式

一.概述 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示: 二.模式中的角色 Builder:为创建一个Product对象的各个部件指定抽象接口: ConcreteBuilder:实现Builder的接口以构造和装配该产品的各个部件:定义并明确它所创建的表示:提供一个检索产品的接口: Director:构造一个使用Builder接口的对象: Product:表示被构造的复杂对象,ConcreteBuilder创建该产品的内部表示并定义它的装配过程:包含定义组成部件的类,

设计模式学习-建造者模式

1.定义 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示. 2.类图 3.代码示例 1 package com.zhaoyangwoo.builder; 2 3 /** 4 * Created by john on 16/5/7. 5 * 场景类 6 */ 7 public class Builder { 8 9 public static void Main(){ 10 BuilderInterface bi = new ConceptBuilder(); 11 Di

设计模式学习05—原型模式

一.动机与定义 之前学习原型模式一直以为原型模式目的是为了方便的创建相同或相似对象,用复制对象的方式替换new的方式,还研究了深克隆和浅克隆.最近仔细看了GOF的设计模式,发现原型模式的本意并不仅仅是复制对象这么简单. 复制对象确实是一方面,当我们需要大量相似,甚至相同对象的时候,除了一个个的new之外,还可以根据一个原型,直接复制出更多的对象.但是如果把原型模式认为只是复制对象这么简单就错了. 创建型模式主要讲如何创建对象,通常包含何时创建,谁来创建,怎么创建等.GOF书里面写的意图是,用原型

设计模式学习之模板方法模式(TemplateMethod,行为型模式)(9)

一.什么是模板方法模式 Template Method模式也叫模板方法模式,是行为模式之一,它把具有特定步骤算法中的某些必要的处理委让给抽象方法,通过子类继承对抽象方法的不同实现改变整个算法的行为. 二.模板方法模式的应用场景 Template Method模式一般应用在具有以下条件的应用中: - 具有统一的操作步骤或操作过程 - 具有不同的操作细节 - 存在多个具有同样操作步骤的应用场景,但某些具体的操作细节却各不相同 private static void Main(string[] arg

设计模式学习之-状态模式

一.状态模式状态模式,当一个对象的内在状态发生改变时允许改变其行为行为,这个对象像是改变了其子类.状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况.把状态的判断逻辑转移到表示不同的状态的一系列类当中,可以把复杂的判断逻辑简化.当然,如果这个状态判断比较简单,那就没有必要用“状态模式”了.这段话有点难以理解,简而言之就是,在一类中根据当前状态去实例化下一状态子类.下面是状态模式结构图. State类,是一个抽象状态类,定义一个接口以封装与Context的特定状态相关的行为.

设计模式学习02—工厂模式

1.动机与定义 我们在程序中使用一个对象时,需要new一下,如果需要设置其他值就再初始化一下.比如我要使用一个按钮,手动new一个矩形按钮,然后初始化一些值,如显示文字,背景色等. // 矩形按钮 IButton btn = new RecButton(); // 初始化其他值 btn.setText("提交"); btn.setBackgroundColor("#00aaff"); // 其他初始化省略 // 圆形按钮 IButton btn2 = new Rou

设计模式学习之外观模式(Facade,结构型模式)(8)

1.什么是外观模式为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用 2.为什么要使用外观模式在软件开发系统中,客户程序经常会与复杂系统的内部子系统之间产生耦合,从而导致客户程序随着子系统的变化而变化,那么如何简化客户程序与子系统之间的交互接口?如何将复杂系统的内部子系统与客户程序之间的依赖解耦? 现在来考虑这样一个抵押系统,当有一个客户来时,有如下几件事情需要确认:到银行子系统查询他是否有足够多的存款,到信用子系统查询他是否有良好的信

设计模式学习之代理模式(Proxy)

一.代理模式的初衷 不想让客户端直接访问具体实现接口,客户端不应该直接访问具体实现,一般会涉及到以下四个对象 业务接口:定义客户端访问的接口,是实现类和代理类都需要实现的 实现类:实现了业务接口,真正的实现是在这里 代理类:同样实现了业务接口,一般都会关联一个实现类的实例,由它来调用实现类 客户端:顾名思义,使用者,与代理类打交道,甚至都不知道具体实现类的存在. 二.代理模式设计图 三.代理模式的简单实现代码 BizInterface.java package com.lipan.designp

设计模式学习--------12.代理模式学习

场景: 福尔摩斯一直想送礼物给花生,但是羞于直接赠送,于是想到让房东太太去帮忙送礼物.编程如何实现呢? 定义: 为其他对象提供一种代理以控制对这个对象的访问. 角色: Proxy:代理对象.有下列功能: 实现与具体的目标对象一样的接口,这样就可以使用代理来代替具体的目标对象. 持有一个具体目标对象的引用,可以在需要时调用具体的目标对象. 可以控制对目标对象的访问,并可以负责创建和删除它. package com.kris.study; public class Proxy implements