建造者模式.md 7.7 KB
Newer Older
ツぃ☆ve芜情's avatar
ツぃ☆ve芜情 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
建造者模式实际上是常用的设计模式。顾名思义,builder 的意思是建造者或者建筑工人,谈到建造自然会想到楼房。楼房是千差万别的,楼房的外形、层数、内部房间的数量、房间的装饰等等都不一样,但是对于建造者来说,抽象出来的建筑流程是确定的,往往建筑一座楼房包括下面的步骤:(1)打桩,建立基础(2)建立框架等。

建造者模式的本质和建造楼房是一致的:即流程不变,但每个流程实现的具体细节则是经常变化的。建造者模式的好处就是保证了流程不会变化,流程即不会增加、也不会遗漏或者产生流程次序错误,这是非常重要的。我们熟知的楼歪歪事件,官方的解释就是由于先建立楼房后,再建设停车场造成的,这是典型的建造次序错乱。



**建造者模式:是将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。**



建造者模式通常包括下面几个角色:

- Builder:给出一个抽象接口,以规范产品对象的各个组成成分的建造。这个接口规定要实现复杂对象的哪些部分的创建,并不涉及具体的对象部件的创建。
- ConcreteBuilder:实现 Builder 接口,针对不同的商业逻辑,具体化复杂对象的各部分的创建。 在建造过程完成后,提供产品的实例。
- Director:调用具体建造者来创建复杂对象的各个部分,在指导者中不涉及具体产品的信息,只负责保证对象各部分完整创建或按某种顺序创建。
- Product:要创建的复杂对象(不是必须的,有些情况仅仅是流程)。

![](img/Builder.png)

## 示例 1

为了身体健康,我们每个人都断练过,而学生有男有女,由于身体差异,能做的动作也是迥然不同,但是都是有着先后顺序,假设我们锻炼都要做三组动作,而男女生的动作并不相同,我们在实现这个过程时可以让男女生分别实现锻炼这个接口,在自己的类中实现不同的锻炼动作。

【Builder】

```java
public interface Exercise {
    Exercise first();
    Exercise second();
    Exercise third();
}
```

【ConcreteBuilder 】

```java
public class Boy implements Exercise{
    @Override
    public Exercise first() {
        System.out.println("卧推");
        return this;
    }

    @Override
    public Exercise second() {
        System.out.println("高位下拉");
        return this;
    }

    @Override
    public Exercise third() {
        System.out.println("硬拉");
        return this;
    }
}
```

```java
public class Girl implements Exercise {
    @Override
    public Exercise first() {
        System.out.println("跑步机慢跑");
        return this;
    }

    @Override
    public Exercise second() {
        System.out.println("引体向上");
        return this;
    }

    @Override
    public Exercise third() {
        System.out.println("仰卧起坐");
        return this;
    }
}
```

【Director】

```java
public class Student {
    private Exercise exercise;

    public Student(Exercise exercise) {
        this.exercise = exercise;
    }

    public void doExercise() {
        exercise.first().second().third();
    }
}
```

【测试类】

```java
public class Test {
    public static void main(String[] args) {
        Student girl = new Student(new Girl());
        girl.doExercise();
        System.out.println("===========================");
        Student boy = new Student(new Boy());
        boy.doExercise();
    }
}
```

输出结果:

```
跑步机慢跑
引体向上
仰卧起坐
===========================
卧推
高位下拉
硬拉
```

## 示例 2

下面我们看一个 KFC 点套餐的例子,我们点餐可以点一个汉堡和一个冷饮,汉堡可以是鸡肉汉堡、虾堡等等,是装在盒子中的,冷饮可以是可乐、雪碧等等,是装在瓶子中的。下面我们来用建造者模式对其进行组合,用户只需提交订单即可,UML 图如下:

![](img/KFC.png)

【Item 接口】

创建一个表示食物条目和食物包装的接口。

```java
public interface Item {

    //获取食物名称
    public String getName();
    //获取包装
    public Packing packing();
    //获取价格
    public float getPrice();

}
```

【可包装接口及其实现类】

Packable 接口的实现类。Wrapper 为纸盒包装,Bottle为瓶装。

```java
public interface Packable {

    //获取包装类型
    public String getPack();

}
```

```java
public class Wrapper implements Packable {

    @Override
    public String getPack() {
        return "纸盒";
    }

}
```

```java
public class Bottle implements Packable {

    @Override
    public String getPack() {
        return "纸杯";
    }

}
```

【食品类】

创建实现 Item 接口的抽象类。Burger 为汉堡,Drink 为饮品。

```java
public abstract class Burger implements Item {

    @Override
    public Packing packing() {
        return new Wrapper();
    }

    @Override
    public abstract float getPrice();

}
```

```java
public abstract class Drink implements Item {

    @Override
    public Packing packing() {
        return new Bottle();
    }

    @Override
    public abstract float getPrice();

}
```

【具体食品类】

创建扩展了 Burger 和 Drink 的具体实现类。这里简单的就设为 Burger1、Burger2、Drink1、Drink2。各写一个,多余的就不赘述了。

```java
public class Burger1 extends Burger {

    @Override
    public String getName() {
        return "汉堡1";
    }

    @Override
    public float getPrice() {
        return 25.0f;
    }

}
```

```java
public class Drink1 extends Drink {

    @Override
    public String getName() {
        return "饮品1";
    }

    @Override
    public float getPrice() {
        return 15.0f;
    }

}
```

【Meal 类】

```java
public class Meal {

    private List<Item> items = new ArrayList<>();

    public void addItem(Item item) {
        items.add(item);
    }

    //获取总消费
    public float getCost() {
        float cost = 0.0f;

        for (Item item : items) {
            cost += item.getPrice();
        }

        return cost;
    }

    public void showItem() {
        for (Item item : items) {
            System.out.print("餐品:" + item.getName());
            System.out.print(",包装:" + item.packing().getPack());
            System.out.println(",价格:¥" + item.getPrice());
        }
    }

}
```

【套餐类】

```java
public class Package {
    //套餐 1
    public Meal package1() {
        Meal meal = new Meal();
        meal.addItem(new Burger1());
        meal.addItem(new Drink1());

        return meal;
    }

    //套餐 2
    public Meal package2() {
        Meal meal = new Meal();
        meal.addItem(new Burger2());
        meal.addItem(new Drink2());

        return meal;
    }
}
```

【消费者】

```java
public class Consumer {
    public static void main(String[] args) {
        Package set = new Package();

        // 购买第一个套餐
        Meal order1 = set.package1();
        System.out.println("------order1------");
        order1.showItem();
        System.out.println("总额:¥" + order1.getCost());

        // 购买第二个套餐
        Meal order2 = set.package2();
        System.out.println("------order2------");
        order2.showItem();
        System.out.println("总额:¥" + order2.getCost());
    }
}
```

输出结果:

```java
------order1------
餐品汉堡1包装纸盒价格:¥25.0
餐品饮品1包装纸杯价格:¥15.0
总额:¥40.0
------order2------
餐品汉堡2包装纸盒价格:¥35.0
餐品饮品2包装纸杯价格:¥18.0
总额:¥53.0
```