Skip to content

Commit 7af634d

Browse files
committed
modify design pattern
1 parent 176ef4e commit 7af634d

6 files changed

+292
-1658
lines changed

docs/java/design-parttern/初探Java设计模式1:创建型模式(工厂,单例等).md

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
# Table of Contents
2-
1+
# 目录
32
* [创建型模式](#创建型模式)
43
* [简单工厂模式](#简单工厂模式)
54
* [工厂模式](#工厂模式)
@@ -30,7 +29,7 @@ Java 设计模式
3029
3130
一直想写一篇介绍设计模式的文章,让读者可以很快看完,而且一看就懂,看懂就会用,同时不会将各个模式搞混。自认为本文还是写得不错的,花了不少心思来写这文章和做图,力求让读者真的能看着简单同时有所收获。
3231

33-
设计模式是对大家实际工作中写的各种代码进行高层次抽象的总结,其中最出名的当属 _Gang of Four_ (_GoF_) 的分类了,他们将设计模式分类为 23 种经典的模式,根据用途我们又可以分为三大类,分别为创建型模式、结构型模式和行为型模式。是的,我不善于扯这些有的没的,还是少点废话吧~~~
32+
设计模式是对大家实际工作中写的各种代码进行高层次抽象的总结,其中最出名的当属_Gang of Four_(_GoF_) 的分类了,他们将设计模式分类为 23 种经典的模式,根据用途我们又可以分为三大类,分别为创建型模式、结构型模式和行为型模式。是的,我不善于扯这些有的没的,还是少点废话吧~~~
3433

3534
有一些重要的设计原则在开篇和大家分享下,这些原则将贯通全文:
3635

@@ -144,8 +143,7 @@ public class APP {
144143

145144
虽然简单,不过我也把所有的构件都画到一张图上,这样读者看着比较清晰:
146145

147-
![](https://javadoop.com/blogimages/design-pattern/factory-1.png)![](https://csdnimg.cn/release/phoenix/write/assets/uploading.gif)转存失败重新上传取消![factory-1](https://javadoop.com/blogimages/design-pattern/factory-1.png)![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "点击并拖拽以移动")
148-
146+
![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404211801.png)
149147
### 抽象工厂模式
150148

151149
当涉及到**产品族**的时候,就需要引入抽象工厂模式了。
@@ -154,7 +152,7 @@ public class APP {
154152

155153
因为电脑是由许多的构件组成的,我们将 CPU 和主板进行抽象,然后 CPU 由 CPUFactory 生产,主板由 MainBoardFactory 生产,然后,我们再将 CPU 和主板搭配起来组合在一起,如下图:
156154

157-
![](https://javadoop.com/blogimages/design-pattern/abstract-factory-1.png)![](https://csdnimg.cn/release/phoenix/write/assets/uploading.gif)转存失败重新上传取消![factory-1](https://javadoop.com/blogimages/design-pattern/abstract-factory-1.png)![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "点击并拖拽以移动")
155+
![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404211822.png)
158156

159157
这个时候的客户端调用是这样的:
160158

@@ -174,20 +172,15 @@ Computer computer = new Computer(cpu, mainBoard);
174172

175173
单独看 CPU 工厂和主板工厂,它们分别是前面我们说的**工厂模式**。这种方式也容易扩展,因为要给电脑加硬盘的话,只需要加一个 HardDiskFactory 和相应的实现即可,不需要修改现有的工厂。
176174

177-
但是,这种方式有一个问题,那就是如果 **Intel 家产的 CPU 和 AMD 产的主板不能兼容使用**,那么这代码就容易出错,因为客户端并不知道它们不兼容,也就会错误地出现随意组合。
175+
但是,这种方式有一个问题,那就是如果**Intel 家产的 CPU 和 AMD 产的主板不能兼容使用**,那么这代码就容易出错,因为客户端并不知道它们不兼容,也就会错误地出现随意组合。
178176

179177
下面就是我们要说的**产品族**的概念,它代表了组成某个产品的一系列附件的集合:
180178

181-
![](https://javadoop.com/blogimages/design-pattern/abstract-factory-2.png)
182-
183-
![abstract-factory-2](https://javadoop.com/blogimages/design-pattern/abstract-factory-2.png)
184-
179+
![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404211846.png)
185180

186181
当涉及到这种产品族的问题的时候,就需要抽象工厂模式来支持了。我们不再定义 CPU 工厂、主板工厂、硬盘工厂、显示屏工厂等等,我们直接定义电脑工厂,每个电脑工厂负责生产所有的设备,这样能保证肯定不存在兼容问题。
187182

188-
![](https://javadoop.com/blogimages/design-pattern/abstract-factory-3.png)
189-
190-
![abstract-factory-3](https://javadoop.com/blogimages/design-pattern/abstract-factory-3.png)
183+
![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404212006.png)
191184

192185
这个时候,对于客户端来说,不再需要单独挑选 CPU厂商、主板厂商、硬盘厂商等,直接选择一家品牌工厂,品牌工厂会负责生产所有的东西,而且能保证肯定是兼容可用的。
193186

@@ -390,7 +383,7 @@ public class APP {
390383
391384
```
392385

393-
说实话,建造者模式的**链式**写法很吸引人,但是,多写了很多“无用”的 builder 的代码,感觉这个模式没什么用。不过,当属性很多,而且有些必填,有些选填的时候,这个模式会使代码清晰很多。我们可以在 **Builder 的构造方法**中强制让调用者提供必填字段,还有,在 build() 方法中校验各个参数比在 User 的构造方法中校验,代码要优雅一些。
386+
说实话,建造者模式的**链式**写法很吸引人,但是,多写了很多“无用”的 builder 的代码,感觉这个模式没什么用。不过,当属性很多,而且有些必填,有些选填的时候,这个模式会使代码清晰很多。我们可以在**Builder 的构造方法**中强制让调用者提供必填字段,还有,在 build() 方法中校验各个参数比在 User 的构造方法中校验,代码要优雅一些。
394387

395388
> 题外话,强烈建议读者使用 lombok,用了 lombok 以后,上面的一大堆代码会变成如下这样:
396389
@@ -407,7 +400,7 @@ class User {
407400

408401
> 怎么样,省下来的时间是不是又可以干点别的了。
409402
410-
当然,如果你只是想要链式写法,不想要建造者模式,有个很简单的办法,User 的 getter 方法不变,所有的 setter 方法都让其 **return this** 就可以了,然后就可以像下面这样调用:
403+
当然,如果你只是想要链式写法,不想要建造者模式,有个很简单的办法,User 的 getter 方法不变,所有的 setter 方法都让其**return this**就可以了,然后就可以像下面这样调用:
411404

412405
```
413406
User user = new User().setName("").setPassword("").setAge(20);

docs/java/design-parttern/初探Java设计模式2:结构型模式(代理模式,适配器模式等).md

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
# Table of Contents
2-
1+
# 目录
32
* [结构型模式](#结构型模式)
43
* [代理模式](#代理模式)
54
* [适配器模式](#适配器模式)
@@ -25,6 +24,7 @@
2524
文章也将发表在我的个人博客,阅读体验更佳:
2625

2726
> www.how2playlife.com
27+
<!-- more -->
2828
2929
## 结构型模式
3030

@@ -97,8 +97,9 @@ foodService.makeChicken();
9797
9898
```
9999

100-
![](https://javadoop.com/blogimages/design-pattern/proxy-1.png)
101-
我们发现没有,代理模式说白了就是做 **“方法包装”** 或做 **“方法增强”**。在面向切面编程中,算了还是不要吹捧这个名词了,在 AOP 中,其实就是动态代理的过程。比如 Spring 中,我们自己不定义代理类,但是 Spring 会帮我们动态来定义代理,然后把我们定义在 @Before@After@Around 中的代码逻辑动态添加到代理中。
100+
![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404212100.png)
101+
102+
我们发现没有,代理模式说白了就是做**“方法包装”**或做**“方法增强”**。在面向切面编程中,算了还是不要吹捧这个名词了,在 AOP 中,其实就是动态代理的过程。比如 Spring 中,我们自己不定义代理类,但是 Spring 会帮我们动态来定义代理,然后把我们定义在 @Before@After@Around 中的代码逻辑动态添加到代理中。
102103

103104
说到动态代理,又可以展开说 …… Spring 中实现动态代理有两种,一种是如果我们的类定义了接口,如 UserService 接口和 UserServiceImpl 实现,那么采用 JDK 的动态代理,感兴趣的读者可以去看看 java.lang.reflect.Proxy 类的源码;另一种是我们自己没有定义接口的,Spring 会采用 CGLIB 进行动态代理,它是一个 jar 包,性能还不错。
104105

@@ -253,17 +254,16 @@ public static void main(String[] args) {
253254

254255
我们用一个图来简单说明下:
255256

256-
![](https://javadoop.com/blogimages/design-pattern/adapter-1.png)
257-
257+
![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404212138.png)
258258
上图应该还是很容易理解的,我就不做更多的解释了。下面,我们看看类适配模式怎么样的。
259259

260260
类适配器模式
261261

262262
废话少说,直接上图:
263263

264-
![](https://javadoop.com/blogimages/design-pattern/adapter-2.png)
264+
![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404212155.png)
265265

266-
看到这个图,大家应该很容易理解的吧,通过继承的方法,适配器自动获得了所需要的大部分方法。这个时候,客户端使用更加简单,直接 `Target t = new SomeAdapter();` 就可以了。
266+
看到这个图,大家应该很容易理解的吧,通过继承的方法,适配器自动获得了所需要的大部分方法。这个时候,客户端使用更加简单,直接`Target t = new SomeAdapter();`就可以了。
267267

268268
适配器模式总结
269269

@@ -279,7 +279,7 @@ public static void main(String[] args) {
279279

280280
比较这两种模式,其实是比较对象适配器模式和代理模式,在代码结构上,它们很相似,都需要一个具体的实现类的实例。但是它们的目的不一样,代理模式做的是增强原方法的活;适配器做的是适配的活,为的是提供“把鸡包装成鸭,然后当做鸭来使用”,而鸡和鸭它们之间原本没有继承关系。
281281

282-
![](https://javadoop.com/blogimages/design-pattern/adapter-5.png)
282+
![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404212256.png)
283283

284284
### 桥梁模式
285285

@@ -392,13 +392,12 @@ public static void main(String[] args) {
392392

393393
首先,我们先看一个简单的图,看这个图的时候,了解下层次结构就可以了:
394394

395-
![](https://javadoop.com/blogimages/design-pattern/decorator-1.png)
396-
397-
我们来说说装饰模式的出发点,从图中可以看到,接口 `Component` 其实已经有了 `ConcreteComponentA` 和 `ConcreteComponentB` 两个实现类了,但是,如果我们要**增强**这两个实现类的话,我们就可以采用装饰模式,用具体的装饰器来**装饰**实现类,以达到增强的目的。
395+
![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404212315.png)
396+
我们来说说装饰模式的出发点,从图中可以看到,接口`Component`其实已经有了`ConcreteComponentA``ConcreteComponentB`两个实现类了,但是,如果我们要**增强**这两个实现类的话,我们就可以采用装饰模式,用具体的装饰器来**装饰**实现类,以达到增强的目的。
398397

399398
> 从名字来简单解释下装饰器。既然说是装饰,那么往往就是**添加小功能**这种,而且,我们要满足可以添加多个小功能。最简单的,代理模式就可以实现功能的增强,但是代理不容易实现多个功能的增强,当然你可以说用代理包装代理的方式,但是那样的话代码就复杂了。
400399
401-
首先明白一些简单的概念,从图中我们看到,所有的具体装饰者们 ConcreteDecorator_ 都可以作为 Component 来使用,因为它们都实现了 Component 中的所有接口。它们和 Component 实现类 ConcreteComponent_ 的区别是,它们只是装饰者,起**装饰**作用,也就是即使它们看上去牛逼轰轰,但是它们都只是在具体的实现中**加了层皮来装饰**而已。
400+
首先明白一些简单的概念,从图中我们看到,所有的具体装饰者们 ConcreteDecorator_都可以作为 Component 来使用,因为它们都实现了 Component 中的所有接口。它们和 Component 实现类 ConcreteComponent_的区别是,它们只是装饰者,起**装饰**作用,也就是即使它们看上去牛逼轰轰,但是它们都只是在具体的实现中**加了层皮来装饰**而已。
402401

403402
> 注意这段话中混杂在各个名词中的 Component 和 Decorator,别搞混了。
404403
@@ -517,13 +516,12 @@ Beverage beverage = new Mongo(new Pearl(new Lemon(new Lemon(new BlackTea()))));
517516

518517
看看下图可能会清晰一些:
519518

520-
![](https://javadoop.com/blogimages/design-pattern/decorator-2.png)
521-
519+
![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404212336.png)
522520
到这里,大家应该已经清楚装饰模式了吧。
523521

524522
下面,我们再来说说 java IO 中的装饰模式。看下图 InputStream 派生出来的部分类:
525523

526-
![](https://javadoop.com/blogimages/design-pattern/decorator-3.png)
524+
![](https://java-tutorial.oss-cn-shanghai.aliyuncs.com/20230404212353.png)
527525

528526
我们知道 InputStream 代表了输入流,具体的输入来源可以是文件(FileInputStream)、管道(PipedInputStream)、数组(ByteArrayInputStream)等,这些就像前面奶茶的例子中的红茶、绿茶,属于基础输入流。
529527

0 commit comments

Comments
 (0)