zl程序教程

您现在的位置是:首页 >  工具

当前栏目

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

2023-09-11 14:15:00 时间
一、动机与定义

     创建对象时,我们知道能够使用工厂方式来创建,使调用者和详细实现解耦,可是有一种情况,当要创建的多个对象之间反复性较大,仅仅有创建步骤、组装顺序或者内部构件不同一时候。工厂模式就须要进一步的演化了,如我们去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、产品组装过程、运行顺序和详细部件、步骤分离。非常灵活,非常easy扩展出新的组装方法和运行顺序。
     4、建造者模式能够对复杂产品的创建过程进行精细化控制,比起其它创建型模式能更好的反应产品的创建过程,每一个建造者的详细步骤都是独立的。因此能够逐步细化,而不正确其它模块产生影响。
     
     缺点:假设产品内部变化复杂。可能会导致须要定义非常多详细建造者(Builder)和指挥者(Director)。导致系统变得非常庞大。
     建造者模式一般创建的产品都具有较多的共同点,组成部分也相似,假设产品之间差异较大,则不适合使用建造者模式。


五、模式扩展

     1、简化模式。假设变化在于指挥者。组装步骤和运行顺序,建造者仅仅有1个,那么能够省略抽象建造者。

再简单的,假设组装方法和步骤稳定,能够省略指挥者,直接将步骤合并到建造者中。

     2、你会发现,建造者模式和工厂模式很相似。事实上他俩还是有差别的,建造者主要功能是对部件的组装,步骤的运行顺序进行控制,也就是部件已经定了,我来控制组装方法,而工厂模式的側重点是在于创建,怎样创建详细的部件。至于怎么组装不是它关心的。能够參考上面提到的组装电脑的样例。建造者模式重点在于怎样组装。而工厂模式在于怎样创建cpu,创建内存等。
     工厂模式更像一种思想。将不确定的,易变的东西延迟到子类去实现(或者说留给以后实现)。达到某种程度的解耦。抽象工厂也是接口,但它不是由于不确定,而是产品等级,产品族太多,进行了一层封装而已。
     建造者模式不是建造模式,有人说改名“导演模式”更能反应它的本质,两者都是一种封装,建造者是过程性的封装。工厂是结构性的封装。

     3、建造者模式常常和其它模式混合使用。比方和模版模式混合使用,建造者模式中有一个角色被忽略了,就是部件。部件的创建非常多时候使用模版模式就非常合适。还有这些部件的创建使用了工厂模式、原型模式等,甚至有时候结合单例来控制类实例的生成。不论用什么模式,不论怎么组合,终于的目的一定是能给系统带来优点,而不是臃肿的结构,复杂的代码。
     4、灵活配置创建顺序,我们能够把建造者模式中的指挥者角色配置到配置文件里,定义好统一标准,读取配置文件来运行创建者的各个方法。这样兴许改动指挥者的运行顺序就非常方便了。

事实上非常多模式都能够把步骤提取到配置文件里,尽管灵活,可是代码复杂了,配置文件管理不善也是个问题。这也是一个取舍。

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


     小结,建造者模式事实上就是将复杂对象的创建分解成非常多精细的小步骤或小部件,然后使用一个指挥者的类来控制这些步骤的运行或部件的组装。从而达到解耦合。易扩展的目的,调用者仅仅须要使用指挥类和建造类,从而达到了对底层对象创建的封装目的。由于建造者是抽象类,不是详细实现,兴许扩展也不影响现有代码。符合了开闭原则。