-
Notifications
You must be signed in to change notification settings - Fork 0
/
search.xml
3922 lines (3492 loc) · 792 KB
/
search.xml
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
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>(四)提升系统的“读”性能</title>
<url>/2023/09/07/Improve-write-performance/</url>
<content><![CDATA[<h1 id="缓存"><a href="#缓存" class="headerlink" title="缓存"></a>缓存</h1><h2 id="本地缓存"><a href="#本地缓存" class="headerlink" title="本地缓存"></a>本地缓存</h2><blockquote>
<p>比如<code>Caffeine</code></p>
</blockquote>
<ul>
<li><p>缓存不太容易变化的数据(保障缓存一致性)</p>
</li>
<li><p>缓存数据量较小的数据(不会受到内存的限制)</p>
<p>如<strong>元数据</strong>、<strong>配置数据</strong>:启动的时候,从DB加载到内存的缓存块里。这样每次读这些数据的时候,就不用访问DB了</p>
</li>
</ul>
<h2 id="中心化缓存"><a href="#中心化缓存" class="headerlink" title="中心化缓存"></a>中心化缓存</h2><blockquote>
<p>比如<code>Redis</code></p>
</blockquote>
<ul>
<li>针对用户请求相关数据的缓存<ul>
<li>用户流量触发的DB的数据(查询)</li>
<li>业务计算的数据</li>
<li>返回结果的数据</li>
</ul>
</li>
</ul>
<h2 id="缓存更新策略"><a href="#缓存更新策略" class="headerlink" title="缓存更新策略"></a>缓存更新策略</h2><p><code>cache aside pattern</code>(经典缓存更新策略)采用的是写数据时删除缓存中旧值,读数据时更新新值</p>
<p>但如果有一个读请求和一个写请求几乎同时到达<code>DB</code></p>
<p>读请求先访问数据库,将(旧)值写入缓存时有些延迟</p>
<p>而写请求后访问数据库(新值),但把缓存删完了,读请求才开始写入缓存</p>
<p>这就造成缓存数据是旧的</p>
<h3 id="解决方案"><a href="#解决方案" class="headerlink" title="解决方案"></a>解决方案</h3><blockquote>
<p>保证即使把旧值更新到cache的情况,在后面的某一个时间点也能淘汰掉有问题的缓存</p>
</blockquote>
<div class="mermaid-wrap"><pre class="mermaid-src" hidden>
sequenceDiagram
Server->>Database: 1.写入数据
Server->>Cache: 2.删除缓存
Server->>Database: 3.读取数据
Server->>Cache: 4.写入缓存(带过期时间)
</pre></div>
<h2 id="缓存雪崩"><a href="#缓存雪崩" class="headerlink" title="缓存雪崩"></a>缓存雪崩</h2><h3 id="方案一"><a href="#方案一" class="headerlink" title="方案一"></a>方案一</h3><p>随机过期时间</p>
<h3 id="方案二"><a href="#方案二" class="headerlink" title="方案二"></a>方案二</h3><p>缓存分片:建立缓存集群,根据key的不同情况访问不同的缓存分片。这样即使有一个分片挂掉,那最多也就是一部分数据挂掉了</p>
<h1 id="缓存当DB用"><a href="#缓存当DB用" class="headerlink" title="缓存当DB用"></a>缓存当DB用</h1><blockquote>
<p>在流量大 + 产品多 + 变化多的典型类电商场景下</p>
<ul>
<li>DB扛不住,原因显而易见</li>
<li>缓存也扛不住:频繁失效,起不到作用</li>
</ul>
</blockquote>
<p>在限制条件全部满足的情况下</p>
<ul>
<li><strong>只读场景</strong>(注意,是只!读!场!景!),因为<strong>仅</strong>写到cache有数据丢失的风险</li>
<li><strong>有高可用方案</strong>(如<code>Redis</code>、<code>memcached</code>这种,保证cache不挂)</li>
</ul>
<p>可以采用:</p>
<ul>
<li>DB定时同步数据到cache</li>
<li>server把cache当DB用</li>
</ul>
<h1 id="DB读写分离"><a href="#DB读写分离" class="headerlink" title="DB读写分离"></a>DB读写分离</h1><h2 id="经典方案"><a href="#经典方案" class="headerlink" title="经典方案"></a>经典方案</h2><ul>
<li><p>有主DB和备DB</p>
</li>
<li><p>往主DB写,通过<code>binlog</code>等方式和备DB数据同步,然后在备DB读</p>
</li>
</ul>
<p>好处:理论上DB读服务可以无限扩容、读库可以单独建索引</p>
<p>问题:主备库数据不一致风险</p>
<h2 id="解决数据不一致"><a href="#解决数据不一致" class="headerlink" title="解决数据不一致"></a>解决数据不一致</h2><h3 id="方案一-1"><a href="#方案一-1" class="headerlink" title="方案一"></a>方案一</h3><blockquote>
<p>最常用</p>
</blockquote>
<p>直接不管:有些业务不要求即时响应,而且主备同步大概也就百毫秒级别</p>
<h3 id="方案二:结合产品设计"><a href="#方案二:结合产品设计" class="headerlink" title="方案二:结合产品设计"></a>方案二:结合产品设计</h3><blockquote>
<p>简单</p>
</blockquote>
<p>让前端整点加载动画,本质上就是延迟用户的读行为</p>
<h3 id="方案三:强制读主"><a href="#方案三:强制读主" class="headerlink" title="方案三:强制读主"></a>方案三:强制读主</h3><blockquote>
<p>也行</p>
</blockquote>
<p>强一致性读主库,弱一致性读备库</p>
<h3 id="方案四:缓存路由"><a href="#方案四:缓存路由" class="headerlink" title="方案四:缓存路由"></a>方案四:缓存路由</h3><blockquote>
<p>复杂度较高,不推荐</p>
</blockquote>
<p>写DB同时写个百毫秒(超过同步时间)超时的缓存</p>
<ul>
<li>没读到:备库更完了,读备库</li>
<li>读到了:那就用呗,保新</li>
</ul>
<h1 id="并发思维"><a href="#并发思维" class="headerlink" title="并发思维"></a>并发思维</h1><blockquote>
<p>有一个文件,有1亿条交易数据,从中找出交易金额最大的前100条数据</p>
</blockquote>
<ul>
<li>你是不是开始考虑如何快速读取文件了?</li>
<li>你是不是开始考虑用什么排序算法了?</li>
<li>你是不是想到内存可能不够用了?</li>
</ul>
<p>你首先应该想到,如何使用<strong>更多的资源</strong>来<strong>并发</strong>处理:</p>
<p>拆分数据,投递各机器,分片排序前100条,再对汇总数据排序</p>
<h1 id="异步化设计"><a href="#异步化设计" class="headerlink" title="异步化设计"></a>异步化设计</h1><blockquote>
<p>对于qps高的功能,只做最重要的事</p>
</blockquote>
<p>不影响主业务的步骤异步执行,先返回主业务结果(使用观察者模式)</p>
<h1 id="优化产品方案"><a href="#优化产品方案" class="headerlink" title="优化产品方案"></a>优化产品方案</h1><ul>
<li>分页查询,减少一次查询量</li>
<li>递进展示</li>
<li>降低极致的准确性要求</li>
<li>高峰流量期间降级部分功能</li>
<li>控制(主动或者被动)重试</li>
</ul>
<h2 id="其他一些读性能优化手段"><a href="#其他一些读性能优化手段" class="headerlink" title="其他一些读性能优化手段"></a>其他一些读性能优化手段</h2><ul>
<li>优化协议</li>
<li>流量拦截</li>
<li>静态缓存</li>
<li>数据压缩</li>
<li>…</li>
</ul>
]]></content>
<categories>
<category>架构学习</category>
</categories>
<tags>
<tag>架构设计</tag>
</tags>
</entry>
<entry>
<title>后端入门</title>
<url>/2023/08/05/backend-guide/</url>
<content><![CDATA[<blockquote>
<p>考虑到读者的学习心态和接受程度,本博客会随考核过程进行逐步更新,防止部分人为图进度造成根基不稳</p>
<p>合适的知识应该放到合适的时候再讲</p>
</blockquote>
<h1 id="什么是后端"><a href="#什么是后端" class="headerlink" title="什么是后端"></a>什么是后端</h1><p>在说明后端的职责之前,我们先来讲讲前端都要干些什么:</p>
<ul>
<li>向后端发出请求,接收后端数据</li>
<li>将数据展示给用户,让用户看到和操作网页</li>
</ul>
<p>当请求最后到达服务器,服务器的程序会按照后端编写的逻辑处理数据,和数据库交互,最后将结果返回给前端</p>
<p>可以用一个图来简单描述一下前后端在一个淘宝用户从下单到完成过程中分别干的事</p>
<div class="mermaid-wrap"><pre class="mermaid-src" hidden>
sequenceDiagram
participant 用户
participant 前端网页
participant 后端服务
participant 数据库
用户->>前端网页: 选择商品并下单
前端网页->>后端服务: 发送下单请求
activate 后端服务
后端服务->>后端服务: 验证用户身份和库存
后端服务->>数据库: 更新库存信息
activate 数据库
数据库-->>后端服务: 返回库存更新结果
deactivate 数据库
后端服务-->>前端网页: 返回下单结果
deactivate 后端服务
前端网页->>用户: 显示下单结果
用户->>前端网页: 完成支付
前端网页->>后端服务: 发送支付请求
activate 后端服务
后端服务->>后端服务: 验证支付信息和库存
后端服务->>数据库: 更新库存信息和订单状态
activate 数据库
数据库-->>后端服务: 返回库存更新结果和订单状态
deactivate 数据库
后端服务-->>前端网页: 返回支付结果和订单状态
deactivate 后端服务
前端网页->>用户: 显示支付结果和订单状态
</pre></div>
<p>当然,这只是个简单的演示,在实际生产中,后端可能还要考虑:</p>
<ul>
<li>并发处理:使用并发处理技术来提高系统的并发能力,同时处理多个用户的下单和支付请求,提高系统的吞吐量。</li>
<li>缓存优化:使用缓存技术来缓存商品的库存信息,减少对数据库的访问,提高系统的响应速度和性能。</li>
<li>异步处理:使用消息队列等异步处理机制,将库存更新和订单状态更新等耗时的操作异步处理,提高系统的响应速度和稳定性。</li>
<li>负载均衡:使用负载均衡技术将请求分发到多个后端服务器上,实现负载均衡,提高系统的可扩展性和容错性。</li>
<li>……</li>
</ul>
<h1 id="Web后端用什么语言"><a href="#Web后端用什么语言" class="headerlink" title="Web后端用什么语言"></a>Web后端用什么语言</h1><h2 id="Java"><a href="#Java" class="headerlink" title="Java"></a>Java</h2><ol>
<li><strong>广泛应用和成熟生态系统</strong>:Java是一种广泛应用的编程语言,拥有庞大的开发者社区和成熟的生态系统。这意味着有大量的开发资源、框架、库和工具可用,可以加快开发速度并提高开发效率。</li>
<li><strong>跨平台性</strong>:Java具有跨平台性,这意味着编写的Java代码可以在不同的操作系统上运行,如Windows、Linux和Mac等。这种跨平台性使得Java后端应用程序可以在多个环境中部署和运行,提供了更大的灵活性和可扩展性。</li>
<li><strong>强大的性能和可伸缩性</strong>:Java具有优秀的性能和可伸缩性,特别适用于处理大规模的数据和高并发的情况。Java的虚拟机(JVM)和优化技术使得Java应用程序能够高效地执行,并且可以通过水平和垂直扩展来满足不断增长的需求。</li>
<li><strong>面向对象编程(OOP)</strong>:Java是一种面向对象的编程语言,它提供了丰富的面向对象特性和概念,如封装、继承和多态等。这使得Java代码更易于理解、维护和扩展,并且可以促进团队协作和代码重用。</li>
<li><strong>丰富的库和框架支持</strong>:Java拥有大量的开源库和框架,如Spring、Hibernate和Apache Commons等。这些库和框架提供了丰富的功能和工具,可以简化开发过程,提高代码质量,并提供了许多可靠的解决方案。</li>
<li><strong>安全性和可靠性</strong>:Java具有强大的安全性和可靠性特性。Java的安全模型和内置的安全机制可以帮助开发人员编写安全的应用程序,并提供对数据和网络的保护。此外,Java的严格类型检查和异常处理机制可以减少错误和异常情况的发生,提高应用程序的可靠性。</li>
</ol>
<h2 id="C"><a href="#C" class="headerlink" title="C++"></a>C++</h2><p>因为<code>C++</code>语言效率高,所以大多用于游戏服务器,但由于复杂、繁琐,代码维护成本高,开发效率相对低,所以在Web后端中占比较少</p>
<h2 id="Go"><a href="#Go" class="headerlink" title="Go"></a>Go</h2><p><code>Go</code>是一种相对较新的编程语言,轻量、快速、易于学习,但相对生态没有<code>Java</code>丰富。现在大多是一些Python转Go或者进行后端重构的大公司使用,比如知乎、字节跳动……</p>
<p>笔者个人也对Go语言进行过小小尝试,最后发现还是更喜欢Java,因为开发体验实在是太爽了…</p>
<h1 id="Java学习路线"><a href="#Java学习路线" class="headerlink" title="Java学习路线"></a>Java学习路线</h1><p>请注意,本文所有提供的教程与链接仅供参考</p>
<p>没有哪个教程是绝对完美的,盲目信从和全盘接受是不可取的。请批判式学习,多多实践</p>
<p>可以根据自己的学习情况和接收程度,自行寻找更适合自己的教程和学习方式</p>
<h2 id="第一阶段:JavaSE"><a href="#第一阶段:JavaSE" class="headerlink" title="第一阶段:JavaSE"></a>第一阶段:JavaSE</h2><p>JavaSE是Java语言的基础,在这个过程中,你应该重点掌握:</p>
<ul>
<li>Java的基础语法、流程控制与方法、数组</li>
<li>面向对象编程的三大特性:封装、继承、多态</li>
<li>异常、常用类、集合、IO流</li>
<li>反射</li>
</ul>
<p>你可以选择了解的知识有</p>
<ul>
<li>并发与多线程、注解</li>
<li>JVM(Java虚拟机)</li>
</ul>
<p><strong>参考教程</strong></p>
<p>⬇️该教程网络通信部分有兴趣可以看,不做强制要求⬇️</p>
]]></content>
<categories>
<category>学习路线</category>
</categories>
<tags>
<tag>学习路线</tag>
</tags>
</entry>
<entry>
<title>防坑集锦</title>
<url>/2023/09/07/avoid-bugs/</url>
<content><![CDATA[<h1 id="谨慎使用-Builder"><a href="#谨慎使用-Builder" class="headerlink" title="谨慎使用@Builder"></a>谨慎使用<code>@Builder</code></h1><p>大多数同学使用 <code>@Builder</code> 无非就是为了链式编程,然而 <code>@Builder</code> 并不是链式编程的最佳实践,它会额外创建内部类,存在继承关系时还需要使用 <code>@SuperBuilder</code> 注解,设置默认值时也需要额外的 <code>@Builder.Default</code> 去设置默认值,无疑增加了很多不必要的复杂度。</p>
<h2 id="为什么"><a href="#为什么" class="headerlink" title="为什么"></a>为什么</h2><p><strong>(1)<code>@Builder</code> 生成的构造器不是完美的</strong>,它不能区分哪些参数是必须的,哪些是可选的。如果没有提供必须的参数,构造器可能会创建出不完整或者不合法的对象。</p>
<blockquote>
<p>@Builder 注解产生的 Builder 类的构造方法默认并不能限定必传参数。</p>
</blockquote>
<p><strong>(2)很多人 喜欢 <code>@Builder</code> 和 <code>@Data</code> 搭配使用,导致生成的构造器是可变的</strong>,它允许使用 <code>setter</code> 方法修改构造器的状态。这违反了构造器模式的原则,构造器应该是不可变的,一旦创建就不能被修改。</p>
<blockquote>
<p>如果非要使用 <code>@Builder</code> ,那么不要用 <code>@Data</code> ,要用 <code>@Getter</code>。相对来说,反而 <code>@Accessors</code> 的行为更符合这个要求。</p>
</blockquote>
<p><strong>(3)@Builder 生成的构造器不适合用于短暂的对象</strong>,它会增加代码的复杂度和冗余。构造器模式更适合用于生命周期较长、有多种变体的对象。</p>
<blockquote>
<p>实际使用中经常发现 <code>@Builder</code> 滥用的情况,有些仅仅一两个属性的类也都要用 <code>@Builder</code>,真的没必要用,直接用全参的构造方法都比这更简洁。</p>
</blockquote>
<p><strong>(4)<code>@Builder</code> 生成的构造器不能处理抽象类型的参数</strong>,它只能接受具体类型的对象。这限制了构造器的灵活性和扩展性,不能根据不同的需求创建不同风格的对象。</p>
<p><strong>(5)继承关系时,子类需要使用 <code>@SuperBuilder</code>。</strong>对象继承后,子类的 <code>Builder</code> 因为构造函数的问题,使用不当大概率会报错,并且无法设置父类的属性,还需要使用 <code>@SuperBuilder</code> 来解决问题。</p>
<p><strong>(6)设置默认值需要使用 <code>@Builder.Default</code>。</strong>很容易因为对此不了解,导致默认值不符合预期导致出现 BUG。</p>
<h2 id="怎么做"><a href="#怎么做" class="headerlink" title="怎么做"></a>怎么做</h2><p>实际运用时,如果属性较多,且分为必传属性和选填属性时,可以将必传参数定义在构造方法中,加上 @Accessors 注解,这样就可以实现必传参数的传入,又可以实现选填参数的链式调用。</p>
<blockquote>
<p>假设 Student 类,它的 学生ID和年级和班级是必填的,姓名、性别、住址是选填的,那么示例代码如下</p>
</blockquote>
<p><strong>使用 @Accessors 注解</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> lombok.Data;</span><br><span class="line"><span class="keyword">import</span> lombok.experimental.Accessors;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="comment">// 使用 @Accessors 注解,设置 chain = true,生成返回 this 的 setter 方法</span></span><br><span class="line"><span class="meta">@Accessors(chain = true)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Student</span> {</span><br><span class="line"> <span class="comment">// 定义必传属性,使用 final 修饰,不提供 setter 方法</span></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">int</span> studentId; <span class="comment">// 学生ID</span></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">int</span> grade; <span class="comment">// 年级</span></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">int</span> classNum; <span class="comment">// 班级</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// 定义选填属性,提供 setter 方法</span></span><br><span class="line"> <span class="keyword">private</span> String name; <span class="comment">// 姓名</span></span><br><span class="line"> <span class="keyword">private</span> String gender; <span class="comment">// 性别</span></span><br><span class="line"> <span class="keyword">private</span> String address; <span class="comment">// 住址</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// 定义构造方法,接收必传参数</span></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">Student</span><span class="params">(<span class="type">int</span> studentId, <span class="type">int</span> grade, <span class="type">int</span> classNum)</span> {</span><br><span class="line"> <span class="built_in">this</span>.studentId = studentId;</span><br><span class="line"> <span class="built_in">this</span>.grade = grade;</span><br><span class="line"> <span class="built_in">this</span>.classNum = classNum;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><strong>等价代码</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Student</span> {</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">int</span> studentId; <span class="comment">// 学生ID</span></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">int</span> grade; <span class="comment">// 年级</span></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">int</span> classNum; <span class="comment">// 班级</span></span><br><span class="line"> <span class="keyword">private</span> String name; <span class="comment">// 姓名</span></span><br><span class="line"> <span class="keyword">private</span> String gender; <span class="comment">// 性别</span></span><br><span class="line"> <span class="keyword">private</span> String address; <span class="comment">// 住址</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">Student</span><span class="params">(<span class="type">int</span> studentId, <span class="type">int</span> grade, <span class="type">int</span> classNum)</span> {</span><br><span class="line"> <span class="built_in">this</span>.studentId = studentId;</span><br><span class="line"> <span class="built_in">this</span>.grade = grade;</span><br><span class="line"> <span class="built_in">this</span>.classNum = classNum;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getStudentId</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">this</span>.studentId;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getGrade</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">this</span>.grade;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getClassNum</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">this</span>.classNum;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> String <span class="title function_">getName</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">this</span>.name;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> Student <span class="title function_">setName</span><span class="params">(String name)</span> {</span><br><span class="line"> <span class="built_in">this</span>.name = name;</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">this</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> String <span class="title function_">getGender</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">this</span>.gender;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> Student <span class="title function_">setGender</span><span class="params">(String gender)</span> {</span><br><span class="line"> <span class="built_in">this</span>.gender = gender;</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">this</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> String <span class="title function_">getAddress</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">this</span>.address;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> Student <span class="title function_">setAddress</span><span class="params">(String address)</span> {</span><br><span class="line"> <span class="built_in">this</span>.address = address;</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">this</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><strong>使用示例</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="type">Student</span> <span class="variable">student</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Student</span>(<span class="number">1001</span>, <span class="number">3</span>, <span class="number">8</span>) <span class="comment">// 创建一个学生对象,传入必传参数</span></span><br><span class="line"> .setName(<span class="string">"张三"</span>) <span class="comment">// 设置姓名</span></span><br><span class="line"> .setGender(<span class="string">"男"</span>) <span class="comment">// 设置性别</span></span><br><span class="line"> .setAddress(<span class="string">"北京市朝阳区"</span>); <span class="comment">// 设置住址</span></span><br></pre></td></tr></table></figure>
<h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p>[^1]: <a href="https://mp.weixin.qq.com/s/yp-lvB9A5dVBrsBT3XkICA">请谨慎使用 @Builder 注解!</a></p>
]]></content>
<categories>
<category>阅读</category>
</categories>
<tags>
<tag>防坑集锦</tag>
</tags>
</entry>
<entry>
<title>(三)如何提高代码的扩展性</title>
<url>/2023/09/05/carry-the-flow/</url>
<content><![CDATA[<p>先来看一段代码</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 转账服务</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> payer 付款方</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> payee 收款方</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> money 转账金额</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> 是否转账成功</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">transfer</span><span class="params">(String payer, String payee, String money)</span> {</span><br><span class="line"> Log.info(<span class="string">"transfer start, payer = "</span> + payer + <span class="string">", payee = "</span> + payee + <span class="string">", money = "</span> + money);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 1. 检查参数</span></span><br><span class="line"> <span class="keyword">if</span> (!isValidUser(payer) || !isValidUser(payee) || !isValidMoney(money)) {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 2. 调用转账服务</span></span><br><span class="line"> <span class="type">TransferResult</span> <span class="variable">transferResult</span> <span class="operator">=</span> transferService.transfer(payer, payee, money);</span><br><span class="line"> <span class="keyword">if</span> (!transferResult.isSuccess()) {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 3. 查询用户通知方式</span></span><br><span class="line"> <span class="type">UserInfo</span> <span class="variable">userInfo</span> <span class="operator">=</span> userInfoService.getUserInfo(payee);</span><br><span class="line"> <span class="keyword">if</span> (userInfo.getNotifyType() == NotifyTypeEnum.SMS) {</span><br><span class="line"> <span class="comment">// smsNotifyService 是第三方 jar 包</span></span><br><span class="line"> smsClient.sendSms(payee, NOTIFY_CONTENT);</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (userInfo.getNotifyType() == NotifyTypeEnum.MAIL) {</span><br><span class="line"> <span class="comment">// mailNotifyService 是第三方 jar 包</span></span><br><span class="line"> mailClient.sendMail(payee, NOTIFY_CONTENT);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 记录转账账单(发送事件给转账系统)</span></span><br><span class="line"> biiiService.sendBiii(transferResult);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 转账监控打点(调用监控 JDK)</span></span><br><span class="line"> monitorService.sendRecord(transferResult);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 记录转账额度(调用额度中心)</span></span><br><span class="line"> quotaService.recordQuota(transferResult);</span><br><span class="line"></span><br><span class="line"> Log.info(<span class="string">"transfer success"</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>是不是感觉还行?但其实<strong>就扩展性而言</strong>,它是有很多问题的。</p>
<h1 id="出参和入参的扩展性"><a href="#出参和入参的扩展性" class="headerlink" title="出参和入参的扩展性"></a>出参和入参的扩展性</h1><h2 id="问题:出入参不具备扩展性,且有巨坑"><a href="#问题:出入参不具备扩展性,且有巨坑" class="headerlink" title="问题:出入参不具备扩展性,且有巨坑"></a>问题:出入参不具备扩展性,且有巨坑</h2><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">transfer</span><span class="params">(String payer, String payee, String money)</span> {</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<ul>
<li><p>入参全是<code>String</code>,这种长入参的方式是不被推崇的:因为他们的类型一致。</p>
<p>如果调用时参数顺序填错,就会出大问题。</p>
</li>
<li><p>出参是一个布尔类型,但往往我们转账失败以后,我们是要告知转账失败的原因的,原因有各种各样,不可能只靠一个布尔值返回</p>
</li>
</ul>
<h2 id="解决措施:封装请求"><a href="#解决措施:封装请求" class="headerlink" title="解决措施:封装请求"></a>解决措施:封装请求</h2><p>封装入参,校验方法接收一个封装好的请求对象。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">transfer</span><span class="params">(TransferRequest transferRequest)</span> {</span><br><span class="line"> Log.info(<span class="string">"transfer start, transferRequest="</span> + transferRequest);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 1. 检查参数(checkParam实现见下文)</span></span><br><span class="line"> validatorManager.checkParam(transferRequest);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h1 id="校验的扩展性"><a href="#校验的扩展性" class="headerlink" title="校验的扩展性"></a>校验的扩展性</h1><h2 id="问题:随着参数新增,校验会变得越来越多"><a href="#问题:随着参数新增,校验会变得越来越多" class="headerlink" title="问题:随着参数新增,校验会变得越来越多"></a>问题:随着参数新增,校验会变得越来越多</h2><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">if</span> (!isValidUser(payer) || !isValidUser(payee) || !isValidMoney(money)) {</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="解决:责任链模式"><a href="#解决:责任链模式" class="headerlink" title="解决:责任链模式"></a>解决:责任链模式</h2><p>把具体的逻辑放在具体的类里去实现,然后串接起来执行</p>
<h3 id="1-定义校验接口"><a href="#1-定义校验接口" class="headerlink" title="1.定义校验接口"></a>1.定义校验接口</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 参数校验器接口</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">ParamValidator</span> {</span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 校验入参</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">void</span> <span class="title function_">checkParam</span><span class="params">(TransferRequest transferRequest)</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="2-实现校验类"><a href="#2-实现校验类" class="headerlink" title="2.实现校验类"></a>2.实现校验类</h3><h3 id="3-实现校验管理器"><a href="#3-实现校验管理器" class="headerlink" title="3.实现校验管理器"></a>3.实现校验管理器</h3><p>使用<code>Spring</code>框架,当<code>ValidatorManager</code>这个<code>bean</code>在初始化以后,会去整个<code>spring上下文</code>把所有<code>ParalValidator</code>的实现类的<code>bean</code>都捞出来,放到一个<code>list</code>里去。</p>
<p>他还会对外暴露一个<code>checkParam</code>的方法,当各种各样的服务去调用<code>checkParam</code>方法的时候,它就会去循环调用所有校验的实现类去校验入参</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ValidatorManager</span> <span class="keyword">implements</span> <span class="title class_">InitializingBean</span> {</span><br><span class="line"> <span class="meta">@Autowired</span></span><br><span class="line"> <span class="keyword">private</span> ApplicationContext applicationContext;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> List<ParamValidator> validatorList = <span class="keyword">new</span> <span class="title class_">ArrayList</span><>();</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">afterPropertiesSet</span><span class="params">()</span> <span class="keyword">throws</span> Exception {</span><br><span class="line"> Map<String, ParamValidator> paramValidatorMap = applicationContext.getBeansOfType(ParamValidator.class);</span><br><span class="line"> validatorList = <span class="keyword">new</span> <span class="title class_">ArrayList</span><>(paramValidatorMap.values());</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">checkParam</span><span class="params">(TransferRequest transferRequest)</span> {</span><br><span class="line"> <span class="keyword">for</span> (ParamValidator paramValidator : validatorList) {</span><br><span class="line"> paramValidator.checkParam(transferRequest);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>这样新增参数或者修改旧参数校验逻辑,无需修改<code>transfer</code>方法。</p>
<p>只需要去实现一个新的校验器,(用刚才从<code>Spring</code>上下文加载的方式)自然而然的就会被纳入到校验管理器的管理范畴</p>
<h3 id="更多优化点"><a href="#更多优化点" class="headerlink" title="更多优化点"></a>更多优化点</h3><p>在校验器上面还可以打上<code>@Order</code>注解,这个<code>@Order</code>注解可以自定义。<code>ValidatorManager</code>去捞取所有实现类的时候,可以根据上面的汪解去判断执行逻辑的顺序(其实就有一个编排的概念在里边了)</p>
<h1 id="渠道的扩展性"><a href="#渠道的扩展性" class="headerlink" title="渠道的扩展性"></a>渠道的扩展性</h1><h2 id="问题:增加通知方式就要加if-else"><a href="#问题:增加通知方式就要加if-else" class="headerlink" title="问题:增加通知方式就要加if-else"></a>问题:增加通知方式就要加<code>if-else</code></h2><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 3. 查询用户通知方式</span></span><br><span class="line"><span class="type">UserInfo</span> <span class="variable">userInfo</span> <span class="operator">=</span> userInfoService.getUserInfo(payee);</span><br><span class="line"><span class="keyword">if</span> (userInfo.getNotifyType() == NotifyTypeEnum.SMS) {</span><br><span class="line"> <span class="comment">// smsNotifyService 是第三方 jar 包</span></span><br><span class="line"> smsClient.sendSms(payee, NOTIFY_CONTENT);</span><br><span class="line">} <span class="keyword">else</span> <span class="keyword">if</span> (userInfo.getNotifyType() == NotifyTypeEnum.MAIL) {</span><br><span class="line"> <span class="comment">// mailNotifyService 是第三方 jar 包</span></span><br><span class="line"> mailClient.sendMail(payee, NOTIFY_CONTENT);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="解决:适配器模式"><a href="#解决:适配器模式" class="headerlink" title="解决:适配器模式"></a>解决:适配器模式</h2><h3 id="1-定义通知接口"><a href="#1-定义通知接口" class="headerlink" title="1.定义通知接口"></a>1.定义通知接口</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 通知服务接口</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">NotifyService</span> {</span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 发送通知</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">void</span> <span class="title function_">notifyMessage</span><span class="params">(String userId, String content)</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="2-适配各种通知"><a href="#2-适配各种通知" class="headerlink" title="2.适配各种通知"></a>2.适配各种通知</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MailNotifyService</span> <span class="keyword">implements</span> <span class="title class_">NotifyService</span> {</span><br><span class="line"> <span class="meta">@Autowired</span></span><br><span class="line"> <span class="keyword">private</span> MailClient mailClient;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">notifyMessage</span><span class="params">(String userId, String content)</span> {</span><br><span class="line"> <span class="comment">// 邮件服务商提供的sdk</span></span><br><span class="line"> mailClient.sendMail(userId, content);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SmsNotifyService</span> <span class="keyword">implements</span> <span class="title class_">NotifyService</span> {</span><br><span class="line"> <span class="meta">@Autowired</span></span><br><span class="line"> <span class="keyword">private</span> SmsClient smsClient;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">notifyMessage</span><span class="params">(String userId, String content)</span> {</span><br><span class="line"> <span class="comment">// 短信服务商提供的sdk</span></span><br><span class="line"> smsClient.sendSms(userId, content);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="3-实现通知管理器"><a href="#3-实现通知管理器" class="headerlink" title="3.实现通知管理器"></a>3.实现通知管理器</h3><p>通知管理器就是在初始化完了以后,会获取到所有被注入的通知服务。</p>
<p>然后,根据不同的这个通知类型去映射成<code>map</code>,对外提供<code>notify</code>方法.</p>
<p>通知的时候,需要传进来具体的通知方式、通知的用户、具体的通知内容。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Service</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">NotifyServiceManager</span> <span class="keyword">implements</span> <span class="title class_">InitializingBean</span> {</span><br><span class="line"> <span class="meta">@Autowired</span></span><br><span class="line"> <span class="keyword">private</span> SmsNotifyService smsNotifyService;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Autowired</span></span><br><span class="line"> <span class="keyword">private</span> MailNotifyService mailNotifyService;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> Map<NotifyTypeEnum, NotifyService> notifyServiceMap = Maps.newHashMap();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">afterPropertiesSet</span><span class="params">()</span> <span class="keyword">throws</span> Exception {</span><br><span class="line"> <span class="comment">// 注册通知类型到通知bean的映射关系</span></span><br><span class="line"> notifyServiceMap.put(NotifyTypeEnum.SMS, smsNotifyService);</span><br><span class="line"> notifyServiceMap.put(NotifyTypeEnum.MAIL, mailNotifyService);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">notify</span><span class="params">(NotifyTypeEnum notifyTypeEnum, String userId, String content)</span> {</span><br><span class="line"> <span class="type">NotifyService</span> <span class="variable">notifyService</span> <span class="operator">=</span> notifyServiceMap.get(notifyTypeEnum);</span><br><span class="line"> <span class="keyword">if</span> (notifyService == <span class="literal">null</span>) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(<span class="string">"Notify service not exist"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> notifyService.notifyMessage(userId, content);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<h3 id="4-替换原先实现"><a href="#4-替换原先实现" class="headerlink" title="4.替换原先实现"></a>4.替换原先实现</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 3. 通知收款方</span></span><br><span class="line"><span class="type">UserInfo</span> <span class="variable">userInfo</span> <span class="operator">=</span> userInfoService.getUserInfo(transferRequest.getPayeeId);</span><br><span class="line"><span class="type">NotifyTypeEnum</span> <span class="variable">notifyTypeEnum</span> <span class="operator">=</span> userInfo.getNotifyType();</span><br><span class="line">notifyServiceManager.notify(notifyTypeEnum, transferRequest.getPayeeId(), NOTIFY_CONTENT);</span><br></pre></td></tr></table></figure>
<p>这样,新增通知也无需修改<code>transfer</code>方法</p>
<p>增加一个后置动作就要修改这个核心业务(转账方法 )的代码 </p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 记录转账账单(发送事件给转账系统)</span></span><br><span class="line">biiiService.sendBiii(transferResult);</span><br><span class="line"><span class="comment">// 转账监控打点(调用监控 JDK)</span></span><br><span class="line">monitorService.sendRecord(transferResult);</span><br><span class="line"><span class="comment">// 记录转账额度(调用额度中心)</span></span><br><span class="line">quotaService.recordQuota(transferResult);</span><br></pre></td></tr></table></figure>
<h1 id="业务的扩展性"><a href="#业务的扩展性" class="headerlink" title="业务的扩展性"></a>业务的扩展性</h1><h2 id="问题:业务变动需要修改核心代码"><a href="#问题:业务变动需要修改核心代码" class="headerlink" title="问题:业务变动需要修改核心代码"></a>问题:业务变动需要修改核心代码</h2><p>增加一个后置动作就要修改这个核心业务(转账方法 )的代码 </p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 记录转账账单(发送事件给转账系统)</span></span><br><span class="line">biiiService.sendBiii(transferResult);</span><br><span class="line"><span class="comment">// 转账监控打点(调用监控 JDK)</span></span><br><span class="line">monitorService.sendRecord(transferResult);</span><br><span class="line"><span class="comment">// 记录转账额度(调用额度中心)</span></span><br><span class="line">quotaService.recordQuota(transferResult);</span><br></pre></td></tr></table></figure>
<h2 id="解决:观察者模式"><a href="#解决:观察者模式" class="headerlink" title="解决:观察者模式"></a>解决:观察者模式</h2><h3 id="1-定义观察者接口"><a href="#1-定义观察者接口" class="headerlink" title="1.定义观察者接口"></a>1.定义观察者接口</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 观察者接口</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">TransferObserver</span> {</span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 回调接口</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">void</span> <span class="title function_">update</span><span class="params">(TransferResult transferResult)</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="2-实现各种观察者"><a href="#2-实现各种观察者" class="headerlink" title="2.实现各种观察者"></a>2.实现各种观察者</h3><h3 id="3-实现主题订阅"><a href="#3-实现主题订阅" class="headerlink" title="3.实现主题订阅"></a>3.实现主题订阅</h3><p>主题订阅和之前实现的管理器是相似的,也是在<code>bean</code>初始化以后,从上下文捞出所有的观察者接口的实现类。然后对外提供<code>notifyObserver</code>,也就是触发观察者的方法,在里边去执行每一个观察者</p>
<p>在触发观察者的行为时,观察者的执行可以全部放到异步线程池去执行(如下)</p>
<p>这代表了和责任链模式的几个不同点:</p>
<ul>
<li>所有观察者的执行之间是不会相互影响的(达到了解耦)</li>
<li>观祭者执行是可以做到并行的(因为解耦)</li>
<li>观察者是在主业务执行完后才触发的。观察者逻辑不会再影响主业务逻辑</li>
</ul>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TransferSubject</span> <span class="keyword">implements</span> <span class="title class_">InitializingBean</span> {</span><br><span class="line"> <span class="meta">@Autowired</span></span><br><span class="line"> <span class="keyword">private</span> ApplicationContext applicationContext;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> List<TransferObserver> transferObserverList = <span class="keyword">new</span> <span class="title class_">ArrayList</span><>();</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 异步线程池</span></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">ExecutorService</span> <span class="variable">executorService</span> <span class="operator">=</span> Executors.newFixedThreadPool(<span class="number">10</span>);</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">afterPropertiesSet</span><span class="params">()</span> <span class="keyword">throws</span> Exception {</span><br><span class="line"> Map<String, TransferObserver> transferObserverMap =</span><br><span class="line"> applicationContext.getBeansOfType(TransferObserver.class);</span><br><span class="line"> </span><br><span class="line"> transferObserverMap.values().forEach(<span class="built_in">this</span>::addObserver);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 触发观察者</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">notifyObservers</span><span class="params">(TransferResult transferResult)</span> {</span><br><span class="line"> transferObserverList.forEach(transferObserver -> {</span><br><span class="line"> <span class="comment">// 异步执行</span></span><br><span class="line"> executorService.execute(() -> transferObserver.update(transferResult));</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 添加观察者</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addObserver</span><span class="params">(TransferObserver transferObserver)</span> {</span><br><span class="line"> transferObserverList.add(transferObserver);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="4-替换原来逻辑"><a href="#4-替换原来逻辑" class="headerlink" title="4.替换原来逻辑"></a>4.替换原来逻辑</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">//4.通知各类观察者</span></span><br><span class="line">transferSubject.notifyObserver(transferResult)</span><br><span class="line"> </span><br><span class="line">log.info(<span class="string">"transfer success"</span>)</span><br><span class="line"><span class="keyword">return</span> <span class="literal">true</span>;</span><br></pre></td></tr></table></figure>
<p>这样,新增观察者也无需修改<code>transfer</code>方法</p>
<h1 id="策略模式"><a href="#策略模式" class="headerlink" title="策略模式"></a>策略模式</h1><p>策略模式就是定义算法族并封装,让他们可以相互替换,算法独立</p>
<blockquote>
<p>假设你开发了一个游戏,游戏里面有多种角色,包括:剑士、射手、医生、护士等。剑士和射手是战斗职业,可以打怪也可以对打。医生和护士是治疗职业,负责给其他角色治疗。</p>
</blockquote>
<p>一种典型的设计方法如下:</p>
<p><img src="https://mmbiz.qpic.cn/mmbiz_jpg/KExqX4mSOWib1h0CZN1QQXbeY7Wgh7cbIw5mPdBhJicfxnH593kkZT0QobHt2FJzftVd8yOGEbdVT3O4OibWX48dg/640?wx_fmt=jpeg&wxfrom=5&wx_lazy=1&wx_co=1" alt="图片"></p>
<p>Role包含了每个角色都需要的属性,实现了每个角色都需要的功能。其他角色继承Role即可。</p>
<p>你一定注意到这里有个明显的问题,那就是Warrior和Shooter会有攻击(attack)行为,而Doctor和Nurse有治疗(cure)行为,并且这些行为都是相似的。</p>
<p>一种实现方式是,把这些行为都放在Role中,就会变成如下这样:</p>
<p><img src="https://mmbiz.qpic.cn/mmbiz_jpg/KExqX4mSOWib1h0CZN1QQXbeY7Wgh7cbIU6XzUFfIma2LowatlFNyIOLXaDicrrhba7tJw9HaCrlicJ3r77DeRR3w/640?wx_fmt=jpeg&wxfrom=5&wx_lazy=1&wx_co=1" alt="图片"></p>
<p>这有个明显的问题,那就是图中所描述的“一些类拥有了自己不需要的行为”。</p>
<p>为了解决这个问题,你也许会这样来修改类设计:</p>
<p><img src="https://mmbiz.qpic.cn/mmbiz_jpg/KExqX4mSOWib1h0CZN1QQXbeY7Wgh7cbICEjpvp15mUJy4Q2zTMMKVhlyzBZVFkWf2DC8aKI2l6xCXibG5NfCK9A/640?wx_fmt=jpeg&wxfrom=5&wx_lazy=1&wx_co=1" alt="图片"></p>
<p>中间增加了Fighter和MedicalStaff这一层,就可以把相似的行为提取出来了,是不是很完美?</p>
<p>那么,问题来了。如果这个时候来了一个新的职业叫做“法师(Wizard)”,这个职业又可以战斗又可以治疗怎么办呢?</p>
<p><img src="https://mmbiz.qpic.cn/mmbiz_jpg/KExqX4mSOWib1h0CZN1QQXbeY7Wgh7cbIPrAqLoLoTu8JKI5tkfTJ1jKKeCjhB9Hs4bF7sBKaXMRYMoOjgEVkqg/640?wx_fmt=jpeg&wxfrom=5&wx_lazy=1&wx_co=1" alt="图片"></p>
<p>明显,Wizard无论是继承Fighter还是MedicalStaff都不合适。</p>
<p>那是不是要建一个FighterAndMedicalStaff类?这显然是更不合适的。为什么呢?</p>
<p>其一,如果以后职业越来越多,这些职业的技能组合会使得FighterAndMedicalStaff这样的类越来越多。</p>
<p>其二,FighterAndMedicalStaff不继承Fighter和MedicalStaff的话,attack和cure方法就要重复写了。完全没有复用性可言。</p>
<p>所以,上面这种设计在扩展性上就显得力不从心。</p>
<p>那要怎么设计呢?那就是:【<strong>使用组合而非继承】</strong>。</p>
<p>上面的设计中,我们使用继承的目的是为了复用行为。例如战士(Warrior)和射手(Shooter)继承Fighter来复用战斗(attack)行为。</p>
<p>但是复用战斗行为并不一定要使用继承,也可以使用组合。我们把【战斗】和【治疗】两种行为抽成接口,然后实现各种不同的战斗和治疗行为。每个角色根据需要去组合这些行为即可。类图如下:</p>
<p><img src="https://mmbiz.qpic.cn/mmbiz_jpg/KExqX4mSOWib1h0CZN1QQXbeY7Wgh7cbIIfljMSGeEnWYKuHlEMJ9vKfA70OjA0HhiasJFQNjPIvIibFFtvbjKhIg/640?wx_fmt=jpeg&wxfrom=5&wx_lazy=1&wx_co=1" alt="图片"></p>
<p>通过上面这样的设计,每个角色的行为就可以非常方便地组合。你不难想象,如果某一天要求Doctor也可以战斗,扩展起来非常简单。</p>
<h1 id="装饰者模式"><a href="#装饰者模式" class="headerlink" title="装饰者模式"></a>装饰者模式</h1><p>装饰者模式可以动态将能力扩展到对象上</p>
<blockquote>
<p>还是那个游戏,里面有几种武器,现在多了绿宝石剑、红宝石剑和绿宝石法杖、红宝石法杖,该怎么设计?</p>
<ul>
<li>剑初始100攻击力法杖初始80攻击力</li>
<li>绿宝石+10攻击力、红宝石+20攻击力</li>
</ul>
</blockquote>
<h2 id="武器"><a href="#武器" class="headerlink" title="武器"></a>武器</h2><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 武器抽象接口</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">Weapon</span> {</span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 攻击力</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="type">int</span> <span class="title function_">damage</span><span class="params">()</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Staff</span> <span class="keyword">implements</span> <span class="title class_">Weapon</span> {</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">damage</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="number">80</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Sword</span> <span class="keyword">implements</span> <span class="title class_">Weapon</span> {</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">damage</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="number">100</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="武器装饰"><a href="#武器装饰" class="headerlink" title="武器装饰"></a>武器装饰</h2><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 武器装饰接口</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">WeaponDecorator</span> <span class="keyword">extends</span> <span class="title class_">Weapon</span> {</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 攻击加成</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AttackBuff</span> <span class="keyword">implements</span> <span class="title class_">WeaponDecorator</span> {</span><br><span class="line"> <span class="keyword">private</span> Weapon weapon;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">AttackBuff</span><span class="params">(Weapon weapon)</span> {</span><br><span class="line"> <span class="built_in">this</span>.weapon = weapon;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">damage</span><span class="params">()</span> {</span><br><span class="line"> <span class="comment">// 攻击力翻倍</span></span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">this</span>.weapon.damage() * <span class="number">2</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 绿宝石</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">GreenDiamond</span> <span class="keyword">implements</span> <span class="title class_">WeaponDecorator</span> {</span><br><span class="line"> <span class="keyword">private</span> Weapon weapon;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">GreenDiamond</span><span class="params">(Weapon weapon)</span> {</span><br><span class="line"> <span class="built_in">this</span>.weapon = weapon;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">damage</span><span class="params">()</span> {</span><br><span class="line"> <span class="comment">// 攻击力+10</span></span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">this</span>.weapon.damage() + <span class="number">10</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 红宝石</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RedDiamond</span> <span class="keyword">implements</span> <span class="title class_">WeaponDecorator</span> {</span><br><span class="line"> <span class="keyword">private</span> Weapon weapon;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">RedDiamond</span><span class="params">(Weapon weapon)</span> {</span><br><span class="line"> <span class="built_in">this</span>.weapon = weapon;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">damage</span><span class="params">()</span> {</span><br><span class="line"> <span class="comment">// 攻击力 +20</span></span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">this</span>.weapon.damage() + <span class="number">20</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="调用"><a href="#调用" class="headerlink" title="调用"></a>调用</h2><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> <span class="type">Weapon</span> <span class="variable">sword</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Sword</span>();</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 加一颗绿宝石</span></span><br><span class="line"> sword = <span class="keyword">new</span> <span class="title class_">GreenDiamond</span>(sword);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 再加一颗红宝石</span></span><br><span class="line"> sword = <span class="keyword">new</span> <span class="title class_">RedDiamond</span>(sword);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 再攻击加成</span></span><br><span class="line"> sword = <span class="keyword">new</span> <span class="title class_">AttackBuff</span>(sword);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 输出 260(100+10+20)*2</span></span><br><span class="line"> System.out.println(sword.damage());</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>从下面常用的七种常用的设计模式中:</p>
<ul>
<li>责任链模式</li>
<li>适配器模式</li>
<li>观察者模式</li>
<li>策略模式</li>
<li>装饰器模式</li>
<li>模板方法模式</li>
<li>代理模式</li>
</ul>
<p>我们可以学到:软件设计的原则:</p>
<ul>
<li>只做一件事:责任链模式、适配器模式、观察者模式</li>
<li>依赖抽象而非具体:责任链模式、适配器模式、观察者模式、代理模式</li>
<li>对扩展开放,对修改关闭:责任链模式、适配器模式、观察者模式、策略模式、装饰器模式、代理模式</li>
<li>(仅)对变化封装:策略模式、模板方法模式</li>
<li>组合优于继承:策略模式</li>
</ul>
<h1 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h1><p>[^1]: <a href="https://www.bilibili.com/video/BV1494y1k7kR">【学架构也可以很有趣】【“趣”学架构】- 6.还得是“设计模式”</a><br>[^2]: <a href="https://mp.weixin.qq.com/s?__biz=MzkzMDI0ODg4NQ==&mid=2247501565&idx=1&sn=eb1d1ca47cc6df48c6325dc37bbc3ac1&chksm=c27fa727f5082e31530674e2c52b7651b8a4b09366979f5531e616a3f384302b8d202c485304&scene=178&cur_album_id=2247053463681564673#rd">【成为架构师】12. 成为工程师 - 如何提高代码的扩展性?</a></p>
]]></content>
<categories>
<category>架构学习</category>
</categories>
<tags>
<tag>架构设计</tag>
<tag>代码美学</tag>
</tags>
</entry>
<entry>
<title>代码美学</title>
<url>/2023/08/12/code-aesthetic/</url>
<content><![CDATA[<h1 id="在代码中命名"><a href="#在代码中命名" class="headerlink" title="在代码中命名"></a>在代码中命名</h1><h2 id="避免使用单个字母命名"><a href="#避免使用单个字母命名" class="headerlink" title="避免使用单个字母命名"></a>避免使用单个字母命名</h2><p>数学家以简洁为荣,他们爱把表达式浓缩成最精简的模式。</p>
<p>问题在于你不用编辑数学式子,但需要编辑代码</p>
<p>因为一个字母无法体现一个变量的相关信息</p>
<h2 id="绝对不要缩写"><a href="#绝对不要缩写" class="headerlink" title="绝对不要缩写"></a>绝对不要缩写</h2><p>缩写的含意依赖于上下文,而你并不总是了解这个上下文。这就会造成读代码的时间可能比写代码的时间还多</p>
<p>因此强行让自己去理解各种不同的命名方式,只会让阅读陌生代码更加困难</p>
<p>既然现在我们有了会自动补全的IDE和大屏幕,为什么不把名字写完整些?</p>
<h2 id="在变量名中带上单位"><a href="#在变量名中带上单位" class="headerlink" title="在变量名中带上单位"></a>在变量名中带上单位</h2><p>比如说你有一个接收延迟时间为参数的函数</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">void</span> <span class="title function_">execute</span><span class="params">(<span class="type">int</span> delay)</span></span><br></pre></td></tr></table></figure>
<p>如果这个值的单位为秒</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">void</span> <span class="title function_">execute</span><span class="params">(<span class="type">int</span> delaySeconds)</span></span><br></pre></td></tr></table></figure>
<p>这样这个函数的用户就能明白,应该传入秒数为参数</p>
<p>同时也让编辑这个类的人能够明白,这个函数用的是什么单位</p>
<p>当然,更好的做法是使用枚举,这样用户就不用关心具体的单位</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Getter</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">enum</span> <span class="title class_">Delay</span> {</span><br><span class="line"> SECONDS_1(<span class="number">1</span>),</span><br><span class="line"> SECONDS_5(<span class="number">5</span>),</span><br><span class="line"> SECONDS_10(<span class="number">10</span>),</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">int</span> seconds;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">void</span> <span class="title function_">execute</span><span class="params">(Delay delay)</span></span><br></pre></td></tr></table></figure>
<h3 id="补充:接口枚举规范"><a href="#补充:接口枚举规范" class="headerlink" title="补充:接口枚举规范"></a>补充:接口枚举规范</h3><p>顺带一提,接口入参可以使用枚举,但返回值不要用枚举。因为在阿里巴巴的Java开发手册中有提到</p>
<blockquote>
<p>二方库里可以定义枚举类型,参数可以使用枚举类型,但是接口返回值不允许使用枚举类型或者包含枚举类型的 POJO 对象</p>
</blockquote>
<blockquote>
<p>笔者注:</p>
<p><strong>二方库</strong> 也称作二方包,一般是指发布到公司内部私服上面的 jar 包,可以供公司内部其他服务依赖使用;</p>
<p><strong>一方库</strong> 是指本工程内部子项目模块依赖的库;</p>
<p><strong>三方库</strong> 是指公司之外的开源库,比如常见的 Spring、Mybatis、Dubbo 等等。</p>
</blockquote>
<p><strong>先解释为什么不能返回枚举</strong>[^1]</p>
<p>一般情况下,A系统想要提供一个远程接口给别人调用的时候,就会定义一个二方库,告诉其调用方如何构造参数,调用哪个接口。</p>
<p>而这个二方库的调用方B会根据A定义的内容来进行调用。而参数的构造过程是由B系统完成的,如果B系统使用到的是一个旧的二方库,使用到的枚举自然是已有的一些,新增的就不会被用到,所以这样也不会出现问题。</p>
<p>但如果A返回值包含二方库中的枚举类型,那么当A的二方库版本更新而它的下游服务没有更新时,返回二方库中的枚举就会发生<code>IllegalArgumentException</code>,进而引起接口调用异常</p>
<p>简单来说就是:上游的二方库版本总是 ≥ 下游的二方库版本,所以为了避免下游被新知识整懵,别携带二方库的枚举</p>
<p><strong>那为什么参数中可以使用枚举类型呢?</strong>[^2]</p>
<p>因为枚举类型是由提供方进行维护的,提供方包含的枚举类型属性肯定是多于等于消费方的枚举类型属性的,所以接口的入参中使用枚举类型是不会出现问题的。而返回值里面就不一样了,如果提供方返回了一个消费方不认识的枚举属性,就会抛出上述的异常。</p>
<h2 id="不用在名称上突出基类"><a href="#不用在名称上突出基类" class="headerlink" title="不用在名称上突出基类"></a>不用在名称上突出基类</h2><p>如无必要,不用在基类上特意加上<code>Base</code>或者<code>Abstract</code>,修改子类的名字就足够了</p>
<p><code>Track</code>比<code>BaseTrack</code>更合适一些,加上<code>Base</code>反而会让人思考编写者的真实用意</p>
<ul>
<li>如果有人拿到的是<code>Track</code>,那就是一辆卡车。他们不需要知道子类的细节。</li>
<li>如果需要指定具体的卡车类型,那就用它的子类比如<code>TrailerTruck</code></li>
</ul>
<h2 id="不要滥写Utils"><a href="#不要滥写Utils" class="headerlink" title="不要滥写Utils"></a>不要滥写Utils</h2><p>不要一股脑的把方法都放到Utils类里面</p>
<p>如果我们有一堆关于电影类的函数,我们可以考虑把一些处理电影的方法放到<code>Movie</code>类里面,把一些处理电影集合的方法放到<code>MovieColletion</code>类里面</p>
<p>如果有需要,我们可以让一些类接受泛型,比如<code>Pager<T></code>,这样我们也可以处理其他类的分页了</p>
<h1 id="组合优于继承"><a href="#组合优于继承" class="headerlink" title="组合优于继承"></a>组合优于继承</h1><p>组合和继承都是为了解决同一个问题:代码复用</p>
<p><strong>为什么不要继承?</strong></p>
<p>当你为了复用代码而继承某个类时,你不得不和基类发生耦合,你可能需要重写基类中那些你用不到的无意义方法,即使你只是让他们抛个异常。而为了解决这个问题,你可能需要另开一个或几个类,然后对代码重构。</p>
<p>修改是完美设计的天敌。继承会自然而然地让你把所有公有的部分放进一个基类,但当你发现特例时,就需要大改</p>
<p><strong>那什么是组合?</strong></p>
<p>不通过继承复用代码就是在组合。</p>
<p>假设说我们有一个<code>DrawImage</code>类,那么比起直接继承<code>Image</code>类来复用<code>Image</code>类的部分方法,还不如直接给他添加一个<code>Image</code>类型的属性,也就是</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DrawImage</span>{</span><br><span class="line"> Image image;</span><br><span class="line"> Pen pen;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>这样不就和<code>Image</code>类解耦了吗,我们可以专注的实现<code>DrawImage</code>的方法,同时该类方法入参中含有<code>Image</code>的,还能对<code>Image</code>的所有子类生效</p>
<p><strong>总结</strong></p>
<p>继承的两大特色就是复用和构造抽象(也就是子类必须要实现某些部分),如果我们仅仅只需要复用,组合就足矣了。</p>
<p>继承的正确做法是让消费者认为它拿到的是某个类的实例,也就是要符合里氏代换原则[^3]</p>
<p>另外,如果父类的某方面抽象能力不是所有子类都需要实现的,是不是就该接口上场了?</p>
<p>由于接口隔离原则[^4],接口都是最小化实现的。接口不关注实现它的是谁,它只关心它的实现类都实现了它的功能</p>
<p>组合并不是绝对完美的,但好歹比继承带来的“摩擦力”要小一些。</p>
<h1 id="不要过早抽象"><a href="#不要过早抽象" class="headerlink" title="不要过早抽象"></a>不要过早抽象</h1><blockquote>
<p>少量的重复代码和过度耦合相比,它不会在修改代码时造成那么大的痛苦</p>
</blockquote>
<p>这里说的抽象同时包含了抽象类和接口</p>
<p>耦合是抽象的反作用力。抽象层次越高,耦合越高</p>
<p>不要过早抽象,也不要因为有重复代码而做意义不大的抽象。如果只是为了减少一两行的代码而将两个没有什么关系的方法抽象成接口,那完全没必要,毕竟它并没有什么逻辑在里面。</p>
<p>但如果重复的地方很多,比如3种以上,又或者我们要接连调用这些有重复部分的代码,那么抽象的性价比就会高很多,也许就有必要抽象了</p>
<p>举个例子,如果只有<code>SaveJson</code>和<code>SaveXml</code>,那完全没必要特地为了他们而抽象。但如果我们还有<code>MysqlSaver</code>和<code>AwsSaver</code>,或者我们要延迟或重复保存,那我们甚至可以专门来定义一个<code>SaverFactory</code>类来帮助抽象</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SaverFactory</span> {</span><br><span class="line"> <span class="keyword">private</span> GameConfig config;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">SaverFactory</span><span class="params">(GameConfig config)</span> {</span><br><span class="line"> <span class="built_in">this</span>.config = config;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> FileSaver <span class="title function_">create</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">switch</span> (config.getSaveMode()) {</span><br><span class="line"> <span class="keyword">case</span> Xml:</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">SaveXml</span>(config.getSaveUri());</span><br><span class="line"> <span class="keyword">case</span> Json:</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">SaveJson</span>(config.getSaveUri());</span><br><span class="line"> <span class="keyword">case</span> SqlLite:</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">MysqlSaver</span>(config.getSaveUri());</span><br><span class="line"> <span class="keyword">case</span> Aws:</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">AwsSaver</span>(config.getAwsKey());</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">"Invalid save option"</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h1 id="“勿写注释”"><a href="#“勿写注释”" class="headerlink" title="“勿写注释”"></a>“勿写注释”</h1><blockquote>
<p>代码本身要比其上的注释更有助于解释意图</p>
<p>一般来说要是你觉得需要用人类语言来解释你的代码,先考虑一下能不能让你的代码更像人话**(see if you could make you code more human)**</p>
</blockquote>
<p><strong>你应该通过命名让读者明白你的代码在干什么</strong></p>
<p><strong>通过注释解释代码的原理表明你为什么要这么做</strong></p>
<p><strong>通过文档解释代码的用法</strong></p>
<h2 id="清晰命名"><a href="#清晰命名" class="headerlink" title="清晰命名"></a>清晰命名</h2><p>对于下面的代码,为了解释<code>5</code>所代表的含意,我们可以加上注释</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"># <span class="number">5</span> 代表发送消息</span><br><span class="line"><span class="keyword">if</span> (status == <span class="number">5</span>) {</span><br><span class="line"> message.markSent(); </span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>但更好的做法是定义一个常量表示它。现在这行if语句本身读起来就像注释了</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">MESSAGE_SENT = <span class="number">5</span>;</span><br><span class="line"><span class="keyword">if</span> (status == MESSAGE_SENT) {</span><br><span class="line"> message.markSent();</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="使用泛型"><a href="#使用泛型" class="headerlink" title="使用泛型"></a>使用泛型</h2><p>为了更好的约束维护者,比起在你的注释声明某段代码需要接受什么类型,还不如直接给原代码上个泛型</p>
<h2 id="使用Optional"><a href="#使用Optional" class="headerlink" title="使用Optional"></a>使用Optional</h2><p>如果我们的某个方法返回<code>String</code>,但<code>String</code>是可选的,也就是说返回<code>null</code>也是有逻辑含意的,那我们可以使用<code>Optional</code></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Example</span> {</span><br><span class="line"> <span class="keyword">public</span> Optional<String> <span class="title function_">getString</span><span class="params">()</span> {</span><br><span class="line"> <span class="comment">// 假设这里有一些逻辑来获取字符串</span></span><br><span class="line"> <span class="type">String</span> <span class="variable">result</span> <span class="operator">=</span> ...;</span><br><span class="line"> <span class="keyword">return</span> Optional.ofNullable(result); <span class="comment">// 返回可能为null的值的Optional</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>这样,我们就暗示告诉调用方他们需要处理返回<code>null</code>的情况,而不用特意给出注释</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="type">Example</span> <span class="variable">example</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Example</span>();</span><br><span class="line">Optional<String> optionalString = example.getString();</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (optionalString.isPresent()) {</span><br><span class="line"> <span class="type">String</span> <span class="variable">value</span> <span class="operator">=</span> optionalString.get();</span><br><span class="line"> System.out.println(<span class="string">"Value: "</span> + value);</span><br><span class="line">} <span class="keyword">else</span> {</span><br><span class="line"> System.out.println(<span class="string">"Value is absent."</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>或者,也可以直接使用<code>orElse</code>方法来指定一个默认值,以防返回的<code>Optional</code>为空:</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="type">String</span> <span class="variable">valueOrDefault</span> <span class="operator">=</span> optionalString.orElse(<span class="string">"Default Value"</span>);</span><br><span class="line">System.out.println(<span class="string">"Value: "</span> + valueOrDefault);</span><br></pre></td></tr></table></figure>
<h2 id="有时候注释也是必要的"><a href="#有时候注释也是必要的" class="headerlink" title="有时候注释也是必要的"></a>有时候注释也是必要的</h2><ul>
<li>如果一段代码为了性能优化写得很嗨涩,注释可以解释这里为何这么怪</li>
<li>如果一段代码用到了某种数学公式或算法,可以考虑给出链接便于后续维护者参考</li>
</ul>
<h1 id="成为“不嵌套主义者”"><a href="#成为“不嵌套主义者”" class="headerlink" title="成为“不嵌套主义者”"></a>成为“不嵌套主义者”</h1><blockquote>
<p>——Linus Torvalds</p>
</blockquote>
<p>从方法开始,如果把每一个左花括号都视作在加深函数的嵌套深度,那么一个没有内部代码块的函数嵌套深度就为1</p>
<p>当函数内有两级嵌套的时候,函数的嵌套深度就为3——这也是无嵌套主义者的最高忍耐限度,再加深到4就有些不礼貌了,比如这个</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="type">int</span> <span class="title function_">calculate</span><span class="params">(<span class="type">int</span> bottom, <span class="type">int</span> top)</span> {</span><br><span class="line"> <span class="keyword">if</span> (top > bottom) {</span><br><span class="line"> <span class="type">int</span> <span class="variable">sum</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">number</span> <span class="operator">=</span> bottom; number <= top; number++) {</span><br><span class="line"> <span class="keyword">if</span> (number % <span class="number">2</span> == <span class="number">0</span>){</span><br><span class="line"> sum += number;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> sum;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="提炼函数"><a href="#提炼函数" class="headerlink" title="提炼函数"></a>提炼函数</h2><p>把函数的一部分提炼成一个单独的函数,这样我们可以将刚才的函数变成</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="type">int</span> <span class="title function_">filterNumber</span><span class="params">(<span class="type">int</span> number)</span> {</span><br><span class="line"> <span class="keyword">if</span> (number %<span class="number">2</span> == <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">return</span> number;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="type">int</span> <span class="title function_">calculate</span><span class="params">(<span class="type">int</span> bottom, <span class="type">int</span> top)</span> {</span><br><span class="line"> <span class="keyword">if</span> (top > bottom) {</span><br><span class="line"> <span class="type">int</span> <span class="variable">sum</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">number</span> <span class="operator">=</span> bottom; number <= top; number++) {</span><br><span class="line"> sum += filterNumber(number);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> sum;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="反转"><a href="#反转" class="headerlink" title="反转"></a>反转</h2><p>反转条件,使函数尽早返回</p>
<p>也就是把负面分支放在前面,当遇到错误条件,直接返回。将正面的分支放在最下面</p>
<p>这样就形成了一种验证守护(validation gatekeeping),就像是声明了函数的要求,然后一步步过滤入参,直到正确条件</p>
<p>再将上面的例子修改:</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="type">int</span> <span class="title function_">filterNumber</span><span class="params">(<span class="type">int</span> number)</span> {</span><br><span class="line"> <span class="keyword">if</span> (number %<span class="number">2</span> == <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">return</span> number;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="type">int</span> <span class="title function_">calculate</span><span class="params">(<span class="type">int</span> bottom, <span class="type">int</span> top)</span> {</span><br><span class="line"> <span class="keyword">if</span> (top < bottom) {</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="type">int</span> <span class="variable">sum</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">number</span> <span class="operator">=</span> bottom; number <= top; number++) {</span><br><span class="line"> sum += filterNumber(number);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> sum;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h1 id="不要过早优化"><a href="#不要过早优化" class="headerlink" title="不要过早优化"></a>不要过早优化</h1><blockquote>
<p>“Premature Optimization is the root of all evil” —— Donald Knuth</p>
<p>过早优化是万恶之源 ——高德纳</p>
</blockquote>
<p>大部分关于性能的讨论都是在浪费时间,并不是说性能不重要,而是因为人们对性能看得太重太重了</p>
<p>假设我们将项目开发的三个重点——性能、开发速度、可拓展性视为一个三角形的三个角</p>
<ul>
<li>专注于速度意味着尽可能快地把某个东西搞出来,找到实现功能的最短路径,置未来维护者的生死于不顾。在这个过程中你会欠下大量技术上的债,最终会拖你后腿。</li>
<li>适应性谈的是要把代码写得能够适应新的需求,比如说可复用、可扩展的组件,精心打造的接口和可配置性。只要有良好的设计,就可以通过减少增加新特性所需要的改动来提高开发速度。但要是你让它太能适应了,速度也会慢下来。要是你打造了一个过度可适应的系统,它甚至能够适应各种根本不会出现的情况,那最终也不过是浪费时间。高度可适应的系统也会降低性能。</li>
</ul>
<p>应该结合项目所处阶段来考虑我们要将三角形的重心往哪倾斜。</p>
<p><strong>性能是问题,但通常不是第一个出现的问题</strong>。你需要慎重考虑要朝哪一角倾斜</p>
<p>通常“更快地找到问题的解决方案”都要好过“更快的代码却迟迟解决不了问题”</p>
<p>所以就笔者而言,可读性 + 适度的拓展性 >> 过早考虑那些<strong>可能</strong>的性能问题</p>
<p>要根据实际场景来看待性能问题,就好比一个访问量不大的网站,只有20万条数据的表,直接用<code>like</code>来模糊查询也不见得比大费周章的使用ES快多少</p>
<h1 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h1><p>[^1]: <a href="https://www.modb.pro/db/141682">为什么阿里 Java 开发手册规定接口返回值不允许包含枚举类型</a><br>[^2]: <a href="https://mp.weixin.qq.com/s/LJuE9cH2FrwN6-1qEud6LA">求你了,不要再在对外接口中使用枚举类型了!</a><br>[^3]: <a href="https://orionli.gitee.io/blog/2023/07/06/software-design-principles/#%E9%87%8C%E6%B0%8F%E4%BB%A3%E6%8D%A2%E5%8E%9F%E5%88%99">OrionLi’s Blog-软件设计原则-里氏代换原则</a><br>[^4]: <a href="https://orionli.gitee.io/blog/2023/07/06/software-design-principles/#%E6%8E%A5%E5%8F%A3%E9%9A%94%E7%A6%BB%E5%8E%9F%E5%88%99">OrionLi’s Blog-软件设计原则-接口隔离原则</a></p>
]]></content>
<categories>
<category>代码美学</category>
</categories>
<tags>
<tag>代码美学</tag>
</tags>
</entry>
<entry>
<title>设计模式</title>
<url>/2023/07/07/design-patterns/</url>
<content><![CDATA[<h1 id="设计模式分类"><a href="#设计模式分类" class="headerlink" title="设计模式分类"></a>设计模式分类</h1><ul>
<li><p><strong>创建型模式</strong></p>
<p>用于描述“怎样创建对象”,它的主要特点是“将对象的创建与使用分离”。GoF(四人组)书中提供了单例、原型、工厂方法、抽象工厂、建造者等 5 种创建型模式。</p>
</li>
<li><p><strong>结构型模式</strong></p>
<p>用于描述如何将类或对象按某种布局组成更大的结构,GoF(四人组)书中提供了代理、适配器、桥接、装饰、外观、享元、组合等 7 种结构型模式。</p>
</li>
<li><p><strong>行为型模式</strong></p>
<p>用于描述类或对象之间怎样相互协作共同完成单个对象无法单独完成的任务,以及怎样分配职责。GoF(四人组)书中提供了模板方法、策略、命令、职责链、状态、观察者、中介者、迭代器、访问者、备忘录、解释器等 11 种行为型模式。</p>
</li>
</ul>
<h1 id="创建型-单例模式-Singleton-pattern"><a href="#创建型-单例模式-Singleton-pattern" class="headerlink" title="创建型-单例模式(Singleton pattern)"></a>创建型-单例模式(Singleton pattern)</h1><blockquote>
<p>单例模式(Singleton pattern)确保一个类只有一个实例,并提供该实例的全局访问点</p>
</blockquote>
<p>通过单例的创建时机分类,可分为懒汉式和饿汉式</p>
<table>
<thead>
<tr>
<th>实现类型</th>
<th>单例创建时机</th>
<th>应用场景</th>
</tr>
</thead>
<tbody><tr>
<td>饿汉式</td>
<td>- 不可控,类加载时自动创建单例</td>
<td>初始化时就需要创建单例,希望单例对象初始化速度快,适合占用内存小的单例</td>
</tr>
<tr>
<td>懒汉式</td>
<td>可控,有需要时才手动创建单例</td>
<td>按需、延迟创建单例,单例初始化时间长,希望后续再加载此单例以提高服务启动速度,单例占用内存高</td>
</tr>
</tbody></table>
<h2 id="饿汉式最佳实践-枚举类"><a href="#饿汉式最佳实践-枚举类" class="headerlink" title="饿汉式最佳实践-枚举类"></a>饿汉式最佳实践-枚举类</h2><p><strong>枚举类的特点是:</strong></p>
<ul>
<li>不可被继承(final)</li>
</ul>
<ul>
<li><p>线程安全,每个枚举元素都是类静态常量,只会被装载一次</p>
</li>
<li><p>枚举元素都通过静态代码块来进行初始化</p>
</li>
<li><p>默认构造方法是私有的</p>
</li>
<li><p>大部分方法都是final</p>
</li>
</ul>
<p><strong>通过枚举类型实现优点是:</strong></p>
<ul>
<li><p>线程安全</p>
</li>
<li><p>自由序列化</p>
</li>
<li><p>实现更加简单、简洁</p>
</li>
</ul>
<p>唯一缺点就是饿汉式的缺点:创建时机不可控</p>
<p><strong>示例</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">enum</span> <span class="title class_">Singleton</span> {</span><br><span class="line"> uniqueInstance;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="懒汉式最佳实现-静态内部类"><a href="#懒汉式最佳实现-静态内部类" class="headerlink" title="懒汉式最佳实现-静态内部类"></a>懒汉式最佳实现-静态内部类</h2><p>当 Singleton 类加载时,静态内部类 SingletonHolder 没有被加载进内存。只有当调用 <code>getUniqueInstance()</code> 方法从而触发 <code>SingletonHolder.INSTANCE</code> 时 SingletonHolder 才会被加载,此时初始化 INSTANCE 实例。</p>
<p>这种方式不仅具有延迟初始化的好处,而且由虚拟机提供了对线程安全的支持。</p>
<p><strong>优点:</strong></p>
<ul>
<li><p>线程安全</p>
</li>
<li><p>节省资源(不需要过多同步开销)</p>
</li>
<li><p>实现简单</p>
</li>
</ul>
<p><strong>示例</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Singleton</span> {</span><br><span class="line"></span><br><span class="line"> <span class="comment">//私有构造方法</span></span><br><span class="line"> <span class="keyword">private</span> <span class="title function_">Singleton</span><span class="params">()</span> {}</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">SingletonHolder</span> {</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">Singleton</span> <span class="variable">INSTANCE</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Singleton</span>();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//对外提供静态方法获取该对象</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> Singleton <span class="title function_">getInstance</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> SingletonHolder.INSTANCE;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h1 id="创建型-简单工厂-Simple-Factory"><a href="#创建型-简单工厂-Simple-Factory" class="headerlink" title="创建型-简单工厂(Simple Factory)"></a>创建型-简单工厂(Simple Factory)</h1><blockquote>
<p>工厂模式是在创建一个对象时不向客户暴露内部细节,并提供一个创建对象的通用接口</p>
<p><strong>用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程</strong></p>
</blockquote>
<p>把实例化的操作单独放到一个类中,这个类就成为简单工厂类</p>
<p>让简单工厂类来决定应该用哪个具体子类来实例化,这样做能把客户类和具体子类的实现解耦,客户类不再需要知道有哪些子类以及应当实例化哪个子类</p>
<p>简单工厂模式适用于以下场景:</p>
<ol>
<li>当需要创建的对象只有一个具体产品类时;</li>
<li>创建对象的逻辑较为简单,不需要涉及复杂的业务逻辑;</li>
<li>不需要频繁地添加新的产品种类。</li>
</ol>
<h2 id="示例:啥咖啡都有的咖啡店"><a href="#示例:啥咖啡都有的咖啡店" class="headerlink" title="示例:啥咖啡都有的咖啡店"></a>示例:啥咖啡都有的咖啡店</h2><blockquote>
<p>简单工厂模式适用于只需要创建单一种类的产品的情况。如果你只需要某一种产品,而且不涉及到多个产品族的情况,可以选择简单工厂模式。简单工厂模式通过一个工厂类,根据传入的参数或条件来创建不同的产品。</p>
</blockquote>
<p>根据咖啡店需求的咖啡类型,咖啡工厂提供对应的咖啡</p>
<p><strong>咖啡类:</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 定义抽象产品Coffee</span></span><br><span class="line"><span class="keyword">interface</span> <span class="title class_">Coffee</span> {</span><br><span class="line"> String <span class="title function_">serve</span><span class="params">()</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义具体产品AmericanCoffee</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">AmericanCoffee</span> <span class="keyword">implements</span> <span class="title class_">Coffee</span> {</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> String <span class="title function_">serve</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"美式咖啡"</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义具体产品Espresso</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Espresso</span> <span class="keyword">implements</span> <span class="title class_">Coffee</span> {</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> String <span class="title function_">serve</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"意式浓缩咖啡"</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><strong>咖啡工厂类:</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 简单咖啡工厂类</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">SimpleCoffeeFactory</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> Coffee <span class="title function_">createCoffee</span><span class="params">(String type)</span> {</span><br><span class="line"> <span class="keyword">switch</span> (type) {</span><br><span class="line"> <span class="keyword">case</span> <span class="string">"American"</span>:</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">AmericanCoffee</span>();</span><br><span class="line"> <span class="keyword">case</span> <span class="string">"Espresso"</span>:</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Espresso</span>();</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">"不支持的咖啡类型:"</span> + type);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><strong>客户端:</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Main</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> <span class="comment">// 使用简单工厂创建咖啡</span></span><br><span class="line"> <span class="type">Coffee</span> <span class="variable">coffee1</span> <span class="operator">=</span> SimpleCoffeeFactory.createCoffee(<span class="string">"American"</span>);</span><br><span class="line"> System.out.println(coffee1.serve()); <span class="comment">// 输出:美式咖啡</span></span><br><span class="line"></span><br><span class="line"> <span class="type">Coffee</span> <span class="variable">coffee2</span> <span class="operator">=</span> SimpleCoffeeFactory.createCoffee(<span class="string">"Espresso"</span>);</span><br><span class="line"> System.out.println(coffee2.serve()); <span class="comment">// 输出:意式浓缩咖啡</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h1 id="创建型-工厂方法-Factory-Method"><a href="#创建型-工厂方法-Factory-Method" class="headerlink" title="创建型-工厂方法(Factory Method)"></a>创建型-工厂方法(Factory Method)</h1><blockquote>
<p>工厂方法模式是简单工厂模式的进一步抽象。由于使用了多态,工厂方法模式保持了简单工厂模式的优点,而且遵守了开闭原则</p>
<p><strong>在系统增加新的产品时只需要添加具体产品类和对应的具体工厂类,无须对原工厂进行任何修改,满足开闭原则</strong></p>
</blockquote>
<p>我们可以定义一个用于创建对象的接口,让子类决定实例化哪个产品类对象。工厂方法使一个产品类的实例化延迟到其工厂的子类</p>
<p>工厂方法模式的主要角色:</p>
<ul>
<li>抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品。</li>
<li>具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。</li>
<li>抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。</li>
<li>具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。</li>
</ul>
<h2 id="示例:美式咖啡店"><a href="#示例:美式咖啡店" class="headerlink" title="示例:美式咖啡店"></a>示例:美式咖啡店</h2><blockquote>
<p>如果需要某一类产品下的几个产品分支,可以选择工厂方法模式。每个具体工厂类负责创建自己特定的产品,从而实现了产品的扩展和变化</p>
</blockquote>
<p>现在,美式咖啡厂专供美式咖啡店,意式咖啡厂专供意式咖啡店</p>
<p><strong>咖啡类:</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 定义抽象产品Coffee</span></span><br><span class="line"><span class="keyword">interface</span> <span class="title class_">Coffee</span> {</span><br><span class="line"> String <span class="title function_">serve</span><span class="params">()</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义具体产品AmericanCoffee</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">AmericanCoffee</span> <span class="keyword">implements</span> <span class="title class_">Coffee</span> {</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> String <span class="title function_">serve</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"美式咖啡"</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义具体产品Espresso</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Espresso</span> <span class="keyword">implements</span> <span class="title class_">Coffee</span> {</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> String <span class="title function_">serve</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"意式浓缩咖啡"</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><strong>工厂类:</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 定义抽象咖啡工厂</span></span><br><span class="line"><span class="keyword">interface</span> <span class="title class_">CoffeeFactory</span> {</span><br><span class="line"> Coffee <span class="title function_">createCoffee</span><span class="params">()</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 具体咖啡工厂实现类</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">AmericanCoffeeFactory</span> <span class="keyword">implements</span> <span class="title class_">CoffeeFactory</span> {</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> Coffee <span class="title function_">createCoffee</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">AmericanCoffee</span>();</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ItalianCoffeeFactory</span> <span class="keyword">implements</span> <span class="title class_">CoffeeFactory</span> {</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> Coffee <span class="title function_">createCoffee</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Espresso</span>();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><strong>客户端代码:</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Main</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> <span class="comment">// 创建具体咖啡工厂</span></span><br><span class="line"> <span class="type">CoffeeFactory</span> <span class="variable">coffeeFactory</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AmericanCoffeeFactory</span>();</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 使用工厂创建咖啡</span></span><br><span class="line"> <span class="type">Coffee</span> <span class="variable">coffee</span> <span class="operator">=</span> coffeeFactory.createCoffee();</span><br><span class="line"> System.out.println(coffee.serve()); <span class="comment">// 输出:美式咖啡</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// 切换到意式咖啡工厂</span></span><br><span class="line"> coffeeFactory = <span class="keyword">new</span> <span class="title class_">ItalianCoffeeFactory</span>();</span><br><span class="line"> coffee = coffeeFactory.createCoffee();</span><br><span class="line"> System.out.println(coffee.serve()); <span class="comment">// 输出:意式浓缩咖啡</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h1 id="创建型-抽象工厂-Abstract-Factory"><a href="#创建型-抽象工厂-Abstract-Factory" class="headerlink" title="创建型-抽象工厂(Abstract Factory)"></a>创建型-抽象工厂(Abstract Factory)</h1><blockquote>
<p>抽象工厂模式适用于需要创建多个相关产品族的情况。如果每个产品分支都需要对应的配套产品,可以选择抽象工厂模式。抽象工厂模式通过定义抽象工厂接口和多个工厂方法来创建不同产品族的产品,确保这些产品相互配合使用</p>
</blockquote>
<p>当多个工厂同时生产不同种类的东西,而不是专职生产同种类产品时,那抽象工厂比工厂方法更加适用。换句话说,当工厂要生产一个带有配套产品的产品而不是单品时,抽象工厂更为合适。</p>
<h2 id="示例:有甜点的美式咖啡店"><a href="#示例:有甜点的美式咖啡店" class="headerlink" title="示例:有甜点的美式咖啡店"></a>示例:有甜点的美式咖啡店</h2><p>和上个示例相比,现在我们的咖啡店不只是卖咖啡,还卖甜点。只不过美式咖啡厅卖美式甜点,意式咖啡店卖意式甜点。与此同时,我们的工厂现在是意式工厂和美式工厂。</p>
<p><strong>咖啡类</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 定义抽象产品Coffee</span></span><br><span class="line"><span class="keyword">interface</span> <span class="title class_">Coffee</span> {</span><br><span class="line"> String <span class="title function_">serve</span><span class="params">()</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义具体产品AmericanCoffee</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">AmericanCoffee</span> <span class="keyword">implements</span> <span class="title class_">Coffee</span> {</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> String <span class="title function_">serve</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"美式咖啡"</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义具体产品Espresso</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Espresso</span> <span class="keyword">implements</span> <span class="title class_">Coffee</span> {</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> String <span class="title function_">serve</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"意式浓缩咖啡"</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><strong>甜品类</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 定义抽象产品Dessert</span></span><br><span class="line"><span class="keyword">interface</span> <span class="title class_">Dessert</span> {</span><br><span class="line"> String <span class="title function_">serve</span><span class="params">()</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义具体产品AmericanDessert</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">AmericanDessert</span> <span class="keyword">implements</span> <span class="title class_">Dessert</span> {</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> String <span class="title function_">serve</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"美式甜点"</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义具体产品ItalianDessert</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ItalianDessert</span> <span class="keyword">implements</span> <span class="title class_">Dessert</span> {</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> String <span class="title function_">serve</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"意式甜点"</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><strong>工厂类</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 定义抽象工厂CoffeeFactory</span></span><br><span class="line"><span class="keyword">interface</span> <span class="title class_">CoffeeFactory</span> {</span><br><span class="line"> Coffee <span class="title function_">createCoffee</span><span class="params">()</span>;</span><br><span class="line"> Dessert <span class="title function_">createDessert</span><span class="params">()</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义具体工厂AmericanCoffeeFactory</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">AmericanCoffeeFactory</span> <span class="keyword">implements</span> <span class="title class_">CoffeeFactory</span> {</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> Coffee <span class="title function_">createCoffee</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">AmericanCoffee</span>();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> Dessert <span class="title function_">createDessert</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">AmericanDessert</span>();</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义具体工厂ItalianCoffeeFactory</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ItalianCoffeeFactory</span> <span class="keyword">implements</span> <span class="title class_">CoffeeFactory</span> {</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> Coffee <span class="title function_">createCoffee</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Espresso</span>();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> Dessert <span class="title function_">createDessert</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">ItalianDessert</span>();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><strong>客户端代码</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Main</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> <span class="comment">// 创建意式咖啡店工厂</span></span><br><span class="line"> <span class="type">CoffeeFactory</span> <span class="variable">italianFactory</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ItalianCoffeeFactory</span>();</span><br><span class="line"> <span class="type">Coffee</span> <span class="variable">espresso</span> <span class="operator">=</span> italianFactory.createCoffee();</span><br><span class="line"> <span class="type">Dessert</span> <span class="variable">italianDessert</span> <span class="operator">=</span> italianFactory.createDessert();</span><br><span class="line"> System.out.println(espresso.serve()); <span class="comment">// 输出:意式浓缩咖啡</span></span><br><span class="line"> System.out.println(italianDessert.serve()); <span class="comment">// 输出:意式甜点</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// 创建美式咖啡店工厂</span></span><br><span class="line"> <span class="type">CoffeeFactory</span> <span class="variable">americanFactory</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AmericanCoffeeFactory</span>();</span><br><span class="line"> <span class="type">Coffee</span> <span class="variable">americano</span> <span class="operator">=</span> americanFactory.createCoffee();</span><br><span class="line"> <span class="type">Dessert</span> <span class="variable">americanDessert</span> <span class="operator">=</span> americanFactory.createDessert();</span><br><span class="line"> System.out.println(americano.serve()); <span class="comment">// 输出:美式咖啡</span></span><br><span class="line"> System.out.println(americanDessert.serve()); <span class="comment">// 输出:美式甜点</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h1 id="创建型-建造者模式-Builder"><a href="#创建型-建造者模式-Builder" class="headerlink" title="创建型-建造者模式(Builder)"></a>创建型-建造者模式(Builder)</h1><blockquote>
<p>将一个复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示。换言之,当某个对象的属性构建过程复杂,但属性注入的程序固定</p>
</blockquote>
<p>以一台计算机为例,使用建造者模式建造可以:</p>
<ul>
<li>分离计算机部件的构造(由Builder来负责)和装配(由Director负责)。 计算机各个零件生产复杂,但装配各个部件的程序是一样的。</li>
<li>由于实现了构建和装配的解耦。不同的计算机硬件构建器,相同的装配,也可以做出不同的计算机对象;相同的计算机硬件构建器,不同的装配顺序也可以做出不同的计算机对象。也就是实现了构建算法、装配算法的解耦,实现了更好的复用。</li>
<li>建造者模式可以将计算机部件和其组装过程分开,一步一步创建一个复杂的计算机对象。用户只需要指定想要哪个装机套餐就可以得到该计算机对象,而无须知道其各零件的具体构造细节。</li>
</ul>
<h2 id="含有指挥者的示例:"><a href="#含有指挥者的示例:" class="headerlink" title="含有指挥者的示例:"></a>含有指挥者的示例:</h2><p>生产自行车是一个复杂的过程,它包含了车架,车座等组件的生产。而车架又有碳纤维,铝合金等材质的,车座有橡胶,真皮等材质。对于自行车的生产就可以使用建造者模式。指挥者类 <code>Director</code> 在建造者模式中具有很重要的作用,它用于指导具体构建者如何构建产品,控制调用先后次序,并向调用者返回完整的产品类</p>
<p><strong>自行车及其生成器:</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 产品类 - 自行车</span></span><br><span class="line"><span class="meta">@Getter</span></span><br><span class="line"><span class="meta">@Setter</span></span><br><span class="line"><span class="meta">@ToString</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Bicycle</span> {</span><br><span class="line"> <span class="keyword">private</span> String frame;</span><br><span class="line"> <span class="keyword">private</span> String seat;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 抽象生成器 - 自行车生成器</span></span><br><span class="line"><span class="keyword">interface</span> <span class="title class_">BicycleBuilder</span> {</span><br><span class="line"> <span class="keyword">void</span> <span class="title function_">buildFrame</span><span class="params">()</span>;</span><br><span class="line"> <span class="keyword">void</span> <span class="title function_">buildSeat</span><span class="params">()</span>;</span><br><span class="line"> Bicycle <span class="title function_">getBicycle</span><span class="params">()</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 具体生成器 - 碳纤维自行车生成器</span></span><br><span class="line"><span class="meta">@Getter</span></span><br><span class="line"><span class="meta">@Setter</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">CarbonFiberBicycleBuilder</span> <span class="keyword">implements</span> <span class="title class_">BicycleBuilder</span> {</span><br><span class="line"> <span class="keyword">private</span> Bicycle bicycle;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">CarbonFiberBicycleBuilder</span><span class="params">()</span> {</span><br><span class="line"> <span class="built_in">this</span>.bicycle = <span class="keyword">new</span> <span class="title class_">Bicycle</span>();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">buildFrame</span><span class="params">()</span> {</span><br><span class="line"> bicycle.setFrame(<span class="string">"Carbon Fiber Frame"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">buildSeat</span><span class="params">()</span> {</span><br><span class="line"> bicycle.setSeat(<span class="string">"Rubber Seat"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> Bicycle <span class="title function_">getBicycle</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> bicycle;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 具体生成器 - 铝合金自行车生成器</span></span><br><span class="line"><span class="meta">@Getter</span></span><br><span class="line"><span class="meta">@Setter</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">AluminumBicycleBuilder</span> <span class="keyword">implements</span> <span class="title class_">BicycleBuilder</span> {</span><br><span class="line"> <span class="keyword">private</span> Bicycle bicycle;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">AluminumBicycleBuilder</span><span class="params">()</span> {</span><br><span class="line"> <span class="built_in">this</span>.bicycle = <span class="keyword">new</span> <span class="title class_">Bicycle</span>();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">buildFrame</span><span class="params">()</span> {</span><br><span class="line"> bicycle.setFrame(<span class="string">"Aluminum Frame"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">buildSeat</span><span class="params">()</span> {</span><br><span class="line"> bicycle.setSeat(<span class="string">"Leather Seat"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> Bicycle <span class="title function_">getBicycle</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> bicycle;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><strong>指挥者:</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">BicycleDirector</span> {</span><br><span class="line"> <span class="keyword">private</span> BicycleBuilder bicycleBuilder;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">BicycleDirector</span><span class="params">(BicycleBuilder bicycleBuilder)</span> {</span><br><span class="line"> <span class="built_in">this</span>.bicycleBuilder = bicycleBuilder;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> Bicycle <span class="title function_">construct</span><span class="params">()</span> {</span><br><span class="line"> bicycleBuilder.buildFrame();</span><br><span class="line"> bicycleBuilder.buildSeat();</span><br><span class="line"> <span class="keyword">return</span> bicycleBuilder.getBicycle();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><strong>客户端:</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Client</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> <span class="type">BicycleDirector</span> <span class="variable">bicycleDirector</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BicycleDirector</span>(<span class="keyword">new</span> <span class="title class_">CarbonFiberBicycleBuilder</span>());</span><br><span class="line"> <span class="type">Bicycle</span> <span class="variable">carbonFiberBicycle</span> <span class="operator">=</span> bicycleDirector.construct();</span><br><span class="line"> System.out.println(<span class="string">"Carbon Fiber Bicycle:"</span>);</span><br><span class="line"> System.out.println(carbonFiberBicycle);</span><br><span class="line"></span><br><span class="line"> System.out.println();</span><br><span class="line"></span><br><span class="line"> bicycleDirector = <span class="keyword">new</span> <span class="title class_">BicycleDirector</span>(<span class="keyword">new</span> <span class="title class_">AluminumBicycleBuilder</span>());</span><br><span class="line"> <span class="type">Bicycle</span> <span class="variable">aluminumBicycle</span> <span class="operator">=</span> bicycleDirector.construct();</span><br><span class="line"> System.out.println(<span class="string">"Aluminum Bicycle:"</span>);</span><br><span class="line"> System.out.println(aluminumBicycle);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="不含指挥者的示例:"><a href="#不含指挥者的示例:" class="headerlink" title="不含指挥者的示例:"></a>不含指挥者的示例:</h2><p>当系统结构相对简单且复杂对象的构建过程比较固定时,比方说我们造的自行车都是碳纤维的,那么可以将指挥者类与抽象生成器进行结合,从而简化系统结构。</p>
<p><strong>自行车及其生成器:</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 产品类 - 自行车</span></span><br><span class="line"><span class="meta">@Getter</span></span><br><span class="line"><span class="meta">@Setter</span></span><br><span class="line"><span class="meta">@ToString</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Bicycle</span> {</span><br><span class="line"> <span class="keyword">private</span> String frame;</span><br><span class="line"> <span class="keyword">private</span> String seat;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 抽象生成器 - 自行车生成器</span></span><br><span class="line"><span class="keyword">interface</span> <span class="title class_">BicycleBuilder</span> {</span><br><span class="line"> <span class="keyword">void</span> <span class="title function_">buildFrame</span><span class="params">()</span>;</span><br><span class="line"> <span class="keyword">void</span> <span class="title function_">buildSeat</span><span class="params">()</span>;</span><br><span class="line"> Bicycle <span class="title function_">getBicycle</span><span class="params">()</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 具体生成器 - 碳纤维自行车生成器</span></span><br><span class="line"><span class="meta">@Getter</span></span><br><span class="line"><span class="meta">@Setter</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">CarbonFiberBicycleBuilder</span> <span class="keyword">implements</span> <span class="title class_">BicycleBuilder</span> {</span><br><span class="line"> <span class="keyword">private</span> Bicycle bicycle;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">CarbonFiberBicycleBuilder</span><span class="params">()</span> {</span><br><span class="line"> <span class="built_in">this</span>.bicycle = <span class="keyword">new</span> <span class="title class_">Bicycle</span>();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">buildFrame</span><span class="params">()</span> {</span><br><span class="line"> bicycle.setFrame(<span class="string">"Carbon Fiber Frame"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">buildSeat</span><span class="params">()</span> {</span><br><span class="line"> bicycle.setSeat(<span class="string">"Rubber Seat"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> Bicycle <span class="title function_">getBicycle</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> bicycle;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><strong>客户端:</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Client</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> <span class="type">BicycleBuilder</span> <span class="variable">bicycleBuilder</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CarbonFiberBicycleBuilder</span>();</span><br><span class="line"></span><br><span class="line"> bicycleBuilder.buildFrame();</span><br><span class="line"> bicycleBuilder.buildSeat();</span><br><span class="line"> <span class="type">Bicycle</span> <span class="variable">bicycle</span> <span class="operator">=</span> bicycleBuilder.getBicycle();</span><br><span class="line"></span><br><span class="line"> System.out.println(<span class="string">"Carbon Fiber Bicycle:"</span>);</span><br><span class="line"> System.out.println(bicycle);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h1 id="创建型-原型模式-Prototype"><a href="#创建型-原型模式-Prototype" class="headerlink" title="创建型-原型模式(Prototype)"></a>创建型-原型模式(Prototype)</h1><blockquote>
<p>原型模式适用于在需要大量创建相似对象时使用。它通过复制现有对象的方式来创建新对象,而无需重复进行初始化和构建过程</p>
</blockquote>
<p>如果我们拥有一批构建好的不同种类的模板,且需要大量输出相同或相似的产品,那么可以使用原型模式把对应模板进行批量复制输出</p>
<p>这种方式特别适合于创建成本较高、创建过程复杂或者需要频繁创建的对象。通过复制现有的对象,我们可以节省创建对象的开销,并且可以方便地对克隆出的对象进行个性化定制。</p>
<h2 id="示例:复制成品"><a href="#示例:复制成品" class="headerlink" title="示例:复制成品"></a>示例:复制成品</h2><p>假设我们有一个简单的图形类 <code>Shape</code>,它有一个 <code>draw()</code> 方法用于绘制图形。我们希望能够复制已有的图形对象,而无需知道具体的图形类型。</p>
<p>首先,我们需要创建一个抽象的原型接口 <code>CloneableShape</code>,它包含一个 <code>clone()</code> 方法用于复制图形对象。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">CloneableShape</span> <span class="keyword">extends</span> <span class="title class_">Cloneable</span> {</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> CloneableShape <span class="title function_">clone</span><span class="params">()</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>然后,我们实现具体的图形类,这里以矩形和圆形为例。它们都需要实现 <code>CloneableShape</code> 接口,并且在 <code>clone()</code> 方法中进行对象的复制。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Rectangle</span> <span class="keyword">implements</span> <span class="title class_">CloneableShape</span> {</span><br><span class="line"> <span class="keyword">private</span> <span class="type">int</span> width;</span><br><span class="line"> <span class="keyword">private</span> <span class="type">int</span> height;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">Rectangle</span><span class="params">(<span class="type">int</span> width, <span class="type">int</span> height)</span> {</span><br><span class="line"> <span class="built_in">this</span>.width = width;</span><br><span class="line"> <span class="built_in">this</span>.height = height;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">draw</span><span class="params">()</span> {</span><br><span class="line"> System.out.println(<span class="string">"绘制矩形"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> CloneableShape <span class="title function_">clone</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Rectangle</span>(<span class="built_in">this</span>.width, <span class="built_in">this</span>.height);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Circle</span> <span class="keyword">implements</span> <span class="title class_">CloneableShape</span> {</span><br><span class="line"> <span class="keyword">private</span> <span class="type">int</span> radius;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">Circle</span><span class="params">(<span class="type">int</span> radius)</span> {</span><br><span class="line"> <span class="built_in">this</span>.radius = radius;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">draw</span><span class="params">()</span> {</span><br><span class="line"> System.out.println(<span class="string">"绘制圆形"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> CloneableShape <span class="title function_">clone</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Circle</span>(<span class="built_in">this</span>.radius);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>接下来,我们可以创建一个图形缓存类 <code>ShapeCache</code>,用于存储已有的图形对象。在该类中,我们使用一个 <code>HashMap</code> 来存储图形对象,其中键为图形类型,值为对应的原型对象。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.HashMap;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ShapeCache</span> {</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> HashMap<String, CloneableShape> shapeMap = <span class="keyword">new</span> <span class="title class_">HashMap</span><>();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> CloneableShape <span class="title function_">getShape</span><span class="params">(String shapeId)</span> {</span><br><span class="line"> <span class="type">CloneableShape</span> <span class="variable">cachedShape</span> <span class="operator">=</span> shapeMap.get(shapeId);</span><br><span class="line"> <span class="keyword">return</span> (CloneableShape) cachedShape.clone();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">loadCache</span><span class="params">()</span> {</span><br><span class="line"> shapeMap.put(<span class="string">"rectangle"</span>, <span class="keyword">new</span> <span class="title class_">Rectangle</span>(<span class="number">10</span>, <span class="number">20</span>));</span><br><span class="line"> shapeMap.put(<span class="string">"circle"</span>, <span class="keyword">new</span> <span class="title class_">Circle</span>(<span class="number">30</span>));</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>最后,我们可以使用原型模式来复制图形对象,而无需知道具体的图形类型。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Main</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> ShapeCache.loadCache();</span><br><span class="line"></span><br><span class="line"> <span class="type">CloneableShape</span> <span class="variable">clonedShape1</span> <span class="operator">=</span> ShapeCache.getShape(<span class="string">"rectangle"</span>);</span><br><span class="line"> clonedShape1.draw(); <span class="comment">// 输出:绘制矩形</span></span><br><span class="line"></span><br><span class="line"> <span class="type">CloneableShape</span> <span class="variable">clonedShape2</span> <span class="operator">=</span> ShapeCache.getShape(<span class="string">"circle"</span>);</span><br><span class="line"> clonedShape2.draw(); <span class="comment">// 输出:绘制圆形</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h1 id="结构型-外观-Facade"><a href="#结构型-外观-Facade" class="headerlink" title="结构型-外观(Facade)"></a>结构型-外观(Facade)</h1><blockquote>
<p>提供了一个统一的接口,用来访问子系统中的一群接口,从而让子系统更容易使用</p>
</blockquote>
<p>在外观模式中,子系统负责各自的功能实现,并且将其封装在具体的类中。<strong>外观类</strong>则作为一个集合器或者接口提供者,<strong>集中收集了子系统的操作</strong>,并且将其封装成简单易用的方法。</p>
<p>用户使用外观类时,只需要调用外观类提供的方法,而不需要关心底层子系统的复杂性和细节。外观类内部会根据用户的操作调用子系统的相应方法,完成一系列的操作。这样,外观类提供了一个简化的接口给用户,让用户可以更加方便地使用整个系统。</p>
<p><strong>缺点:</strong></p>
<ul>
<li>不符合开闭原则,修改很麻烦</li>
</ul>
<h2 id="示例:一键看电视"><a href="#示例:一键看电视" class="headerlink" title="示例:一键看电视"></a>示例:一键看电视</h2><p>在下面代码示例中,可能会有人觉得外观模式和责任链模式目的是相似的,但是它们的目的和应用场景却是不同的。</p>
<blockquote>
<p>外观模式更倾向于分类接口来方便调用,而责任链模式更倾向于让一串任务开始执行的开关</p>
</blockquote>
<p>通过语音直接控制智能家电的开启和关闭:</p>
<ul>
<li><p>打开灯、打开电视、打开空调</p>
</li>
<li><p>关闭灯、关闭电视、关闭空调</p>
</li>
</ul>
<p><strong>子系统:</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 子系统:灯</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Light</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">turnOn</span><span class="params">()</span> {</span><br><span class="line"> System.out.println(<span class="string">"灯已打开"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">turnOff</span><span class="params">()</span> {</span><br><span class="line"> System.out.println(<span class="string">"灯已关闭"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 子系统:电视</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TV</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">turnOn</span><span class="params">()</span> {</span><br><span class="line"> System.out.println(<span class="string">"电视已打开"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">turnOff</span><span class="params">()</span> {</span><br><span class="line"> System.out.println(<span class="string">"电视已关闭"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 子系统:空调</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AirConditioner</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">turnOn</span><span class="params">()</span> {</span><br><span class="line"> System.out.println(<span class="string">"空调已打开"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">turnOff</span><span class="params">()</span> {</span><br><span class="line"> System.out.println(<span class="string">"空调已关闭"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><strong>外观类:</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 外观类</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SmartHomeFacade</span> {</span><br><span class="line"> <span class="keyword">private</span> Light light; <span class="comment">// 灯子系统</span></span><br><span class="line"> <span class="keyword">private</span> TV tv; <span class="comment">// 电视子系统</span></span><br><span class="line"> <span class="keyword">private</span> AirConditioner airConditioner; <span class="comment">// 空调子系统</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">SmartHomeFacade</span><span class="params">()</span> {</span><br><span class="line"> light = <span class="keyword">new</span> <span class="title class_">Light</span>();</span><br><span class="line"> tv = <span class="keyword">new</span> <span class="title class_">TV</span>();</span><br><span class="line"> airConditioner = <span class="keyword">new</span> <span class="title class_">AirConditioner</span>();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 打开所有智能家电</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">turnOnAllDevices</span><span class="params">()</span> {</span><br><span class="line"> light.turnOn(); <span class="comment">// 打开灯</span></span><br><span class="line"> tv.turnOn(); <span class="comment">// 打开电视</span></span><br><span class="line"> airConditioner.turnOn(); <span class="comment">// 打开空调</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 关闭所有智能家电</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">turnOffAllDevices</span><span class="params">()</span> {</span><br><span class="line"> light.turnOff(); <span class="comment">// 关闭灯</span></span><br><span class="line"> tv.turnOff(); <span class="comment">// 关闭电视</span></span><br><span class="line"> airConditioner.turnOff(); <span class="comment">// 关闭空调</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><strong>客户端:</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">VoiceControlDemo</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> <span class="type">SmartHomeFacade</span> <span class="variable">smartHome</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SmartHomeFacade</span>();</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 通过语音控制打开智能家电</span></span><br><span class="line"> smartHome.turnOnAllDevices();</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 通过语音控制关闭智能家电</span></span><br><span class="line"> smartHome.turnOffAllDevices();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h1 id="结构型-适配器-Adapter"><a href="#结构型-适配器-Adapter" class="headerlink" title="结构型-适配器(Adapter)"></a>结构型-适配器(Adapter)</h1><blockquote>
<p>适配器模式(Adapter pattern)可以将一个类的接口, 转换成客户期望的另一个接口。 适配器让原本接口不兼容的类可以合作无间。 对象适配器使用组合, 类适配器使用多重继承。我们一般用对象适配器</p>
</blockquote>
<h2 id="示例:TF卡转SD卡"><a href="#示例:TF卡转SD卡" class="headerlink" title="示例:TF卡转SD卡"></a>示例:TF卡转SD卡</h2><p>现有一台电脑只能读取SD卡,而要读取TF卡中的内容的话就需要使用到适配器模式。创建一个读卡器,将TF卡中的内容读取出来</p>
<p><strong>SD卡接口和实现类</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">//SD卡的接口</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">SDCard</span> {</span><br><span class="line"> <span class="comment">//读取SD卡方法</span></span><br><span class="line"> String <span class="title function_">readSD</span><span class="params">()</span>;</span><br><span class="line"> <span class="comment">//写入SD卡功能</span></span><br><span class="line"> <span class="keyword">void</span> <span class="title function_">writeSD</span><span class="params">(String msg)</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//SD卡实现类</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SDCardImpl</span> <span class="keyword">implements</span> <span class="title class_">SDCard</span> {</span><br><span class="line"> <span class="keyword">public</span> String <span class="title function_">readSD</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"sd card read a msg :hello word SD"</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">writeSD</span><span class="params">(String msg)</span> {</span><br><span class="line"> System.out.println(<span class="string">"sd card write msg : "</span> + msg);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><strong>电脑类</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Computer</span> {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> String <span class="title function_">readSD</span><span class="params">(SDCard sdCard)</span> {</span><br><span class="line"> <span class="keyword">if</span>(sdCard == <span class="literal">null</span>) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">NullPointerException</span>(<span class="string">"sd card null"</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> sdCard.readSD();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><strong>TF卡接口和实现类</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">//TF卡接口</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">TFCard</span> {</span><br><span class="line"> <span class="comment">//读取TF卡方法</span></span><br><span class="line"> String <span class="title function_">readTF</span><span class="params">()</span>;</span><br><span class="line"> <span class="comment">//写入TF卡功能</span></span><br><span class="line"> <span class="keyword">void</span> <span class="title function_">writeTF</span><span class="params">(String msg)</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//TF卡实现类</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TFCardImpl</span> <span class="keyword">implements</span> <span class="title class_">TFCard</span> {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> String <span class="title function_">readTF</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"tf card read msg : hello word tf card"</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">writeTF</span><span class="params">(String msg)</span> {</span><br><span class="line"> System.out.println(<span class="string">"tf card write a msg : "</span> + msg);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><strong>适配器类</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">//创建适配器对象(SD兼容TF)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SDAdapterTF</span> <span class="keyword">implements</span> <span class="title class_">SDCard</span> {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> TFCard tfCard;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">SDAdapterTF</span><span class="params">(TFCard tfCard)</span> {</span><br><span class="line"> <span class="built_in">this</span>.tfCard = tfCard;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> String <span class="title function_">readSD</span><span class="params">()</span> {</span><br><span class="line"> System.out.println(<span class="string">"adapter read tf card "</span>);</span><br><span class="line"> <span class="keyword">return</span> tfCard.readTF();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">writeSD</span><span class="params">(String msg)</span> {</span><br><span class="line"> System.out.println(<span class="string">"adapter write tf card"</span>);</span><br><span class="line"> tfCard.writeTF(msg);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><strong>测试类</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Client</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> <span class="type">Computer</span> <span class="variable">computer</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Computer</span>();</span><br><span class="line"> <span class="type">SDCard</span> <span class="variable">sdCard</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SDCardImpl</span>();</span><br><span class="line"> System.out.println(computer.readSD(sdCard));</span><br><span class="line"></span><br><span class="line"> System.out.println(<span class="string">"------------"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="type">TFCard</span> <span class="variable">tfCard</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TFCardImpl</span>();</span><br><span class="line"> <span class="type">SDAdapterTF</span> <span class="variable">adapter</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SDAdapterTF</span>(tfCard);</span><br><span class="line"> System.out.println(computer.readSD(adapter));</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h1 id="结构型-桥接-Bridge"><a href="#结构型-桥接-Bridge" class="headerlink" title="结构型-桥接(Bridge)"></a>结构型-桥接(Bridge)</h1><blockquote>
<p>桥接模式主要是为了解决两个维度的东西使用继承方式配对时造成的继承爆炸</p>
</blockquote>
<p>现在有一个需求,需要创建不同的图形,并且每个图形都有可能会有不同的颜色。我们可以利用继承的方式来设计类的关系。但使用继承的话,后续不断的添加子类就会造成继承爆炸(子类过多)</p>
<p>颜色和形状是两个维度的东西。试想,在一个有多种可能会变化的维度的系统中,用继承方式会造成类爆炸,扩展起来不灵活。每次在一个维度上新增一个具体实现都要增加多个子类。为了更加灵活的设计系统,我们此时可以考虑使用桥接模式。</p>
<p>现在你大概理解了我们为什么要使用桥接,用正规的术语来描述使用场景,就是:</p>
<ul>
<li>当一个类存在两个独立变化的维度,且这两个维度都需要进行扩展时。</li>
<li>当一个系统不希望使用继承或因为多层次继承导致系统类的个数急剧增加时。</li>
<li>当一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性时。避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。</li>
</ul>
<h2 id="示例:不同系统播不同格式视频"><a href="#示例:不同系统播不同格式视频" class="headerlink" title="示例:不同系统播不同格式视频"></a>示例:不同系统播不同格式视频</h2><p>需要开发一个跨平台视频播放器,可以在不同操作系统平台(如Windows、Mac、Linux等)上播放多种格式的视频文件,常见的视频格式包括RMVB、AVI、WMV等。该播放器包含了两个维度,适合使用桥接模式</p>
<p>在操作系统抽象类中不提供默认实现的原因是,父类无法确定子类应该如何具体实现播放操作。不同的操作系统版本可能会有特定的实现细节和环境差异,因此需要交给子类去实现</p>
<p><strong>视频接口:</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">VideoFile</span> {</span><br><span class="line"> <span class="keyword">void</span> <span class="title function_">decode</span><span class="params">(String fileName)</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><strong>不同格式的视频实现类:</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// avi文件</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AVIFile</span> <span class="keyword">implements</span> <span class="title class_">VideoFile</span> {</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">decode</span><span class="params">(String fileName)</span> {</span><br><span class="line"> System.out.println(<span class="string">"avi视频文件:"</span> + fileName);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// rmvb文件</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">REVBBFile</span> <span class="keyword">implements</span> <span class="title class_">VideoFile</span> {</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">decode</span><span class="params">(String fileName)</span> {</span><br><span class="line"> System.out.println(<span class="string">"rmvb文件:"</span> + fileName);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><strong>抽象的操作系统:</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">class</span> <span class="title class_">OperatingSystemVersion</span> {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">protected</span> VideoFile videoFile;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">OperatingSystemVersion</span><span class="params">(VideoFile videoFile)</span> {</span><br><span class="line"> <span class="built_in">this</span>.videoFile = videoFile;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title function_">play</span><span class="params">(String fileName)</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><strong>不同的操作系统子类:</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// Windows系统</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Windows</span> <span class="keyword">extends</span> <span class="title class_">OperatingSystemVersion</span> {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">Windows</span><span class="params">(VideoFile videoFile)</span> {</span><br><span class="line"> <span class="built_in">super</span>(videoFile);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">play</span><span class="params">(String fileName)</span> {</span><br><span class="line"> videoFile.decode(fileName);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// Mac系统</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Mac</span> <span class="keyword">extends</span> <span class="title class_">OperatingSystemVersion</span> {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">Mac</span><span class="params">(VideoFile videoFile)</span> {</span><br><span class="line"> <span class="built_in">super</span>(videoFile);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">play</span><span class="params">(String fileName)</span> {</span><br><span class="line"> videoFile.decode(fileName);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><strong>测试类:</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Client</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> <span class="type">OperatingSystem</span> <span class="variable">os</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Windows</span>(<span class="keyword">new</span> <span class="title class_">AVIFile</span>());</span><br><span class="line"> os.play(<span class="string">"三体"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="为什么是抽象类对接口?"><a href="#为什么是抽象类对接口?" class="headerlink" title="为什么是抽象类对接口?"></a>为什么是抽象类对接口?</h2><p><strong>我们来重温一下接口和抽象类的区别:</strong></p>
<p>抽象类和接口在设计上有不同的用途和特点,选择使用抽象类或接口取决于你的设计需求。</p>
<ol>
<li><p>抽象类:</p>
<ul>
<li><strong>抽象类可以包含成员变量、非抽象方法和抽象方法。</strong></li>
<li>抽象类可以提供默认实现,并且子类可以通过继承来复用这些实现。</li>
<li>抽象类适合用于拥有共享代码和行为的相关类之间的继承关系。</li>
</ul>
</li>
<li><p>接口:</p>
<ul>
<li>接口只能包含常量和抽象方法。接口中的方法没有默认实现,需要在实现时进行具体实现。</li>
<li>类可以实现多个接口,从而具备多个接口定义的行为。这种多继承的特性使得接口在实现类的灵活性上更加强大。</li>
<li>接口适合用于描述一组相似的行为,并且这些行为可以被不同的类实现。</li>
</ul>
</li>
</ol>
<p>因此,当你需要定义一组行为,并希望多个类实现这些行为时,使用接口是一个不错的选择。接口更加灵活并且支持类的多继承。</p>
<p>在代码片段中,<code>VideoFile</code>是一个接口,描述了视频文件的解码行为。<code>OperatingSystemVersion</code>是一个抽象类,提供了操作系统版本的通用行为,并将具体的视频文件解码行为委托给了实现了<code>VideoFile</code>接口的具体类。这样设计的好处是,可以通过定义不同的视频文件实现类来支持不同格式的视频文件解码,而不需要修改操作系统版本类。</p>
<p>总结来说,抽象类适用于共享代码和行为的继承关系,而接口适用于描述一组相似行为并让多个类实现。在特定的设计场景中,你可以选择使用抽象类、接口或二者的结合,以满足你的需求。</p>
<h1 id="结构型-组合-Composite"><a href="#结构型-组合-Composite" class="headerlink" title="结构型 - 组合(Composite)"></a>结构型 - 组合(Composite)</h1><blockquote>
<p>组合模式主要用来解决树形结构和嵌套结构的问题。它通过将对象组织成树状结构,使得我们可以统一处理单个对象和对象组合,从而简化对树形结构的操作和管理</p>
</blockquote>
<p>组合模式主要包含三种角色:</p>
<ul>
<li>抽象根节点(Component):定义系统各层次对象的共有方法和属性,可以预先定义一些默认行为和属性。</li>
<li>树枝节点(Composite):定义树枝节点的行为,存储子节点,组合树枝节点和叶子节点形成一个树形结构。</li>
<li>叶子节点(Leaf):叶子节点对象,其下再无分支,是系统层次遍历的最小单位。</li>
</ul>
<h2 id="示例:软件菜单"><a href="#示例:软件菜单" class="headerlink" title="示例:软件菜单"></a>示例:软件菜单</h2><p>如下图,我们在访问别的一些管理系统时,经常可以看到类似的菜单。一个菜单可以包含菜单项(菜单项是指不再包含其他内容的菜单条目),也可以包含带有其他菜单项的菜单,因此使用组合模式描述菜单就很恰当,我们的需求是针对一个菜单,打印出其包含的所有菜单以及菜单项的名称</p>
<figure class="highlight markdown"><table><tr><td class="code"><pre><span class="line"><span class="bullet">-</span> 主菜单</span><br><span class="line"><span class="bullet"> -</span> 子菜单 1</span><br><span class="line"><span class="bullet"> -</span> 菜单项 1.1</span><br><span class="line"><span class="bullet"> -</span> 菜单项 1.2</span><br><span class="line"><span class="bullet"> -</span> 子菜单 2</span><br><span class="line"><span class="bullet"> -</span> 菜单项 2.1</span><br><span class="line"><span class="bullet"> -</span> 菜单项 2.2</span><br><span class="line"><span class="bullet"> -</span> 菜单项 2.3</span><br><span class="line"><span class="bullet"> -</span> 子菜单 3</span><br></pre></td></tr></table></figure>
<p>不管是菜单还是菜单项,都应该继承自统一的接口,这里姑且将这个统一的接口称为菜单组件。</p>
<p>这里的MenuComponent定义为抽象类,因为有一些共有的属性和行为要在该类中实现。Menu和MenuItem类就可以只覆盖自己感兴趣的方法,而不用搭理不需要或者不感兴趣的方法。</p>
<p>举例来说,Menu类可以包含子菜单,因此需要覆盖add()、remove()、getChild()方法,但是MenuItem就不应该有这些方法。这里给出的默认实现是抛出异常,也可以根据自己的需要改写默认实现。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">//菜单组件 不管是菜单还是菜单项,都应该继承该类</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">class</span> <span class="title class_">MenuComponent</span> {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">protected</span> String name;</span><br><span class="line"> <span class="keyword">protected</span> <span class="type">int</span> level;</span><br><span class="line"></span><br><span class="line"> <span class="comment">//添加菜单</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">add</span><span class="params">(MenuComponent menuComponent)</span>{</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">UnsupportedOperationException</span>();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//移除菜单</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">remove</span><span class="params">(MenuComponent menuComponent)</span>{</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">UnsupportedOperationException</span>();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//获取指定的子菜单</span></span><br><span class="line"> <span class="keyword">public</span> MenuComponent <span class="title function_">getChild</span><span class="params">(<span class="type">int</span> i)</span>{</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">UnsupportedOperationException</span>();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//获取菜单名称</span></span><br><span class="line"> <span class="keyword">public</span> String <span class="title function_">getName</span><span class="params">()</span>{</span><br><span class="line"> <span class="keyword">return</span> name;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">print</span><span class="params">()</span>{</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">UnsupportedOperationException</span>();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>Menu类已经实现了除了getName方法的其他所有方法,因为Menu类具有添加菜单,移除菜单和获取子菜单的功能。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Menu</span> <span class="keyword">extends</span> <span class="title class_">MenuComponent</span> {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> List<MenuComponent> menuComponentList;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">Menu</span><span class="params">(String name,<span class="type">int</span> level)</span>{</span><br><span class="line"> <span class="built_in">this</span>.level = level;</span><br><span class="line"> <span class="built_in">this</span>.name = name;</span><br><span class="line"> menuComponentList = <span class="keyword">new</span> <span class="title class_">ArrayList</span><MenuComponent>();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">add</span><span class="params">(MenuComponent menuComponent)</span> {</span><br><span class="line"> menuComponentList.add(menuComponent);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">remove</span><span class="params">(MenuComponent menuComponent)</span> {</span><br><span class="line"> menuComponentList.remove(menuComponent);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> MenuComponent <span class="title function_">getChild</span><span class="params">(<span class="type">int</span> i)</span> {</span><br><span class="line"> <span class="keyword">return</span> menuComponentList.get(i);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">print</span><span class="params">()</span> {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span>; i < level; i++) {</span><br><span class="line"> System.out.print(<span class="string">"--"</span>);</span><br><span class="line"> }</span><br><span class="line"> System.out.println(name);</span><br><span class="line"> <span class="keyword">for</span> (MenuComponent menuComponent : menuComponentList) {</span><br><span class="line"> menuComponent.print();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>MenuItem是菜单项,不能再有子菜单,所以添加菜单,移除菜单和获取子菜单的功能并不能实现。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MenuItem</span> <span class="keyword">extends</span> <span class="title class_">MenuComponent</span> {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">MenuItem</span><span class="params">(String name,<span class="type">int</span> level)</span> {</span><br><span class="line"> <span class="built_in">this</span>.name = name;</span><br><span class="line"> <span class="built_in">this</span>.level = level;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">print</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span>; i < level; i++) {</span><br><span class="line"> System.out.print(<span class="string">"--"</span>);</span><br><span class="line"> }</span><br><span class="line"> System.out.println(name);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h1 id="结构型-装饰-Decorator"><a href="#结构型-装饰-Decorator" class="headerlink" title="结构型 - 装饰(Decorator)"></a>结构型 - 装饰(Decorator)</h1><blockquote>
<p>指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式。简单来说,装饰者模式是为插件设计而生</p>
</blockquote>
<h2 id="示例:添加配菜"><a href="#示例:添加配菜" class="headerlink" title="示例:添加配菜"></a>示例:添加配菜</h2><p>快餐店有炒面、炒饭这些快餐,可以额外附加鸡蛋、火腿、培根这些配菜,当然加配菜需要额外加钱,每个配菜的价钱通常不太一样,那么计算总价就会显得比较麻烦。使用继承方式实现的话扩展性差,且会造成继承爆炸。现在我们用装饰者模式实现</p>
<p><strong>快餐接口:</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Getter</span></span><br><span class="line"><span class="meta">@Setter</span></span><br><span class="line"><span class="meta">@NoArgsConstructor</span></span><br><span class="line"><span class="meta">@AllArgsConstructor</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">class</span> <span class="title class_">FastFood</span> {</span><br><span class="line"> <span class="keyword">private</span> <span class="type">float</span> price;</span><br><span class="line"> <span class="keyword">private</span> String desc;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 获取价格</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">abstract</span> <span class="type">float</span> <span class="title function_">cost</span><span class="params">()</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><strong>快餐实现类:</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">//炒饭</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">FriedRice</span> <span class="keyword">extends</span> <span class="title class_">FastFood</span> {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">FriedRice</span><span class="params">()</span> {</span><br><span class="line"> <span class="built_in">super</span>(<span class="number">10</span>, <span class="string">"炒饭"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="type">float</span> <span class="title function_">cost</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> getPrice();</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//炒面</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">FriedNoodles</span> <span class="keyword">extends</span> <span class="title class_">FastFood</span> {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">FriedNoodles</span><span class="params">()</span> {</span><br><span class="line"> <span class="built_in">super</span>(<span class="number">12</span>, <span class="string">"炒面"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="type">float</span> <span class="title function_">cost</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> getPrice();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>配菜抽象类</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Getter</span></span><br><span class="line"><span class="meta">@Setter</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">class</span> <span class="title class_">Garnish</span> <span class="keyword">extends</span> <span class="title class_">FastFood</span> {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> FastFood fastFood;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">Garnish</span><span class="params">(FastFood fastFood, <span class="type">float</span> price, String desc)</span> {</span><br><span class="line"> <span class="built_in">super</span>(price, desc);</span><br><span class="line"> <span class="built_in">this</span>.fastFood = fastFood;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><strong>配菜实现类:</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">//鸡蛋配料</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Egg</span> <span class="keyword">extends</span> <span class="title class_">Garnish</span> {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">Egg</span><span class="params">(FastFood fastFood)</span> {</span><br><span class="line"> <span class="built_in">super</span>(fastFood, <span class="number">1</span>, <span class="string">"鸡蛋"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="type">float</span> <span class="title function_">cost</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> getPrice() + getFastFood().getPrice();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> String <span class="title function_">getDesc</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">super</span>.getDesc() + getFastFood().getDesc();</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//培根配料</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Bacon</span> <span class="keyword">extends</span> <span class="title class_">Garnish</span> {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">Bacon</span><span class="params">(FastFood fastFood)</span> {</span><br><span class="line"> <span class="built_in">super</span>(fastFood, <span class="number">2</span>, <span class="string">"培根"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="type">float</span> <span class="title function_">cost</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> getPrice() + getFastFood().getPrice();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> String <span class="title function_">getDesc</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">super</span>.getDesc() + getFastFood().getDesc();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><strong>测试类:</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Client</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> <span class="comment">//点一份炒饭</span></span><br><span class="line"> <span class="type">FastFood</span> <span class="variable">food</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">FriedRice</span>();</span><br><span class="line"> <span class="comment">//花费的价格</span></span><br><span class="line"> System.out.println(food.getDesc() + <span class="string">" "</span> + food.cost() + <span class="string">"元"</span>);</span><br><span class="line"> System.out.println(<span class="string">"========"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">//点一份加鸡蛋的炒饭</span></span><br><span class="line"> <span class="type">FastFood</span> <span class="variable">food1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">FriedRice</span>();</span><br><span class="line"> food1 = <span class="keyword">new</span> <span class="title class_">Egg</span>(food1);</span><br><span class="line"> <span class="comment">//花费的价格</span></span><br><span class="line"> System.out.println(food1.getDesc() + <span class="string">" "</span> + food1.cost() + <span class="string">"元"</span>);</span><br><span class="line"> System.out.println(<span class="string">"========"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">//点一份加培根的炒面</span></span><br><span class="line"> <span class="type">FastFood</span> <span class="variable">food2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">FriedNoodles</span>();</span><br><span class="line"> food2 = <span class="keyword">new</span> <span class="title class_">Bacon</span>(food2);</span><br><span class="line"> <span class="comment">//花费的价格</span></span><br><span class="line"> System.out.println(food2.getDesc() + <span class="string">" "</span> + food2.cost() + <span class="string">"元"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h1 id="结构型-享元-Flyweight"><a href="#结构型-享元-Flyweight" class="headerlink" title="结构型 - 享元(Flyweight)"></a>结构型 - 享元(Flyweight)</h1><blockquote>
<p>我们可以将单例对象放到一个对象池中给大家共享,这样可以极大减少内存中相似或相同对象数量,节约系统资源,提供系统性能</p>
</blockquote>
<p>享元模式中的外部状态相对独立,且不影响内部状态</p>
<p>但同时,也有缺点:为了使对象可以共享,需要将享元对象的部分状态外部化,分离内部状态和外部状态,使程序逻辑复杂</p>
<h2 id="示例:俄罗斯方块"><a href="#示例:俄罗斯方块" class="headerlink" title="示例:俄罗斯方块"></a>示例:俄罗斯方块</h2><p>下面的图片是众所周知的俄罗斯方块中的一个个方块,如果在俄罗斯方块这个游戏中,每个不同的方块都是一个实例对象,这些对象就要占用很多的内存空间,下面利用享元模式进行实现。</p>
<p><img src="http://notes.xiyankt.com/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/img/%E4%BF%84%E7%BD%97%E6%96%AF%E6%96%B9%E5%9D%97.jpeg" alt="俄罗斯方块"></p>
<p>俄罗斯方块有不同的形状,我们可以对这些形状向上抽取出AbstractBox,用来定义共性的属性和行为。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">class</span> <span class="title class_">AbstractBox</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">abstract</span> String <span class="title function_">getShape</span><span class="params">()</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">display</span><span class="params">(String color)</span> {</span><br><span class="line"> System.out.println(<span class="string">"方块形状:"</span> + <span class="built_in">this</span>.getShape() + <span class="string">" 颜色:"</span> + color);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>接下来就是定义不同的形状了,IBox类、LBox类、OBox类等。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">IBox</span> <span class="keyword">extends</span> <span class="title class_">AbstractBox</span> {</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> String <span class="title function_">getShape</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"I"</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">LBox</span> <span class="keyword">extends</span> <span class="title class_">AbstractBox</span> {</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> String <span class="title function_">getShape</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"L"</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">OBox</span> <span class="keyword">extends</span> <span class="title class_">AbstractBox</span> {</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> String <span class="title function_">getShape</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"O"</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>提供了一个工厂类(BoxFactory),用来管理享元对象(也就是AbstractBox子类对象),该工厂类对象只需要一个,所以可以使用单例模式。并给工厂类提供一个获取形状的方法。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BoxFactory</span> {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> HashMap<String, AbstractBox> map;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> BoxFactory <span class="title function_">getInstance</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> SingletonHolder.INSTANCE;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">SingletonHolder</span> {</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">BoxFactory</span> <span class="variable">INSTANCE</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BoxFactory</span>();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="title function_">BoxFactory</span><span class="params">()</span> {</span><br><span class="line"> map = <span class="keyword">new</span> <span class="title class_">HashMap</span><String, AbstractBox>();</span><br><span class="line"> <span class="type">AbstractBox</span> <span class="variable">iBox</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">IBox</span>();</span><br><span class="line"> <span class="type">AbstractBox</span> <span class="variable">lBox</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">LBox</span>();</span><br><span class="line"> <span class="type">AbstractBox</span> <span class="variable">oBox</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">OBox</span>();</span><br><span class="line"> map.put(<span class="string">"I"</span>, iBox);</span><br><span class="line"> map.put(<span class="string">"L"</span>, lBox);</span><br><span class="line"> map.put(<span class="string">"O"</span>, oBox);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> AbstractBox <span class="title function_">getBox</span><span class="params">(String key)</span> {</span><br><span class="line"> <span class="keyword">return</span> map.get(key);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h2 id="JDK源码示例:Integer"><a href="#JDK源码示例:Integer" class="headerlink" title="JDK源码示例:Integer"></a>JDK源码示例:Integer</h2><blockquote>
<p> <code>Integer</code> 默认先创建并缓存 <code>-128 ~ 127</code> 之间数的 <code>Integer</code> 对象,当调用 <code>valueOf</code> 时如果参数在 <code>-128 ~ 127</code> 之间则计算下标并从缓存中返回,否则创建一个新的 <code>Integer</code> 对象</p>
</blockquote>
<p>对于下面的代码</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Demo</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> <span class="type">Integer</span> <span class="variable">i1</span> <span class="operator">=</span> <span class="number">127</span>;</span><br><span class="line"> <span class="type">Integer</span> <span class="variable">i2</span> <span class="operator">=</span> <span class="number">127</span>;</span><br><span class="line"> System.out.println(<span class="string">"i1和i2对象是否是同一个对象?"</span> + (i1 == i2)); <span class="comment">// 输出true</span></span><br><span class="line"></span><br><span class="line"> <span class="type">Integer</span> <span class="variable">i3</span> <span class="operator">=</span> <span class="number">128</span>;</span><br><span class="line"> <span class="type">Integer</span> <span class="variable">i4</span> <span class="operator">=</span> <span class="number">128</span>;</span><br><span class="line"> System.out.println(<span class="string">"i3和i4对象是否是同一个对象?"</span> + (i3 == i4)); <span class="comment">// 输出false</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>编译后有:</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Demo</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> <span class="type">Integer</span> <span class="variable">i1</span> <span class="operator">=</span> Integer.valueOf((<span class="type">int</span>)<span class="number">127</span>);</span><br><span class="line"> Integer i2 Integer.valueOf((<span class="type">int</span>)<span class="number">127</span>);</span><br><span class="line"> System.out.println((String)<span class="keyword">new</span> <span class="title class_">StringBuilder</span>().append((String)<span class="string">"i1\u548ci2\u5bf9\u8c61\u662f\u5426\u662f\u540c\u4e00\u4e2a\u5bf9\u8c61\uff1f"</span>).append((<span class="type">boolean</span>)(i1 == i2)).toString());</span><br><span class="line"> <span class="type">Integer</span> <span class="variable">i3</span> <span class="operator">=</span> Integer.valueOf((<span class="type">int</span>)<span class="number">128</span>);</span><br><span class="line"> <span class="type">Integer</span> <span class="variable">i4</span> <span class="operator">=</span> Integer.valueOf((<span class="type">int</span>)<span class="number">128</span>);</span><br><span class="line"> System.out.println((String)<span class="keyword">new</span> <span class="title class_">StringBuilder</span>().append((String)<span class="string">"i3\u548ci4\u5bf9\u8c61\u662f\u5426\u662f\u540c\u4e00\u4e2a\u5bf9\u8c61\uff1f"</span>).append((<span class="type">boolean</span>)(i3 == i4)).toString());</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>上面代码可以看到,直接给Integer类型的变量赋值基本数据类型数据的操作底层使用的就是 <code>valueOf()</code>:</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">Integer</span> <span class="keyword">extends</span> <span class="title class_">Number</span> <span class="keyword">implements</span> <span class="title class_">Comparable</span><Integer> {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> Integer <span class="title function_">valueOf</span><span class="params">(<span class="type">int</span> i)</span> {</span><br><span class="line"> <span class="keyword">if</span> (i >= IntegerCache.low && i <= IntegerCache.high)</span><br><span class="line"> <span class="keyword">return</span> IntegerCache.cache[i + (-IntegerCache.low)];</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Integer</span>(i);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">IntegerCache</span> {</span><br><span class="line"> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">low</span> <span class="operator">=</span> -<span class="number">128</span>;</span><br><span class="line"> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> high;</span><br><span class="line"> <span class="keyword">static</span> <span class="keyword">final</span> Integer cache[];</span><br><span class="line"></span><br><span class="line"> <span class="keyword">static</span> {</span><br><span class="line"> <span class="type">int</span> <span class="variable">h</span> <span class="operator">=</span> <span class="number">127</span>;</span><br><span class="line"> <span class="type">String</span> <span class="variable">integerCacheHighPropValue</span> <span class="operator">=</span></span><br><span class="line"> sun.misc.VM.getSavedProperty(<span class="string">"java.lang.Integer.IntegerCache.high"</span>);</span><br><span class="line"> <span class="keyword">if</span> (integerCacheHighPropValue != <span class="literal">null</span>) {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> parseInt(integerCacheHighPropValue);</span><br><span class="line"> i = Math.max(i, <span class="number">127</span>);</span><br><span class="line"> <span class="comment">// Maximum array size is Integer.MAX_VALUE</span></span><br><span class="line"> h = Math.min(i, Integer.MAX_VALUE - (-low) -<span class="number">1</span>);</span><br><span class="line"> } <span class="keyword">catch</span>( NumberFormatException nfe) {</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> high = h;</span><br><span class="line"> cache = <span class="keyword">new</span> <span class="title class_">Integer</span>[(high - low) + <span class="number">1</span>];</span><br><span class="line"> <span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> low;</span><br><span class="line"> <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">k</span> <span class="operator">=</span> <span class="number">0</span>; k < cache.length; k++)</span><br><span class="line"> cache[k] = <span class="keyword">new</span> <span class="title class_">Integer</span>(j++);</span><br><span class="line"> <span class="comment">// range [-128, 127] must be interned (JLS7 5.1.7)</span></span><br><span class="line"> <span class="keyword">assert</span> IntegerCache.high >= <span class="number">127</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="title function_">IntegerCache</span><span class="params">()</span> {}</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h1 id="结构型-代理-Proxy"><a href="#结构型-代理-Proxy" class="headerlink" title="结构型 - 代理(Proxy)"></a>结构型 - 代理(Proxy)</h1><blockquote>
<p>代理模式的目的是为另一个对象提供一个替身或占位符以控制对这个对象的访问。</p>
</blockquote>
<ul>
<li>代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;</li>
<li>代理对象可以扩展目标对象的功能;</li>
<li>代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度;</li>
</ul>
<p>Java中的代理按照代理类生成时机不同又分为静态代理和动态代理。静态代理代理类在编译期就生成,而动态代理代理类则是在Java运行时动态生成。动态代理又有JDK代理和CGLib代理两种。</p>
<h2 id="三种代理的对比:"><a href="#三种代理的对比:" class="headerlink" title="三种代理的对比:"></a>三种代理的对比:</h2><ul>
<li><p>动态代理和静态代理</p>
<p>动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。</p>
<p>如果接口增加一个方法,静态代理模式除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。而动态代理不会出现该问题。</p>
</li>
<li><p>使用CGLib实现动态代理,CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,在JDK1.6之前比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的类或者方法进行代理,因为CGLib原理是动态生成被代理类的子类。</p>
<p>在JDK1.6、JDK1.7、JDK1.8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLib代理效率,只有当进行大量调用的时候,JDK1.6和JDK1.7比CGLib代理效率低一点,但是到JDK1.8的时候,JDK代理效率高于CGLib代理。所以如果有接口使用JDK动态代理,如果没有接口使用CGLIB代理。</p>
</li>
</ul>
<h2 id="JDK动态代理示例"><a href="#JDK动态代理示例" class="headerlink" title="JDK动态代理示例"></a>JDK动态代理示例</h2><p>如果要买火车票的话,需要去火车站买票,坐车到火车站,排队等一系列的操作,显然比较麻烦。而火车站在多个地方都有代售点,我们去代售点买票就方便很多了。这个例子其实就是典型的代理模式,火车站是目标对象,代售点是代理对象</p>
<p>Java中提供了一个动态代理类<code>Proxy</code>,<code>Proxy</code>并不是我们上述所说的代理对象的类,而是提供了一个创建代理对象的静态方法(<code>newProxyInstance</code>方法)来获取代理对象:</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> java.lang.reflect.InvocationHandler;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Method;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Proxy;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 卖票接口</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">SellTickets</span> {</span><br><span class="line"> <span class="keyword">void</span> <span class="title function_">sell</span><span class="params">()</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 火车站 火车站具有卖票功能,所以需要实现SellTickets接口</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TrainStation</span> <span class="keyword">implements</span> <span class="title class_">SellTickets</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sell</span><span class="params">()</span> {</span><br><span class="line"> System.out.println(<span class="string">"火车站卖票"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 代理工厂,用来创建代理对象</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ProxyFactory</span> {</span><br><span class="line"> <span class="keyword">private</span> <span class="type">TrainStation</span> <span class="variable">station</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TrainStation</span>();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> SellTickets <span class="title function_">getProxyObject</span><span class="params">()</span> {</span><br><span class="line"> <span class="comment">// 使用Proxy获取代理对象</span></span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> newProxyInstance()方法参数说明:</span></span><br><span class="line"><span class="comment"> ClassLoader loader : 类加载器,用于加载代理类,使用真实对象的类加载器即可</span></span><br><span class="line"><span class="comment"> Class<?>[] interfaces : 真实对象所实现的接口,代理模式真实对象和代理对象实现相同的接口</span></span><br><span class="line"><span class="comment"> InvocationHandler h : 代理对象的调用处理程序</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="type">SellTickets</span> <span class="variable">sellTickets</span> <span class="operator">=</span> (SellTickets) Proxy.newProxyInstance(</span><br><span class="line"> station.getClass().getClassLoader(),</span><br><span class="line"> station.getClass().getInterfaces(),</span><br><span class="line"> <span class="keyword">new</span> <span class="title class_">InvocationHandler</span>() {</span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> InvocationHandler中invoke方法参数说明:</span></span><br><span class="line"><span class="comment"> proxy : 代理对象</span></span><br><span class="line"><span class="comment"> method : 对应于在代理对象上调用的接口方法的 Method 实例</span></span><br><span class="line"><span class="comment"> args : 代理对象调用接口方法时传递的实际参数</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">public</span> Object <span class="title function_">invoke</span><span class="params">(Object proxy, Method method, Object[] args)</span> <span class="keyword">throws</span> Throwable {</span><br><span class="line"> System.out.println(<span class="string">"代理点收取一些服务费用(JDK动态代理方式)"</span>);</span><br><span class="line"> <span class="comment">// 执行真实对象</span></span><br><span class="line"> <span class="type">Object</span> <span class="variable">result</span> <span class="operator">=</span> method.invoke(station, args);</span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line"> <span class="keyword">return</span> sellTickets;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 测试类</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Client</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> <span class="comment">// 获取代理对象</span></span><br><span class="line"> <span class="type">ProxyFactory</span> <span class="variable">factory</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ProxyFactory</span>();</span><br><span class="line"> <span class="type">SellTickets</span> <span class="variable">proxyObject</span> <span class="operator">=</span> factory.getProxyObject();</span><br><span class="line"> proxyObject.sell();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>请注意,<code>ProxyFactory</code>不是代理模式中所说的代理类,而代理类是程序在运行过程中动态的在内存中生成的类。通过阿里巴巴开源的 Java 诊断工具——<code>Arthas</code>查看代理类的结构:</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">package</span> com.sun.proxy;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> com.itheima.proxy.dynamic.jdk.SellTickets;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.InvocationHandler;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Method;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.Proxy;</span><br><span class="line"><span class="keyword">import</span> java.lang.reflect.UndeclaredThrowableException;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">$Proxy0</span> <span class="keyword">extends</span> <span class="title class_">Proxy</span> <span class="keyword">implements</span> <span class="title class_">SellTickets</span> {</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> Method m1;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> Method m2;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> Method m3;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> Method m0;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> $Proxy0(InvocationHandler invocationHandler) {</span><br><span class="line"> <span class="built_in">super</span>(invocationHandler);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">static</span> {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> m1 = Class.forName(<span class="string">"java.lang.Object"</span>).getMethod(<span class="string">"equals"</span>, Class.forName(<span class="string">"java.lang.Object"</span>));</span><br><span class="line"> m2 = Class.forName(<span class="string">"java.lang.Object"</span>).getMethod(<span class="string">"toString"</span>, <span class="keyword">new</span> <span class="title class_">Class</span>[<span class="number">0</span>]);</span><br><span class="line"> m3 = Class.forName(<span class="string">"com.itheima.proxy.dynamic.jdk.SellTickets"</span>).getMethod(<span class="string">"sell"</span>, <span class="keyword">new</span> <span class="title class_">Class</span>[<span class="number">0</span>]);</span><br><span class="line"> m0 = Class.forName(<span class="string">"java.lang.Object"</span>).getMethod(<span class="string">"hashCode"</span>, <span class="keyword">new</span> <span class="title class_">Class</span>[<span class="number">0</span>]);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">catch</span> (NoSuchMethodException noSuchMethodException) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">NoSuchMethodError</span>(noSuchMethodException.getMessage());</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">catch</span> (ClassNotFoundException classNotFoundException) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">NoClassDefFoundError</span>(classNotFoundException.getMessage());</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">final</span> <span class="type">boolean</span> <span class="title function_">equals</span><span class="params">(Object object)</span> {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">return</span> (Boolean)<span class="built_in">this</span>.h.invoke(<span class="built_in">this</span>, m1, <span class="keyword">new</span> <span class="title class_">Object</span>[]{object});</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">catch</span> (Error | RuntimeException throwable) {</span><br><span class="line"> <span class="keyword">throw</span> throwable;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">catch</span> (Throwable throwable) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">UndeclaredThrowableException</span>(throwable);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">final</span> String <span class="title function_">toString</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">return</span> (String)<span class="built_in">this</span>.h.invoke(<span class="built_in">this</span>, m2, <span class="literal">null</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">catch</span> (Error | RuntimeException throwable) {</span><br><span class="line"> <span class="keyword">throw</span> throwable;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">catch</span> (Throwable throwable) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">UndeclaredThrowableException</span>(throwable);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">final</span> <span class="type">int</span> <span class="title function_">hashCode</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">return</span> (Integer)<span class="built_in">this</span>.h.invoke(<span class="built_in">this</span>, m0, <span class="literal">null</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">catch</span> (Error | RuntimeException throwable) {</span><br><span class="line"> <span class="keyword">throw</span> throwable;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">catch</span> (Throwable throwable) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">UndeclaredThrowableException</span>(throwable);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title function_">sell</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="built_in">this</span>.h.invoke(<span class="built_in">this</span>, m3, <span class="literal">null</span>);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">catch</span> (Error | RuntimeException throwable) {</span><br><span class="line"> <span class="keyword">throw</span> throwable;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">catch</span> (Throwable throwable) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">UndeclaredThrowableException</span>(throwable);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>从上面的类中,我们可以看到以下几个信息:</p>
<ul>
<li>代理类<code>$Proxy0</code>实现了<code>SellTickets</code>。这也就印证了我们之前说的真实类和代理类实现同样的接口。</li>
<li>代理类<code>$Proxy0</code>将我们提供了的匿名内部类对象传递给了父类。</li>
</ul>
<p>动态代理的执行流程是什么样的?</p>
<p>下面是摘取的重点代码:</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">//程序运行过程中动态生成的代理类</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">$Proxy0</span> <span class="keyword">extends</span> <span class="title class_">Proxy</span> <span class="keyword">implements</span> <span class="title class_">SellTickets</span> {</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> Method m3;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> $Proxy0(InvocationHandler invocationHandler) {</span><br><span class="line"> <span class="built_in">super</span>(invocationHandler);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">static</span> {</span><br><span class="line"> m3 = Class.forName(<span class="string">"com.itheima.proxy.dynamic.jdk.SellTickets"</span>).getMethod(<span class="string">"sell"</span>, <span class="keyword">new</span> <span class="title class_">Class</span>[<span class="number">0</span>]);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title function_">sell</span><span class="params">()</span> {</span><br><span class="line"> <span class="built_in">this</span>.h.invoke(<span class="built_in">this</span>, m3, <span class="literal">null</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//Java提供的动态代理相关类</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Proxy</span> <span class="keyword">implements</span> <span class="title class_">java</span>.io.Serializable {</span><br><span class="line"> <span class="keyword">protected</span> InvocationHandler h;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">protected</span> <span class="title function_">Proxy</span><span class="params">(InvocationHandler h)</span> {</span><br><span class="line"> <span class="built_in">this</span>.h = h;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//代理工厂类</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ProxyFactory</span> {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="type">TrainStation</span> <span class="variable">station</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TrainStation</span>();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> SellTickets <span class="title function_">getProxyObject</span><span class="params">()</span> {</span><br><span class="line"> <span class="type">SellTickets</span> <span class="variable">sellTickets</span> <span class="operator">=</span> (SellTickets) Proxy.newProxyInstance(station.getClass().getClassLoader(),</span><br><span class="line"> station.getClass().getInterfaces(),</span><br><span class="line"> <span class="keyword">new</span> <span class="title class_">InvocationHandler</span>() {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> Object <span class="title function_">invoke</span><span class="params">(Object proxy, Method method, Object[] args)</span> <span class="keyword">throws</span> Throwable {</span><br><span class="line"></span><br><span class="line"> System.out.println(<span class="string">"代理点收取一些服务费用(JDK动态代理方式)"</span>);</span><br><span class="line"> <span class="type">Object</span> <span class="variable">result</span> <span class="operator">=</span> method.invoke(station, args);</span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line"> <span class="keyword">return</span> sellTickets;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//测试访问类</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Client</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> <span class="comment">//获取代理对象</span></span><br><span class="line"> <span class="type">ProxyFactory</span> <span class="variable">factory</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ProxyFactory</span>();</span><br><span class="line"> <span class="type">SellTickets</span> <span class="variable">proxyObject</span> <span class="operator">=</span> factory.getProxyObject();</span><br><span class="line"> proxyObject.sell();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>执行流程如下:</p>
<ol>
<li>在测试类中通过代理对象调用<code>sell()</code>方法</li>
<li>根据多态的特性,执行的是代理类<code>$Proxy0</code>中的<code>sell()</code>方法</li>
<li>代理类<code>$Proxy0</code>中的<code>sell()</code>方法中又调用了<code>InvocationHandler</code>接口的子实现类对象的<code>invoke</code>方法</li>
<li><code>invoke</code>方法通过反射执行了真实对象所属类<code>TrainStation</code>中的<code>sell()</code>方法</li>
</ol>
<h2 id="CGLIB动态代理"><a href="#CGLIB动态代理" class="headerlink" title="CGLIB动态代理"></a>CGLIB动态代理</h2><p>同样是上面的案例,我们再次使用CGLIB代理实现。</p>
<p>如果没有定义SellTickets接口,只定义了TrainStation(火车站类)。很显然JDK代理是无法使用了,因为JDK动态代理要求必须定义接口,对接口进行代理。</p>
<p>CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。</p>
<p>CGLIB是第三方提供的包,所以需要引入jar包的坐标:</p>
<figure class="highlight xml"><table><tr><td class="code"><pre><span class="line"><span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>cglib<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>cglib<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>3.3.0<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"><span class="tag"></<span class="name">dependency</span>></span></span><br></pre></td></tr></table></figure>
<p><strong>火车站:</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TrainStation</span> {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sell</span><span class="params">()</span> {</span><br><span class="line"> System.out.println(<span class="string">"火车站卖票"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><strong>代理工厂:</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ProxyFactory</span> <span class="keyword">implements</span> <span class="title class_">MethodInterceptor</span> {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="type">TrainStation</span> <span class="variable">target</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TrainStation</span>();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> TrainStation <span class="title function_">getProxyObject</span><span class="params">()</span> {</span><br><span class="line"> <span class="comment">//创建Enhancer对象,类似于JDK动态代理的Proxy类,下一步就是设置几个参数</span></span><br><span class="line"> <span class="type">Enhancer</span> <span class="variable">enhancer</span> <span class="operator">=</span><span class="keyword">new</span> <span class="title class_">Enhancer</span>();</span><br><span class="line"> <span class="comment">//设置父类的字节码对象</span></span><br><span class="line"> enhancer.setSuperclass(target.getClass());</span><br><span class="line"> <span class="comment">//设置回调函数</span></span><br><span class="line"> enhancer.setCallback(<span class="built_in">this</span>);</span><br><span class="line"> <span class="comment">//创建代理对象</span></span><br><span class="line"> <span class="type">TrainStation</span> <span class="variable">obj</span> <span class="operator">=</span> (TrainStation) enhancer.create();</span><br><span class="line"> <span class="keyword">return</span> obj;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/*</span></span><br><span class="line"><span class="comment"> intercept方法参数说明:</span></span><br><span class="line"><span class="comment"> o : 代理对象</span></span><br><span class="line"><span class="comment"> method : 真实对象中的方法的Method实例</span></span><br><span class="line"><span class="comment"> args : 实际参数</span></span><br><span class="line"><span class="comment"> methodProxy :代理对象中的方法的method实例</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">public</span> TrainStation <span class="title function_">intercept</span><span class="params">(Object o, Method method, Object[] args, MethodProxy methodProxy)</span> <span class="keyword">throws</span> Throwable {</span><br><span class="line"> System.out.println(<span class="string">"代理点收取一些服务费用(CGLIB动态代理方式)"</span>);</span><br><span class="line"> <span class="type">TrainStation</span> <span class="variable">result</span> <span class="operator">=</span> (TrainStation) methodProxy.invokeSuper(o, args);</span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><strong>测试类:</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Client</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> <span class="comment">//创建代理工厂对象</span></span><br><span class="line"> <span class="type">ProxyFactory</span> <span class="variable">factory</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ProxyFactory</span>();</span><br><span class="line"> <span class="comment">//获取代理对象</span></span><br><span class="line"> <span class="type">TrainStation</span> <span class="variable">proxyObject</span> <span class="operator">=</span> factory.getProxyObject();</span><br><span class="line"></span><br><span class="line"> proxyObject.sell();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h1 id="行为型-责任链-Chain-Of-Responsibility"><a href="#行为型-责任链-Chain-Of-Responsibility" class="headerlink" title="行为型 - 责任链(Chain Of Responsibility)"></a>行为型 - 责任链(Chain Of Responsibility)</h1><blockquote>
<p>通过责任链模式, 你可以为某个请求创建一个对象链. 每个对象依序检查此请求并对其进行处理或者将它传给链中的下一个对象。</p>
</blockquote>
<p>可以把责任链看做一条流水线,产品经过某个工序处理,或被中途丢弃,或被传到下一个工序处理。我们可以很方便的插入或去掉任意一道工序,而不影响其他工序的运行,就像链表一样。每道工序只需要接收一个固定的对象,也就是上下文</p>
<p><strong>缺点:</strong></p>
<ul>
<li>不能保证每个请求一定被处理。由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。</li>
<li>对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响。</li>
<li>职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。</li>
</ul>
<h2 id="示例:请假流程"><a href="#示例:请假流程" class="headerlink" title="示例:请假流程"></a>示例:请假流程</h2><p>现需要开发一个请假流程控制系统。请假一天以下的假只需要小组长同意即可;请假1天到3天的假还需要部门经理同意;请求3天到7天还需要总经理同意才行</p>
<p><strong>上下文-请假条:</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="meta">@NoArgsConstructor</span></span><br><span class="line"><span class="meta">@AllArgsConstructor</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">LeaveRequest</span> {</span><br><span class="line"> <span class="keyword">private</span> String employeeName; <span class="comment">// 员工姓名</span></span><br><span class="line"> <span class="keyword">private</span> <span class="type">int</span> days;</span><br><span class="line"> <span class="keyword">private</span> <span class="type">boolean</span> <span class="variable">approved</span> <span class="operator">=</span> <span class="literal">false</span>; <span class="comment">// 默认未批准</span></span><br><span class="line"> <span class="keyword">private</span> String reason; <span class="comment">// 请假事由</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><strong>审批者接口:</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">Approver</span> {</span><br><span class="line"> <span class="comment">// 下一位审批者</span></span><br><span class="line"> <span class="keyword">void</span> <span class="title function_">setNextApprover</span><span class="params">(Approver nextApprover)</span>;</span><br><span class="line"> <span class="comment">// 上下文</span></span><br><span class="line"> <span class="keyword">void</span> <span class="title function_">approveLeave</span><span class="params">(LeaveRequest request)</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><strong>审批者实现类:</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 小组长</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="meta">@NoArgsConstructor</span></span><br><span class="line"><span class="meta">@AllArgsConstructor</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TeamLeader</span> <span class="keyword">implements</span> <span class="title class_">Approver</span> {</span><br><span class="line"> <span class="keyword">private</span> Approver nextApprover;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">approveLeave</span><span class="params">(LeaveRequest request)</span> {</span><br><span class="line"> <span class="keyword">if</span> (request.getDays() < <span class="number">1</span>) {</span><br><span class="line"> request.setApproved(<span class="literal">true</span>);</span><br><span class="line"> System.out.println(<span class="string">"小组长同意"</span> + request.getEmployeeName() + <span class="string">"的请假申请"</span>);</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (nextApprover != <span class="literal">null</span>) {</span><br><span class="line"> nextApprover.approveLeave(request);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 部门经理</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="meta">@NoArgsConstructor</span></span><br><span class="line"><span class="meta">@AllArgsConstructor</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DepartmentManager</span> <span class="keyword">implements</span> <span class="title class_">Approver</span> {</span><br><span class="line"> <span class="keyword">private</span> Approver nextApprover;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">approveLeave</span><span class="params">(LeaveRequest request)</span> {</span><br><span class="line"> <span class="keyword">if</span> (request.getDays() >= <span class="number">1</span> && request.getDays() <= <span class="number">3</span>) {</span><br><span class="line"> request.setApproved(<span class="literal">true</span>);</span><br><span class="line"> System.out.println(<span class="string">"部门经理同意"</span> + request.getEmployeeName() + <span class="string">"的请假申请"</span>);</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (nextApprover != <span class="literal">null</span>) {</span><br><span class="line"> nextApprover.approveLeave(request);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 总经理</span></span><br><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="meta">@NoArgsConstructor</span></span><br><span class="line"><span class="meta">@AllArgsConstructor</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">GeneralManager</span> <span class="keyword">implements</span> <span class="title class_">Approver</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">approveLeave</span><span class="params">(LeaveRequest request)</span> {</span><br><span class="line"> <span class="keyword">if</span> (request.getDays() > <span class="number">3</span> && request.getDays() <= <span class="number">7</span>) {</span><br><span class="line"> request.setApproved(<span class="literal">true</span>);</span><br><span class="line"> System.out.println(<span class="string">"总经理同意"</span> + request.getEmployeeName() + <span class="string">"的请假申请"</span>);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> request.setApproved(<span class="literal">false</span>);</span><br><span class="line"> System.out.println(<span class="string">"请假超过7天,需要进一步审批"</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><strong>请假流程控制类:</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">LeaveApprovalChain</span> {</span><br><span class="line"> <span class="keyword">private</span> Approver chain;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">LeaveApprovalChain</span><span class="params">()</span> {</span><br><span class="line"> <span class="comment">// 创建审批者实例</span></span><br><span class="line"> <span class="type">Approver</span> <span class="variable">teamLeader</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TeamLeader</span>();</span><br><span class="line"> <span class="type">Approver</span> <span class="variable">departmentManager</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DepartmentManager</span>();</span><br><span class="line"> <span class="type">Approver</span> <span class="variable">generalManager</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">GeneralManager</span>();</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 将责任链的头部设置为小组长</span></span><br><span class="line"> chain = teamLeader;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 设置责任链的顺序</span></span><br><span class="line"> teamLeader.setNextApprover(departmentManager);</span><br><span class="line"> departmentManager.setNextApprover(generalManager);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">processLeaveRequest</span><span class="params">(LeaveRequest request)</span> {</span><br><span class="line"> chain.approveLeave(request);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!request.isApproved()) {</span><br><span class="line"> System.out.println(request.getEmployeeName() + <span class="string">"的请假申请被拒绝"</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><strong>客户端调用:</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Client</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> <span class="type">LeaveRequest</span> <span class="variable">request1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">LeaveRequest</span>(<span class="string">"张三"</span>, <span class="number">0</span>, <span class="string">"家庭原因"</span>); <span class="comment">// 张三请假0天</span></span><br><span class="line"> <span class="type">LeaveRequest</span> <span class="variable">request2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">LeaveRequest</span>(<span class="string">"李四"</span>, <span class="number">2</span>, <span class="string">"病假"</span>); <span class="comment">// 李四请假2天</span></span><br><span class="line"> <span class="type">LeaveRequest</span> <span class="variable">request3</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">LeaveRequest</span>(<span class="string">"王五"</span>, <span class="number">4</span>, <span class="string">"探亲假"</span>); <span class="comment">// 王五请假4天</span></span><br><span class="line"> <span class="type">LeaveRequest</span> <span class="variable">request4</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">LeaveRequest</span>(<span class="string">"赵六"</span>, <span class="number">8</span>, <span class="string">"年休假"</span>); <span class="comment">// 赵六请假8天</span></span><br><span class="line"></span><br><span class="line"> <span class="type">LeaveApprovalChain</span> <span class="variable">chain</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">LeaveApprovalChain</span>();</span><br><span class="line"> chain.processLeaveRequest(request1);</span><br><span class="line"> chain.processLeaveRequest(request2);</span><br><span class="line"> chain.processLeaveRequest(request3);</span><br><span class="line"> chain.processLeaveRequest(request4);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><strong>运行客户端输出:</strong></p>
<figure class="highlight console"><table><tr><td class="code"><pre><span class="line">小组长同意张三的请假申请</span><br><span class="line">部门经理同意李四的请假申请</span><br><span class="line">总经理同意王五的请假申请</span><br><span class="line">请假超过7天,需要进一步审批</span><br><span class="line">赵六的请假申请被拒绝</span><br></pre></td></tr></table></figure>
<h1 id="行为型-策略-Strategy"><a href="#行为型-策略-Strategy" class="headerlink" title="行为型 - 策略(Strategy)"></a>行为型 - 策略(Strategy)</h1><blockquote>
<p> 该模式定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的变化不会影响使用算法的客户。</p>
</blockquote>
<p>我们去旅游选择出行模式有很多种,可以骑自行车、可以坐汽车、可以坐火车、可以坐飞机……</p>
<p>作为一个程序猿,开发需要选择一款开发工具,当然可以进行代码开发的工具有很多,可以选择Idea进行开发,也可以使用eclipse进行开发,也可以使用其他的一些开发工具。</p>
<p>上述例子的共同点是:我们可以自由选择策略来执行某个任务。</p>
<p>策略模式的主要角色如下:</p>
<ul>
<li>抽象策略(Strategy)类:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。</li>
<li>具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现或行为。</li>
<li>环境(Context)类:持有一个策略类的引用,最终给客户端调用。</li>
</ul>
<p><strong>缺点:</strong></p>
<ul>
<li>客户端必须知道所有的策略类,并自行决定使用哪一个策略类。</li>