Skip to content

Commit 03551d3

Browse files
author
jiahaixin
committed
build pattern
1 parent f9ea4a8 commit 03551d3

File tree

9 files changed

+415
-0
lines changed

9 files changed

+415
-0
lines changed

docs/.DS_Store

0 Bytes
Binary file not shown.
16.9 KB
Loading
54.6 KB
Loading

docs/data-management/.DS_Store

0 Bytes
Binary file not shown.
0 Bytes
Binary file not shown.

docs/data-management/Big-Data/Kylin.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,14 @@ select Time, Location, Sum(GMV) as GMV from Sales group by Time, Location
9090

9191

9292

93+
94+
95+
### 执行 “select *” 报错
96+
97+
Cube 中只包含聚合数据,所以用户的所有查询都应该是聚合查询 (包含 “group by”)。
98+
99+
100+
93101
## 概览
94102

95103
Apache Kylin™是一个开源的、分布式的分析型数据仓库,提供Hadoop/Spark 之上的 SQL 查询接口及多维分析(OLAP)能力以支持超大规模数据,最初由 eBay 开发并贡献至开源社区。它能在亚秒内查询巨大的表。
Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
# 建造者模式
2+
3+
![](https://cdn.jsdelivr.net/gh/Jstarfish/picBed/design-pattern/1*jGC9cgC7N3eUKDafMzRnCw.jpeg)
4+
5+
> StringBuilder 你肯定用过,JDK 中的建造者模式
6+
>
7+
> lombok 中的 @Bulider,你可能也用过,恩,这也是我们要说的建造者模式
8+
9+
10+
11+
12+
13+
> 直接使用构造函数或者配合 set 方法就能创建对象,为什么还需要建造者模式来创建呢?
14+
>
15+
> 建造者模式和工厂模式都可以创建对象,那它们两个的区别在哪里呢?
16+
17+
## 简介
18+
19+
Builder Pattern,中文翻译为**建造者模式**或者**构建者模式**,也有人叫它**生成器模式**
20+
21+
**建造者模式**是一种创建型设计模式, 使你能够分步骤创建复杂对象。它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。
22+
23+
**定义**:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
24+
25+
![](https://tva1.sinaimg.cn/large/008i3skNly1grd7fknsyig60ek0854qp02.gif)
26+
27+
28+
29+
30+
31+
## hello world
32+
33+
程序员麽,先上个 `hello world` 热热身
34+
35+
```java
36+
public class User {
37+
38+
private Long id;
39+
private String name;
40+
private Integer age; //可选
41+
private String desc; //可选
42+
43+
private User(Builder builder) {
44+
this.id = builder.id;
45+
this.name = builder.name;
46+
this.age = builder.age;
47+
this.desc = builder.desc;
48+
}
49+
50+
public static Builder newBuilder(Long id, String name) {
51+
return new Builder(id, name);
52+
}
53+
54+
public Long getId() {return id;}
55+
public String getName() {return name;}
56+
public Integer getAge() {return age;}
57+
public String getDesc() {return desc;}
58+
59+
@Override
60+
public String toString() {
61+
return "Builder{" +
62+
"id=" + id +
63+
", name='" + name + '\'' +
64+
", age=" + age +
65+
", desc='" + desc + '\'' +
66+
'}';
67+
}
68+
69+
public static class Builder {
70+
private Long id;
71+
private String name;
72+
private Integer age;
73+
private String desc;
74+
75+
private Builder(Long id, String name) {
76+
Assert.assertNotNull("标识不能为空",id);
77+
Assert.assertNotNull("名称不能为空",name);
78+
this.id = id;
79+
this.name = name;
80+
}
81+
public Builder age(Integer age) {
82+
this.age = age;
83+
return this;
84+
}
85+
public Builder desc(String desc) {
86+
this.desc = desc;
87+
return this;
88+
}
89+
public User build() {
90+
return new User(this);
91+
}
92+
93+
}
94+
95+
public static void main(String[] args) {
96+
User user = User.newBuilder(1L, "starfish").age(22).desc("test").build();
97+
System.out.println(user.toString());
98+
}
99+
}
100+
```
101+
102+
这样的代码有什么优缺点呢?
103+
104+
主要优点:
105+
106+
1. 明确了必填参数和可选参数,在构造方法中进行验证;
107+
2. 可以定义为不可变类,初始化后属性字段值不可变更;
108+
3. 赋值代码可读性较好,明确知道哪个属性字段对应哪个值;
109+
4. 支持链式方法调用,相比于调用 Setter 方法,代码更简洁。
110+
111+
主要缺点:
112+
113+
1. 代码量较大,多定义了一个 Builder 类,多定义了一套属性字段,多实现了一套赋值方法;
114+
2. 运行效率低,需要先创建 Builder 实例,再赋值属性字段,再创建目标实例,最后拷贝属性字段。
115+
116+
> 当然,以上代码,就可以通过 Lombok 的 @Builder 简化代码
117+
>
118+
> 如果我们就那么三三两两个参数,直接构造函数配合 set 方法就能搞定的,就不用套所谓的模式了。
119+
>
120+
> 高射炮打蚊子——不合算
121+
>
122+
> 假设有这样一个复杂对象, 在对其进行构造时需要对诸多成员变量和嵌套对象进行繁复的初始化工作。 这些初始化代码通常深藏于一个包含众多参数且让人基本看不懂的构造函数中; 甚至还有更糟糕的情况, 那就是这些代码散落在客户端代码的多个位置。
123+
>
124+
> 这时候才是构造器模式上场的时候
125+
126+
127+
128+
上边的例子,其实属于简化版的建造者模式,只是为了方便构建类中的各个参数,”正经“的和这个有点差别,更倾向于用同样的构建过程分步创建不同的产品类。
129+
130+
我们接着扯~
131+
132+
## 结构
133+
134+
![](https://cdn.jsdelivr.net/gh/Jstarfish/picBed/design-pattern/builder-UML.png)
135+
136+
从 UML 图上可以看到有 4 个不同的角色
137+
138+
- 抽象建造者(Builder):创建一个 Produc 对象的各个部件指定的接口/抽象类
139+
- 具体建造者(ConcreteBuilder):实现接口,构建和装配各个组件
140+
- 指挥者/导演类(Director):构建一个使用 Builder 接口的对象。负责调用适当的建造者来组建产品,导演类一般不与产品类发生依赖关系,与导演类直接交互的是建造者类。
141+
- 产品类(Product):一个具体的产品对象
142+
143+
144+
145+
## demo
146+
147+
假设我是个汽车工厂,需求就是能造各种车(或者造电脑、造房子、做煎饼、生成不同文件TextBuilder、HTMLBuilder等等,都是一个道理)
148+
149+
![](https://cdn.jsdelivr.net/gh/Jstarfish/picBed/design-pattern/builder-car.png)
150+
151+
1、生成器(Builder)接口声明在所有类型生成器中通用的产品构造步骤
152+
153+
```java
154+
public interface CarBuilder {
155+
void setCarType(CarType type);
156+
void setSeats(int seats);
157+
void setEngine(Engine engine);
158+
void setGPS(GPS gps);
159+
}
160+
```
161+
162+
2、具体的生成器(Concrete Builders)提供构造过程的不同实现
163+
164+
```java
165+
public class SportsCarBuilder implements CarBuilder {
166+
167+
private CarType carType;
168+
private int seats;
169+
private Engine engine;
170+
private GPS gps;
171+
172+
@Override
173+
public void setCarType(CarType type) {
174+
this.carType = type;
175+
}
176+
177+
@Override
178+
public void setSeats(int seats) {
179+
this.seats = seats;
180+
}
181+
182+
@Override
183+
public void setEngine(Engine engine) {
184+
this.engine = engine;
185+
}
186+
187+
@Override
188+
public void setGPS(GPS gps) {
189+
this.gps = gps;
190+
}
191+
192+
public Car getResult() {
193+
return new Car(carType, seats, engine, gps);
194+
}
195+
}
196+
```
197+
198+
3、产品(Products)是最终生成的对象
199+
200+
```java
201+
@Setter
202+
@Getter
203+
@ToString
204+
public class Car {
205+
206+
private final CarType carType;
207+
private final int seats;
208+
private final Engine engine;
209+
private final GPS gps;
210+
private double fuel;
211+
212+
public Car(CarType carType,int seats,Engine engine,GPS gps){
213+
this.carType = carType;
214+
this.seats = seats;
215+
this.engine = engine;
216+
this.gps = gps;
217+
}
218+
}
219+
```
220+
221+
4、主管(Director)类定义调用构造步骤的顺序,这样就可以创建和复用特定的产品配置(Director 类的构造函数的参数是 CarBuilder,但实际上没有实例传递出去作参数,因为 CarBuilder 是接口或抽象类,无法产生对象实例,实际传递的是 Builder 的子类,根据子类类型,决定生产内容)
222+
223+
```java
224+
public class Director {
225+
226+
public void constructSportsCar(CarBuilder builder){
227+
builder.setCarType(CarType.SPORTS_CAR);
228+
builder.setSeats(2);
229+
builder.setEngine(new Engine(2.0,0));
230+
builder.setGPS(new GPS());
231+
}
232+
233+
public void constructCityCar(CarBuilder builder){
234+
builder.setCarType(CarType.CITY_CAR);
235+
builder.setSeats(4);
236+
builder.setEngine(new Engine(1.5,0));
237+
builder.setGPS(new GPS());
238+
}
239+
240+
public void constructSUVCar(CarBuilder builder){
241+
builder.setCarType(CarType.SUV);
242+
builder.setSeats(4);
243+
builder.setEngine(new Engine(2.5,0));
244+
builder.setGPS(new GPS());
245+
}
246+
247+
}
248+
```
249+
250+
5、客户端使用(最终结果从建造者对象中获取,主管并不知道最终产品的类型)
251+
252+
```java
253+
public class Client {
254+
255+
public static void main(String[] args) {
256+
Director director = new Director();
257+
SportsCarBuilder builder = new SportsCarBuilder();
258+
director.constructSportsCar(builder);
259+
260+
Car car = builder.getResult();
261+
System.out.println(car.toString());
262+
}
263+
}
264+
```
265+
266+
267+
268+
## 适用场景
269+
270+
适用场景其实才是理解设计模式最重要的,只要知道这个业务场景需要什么模式,网上浪程序员能不会吗
271+
272+
- **使用建造者模式可避免重叠构造函数的出现**
273+
274+
假设你的构造函数中有 N 个可选参数,那 new 各种实例的时候就很麻烦,需要重载构造函数多次
275+
276+
- 当你希望使用代码创建不同形式的产品 (例如石头或木头房屋) 时, 可使用建造者模式。
277+
278+
如果你需要创建的各种形式的产品, 它们的制造过程相似且仅有细节上的差异, 此时可使用建造者模式。
279+
280+
- **使用生成器构造组合树或其他复杂对象**
281+
282+
建造者模式让你能分步骤构造产品。 你可以延迟执行某些步骤而不会影响最终产品。 你甚至可以递归调用这些步骤, 这在创建对象树时非常方便。
283+
284+
285+
286+
## VS 抽象工厂
287+
288+
抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列产品:具有不同分类维度的产品组合,采用抽象工厂模式不需要关心抽象过程,只关心什么产品由什么工厂生产即可。而建造者模式则是要求按照指定的蓝图建造产品,它的主要目的是通过组装零配件而生产一个新的产品。
289+
290+
291+
292+
## 最后
293+
294+
设计模式,这玩意看简单的例子,肯定能看得懂,主要是结合自己的业务思考怎么应用,让系统设计更完善,懂了每种模式后,可以找找各种框架源码或在 github 搜搜相关内容,看看实际中是怎么应用的。
295+
296+
297+
298+
> 公众号回复 ”设计模式“,领取 10 本设计模式 pdf 书籍
299+
300+
301+
302+
## 参考
303+
304+
- refactoringguru.cn
305+

docs/others/.DS_Store

0 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)