加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
search.xml 792.14 KB
一键复制 编辑 原始数据 按行查看 历史
OrionLi 提交于 2023-12-18 14:38 . Site updated: 2023-12-18 14:38:42
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922
<?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-&gt;&gt;Database: 1.写入数据
Server-&gt;&gt;Cache: 2.删除缓存
Server-&gt;&gt;Database: 3.读取数据
Server-&gt;&gt;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 数据库
用户-&gt;&gt;前端网页: 选择商品并下单
前端网页-&gt;&gt;后端服务: 发送下单请求
activate 后端服务
后端服务-&gt;&gt;后端服务: 验证用户身份和库存
后端服务-&gt;&gt;数据库: 更新库存信息
activate 数据库
数据库--&gt;&gt;后端服务: 返回库存更新结果
deactivate 数据库
后端服务--&gt;&gt;前端网页: 返回下单结果
deactivate 后端服务
前端网页-&gt;&gt;用户: 显示下单结果
用户-&gt;&gt;前端网页: 完成支付
前端网页-&gt;&gt;后端服务: 发送支付请求
activate 后端服务
后端服务-&gt;&gt;后端服务: 验证支付信息和库存
后端服务-&gt;&gt;数据库: 更新库存信息和订单状态
activate 数据库
数据库--&gt;&gt;后端服务: 返回库存更新结果和订单状态
deactivate 数据库
后端服务--&gt;&gt;前端网页: 返回支付结果和订单状态
deactivate 后端服务
前端网页-&gt;&gt;用户: 显示支付结果和订单状态
</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> &#123;</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> &#123;</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"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</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"> &#125;</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> &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">this</span>.studentId;</span><br><span class="line"> &#125;</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> &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">this</span>.grade;</span><br><span class="line"> &#125;</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> &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">this</span>.classNum;</span><br><span class="line"> &#125;</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> &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">this</span>.name;</span><br><span class="line"> &#125;</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> &#123;</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"> &#125;</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> &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">this</span>.gender;</span><br><span class="line"> &#125;</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> &#123;</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"> &#125;</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> &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">this</span>.address;</span><br><span class="line"> &#125;</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> &#123;</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"> &#125;</span><br><span class="line">&#125;</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">&quot;张三&quot;</span>) <span class="comment">// 设置姓名</span></span><br><span class="line"> .setGender(<span class="string">&quot;男&quot;</span>) <span class="comment">// 设置性别</span></span><br><span class="line"> .setAddress(<span class="string">&quot;北京市朝阳区&quot;</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> &#123;</span><br><span class="line"> Log.info(<span class="string">&quot;transfer start, payer = &quot;</span> + payer + <span class="string">&quot;, payee = &quot;</span> + payee + <span class="string">&quot;, money = &quot;</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)) &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> &#125;</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()) &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> &#125;</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) &#123;</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"> &#125; <span class="keyword">else</span> <span class="keyword">if</span> (userInfo.getNotifyType() == NotifyTypeEnum.MAIL) &#123;</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"> &#125;</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">&quot;transfer success&quot;</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</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> &#123;</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line">&#125;</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> &#123;</span><br><span class="line"> Log.info(<span class="string">&quot;transfer start, transferRequest=&quot;</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">&#125;</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)) &#123;</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line">&#125;</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> &#123;</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">&#125;</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> &#123;</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&lt;ParamValidator&gt; validatorList = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</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 &#123;</span><br><span class="line"> Map&lt;String, ParamValidator&gt; paramValidatorMap = applicationContext.getBeansOfType(ParamValidator.class);</span><br><span class="line"> validatorList = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;(paramValidatorMap.values());</span><br><span class="line"> &#125;</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> &#123;</span><br><span class="line"> <span class="keyword">for</span> (ParamValidator paramValidator : validatorList) &#123;</span><br><span class="line"> paramValidator.checkParam(transferRequest);</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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) &#123;</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">&#125; <span class="keyword">else</span> <span class="keyword">if</span> (userInfo.getNotifyType() == NotifyTypeEnum.MAIL) &#123;</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">&#125;</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> &#123;</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">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> <span class="comment">// 邮件服务商提供的sdk</span></span><br><span class="line"> mailClient.sendMail(userId, content);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> <span class="comment">// 短信服务商提供的sdk</span></span><br><span class="line"> smsClient.sendSms(userId, content);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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&lt;NotifyTypeEnum, NotifyService&gt; 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 &#123;</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"> &#125;</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> &#123;</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>) &#123;</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(<span class="string">&quot;Notify service not exist&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> notifyService.notifyMessage(userId, content);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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">&#125;</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> &#123;</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&lt;TransferObserver&gt; transferObserverList = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</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 &#123;</span><br><span class="line"> Map&lt;String, TransferObserver&gt; 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"> &#125;</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> &#123;</span><br><span class="line"> transferObserverList.forEach(transferObserver -&gt; &#123;</span><br><span class="line"> <span class="comment">// 异步执行</span></span><br><span class="line"> executorService.execute(() -&gt; transferObserver.update(transferResult));</span><br><span class="line"> &#125;);</span><br><span class="line"> &#125;</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> &#123;</span><br><span class="line"> transferObserverList.add(transferObserver);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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">&quot;transfer success&quot;</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> &#123;</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">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">80</span>;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">100</span>;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> <span class="built_in">this</span>.weapon = weapon;</span><br><span class="line"> &#125;</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> &#123;</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"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> <span class="built_in">this</span>.weapon = weapon;</span><br><span class="line"> &#125;</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> &#123;</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"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> <span class="built_in">this</span>.weapon = weapon;</span><br><span class="line"> &#125;</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> &#123;</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"> &#125;</span><br><span class="line">&#125;</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> &#123;</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">&#125;</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> &#123;</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">&#125;</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&lt;T&gt;</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>&#123;</span><br><span class="line"> Image image;</span><br><span class="line"> Pen pen;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> <span class="built_in">this</span>.config = config;</span><br><span class="line"> &#125;</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> &#123;</span><br><span class="line"> <span class="keyword">switch</span> (config.getSaveMode()) &#123;</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">&quot;Invalid save option&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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>) &#123;</span><br><span class="line"> message.markSent(); </span><br><span class="line">&#125;</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) &#123;</span><br><span class="line"> message.markSent();</span><br><span class="line">&#125;</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> &#123;</span><br><span class="line"> <span class="keyword">public</span> Optional&lt;String&gt; <span class="title function_">getString</span><span class="params">()</span> &#123;</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"> &#125;</span><br><span class="line">&#125;</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&lt;String&gt; optionalString = example.getString();</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (optionalString.isPresent()) &#123;</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">&quot;Value: &quot;</span> + value);</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;Value is absent.&quot;</span>);</span><br><span class="line">&#125;</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">&quot;Default Value&quot;</span>);</span><br><span class="line">System.out.println(<span class="string">&quot;Value: &quot;</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> &#123;</span><br><span class="line"> <span class="keyword">if</span> (top &gt; bottom) &#123;</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 &lt;= top; number++) &#123;</span><br><span class="line"> <span class="keyword">if</span> (number % <span class="number">2</span> == <span class="number">0</span>)&#123;</span><br><span class="line"> sum += number;</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">return</span> sum;</span><br><span class="line"> &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</span><br><span class="line"> <span class="keyword">if</span> (number %<span class="number">2</span> == <span class="number">0</span>) &#123;</span><br><span class="line"> <span class="keyword">return</span> number;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</span><br><span class="line"> <span class="keyword">if</span> (top &gt; bottom) &#123;</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 &lt;= top; number++) &#123;</span><br><span class="line"> sum += filterNumber(number);</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">return</span> sum;</span><br><span class="line"> &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</span><br><span class="line"> <span class="keyword">if</span> (number %<span class="number">2</span> == <span class="number">0</span>) &#123;</span><br><span class="line"> <span class="keyword">return</span> number;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</span><br><span class="line"> <span class="keyword">if</span> (top &lt; bottom) &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> &#125;</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 &lt;= top; number++) &#123;</span><br><span class="line"> sum += filterNumber(number);</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">return</span> sum;</span><br><span class="line">&#125;</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>所以就笔者而言,可读性 + 适度的拓展性 &gt;&gt; 过早考虑那些<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> &#123;</span><br><span class="line"> uniqueInstance;</span><br><span class="line">&#125;</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> &#123;</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> &#123;&#125;</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> &#123;</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"> &#125;</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> &#123;</span><br><span class="line"> <span class="keyword">return</span> SingletonHolder.INSTANCE;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</span><br><span class="line"> String <span class="title function_">serve</span><span class="params">()</span>;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="string">&quot;美式咖啡&quot;</span>;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="string">&quot;意式浓缩咖啡&quot;</span>;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> <span class="keyword">switch</span> (type) &#123;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">&quot;American&quot;</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">&quot;Espresso&quot;</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">&quot;不支持的咖啡类型:&quot;</span> + type);</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</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">&quot;American&quot;</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">&quot;Espresso&quot;</span>);</span><br><span class="line"> System.out.println(coffee2.serve()); <span class="comment">// 输出:意式浓缩咖啡</span></span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</span><br><span class="line"> String <span class="title function_">serve</span><span class="params">()</span>;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="string">&quot;美式咖啡&quot;</span>;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="string">&quot;意式浓缩咖啡&quot;</span>;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</span><br><span class="line"> Coffee <span class="title function_">createCoffee</span><span class="params">()</span>;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</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"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</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"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</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"> &#125;</span><br><span class="line">&#125;</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> &#123;</span><br><span class="line"> String <span class="title function_">serve</span><span class="params">()</span>;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="string">&quot;美式咖啡&quot;</span>;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="string">&quot;意式浓缩咖啡&quot;</span>;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</span><br><span class="line"> String <span class="title function_">serve</span><span class="params">()</span>;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="string">&quot;美式甜点&quot;</span>;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="string">&quot;意式甜点&quot;</span>;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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">&#125;</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> &#123;</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> &#123;</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"> &#125;</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> &#123;</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"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</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"> &#125;</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> &#123;</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"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</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"> &#125;</span><br><span class="line">&#125;</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> &#123;</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">&#125;</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> &#123;</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">&#125;</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> &#123;</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> &#123;</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"> &#125;</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> &#123;</span><br><span class="line"> bicycle.setFrame(<span class="string">&quot;Carbon Fiber Frame&quot;</span>);</span><br><span class="line"> &#125;</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> &#123;</span><br><span class="line"> bicycle.setSeat(<span class="string">&quot;Rubber Seat&quot;</span>);</span><br><span class="line"> &#125;</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> &#123;</span><br><span class="line"> <span class="keyword">return</span> bicycle;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</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"> &#125;</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> &#123;</span><br><span class="line"> bicycle.setFrame(<span class="string">&quot;Aluminum Frame&quot;</span>);</span><br><span class="line"> &#125;</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> &#123;</span><br><span class="line"> bicycle.setSeat(<span class="string">&quot;Leather Seat&quot;</span>);</span><br><span class="line"> &#125;</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> &#123;</span><br><span class="line"> <span class="keyword">return</span> bicycle;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> <span class="built_in">this</span>.bicycleBuilder = bicycleBuilder;</span><br><span class="line"> &#125;</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> &#123;</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"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</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">&quot;Carbon Fiber Bicycle:&quot;</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">&quot;Aluminum Bicycle:&quot;</span>);</span><br><span class="line"> System.out.println(aluminumBicycle);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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">&#125;</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> &#123;</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">&#125;</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> &#123;</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> &#123;</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"> &#125;</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> &#123;</span><br><span class="line"> bicycle.setFrame(<span class="string">&quot;Carbon Fiber Frame&quot;</span>);</span><br><span class="line"> &#125;</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> &#123;</span><br><span class="line"> bicycle.setSeat(<span class="string">&quot;Rubber Seat&quot;</span>);</span><br><span class="line"> &#125;</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> &#123;</span><br><span class="line"> <span class="keyword">return</span> bicycle;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</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">&quot;Carbon Fiber Bicycle:&quot;</span>);</span><br><span class="line"> System.out.println(bicycle);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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">&#125;</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> &#123;</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> &#123;</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"> &#125;</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> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;绘制矩形&quot;</span>);</span><br><span class="line"> &#125;</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> &#123;</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"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> <span class="built_in">this</span>.radius = radius;</span><br><span class="line"> &#125;</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> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;绘制圆形&quot;</span>);</span><br><span class="line"> &#125;</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> &#123;</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"> &#125;</span><br><span class="line">&#125;</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> &#123;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> HashMap&lt;String, CloneableShape&gt; shapeMap = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</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> &#123;</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"> &#125;</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> &#123;</span><br><span class="line"> shapeMap.put(<span class="string">&quot;rectangle&quot;</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">&quot;circle&quot;</span>, <span class="keyword">new</span> <span class="title class_">Circle</span>(<span class="number">30</span>));</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</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">&quot;rectangle&quot;</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">&quot;circle&quot;</span>);</span><br><span class="line"> clonedShape2.draw(); <span class="comment">// 输出:绘制圆形</span></span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;灯已打开&quot;</span>);</span><br><span class="line"> &#125;</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> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;灯已关闭&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;电视已打开&quot;</span>);</span><br><span class="line"> &#125;</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> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;电视已关闭&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;空调已打开&quot;</span>);</span><br><span class="line"> &#125;</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> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;空调已关闭&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</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"> &#125;</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> &#123;</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"> &#125;</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> &#123;</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"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</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"> &#125;</span><br><span class="line">&#125;</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> &#123;</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">&#125;</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> &#123;</span><br><span class="line"> <span class="keyword">public</span> String <span class="title function_">readSD</span><span class="params">()</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="string">&quot;sd card read a msg :hello word SD&quot;</span>;</span><br><span class="line"> &#125;</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> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;sd card write msg : &quot;</span> + msg);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> <span class="keyword">if</span>(sdCard == <span class="literal">null</span>) &#123;</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">NullPointerException</span>(<span class="string">&quot;sd card null&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">return</span> sdCard.readSD();</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="string">&quot;tf card read msg : hello word tf card&quot;</span>;</span><br><span class="line"> &#125;</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> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;tf card write a msg : &quot;</span> + msg);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> <span class="built_in">this</span>.tfCard = tfCard;</span><br><span class="line"> &#125;</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> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;adapter read tf card &quot;</span>);</span><br><span class="line"> <span class="keyword">return</span> tfCard.readTF();</span><br><span class="line"> &#125;</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> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;adapter write tf card&quot;</span>);</span><br><span class="line"> tfCard.writeTF(msg);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</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">&quot;------------&quot;</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"> &#125;</span><br><span class="line">&#125;</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> &#123;</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">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;avi视频文件:&quot;</span> + fileName);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;rmvb文件:&quot;</span> + fileName);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> <span class="built_in">this</span>.videoFile = videoFile;</span><br><span class="line"> &#125;</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">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> <span class="built_in">super</span>(videoFile);</span><br><span class="line"> &#125;</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> &#123;</span><br><span class="line"> videoFile.decode(fileName);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> <span class="built_in">super</span>(videoFile);</span><br><span class="line"> &#125;</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> &#123;</span><br><span class="line"> videoFile.decode(fileName);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</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">&quot;三体&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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>&#123;</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"> &#125;</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>&#123;</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"> &#125;</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>&#123;</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"> &#125;</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>&#123;</span><br><span class="line"> <span class="keyword">return</span> name;</span><br><span class="line"> &#125;</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>&#123;</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"> &#125;</span><br><span class="line">&#125;</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> &#123;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> List&lt;MenuComponent&gt; 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>&#123;</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>&lt;MenuComponent&gt;();</span><br><span class="line"> &#125;</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> &#123;</span><br><span class="line"> menuComponentList.add(menuComponent);</span><br><span class="line"> &#125;</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> &#123;</span><br><span class="line"> menuComponentList.remove(menuComponent);</span><br><span class="line"> &#125;</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> &#123;</span><br><span class="line"> <span class="keyword">return</span> menuComponentList.get(i);</span><br><span class="line"> &#125;</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> &#123;</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 &lt; level; i++) &#123;</span><br><span class="line"> System.out.print(<span class="string">&quot;--&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line"> System.out.println(name);</span><br><span class="line"> <span class="keyword">for</span> (MenuComponent menuComponent : menuComponentList) &#123;</span><br><span class="line"> menuComponent.print();</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</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"> &#125;</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> &#123;</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 &lt; level; i++) &#123;</span><br><span class="line"> System.out.print(<span class="string">&quot;--&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line"> System.out.println(name);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> <span class="built_in">super</span>(<span class="number">10</span>, <span class="string">&quot;炒饭&quot;</span>);</span><br><span class="line"> &#125;</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> &#123;</span><br><span class="line"> <span class="keyword">return</span> getPrice();</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> <span class="built_in">super</span>(<span class="number">12</span>, <span class="string">&quot;炒面&quot;</span>);</span><br><span class="line"> &#125;</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> &#123;</span><br><span class="line"> <span class="keyword">return</span> getPrice();</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</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"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> <span class="built_in">super</span>(fastFood, <span class="number">1</span>, <span class="string">&quot;鸡蛋&quot;</span>);</span><br><span class="line"> &#125;</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> &#123;</span><br><span class="line"> <span class="keyword">return</span> getPrice() + getFastFood().getPrice();</span><br><span class="line"> &#125;</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> &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">super</span>.getDesc() + getFastFood().getDesc();</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> <span class="built_in">super</span>(fastFood, <span class="number">2</span>, <span class="string">&quot;培根&quot;</span>);</span><br><span class="line"> &#125;</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> &#123;</span><br><span class="line"> <span class="keyword">return</span> getPrice() + getFastFood().getPrice();</span><br><span class="line"> &#125;</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> &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">super</span>.getDesc() + getFastFood().getDesc();</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</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">&quot; &quot;</span> + food.cost() + <span class="string">&quot;元&quot;</span>);</span><br><span class="line"> System.out.println(<span class="string">&quot;========&quot;</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">&quot; &quot;</span> + food1.cost() + <span class="string">&quot;元&quot;</span>);</span><br><span class="line"> System.out.println(<span class="string">&quot;========&quot;</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">&quot; &quot;</span> + food2.cost() + <span class="string">&quot;元&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;方块形状:&quot;</span> + <span class="built_in">this</span>.getShape() + <span class="string">&quot; 颜色:&quot;</span> + color);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="string">&quot;I&quot;</span>;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="string">&quot;L&quot;</span>;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="string">&quot;O&quot;</span>;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> HashMap&lt;String, AbstractBox&gt; 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> &#123;</span><br><span class="line"> <span class="keyword">return</span> SingletonHolder.INSTANCE;</span><br><span class="line"> &#125;</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> &#123;</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"> &#125;</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> &#123;</span><br><span class="line"> map = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;String, AbstractBox&gt;();</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">&quot;I&quot;</span>, iBox);</span><br><span class="line"> map.put(<span class="string">&quot;L&quot;</span>, lBox);</span><br><span class="line"> map.put(<span class="string">&quot;O&quot;</span>, oBox);</span><br><span class="line"> &#125;</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> &#123;</span><br><span class="line"> <span class="keyword">return</span> map.get(key);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</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">&quot;i1和i2对象是否是同一个对象?&quot;</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">&quot;i3和i4对象是否是同一个对象?&quot;</span> + (i3 == i4)); <span class="comment">// 输出false</span></span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</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">&quot;i1\u548ci2\u5bf9\u8c61\u662f\u5426\u662f\u540c\u4e00\u4e2a\u5bf9\u8c61\uff1f&quot;</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">&quot;i3\u548ci4\u5bf9\u8c61\u662f\u5426\u662f\u540c\u4e00\u4e2a\u5bf9\u8c61\uff1f&quot;</span>).append((<span class="type">boolean</span>)(i3 == i4)).toString());</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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>&lt;Integer&gt; &#123;</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> &#123;</span><br><span class="line"> <span class="keyword">if</span> (i &gt;= IntegerCache.low &amp;&amp; i &lt;= 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"> &#125;</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> &#123;</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> &#123;</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">&quot;java.lang.Integer.IntegerCache.high&quot;</span>);</span><br><span class="line"> <span class="keyword">if</span> (integerCacheHighPropValue != <span class="literal">null</span>) &#123;</span><br><span class="line"> <span class="keyword">try</span> &#123;</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"> &#125; <span class="keyword">catch</span>( NumberFormatException nfe) &#123;</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</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 &lt; 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 &gt;= <span class="number">127</span>;</span><br><span class="line"> &#125;</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> &#123;&#125;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;火车站卖票&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</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&lt;?&gt;[] 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>() &#123;</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 &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;代理点收取一些服务费用(JDK动态代理方式)&quot;</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"> &#125;</span><br><span class="line"> &#125;);</span><br><span class="line"> <span class="keyword">return</span> sellTickets;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</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"> &#125;</span><br><span class="line">&#125;</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> &#123;</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) &#123;</span><br><span class="line"> <span class="built_in">super</span>(invocationHandler);</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">static</span> &#123;</span><br><span class="line"> <span class="keyword">try</span> &#123;</span><br><span class="line"> m1 = Class.forName(<span class="string">&quot;java.lang.Object&quot;</span>).getMethod(<span class="string">&quot;equals&quot;</span>, Class.forName(<span class="string">&quot;java.lang.Object&quot;</span>));</span><br><span class="line"> m2 = Class.forName(<span class="string">&quot;java.lang.Object&quot;</span>).getMethod(<span class="string">&quot;toString&quot;</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">&quot;com.itheima.proxy.dynamic.jdk.SellTickets&quot;</span>).getMethod(<span class="string">&quot;sell&quot;</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">&quot;java.lang.Object&quot;</span>).getMethod(<span class="string">&quot;hashCode&quot;</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"> &#125;</span><br><span class="line"> <span class="keyword">catch</span> (NoSuchMethodException noSuchMethodException) &#123;</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"> &#125;</span><br><span class="line"> <span class="keyword">catch</span> (ClassNotFoundException classNotFoundException) &#123;</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"> &#125;</span><br><span class="line"> &#125;</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> &#123;</span><br><span class="line"> <span class="keyword">try</span> &#123;</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>[]&#123;object&#125;);</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">catch</span> (Error | RuntimeException throwable) &#123;</span><br><span class="line"> <span class="keyword">throw</span> throwable;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">catch</span> (Throwable throwable) &#123;</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"> &#125;</span><br><span class="line"> &#125;</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> &#123;</span><br><span class="line"> <span class="keyword">try</span> &#123;</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"> &#125;</span><br><span class="line"> <span class="keyword">catch</span> (Error | RuntimeException throwable) &#123;</span><br><span class="line"> <span class="keyword">throw</span> throwable;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">catch</span> (Throwable throwable) &#123;</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"> &#125;</span><br><span class="line"> &#125;</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> &#123;</span><br><span class="line"> <span class="keyword">try</span> &#123;</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"> &#125;</span><br><span class="line"> <span class="keyword">catch</span> (Error | RuntimeException throwable) &#123;</span><br><span class="line"> <span class="keyword">throw</span> throwable;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">catch</span> (Throwable throwable) &#123;</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"> &#125;</span><br><span class="line"> &#125;</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> &#123;</span><br><span class="line"> <span class="keyword">try</span> &#123;</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"> &#125;</span><br><span class="line"> <span class="keyword">catch</span> (Error | RuntimeException throwable) &#123;</span><br><span class="line"> <span class="keyword">throw</span> throwable;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">catch</span> (Throwable throwable) &#123;</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"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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) &#123;</span><br><span class="line"> <span class="built_in">super</span>(invocationHandler);</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">static</span> &#123;</span><br><span class="line"> m3 = Class.forName(<span class="string">&quot;com.itheima.proxy.dynamic.jdk.SellTickets&quot;</span>).getMethod(<span class="string">&quot;sell&quot;</span>, <span class="keyword">new</span> <span class="title class_">Class</span>[<span class="number">0</span>]);</span><br><span class="line"> &#125;</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> &#123;</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"> &#125;</span><br><span class="line">&#125;</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 &#123;</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> &#123;</span><br><span class="line"> <span class="built_in">this</span>.h = h;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</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>() &#123;</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 &#123;</span><br><span class="line"></span><br><span class="line"> System.out.println(<span class="string">&quot;代理点收取一些服务费用(JDK动态代理方式)&quot;</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"> &#125;</span><br><span class="line"> &#125;);</span><br><span class="line"> <span class="keyword">return</span> sellTickets;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</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"> &#125;</span><br><span class="line">&#125;</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">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line"> <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>cglib<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line"> <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>cglib<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"> <span class="tag">&lt;<span class="name">version</span>&gt;</span>3.3.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</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> &#123;</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> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;火车站卖票&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</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"> &#125;</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 &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;代理点收取一些服务费用(CGLIB动态代理方式)&quot;</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"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</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"> &#125;</span><br><span class="line">&#125;</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> &#123;</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">&#125;</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> &#123;</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">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> <span class="keyword">if</span> (request.getDays() &lt; <span class="number">1</span>) &#123;</span><br><span class="line"> request.setApproved(<span class="literal">true</span>);</span><br><span class="line"> System.out.println(<span class="string">&quot;小组长同意&quot;</span> + request.getEmployeeName() + <span class="string">&quot;的请假申请&quot;</span>);</span><br><span class="line"> &#125; <span class="keyword">else</span> <span class="keyword">if</span> (nextApprover != <span class="literal">null</span>) &#123;</span><br><span class="line"> nextApprover.approveLeave(request);</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> <span class="keyword">if</span> (request.getDays() &gt;= <span class="number">1</span> &amp;&amp; request.getDays() &lt;= <span class="number">3</span>) &#123;</span><br><span class="line"> request.setApproved(<span class="literal">true</span>);</span><br><span class="line"> System.out.println(<span class="string">&quot;部门经理同意&quot;</span> + request.getEmployeeName() + <span class="string">&quot;的请假申请&quot;</span>);</span><br><span class="line"> &#125; <span class="keyword">else</span> <span class="keyword">if</span> (nextApprover != <span class="literal">null</span>) &#123;</span><br><span class="line"> nextApprover.approveLeave(request);</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> <span class="keyword">if</span> (request.getDays() &gt; <span class="number">3</span> &amp;&amp; request.getDays() &lt;= <span class="number">7</span>) &#123;</span><br><span class="line"> request.setApproved(<span class="literal">true</span>);</span><br><span class="line"> System.out.println(<span class="string">&quot;总经理同意&quot;</span> + request.getEmployeeName() + <span class="string">&quot;的请假申请&quot;</span>);</span><br><span class="line"> &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> request.setApproved(<span class="literal">false</span>);</span><br><span class="line"> System.out.println(<span class="string">&quot;请假超过7天,需要进一步审批&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</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"> &#125;</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> &#123;</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()) &#123;</span><br><span class="line"> System.out.println(request.getEmployeeName() + <span class="string">&quot;的请假申请被拒绝&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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> &#123;</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> &#123;</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">&quot;张三&quot;</span>, <span class="number">0</span>, <span class="string">&quot;家庭原因&quot;</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">&quot;李四&quot;</span>, <span class="number">2</span>, <span class="string">&quot;病假&quot;</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">&quot;王五&quot;</span>, <span class="number">4</span>, <span class="string">&quot;探亲假&quot;</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">&quot;赵六&quot;</span>, <span class="number">8</span>, <span class="string">&quot;年休假&quot;</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"> &#125;</span><br><span class="line">&#125;</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>
<li>策略模式将造成产生很多策略类,可以通过<strong>使用享元模式</strong>在一定程度上减少对象的数量。</li>
</ul>
<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="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">Strategy</span> &#123;</span><br><span class="line"> <span class="keyword">void</span> <span class="title function_">show</span><span class="params">()</span>;</span><br><span class="line">&#125;</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">//为春节准备的促销活动A</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">StrategyA</span> <span class="keyword">implements</span> <span class="title class_">Strategy</span> &#123; </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_">show</span><span class="params">()</span> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;买一送一&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//为中秋准备的促销活动B</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">StrategyB</span> <span class="keyword">implements</span> <span class="title class_">Strategy</span> &#123;</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_">show</span><span class="params">()</span> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;满200元减50元&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//为圣诞准备的促销活动C</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">StrategyC</span> <span class="keyword">implements</span> <span class="title class_">Strategy</span> &#123;</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_">show</span><span class="params">()</span> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;满1000元加一元换购任意200元以下商品&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><strong>环境角色-推送者:</strong></p>
<blockquote>
<p>用于连接上下文,即把促销活动推销给客户,这里可以理解为销售员</p>
</blockquote>
<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_">SalesMan</span> &#123; </span><br><span class="line"> <span class="comment">//持有抽象策略角色的引用 </span></span><br><span class="line"> <span class="keyword">private</span> Strategy strategy; </span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">SalesMan</span><span class="params">(Strategy strategy)</span> &#123; </span><br><span class="line"> <span class="built_in">this</span>.strategy = strategy; </span><br><span class="line"> &#125; </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_">salesManShow</span><span class="params">()</span>&#123; </span><br><span class="line"> strategy.show(); </span><br><span class="line"> &#125; </span><br><span class="line">&#125;</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> &#123;</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> &#123;</span><br><span class="line"> <span class="comment">// 创建具体的促销活动对象</span></span><br><span class="line"> <span class="type">Strategy</span> <span class="variable">strategyA</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StrategyA</span>(); <span class="comment">// 春节促销活动</span></span><br><span class="line"> <span class="type">Strategy</span> <span class="variable">strategyB</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StrategyB</span>(); <span class="comment">// 中秋节促销活动</span></span><br><span class="line"> <span class="type">Strategy</span> <span class="variable">strategyC</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StrategyC</span>(); <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">SalesMan</span> <span class="variable">salesMan1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SalesMan</span>(strategyA); <span class="comment">// 绑定春节促销活动</span></span><br><span class="line"> <span class="type">SalesMan</span> <span class="variable">salesMan2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SalesMan</span>(strategyB); <span class="comment">// 绑定中秋节促销活动</span></span><br><span class="line"> <span class="type">SalesMan</span> <span class="variable">salesMan3</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SalesMan</span>(strategyC); <span class="comment">// 绑定圣诞节促销活动</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// 销售员向客户展示促销活动</span></span><br><span class="line"> salesMan1.salesManShow();</span><br><span class="line"> salesMan2.salesManShow();</span><br><span class="line"> salesMan3.salesManShow();</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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">满200元减50元</span><br><span class="line">满1000元加一元换购任意200元以下商品</span><br></pre></td></tr></table></figure>
<h1 id="行为型-模板方法-Template-Method"><a href="#行为型-模板方法-Template-Method" class="headerlink" title="行为型 - 模板方法(Template Method)"></a>行为型 - 模板方法(Template Method)</h1><blockquote>
<p>在一个方法中定义一个算法的骨架, 而将一些步骤延迟到子类中. 模板方法使得子类可以在不改变算法结构的情况下, 重新定义算法中的某些步骤。</p>
</blockquote>
<p>在面向对象程序设计过程中,程序员常常会遇到这种情况:设计一个系统时知道了算法所需的关键步骤,而且确定了这些步骤的执行顺序,但某些步骤的具体实现还未知,或者说某些步骤的实现与具体的环境相关。</p>
<p>例如,去银行办理业务一般要经过以下4个流程:取号、排队、办理具体业务、对银行工作人员进行评分等,其中取号、排队和对银行工作人员进行评分的业务对每个客户是一样的,可以在父类中实现,但是办理具体业务却因人而异,它可能是存款、取款或者转账等,可以延迟到子类中实现。</p>
<p><strong>优点:</strong></p>
<ul>
<li><p>提高代码复用性</p>
<p>将相同部分的代码放在抽象的父类中,而将不同的代码放入不同的子类中。</p>
</li>
<li><p>实现了反向控制</p>
<p>通过一个父类调用其子类的操作,通过对子类的具体实现扩展不同的行为,实现了反向控制 ,并符合“开闭原则”。</p>
</li>
</ul>
<p><strong>缺点:</strong></p>
<ul>
<li>对每个不同的实现都需要定义一个子类,这会导致类的个数增加,系统更加庞大,设计也更加抽象。</li>
<li>父类中的抽象方法由子类实现,子类执行的结果会影响父类的结果,这导致一种反向的控制结构,它提高了代码阅读的难度。</li>
</ul>
<h2 id="示例:炒菜"><a href="#示例:炒菜" class="headerlink" title="示例:炒菜"></a>示例:炒菜</h2><blockquote>
<p><strong>注意:为防止恶意操作,一般模板方法都加上 final 关键词。</strong></p>
</blockquote>
<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">abstract</span> <span class="keyword">class</span> <span class="title class_">AbstractCook</span> &#123;</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_">cookProcess</span><span class="params">()</span> &#123;</span><br><span class="line"> <span class="comment">//第一步:倒油</span></span><br><span class="line"> <span class="built_in">this</span>.pourOil();</span><br><span class="line"> <span class="comment">//第二步:热油</span></span><br><span class="line"> <span class="built_in">this</span>.heatOil();</span><br><span class="line"> <span class="comment">//第三步:倒蔬菜</span></span><br><span class="line"> <span class="built_in">this</span>.pourVegetable();</span><br><span class="line"> <span class="comment">//第四步:倒调味料</span></span><br><span class="line"> <span class="built_in">this</span>.pourSauce();</span><br><span class="line"> <span class="comment">//第五步:翻炒</span></span><br><span class="line"> <span class="built_in">this</span>.fry();</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">pourOil</span><span class="params">()</span> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;倒油&quot;</span>);</span><br><span class="line"> &#125;</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_">heatOil</span><span class="params">()</span> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;热油&quot;</span>);</span><br><span class="line"> &#125;</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="keyword">void</span> <span class="title function_">pourVegetable</span><span class="params">()</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="keyword">abstract</span> <span class="keyword">void</span> <span class="title function_">pourSauce</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">public</span> <span class="keyword">void</span> <span class="title function_">fry</span><span class="params">()</span>&#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;炒啊炒啊炒到熟啊&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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_">BaoCaiCook</span> <span class="keyword">extends</span> <span class="title class_">AbstractCook</span> &#123;</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_">pourVegetable</span><span class="params">()</span> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;下锅的蔬菜是包菜&quot;</span>);</span><br><span class="line"> &#125;</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_">pourSauce</span><span class="params">()</span> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;下锅的酱料是辣椒&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CaiXinCook</span> <span class="keyword">extends</span> <span class="title class_">AbstractCook</span> &#123;</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_">pourVegetable</span><span class="params">()</span> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;下锅的蔬菜是菜心&quot;</span>);</span><br><span class="line"> &#125;</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_">pourSauce</span><span class="params">()</span> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;下锅的酱料是蒜蓉&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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_">CookClient</span> &#123;</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> &#123;</span><br><span class="line"> <span class="comment">//炒手撕包菜</span></span><br><span class="line"> <span class="type">BaoCaiCook</span> <span class="variable">baoCai</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BaoCaiCook</span>();</span><br><span class="line"> baoCai.cookProcess();</span><br><span class="line"></span><br><span class="line"> <span class="comment">//炒蒜蓉菜心</span></span><br><span class="line"> <span class="type">CaiXinCook</span> <span class="variable">caiXin</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CaiXinCook</span>();</span><br><span class="line"> caiXin.cookProcess();</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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">下锅的酱料是辣椒</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><br><span class="line">下锅的酱料是蒜蓉</span><br><span class="line">炒啊炒啊炒到熟啊</span><br></pre></td></tr></table></figure>
<h1 id="行为型-命令模式-Command"><a href="#行为型-命令模式-Command" class="headerlink" title="行为型 - 命令模式(Command)"></a>行为型 - 命令模式(Command)</h1><blockquote>
<p>将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行存储、传递、调用、增加与管理。</p>
</blockquote>
<p>命令模式包含以下主要角色:</p>
<ul>
<li>抽象命令类(Command)角色: 定义命令的接口,声明执行的方法。</li>
<li>具体命令(Concrete Command)角色:具体的命令,实现命令接口;通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。</li>
<li>实现者&#x2F;接收者(Receiver)角色: 接收者,真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。</li>
<li>调用者&#x2F;请求者(Invoker)角色: 要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。</li>
</ul>
<h2 id="示例:饭店吃饭"><a href="#示例:饭店吃饭" class="headerlink" title="示例:饭店吃饭"></a>示例:饭店吃饭</h2><ol>
<li><p>点单</p>
</li>
<li><p>订单交给厨师</p>
</li>
<li><p>厨师备菜</p>
</li>
</ol>
<p>将上面的案例用代码实现,那我们就需要分析命令模式的角色在该案例中由谁来充当。</p>
<ul>
<li><p>服务员: 就是调用者角色,由她来发起命令。</p>
</li>
<li><p>资深大厨: 就是接收者角色,真正命令执行的对象。</p>
</li>
<li><p>订单: 命令中包含订单。</p>
</li>
</ul>
<p><strong>pojo类:</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">@AllArgsConstructor</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Order</span> &#123;</span><br><span class="line"> <span class="keyword">private</span> String dishName;</span><br><span class="line">&#125;</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_">Waiter</span> &#123;</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">placeOrder</span><span class="params">(Command command)</span> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;服务员接收到订单&quot;</span>);</span><br><span class="line"> command.execute();</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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_">SeniorChef</span> &#123;</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">cookDish</span><span class="params">(String dishName)</span> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;资深大厨开始烹饪:&quot;</span> + dishName);</span><br><span class="line"> <span class="comment">// 在这里执行具体的烹饪逻辑</span></span><br><span class="line"> System.out.println(<span class="string">&quot;资深大厨完成烹饪:&quot;</span> + dishName);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><strong>命令接口<code>Command</code>和具体命令类<code>CookingCommand</code>:</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">interface</span> <span class="title class_">Command</span> &#123;</span><br><span class="line"> <span class="keyword">void</span> <span class="title function_">execute</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 具体命令类</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_">CookingCommand</span> <span class="keyword">implements</span> <span class="title class_">Command</span> &#123;</span><br><span class="line"> <span class="keyword">private</span> SeniorChef chef;</span><br><span class="line"> <span class="keyword">private</span> Order order;</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_">execute</span><span class="params">()</span> &#123;</span><br><span class="line"> chef.cookDish(order.getDishName());</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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_">CommandPatternExample</span> &#123;</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> &#123;</span><br><span class="line"> <span class="type">Waiter</span> <span class="variable">waiter</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Waiter</span>();</span><br><span class="line"> <span class="type">SeniorChef</span> <span class="variable">chef</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SeniorChef</span>();</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 创建订单</span></span><br><span class="line"> <span class="type">Order</span> <span class="variable">order</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Order</span>(<span class="string">&quot;红烧肉&quot;</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 创建命令对象,并将订单和大厨传递进去</span></span><br><span class="line"> <span class="type">Command</span> <span class="variable">cookingCommand</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CookingCommand</span>(chef, order);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 服务员接收到订单,并发起命令</span></span><br><span class="line"> waiter.placeOrder(cookingCommand);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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></pre></td></tr></table></figure>
<h1 id="行为型-观察者-Observer"><a href="#行为型-观察者-Observer" class="headerlink" title="行为型 - 观察者(Observer)"></a>行为型 - 观察者(Observer)</h1><blockquote>
<p>又被称为发布-订阅(Publish&#x2F;Subscribe)模式。在对象之间定义一对多的依赖, 这样一来, 当一个对象改变状态, 依赖它的对象都会收到通知, 并自动更新。没错,就像公众号订阅。</p>
</blockquote>
<h2 id="示例:微信公众号"><a href="#示例:微信公众号" class="headerlink" title="示例:微信公众号"></a>示例:微信公众号</h2><p>使用微信公众号时,当公众号有新内容更新,订阅者就会收到通知。我们使用观察者模式来模拟这样的场景,微信用户就是观察者,微信公众号是被观察者,一个公众号被多个微信用户订阅。</p>
<p><strong>发布者接口</strong><code>Observable</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_">Observable</span> &#123;</span><br><span class="line"> <span class="comment">// 添加订阅者</span></span><br><span class="line"> <span class="keyword">void</span> <span class="title function_">addObserver</span><span class="params">(Observer observer)</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 删除订阅者</span></span><br><span class="line"> <span class="keyword">void</span> <span class="title function_">removeObserver</span><span class="params">(Observer observer)</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 通知所有订阅者</span></span><br><span class="line"> <span class="keyword">void</span> <span class="title function_">notifyObservers</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><strong>发布者实现类</strong><code>WechatAccount</code>:</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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">WechatAccount</span> <span class="keyword">implements</span> <span class="title class_">Observable</span> &#123;</span><br><span class="line"> <span class="keyword">private</span> String accountName;</span><br><span class="line"> <span class="keyword">private</span> List&lt;Observer&gt; observers;</span><br><span class="line"> <span class="keyword">private</span> String latestContent;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">WechatAccount</span><span class="params">(String accountName)</span> &#123;</span><br><span class="line"> <span class="built_in">this</span>.accountName = accountName;</span><br><span class="line"> <span class="built_in">this</span>.observers = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line"> &#125;</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_">addObserver</span><span class="params">(Observer observer)</span> &#123;</span><br><span class="line"> observers.add(observer);</span><br><span class="line"> &#125;</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_">removeObserver</span><span class="params">(Observer observer)</span> &#123;</span><br><span class="line"> observers.remove(observer);</span><br><span class="line"> &#125;</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_">notifyObservers</span><span class="params">()</span> &#123;</span><br><span class="line"> <span class="keyword">for</span> (Observer observer : observers) &#123;</span><br><span class="line"> observer.update(<span class="built_in">this</span>);</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</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_">publishNewContent</span><span class="params">(String content)</span> &#123;</span><br><span class="line"> <span class="built_in">this</span>.latestContent = content;</span><br><span class="line"> notifyObservers();</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><strong>订阅者接口</strong><code>Observer</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_">Observer</span> &#123;</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">(Observable observable)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><strong>订阅者实现类</strong><code>WechatUser</code>:</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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">WechatUser</span> <span class="keyword">implements</span> <span class="title class_">Observer</span> &#123;</span><br><span class="line"> <span class="keyword">private</span> String userName;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">WechatUser</span><span class="params">(String userName)</span> &#123;</span><br><span class="line"> <span class="built_in">this</span>.userName = userName;</span><br><span class="line"> &#125;</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_">update</span><span class="params">(Observable observable)</span> &#123;</span><br><span class="line"> <span class="keyword">if</span> (observable <span class="keyword">instanceof</span> WechatAccount) &#123;</span><br><span class="line"> <span class="type">WechatAccount</span> <span class="variable">wechatAccount</span> <span class="operator">=</span> (WechatAccount) observable;</span><br><span class="line"> <span class="type">String</span> <span class="variable">latestContent</span> <span class="operator">=</span> wechatAccount.getLatestContent();</span><br><span class="line"> System.out.println(<span class="string">&quot;用户 &quot;</span> + userName + <span class="string">&quot; 收到了公众号 &quot;</span> + wechatAccount.getAccountName() + <span class="string">&quot; 的新内容:&quot;</span> + latestContent);</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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_">ObserverPatternExample</span> &#123;</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> &#123;</span><br><span class="line"> <span class="comment">// 创建微信公众号</span></span><br><span class="line"> <span class="type">WechatAccount</span> <span class="variable">accountA</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">WechatAccount</span>(<span class="string">&quot;公众号A&quot;</span>);</span><br><span class="line"> <span class="type">WechatAccount</span> <span class="variable">accountB</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">WechatAccount</span>(<span class="string">&quot;公众号B&quot;</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 创建微信用户</span></span><br><span class="line"> <span class="type">WechatUser</span> <span class="variable">user1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">WechatUser</span>(<span class="string">&quot;用户A&quot;</span>);</span><br><span class="line"> <span class="type">WechatUser</span> <span class="variable">user2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">WechatUser</span>(<span class="string">&quot;用户B&quot;</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 用户A订阅公众号A</span></span><br><span class="line"> accountA.addObserver(user1);</span><br><span class="line"> <span class="comment">// 用户A订阅公众号B</span></span><br><span class="line"> accountB.addObserver(user1);</span><br><span class="line"> <span class="comment">// 用户B订阅公众号B</span></span><br><span class="line"> accountB.addObserver(user2);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 发布新内容</span></span><br><span class="line"> accountA.publishNewContent(<span class="string">&quot;新内容A1&quot;</span>);</span><br><span class="line"> accountB.publishNewContent(<span class="string">&quot;新内容B1&quot;</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 用户A取消订阅公众号A</span></span><br><span class="line"> accountA.removeObserver(user1);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 发布新内容</span></span><br><span class="line"> accountA.publishNewContent(<span class="string">&quot;新内容A2&quot;</span>);</span><br><span class="line"> accountB.publishNewContent(<span class="string">&quot;新内容B2&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><strong>输出结果:</strong></p>
<figure class="highlight console"><table><tr><td class="code"><pre><span class="line">用户 用户A 收到了公众号 公众号A 的新内容:新内容A1</span><br><span class="line">用户 用户A 收到了公众号 公众号B 的新内容:新内容B1</span><br><span class="line">用户 用户B 收到了公众号 公众号B 的新内容:新内容B1</span><br><span class="line">用户 用户A 收到了公众号 公众号A 的新内容:新内容A2</span><br><span class="line">用户 用户B 收到了公众号 公众号B 的新内容:新内容B2</span><br></pre></td></tr></table></figure>
<h1 id="行为型-访问者-Visitor"><a href="#行为型-访问者-Visitor" class="headerlink" title="行为型 - 访问者(Visitor)"></a>行为型 - 访问者(Visitor)</h1><blockquote>
<p>当你想要为一个对象的组合增加新的能力, 且封装并不重要时, 就使用访问者模式</p>
</blockquote>
<h2 id="示例:喂宠物"><a href="#示例:喂宠物" class="headerlink" title="示例:喂宠物"></a>示例:喂宠物</h2><p>现在养宠物的人特别多,我们就以这个为例,当然宠物还分为狗,猫等,要给宠物喂食的话,主人可以喂,其他人也可以喂食</p>
<p><strong>被访问元素抽象类<code>Animal</code>:</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="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">class</span> <span class="title class_">Animal</span> &#123;</span><br><span class="line"> <span class="keyword">private</span> String name;</span><br><span class="line"> <span class="keyword">private</span> <span class="type">int</span> age;</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_">accept</span><span class="params">(PetVisitor visitor)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><strong>被访问元素</strong>——宠物狗类<code>Dog</code>和宠物猫类<code>Cat</code>:</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">@AllArgsConstructor</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Dog</span> <span class="keyword">extends</span> <span class="title class_">Animal</span> &#123;</span><br><span class="line"> <span class="keyword">private</span> String breed;</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_">accept</span><span class="params">(PetVisitor visitor)</span> &#123;</span><br><span class="line"> visitor.visit(<span class="built_in">this</span>);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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">@AllArgsConstructor</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Cat</span> <span class="keyword">extends</span> <span class="title class_">Animal</span> &#123;</span><br><span class="line"> <span class="keyword">private</span> String color;</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_">accept</span><span class="params">(PetVisitor visitor)</span> &#123;</span><br><span class="line"> visitor.visit(<span class="built_in">this</span>);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>访问者接口<code>PetVisitor</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_">PetVisitor</span> &#123;</span><br><span class="line"> <span class="keyword">void</span> <span class="title function_">visit</span><span class="params">(Dog dog)</span>;</span><br><span class="line"> <span class="keyword">void</span> <span class="title function_">visit</span><span class="params">(Cat cat)</span>;</span><br><span class="line">&#125;</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_">Owner</span> <span class="keyword">implements</span> <span class="title class_">PetVisitor</span> &#123;</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_">visit</span><span class="params">(Dog dog)</span> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;主人给狗狗 &quot;</span> + dog.getName() + <span class="string">&quot; 喂食&quot;</span>);</span><br><span class="line"> <span class="comment">// 具体的喂食逻辑</span></span><br><span class="line"> &#125;</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_">visit</span><span class="params">(Cat cat)</span> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;主人给猫咪 &quot;</span> + cat.getName() + <span class="string">&quot; 喂食&quot;</span>);</span><br><span class="line"> <span class="comment">// 具体的喂食逻辑</span></span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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_">Others</span> <span class="keyword">implements</span> <span class="title class_">PetVisitor</span> &#123;</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_">visit</span><span class="params">(Dog dog)</span> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;其他人给狗狗 &quot;</span> + dog.getName() + <span class="string">&quot; 喂食&quot;</span>);</span><br><span class="line"> <span class="comment">// 具体的喂食逻辑</span></span><br><span class="line"> &#125;</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_">visit</span><span class="params">(Cat cat)</span> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;其他人给猫咪 &quot;</span> + cat.getName() + <span class="string">&quot; 喂食&quot;</span>);</span><br><span class="line"> <span class="comment">// 具体的喂食逻辑</span></span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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_">VisitorPatternExample</span> &#123;</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> &#123;</span><br><span class="line"> <span class="comment">// 创建具体的宠物对象</span></span><br><span class="line"> <span class="type">Animal</span> <span class="variable">dog</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Dog</span>(<span class="string">&quot;旺财&quot;</span>, <span class="string">&quot;金毛&quot;</span>);</span><br><span class="line"> <span class="type">Animal</span> <span class="variable">cat</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Cat</span>(<span class="string">&quot;小花&quot;</span>, <span class="string">&quot;橘猫&quot;</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 创建具体的访问者对象</span></span><br><span class="line"> <span class="type">PetVisitor</span> <span class="variable">owner</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Owner</span>();</span><br><span class="line"> <span class="type">PetVisitor</span> <span class="variable">others</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Others</span>();</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 被访问元素接受访问者的访问</span></span><br><span class="line"> dog.accept(owner);</span><br><span class="line"> cat.accept(others);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>运行上述代码,输出将是:</p>
<figure class="highlight console"><table><tr><td class="code"><pre><span class="line">主人给狗狗 旺财 喂食</span><br><span class="line">其他人给猫咪 小花 喂食</span><br></pre></td></tr></table></figure>
<h1 id="行为型-状态-State"><a href="#行为型-状态-State" class="headerlink" title="行为型 - 状态(State)"></a>行为型 - 状态(State)</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">@Data</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">VendingMachine</span> &#123;</span><br><span class="line"> <span class="keyword">private</span> VendingMachineState state;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">insertCoin</span><span class="params">()</span> &#123;</span><br><span class="line"> state.insertCoin();</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">selectProduct</span><span class="params">()</span> &#123;</span><br><span class="line"> state.selectProduct();</span><br><span class="line"> &#125;</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_">dispenseProduct</span><span class="params">()</span> &#123;</span><br><span class="line"> state.dispenseProduct();</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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_">VendingMachineState</span> &#123;</span><br><span class="line"> <span class="keyword">void</span> <span class="title function_">insertCoin</span><span class="params">()</span>;</span><br><span class="line"> <span class="keyword">void</span> <span class="title function_">selectProduct</span><span class="params">()</span>;</span><br><span class="line"> <span class="keyword">void</span> <span class="title function_">dispenseProduct</span><span class="params">()</span>;</span><br><span class="line">&#125;</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">@RequiredArgsConstructor</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">IdleState</span> <span class="keyword">implements</span> <span class="title class_">VendingMachineState</span> &#123;</span><br><span class="line"> <span class="keyword">private</span> VendingMachine vendingMachine;</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_">insertCoin</span><span class="params">()</span> &#123;</span><br><span class="line"> <span class="comment">// 执行插入硬币操作</span></span><br><span class="line"> System.out.println(<span class="string">&quot;已插入硬币&quot;</span>);</span><br><span class="line"> vendingMachine.setState(<span class="keyword">new</span> <span class="title class_">SelectProductState</span>(vendingMachine));</span><br><span class="line"> &#125;</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_">selectProduct</span><span class="params">()</span> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;请先插入硬币&quot;</span>);</span><br><span class="line"> &#125;</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_">dispenseProduct</span><span class="params">()</span> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;请先选择商品&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 选择商品状态类</span></span><br><span class="line"><span class="meta">@RequiredArgsConstructor</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">SelectProductState</span> <span class="keyword">implements</span> <span class="title class_">VendingMachineState</span> &#123;</span><br><span class="line"> <span class="keyword">private</span> VendingMachine vendingMachine;</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_">insertCoin</span><span class="params">()</span> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;已插入硬币,请选择商品&quot;</span>);</span><br><span class="line"> &#125;</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_">selectProduct</span><span class="params">()</span> &#123;</span><br><span class="line"> <span class="comment">// 执行选择商品操作</span></span><br><span class="line"> System.out.println(<span class="string">&quot;已选择商品&quot;</span>);</span><br><span class="line"> vendingMachine.setState(<span class="keyword">new</span> <span class="title class_">SoldOutState</span>(vendingMachine));</span><br><span class="line"> &#125;</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_">dispenseProduct</span><span class="params">()</span> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;请先选择商品&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 售出商品状态类</span></span><br><span class="line"><span class="meta">@RequiredArgsConstructor</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">SoldOutState</span> <span class="keyword">implements</span> <span class="title class_">VendingMachineState</span> &#123;</span><br><span class="line"> <span class="keyword">private</span> VendingMachine vendingMachine;</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_">insertCoin</span><span class="params">()</span> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;商品已售罄&quot;</span>);</span><br><span class="line"> &#125;</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_">selectProduct</span><span class="params">()</span> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;商品已售罄&quot;</span>);</span><br><span class="line"> &#125;</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_">dispenseProduct</span><span class="params">()</span> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;商品已售罄&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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_">VendingMachineExample</span> &#123;</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> &#123;</span><br><span class="line"> <span class="type">VendingMachine</span> <span class="variable">vendingMachine</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">VendingMachine</span>();</span><br><span class="line"> vendingMachine.setState(<span class="keyword">new</span> <span class="title class_">IdleState</span>(vendingMachine));</span><br><span class="line"></span><br><span class="line"> vendingMachine.insertCoin();</span><br><span class="line"> vendingMachine.selectProduct();</span><br><span class="line"> vendingMachine.dispenseProduct();</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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></pre></td></tr></table></figure>
<h1 id="行为型-解释器-Interpreter"><a href="#行为型-解释器-Interpreter" class="headerlink" title="行为型 - 解释器(Interpreter)"></a>行为型 - 解释器(Interpreter)</h1><blockquote>
<p>使用解释器模式为语言创建解释器,通常由语言的语法和语法分析来定义</p>
</blockquote>
<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"><span class="keyword">interface</span> <span class="title class_">Expression</span> &#123;</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="comment"> * <span class="doctag">@param</span> input 输入字符串</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> 如果输入符合表达式则返回true,否则返回false</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="type">boolean</span> <span class="title function_">interpret</span><span class="params">(String input)</span>;</span><br><span class="line">&#125;</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">@AllArgsConstructor</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">CharacterExpression</span> <span class="keyword">implements</span> <span class="title class_">Expression</span> &#123;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">char</span> character;</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="comment"> * <span class="doctag">@param</span> input 输入字符串</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> 如果输入包含字符表达式中的字符则返回true,否则返回false</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">interpret</span><span class="params">(String input)</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> input.contains(Character.toString(character));</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 非终结符表达式类 - 或表达式</span></span><br><span class="line"><span class="meta">@AllArgsConstructor</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">OrExpression</span> <span class="keyword">implements</span> <span class="title class_">Expression</span> &#123;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> Expression expression1;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> Expression expression2;</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="comment"> * <span class="doctag">@param</span> input 输入字符串</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> 如果输入符合或表达式的条件则返回true,否则返回false</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">interpret</span><span class="params">(String input)</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> expression1.interpret(input) || expression2.interpret(input);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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_">RegularExpressionEngine</span> &#123;</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> &#123;</span><br><span class="line"> <span class="comment">// 构建正则表达式:b|c</span></span><br><span class="line"> <span class="type">Expression</span> <span class="variable">expression</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">OrExpression</span>(</span><br><span class="line"> <span class="keyword">new</span> <span class="title class_">CharacterExpression</span>(<span class="string">&#x27;b&#x27;</span>),</span><br><span class="line"> <span class="keyword">new</span> <span class="title class_">CharacterExpression</span>(<span class="string">&#x27;c&#x27;</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="type">String</span> <span class="variable">input</span> <span class="operator">=</span> <span class="string">&quot;abcd&quot;</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 使用正则表达式引擎解析和执行正则表达式</span></span><br><span class="line"> <span class="type">boolean</span> <span class="variable">isMatch</span> <span class="operator">=</span> expression.interpret(input);</span><br><span class="line"> System.out.println(<span class="string">&quot;Input: &quot;</span> + input);</span><br><span class="line"> System.out.println(<span class="string">&quot;Match result: &quot;</span> + isMatch); <span class="comment">// Output: true</span></span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h1 id="行为型-迭代器-Iterator"><a href="#行为型-迭代器-Iterator" class="headerlink" title="行为型 - 迭代器(Iterator)"></a>行为型 - 迭代器(Iterator)</h1><blockquote>
<p>提供一种方法顺序访问一个聚合对象中的各个元素, 而又不暴露其内部的表示</p>
</blockquote>
<p>直接用for循环替代吧,没啥好说的</p>
<h1 id="行为型-中介者-Mediator"><a href="#行为型-中介者-Mediator" class="headerlink" title="行为型 - 中介者(Mediator)"></a>行为型 - 中介者(Mediator)</h1><blockquote>
<p>中介者模式(Mediator Pattern)用于解耦一组对象之间的交互关系。它通过引入一个中介者对象,将各个对象之间的交互逻辑集中处理。中介者模式的重点在于对象之间的通信和协调。</p>
</blockquote>
<h2 id="中介者vs外观"><a href="#中介者vs外观" class="headerlink" title="中介者vs外观"></a>中介者vs外观</h2><ul>
<li><p>中介者模式(Mediator Pattern)用于解耦一组对象之间的交互关系。它通过引入一个中介者对象,将各个对象之间的交互逻辑集中处理。中介者模式的重点在于对象之间的通信和协调。</p>
</li>
<li><p>外观模式(Facade Pattern)主要用于简化复杂系统的接口。它提供了一个统一的接口,隐藏系统的复杂性,使得客户端可以更方便地访问子系统。外观模式的重点在于封装和简化接口。</p>
</li>
<li><p>简单来说,中介者更像是一个中转站,而外观模式则是起点(入口)</p>
</li>
</ul>
<h2 id="示例:GUI开发"><a href="#示例:GUI开发" class="headerlink" title="示例:GUI开发"></a>示例:GUI开发</h2><p>在GUI中,有多个组件(如按钮、文本框、下拉列表等),它们之间需要进行交互和通信。使用中介者模式可以将这些交互逻辑集中处理,并避免组件之间的紧耦合。</p>
<p><strong>中介者接口<code>Mediator</code>,它包含了组件之间的交互方法:</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_">Mediator</span> &#123;</span><br><span class="line"> <span class="keyword">void</span> <span class="title function_">notify</span><span class="params">(Component sender, String event)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><strong>中介者实现类<code>GUIManager</code>,实现中介者接口,负责协调组件之间的交互:</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><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_">GUIManager</span> <span class="keyword">implements</span> <span class="title class_">Mediator</span> &#123;</span><br><span class="line"> <span class="keyword">private</span> Button button;</span><br><span class="line"> <span class="keyword">private</span> TextBox textBox;</span><br><span class="line"> <span class="keyword">private</span> ComboBox comboBox;</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_">notify</span><span class="params">(Component sender, String event)</span> &#123;</span><br><span class="line"> <span class="keyword">if</span> (sender == button &amp;&amp; event.equals(<span class="string">&quot;click&quot;</span>)) &#123;</span><br><span class="line"> <span class="comment">// 处理按钮点击事件的逻辑</span></span><br><span class="line"> <span class="type">String</span> <span class="variable">selectedValue</span> <span class="operator">=</span> comboBox.getSelectedValue();</span><br><span class="line"> textBox.setText(<span class="string">&quot;Selected: &quot;</span> + selectedValue);</span><br><span class="line"> &#125; <span class="keyword">else</span> <span class="keyword">if</span> (sender == textBox &amp;&amp; event.equals(<span class="string">&quot;change&quot;</span>)) &#123;</span><br><span class="line"> <span class="comment">// 处理文本框内容变化事件的逻辑</span></span><br><span class="line"> <span class="type">String</span> <span class="variable">text</span> <span class="operator">=</span> textBox.getText();</span><br><span class="line"> comboBox.filterOptions(text);</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><strong>组件类接口及其实现:</strong></p>
<blockquote>
<p>在中介者模式中,将<code>Component</code>定义为一个接口是为了强调组件的行为和协议,而不关注具体实现细节。</p>
<p>使用接口的好处是可以实现多重继承,即一个类可以同时实现多个接口。这样,在需要与其他组件进行交互时,组件类可以通过实现<code>Component</code>接口来获得中介者的引用,并实现中介者模式所需的方法。接口的使用还可以帮助实现松耦合,使得组件之间更加灵活和可替换。</p>
</blockquote>
<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_">Component</span> &#123;</span><br><span class="line"> <span class="keyword">void</span> <span class="title function_">setMediator</span><span class="params">(Mediator mediator)</span>;</span><br><span class="line"> <span class="keyword">void</span> <span class="title function_">notify</span><span class="params">(String event)</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Button</span> <span class="keyword">implements</span> <span class="title class_">Component</span> &#123;</span><br><span class="line"> <span class="keyword">private</span> Mediator mediator;</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_">setMediator</span><span class="params">(Mediator mediator)</span> &#123;</span><br><span class="line"> <span class="built_in">this</span>.mediator = mediator;</span><br><span class="line"> &#125;</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_">notify</span><span class="params">(String event)</span> &#123;</span><br><span class="line"> mediator.notify(<span class="built_in">this</span>, event);</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">click</span><span class="params">()</span> &#123;</span><br><span class="line"> notify(<span class="string">&quot;click&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TextBox</span> <span class="keyword">implements</span> <span class="title class_">Component</span> &#123;</span><br><span class="line"> <span class="keyword">private</span> Mediator mediator;</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_">setMediator</span><span class="params">(Mediator mediator)</span> &#123;</span><br><span class="line"> <span class="built_in">this</span>.mediator = mediator;</span><br><span class="line"> &#125;</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_">notify</span><span class="params">(String event)</span> &#123;</span><br><span class="line"> mediator.notify(<span class="built_in">this</span>, event);</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">change</span><span class="params">()</span> &#123;</span><br><span class="line"> notify(<span class="string">&quot;change&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ComboBox</span> <span class="keyword">implements</span> <span class="title class_">Component</span> &#123;</span><br><span class="line"> <span class="keyword">private</span> Mediator mediator;</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_">setMediator</span><span class="params">(Mediator mediator)</span> &#123;</span><br><span class="line"> <span class="built_in">this</span>.mediator = mediator;</span><br><span class="line"> &#125;</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_">notify</span><span class="params">(String event)</span> &#123;</span><br><span class="line"> mediator.notify(<span class="built_in">this</span>, event);</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">filterOptions</span><span class="params">(String text)</span> &#123;</span><br><span class="line"> <span class="comment">// 根据文本过滤选项</span></span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> String <span class="title function_">getSelectedValue</span><span class="params">()</span> &#123;</span><br><span class="line"> <span class="comment">// 获取选中的值</span></span><br><span class="line"> <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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_">MediatorExample</span> &#123;</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> &#123;</span><br><span class="line"> <span class="type">Button</span> <span class="variable">button</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Button</span>();</span><br><span class="line"> <span class="type">TextBox</span> <span class="variable">textBox</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TextBox</span>();</span><br><span class="line"> <span class="type">ComboBox</span> <span class="variable">comboBox</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ComboBox</span>();</span><br><span class="line"></span><br><span class="line"> <span class="type">Mediator</span> <span class="variable">mediator</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">GUIManager</span>(button, textBox, comboBox);</span><br><span class="line"></span><br><span class="line"> button.setMediator(mediator);</span><br><span class="line"> textBox.setMediator(mediator);</span><br><span class="line"> comboBox.setMediator(mediator);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 模拟按钮点击事件</span></span><br><span class="line"> button.click();</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 模拟文本框内容变化事件</span></span><br><span class="line"> textBox.change();</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>在这个示例中,各个组件通过中介者接口进行交互,而不是直接相互引用。中介者负责协调和处理组件之间的交互逻辑,从而实现了解耦和集中管理的效果。这样,当需要新增、修改或删除交互逻辑时,只需修改中介者类,而不需要修改多个组件类,提高了代码的可维护性和可扩展性。</p>
<h1 id="行为型-备忘录-Memento"><a href="#行为型-备忘录-Memento" class="headerlink" title="行为型 - 备忘录(Memento)"></a>行为型 - 备忘录(Memento)</h1><blockquote>
<p>当你需要让对象返回之前的状态时,可以使用备忘录模式</p>
</blockquote>
<h2 id="示例:恢复游戏存档"><a href="#示例:恢复游戏存档" class="headerlink" title="示例:恢复游戏存档"></a>示例:恢复游戏存档</h2><p><strong>Originator(发起人)——游戏类:</strong>包含了当前游戏的状态数据,并提供了保存和恢复状态的方法。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><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_">Game</span> &#123;</span><br><span class="line"> <span class="keyword">private</span> <span class="type">int</span> level;</span><br><span class="line"> <span class="keyword">private</span> String checkpoint;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">play</span><span class="params">()</span> &#123;</span><br><span class="line"> <span class="comment">// 在游戏中玩游戏并更新游戏状态</span></span><br><span class="line"> level++;</span><br><span class="line"> checkpoint = <span class="string">&quot;Check Point &quot;</span> + level;</span><br><span class="line"> System.out.println(<span class="string">&quot;Playing game at Level &quot;</span> + level);</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> GameState <span class="title function_">save</span><span class="params">()</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">GameState</span>(level, checkpoint);</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">restore</span><span class="params">(GameState gameState)</span> &#123;</span><br><span class="line"> <span class="built_in">this</span>.level = gameState.getLevel();</span><br><span class="line"> <span class="built_in">this</span>.checkpoint = gameState.getCheckpoint();</span><br><span class="line"> System.out.println(<span class="string">&quot;Restoring game at Level &quot;</span> + level + <span class="string">&quot;, Check Point: &quot;</span> + checkpoint);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><strong>Memento(备忘录)——游戏状态类:</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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">GameState</span> &#123;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">int</span> level;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> String checkpoint;</span><br><span class="line"> <span class="comment">// 其他游戏状态相关的属性</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><strong>Caretaker(管理者)——游戏管理类:</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_">GameCareTaker</span> &#123;</span><br><span class="line"> <span class="keyword">private</span> List&lt;GameState&gt; gameStateList = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">saveState</span><span class="params">(GameState gameState)</span> &#123;</span><br><span class="line"> gameStateList.add(gameState);</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> GameState <span class="title function_">restoreState</span><span class="params">(<span class="type">int</span> index)</span> &#123;</span><br><span class="line"> <span class="keyword">if</span> (index &gt;= <span class="number">0</span> &amp;&amp; index &lt; gameStateList.size()) &#123;</span><br><span class="line"> <span class="keyword">return</span> gameStateList.get(index);</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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_">GameSaveDemo</span> &#123;</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> &#123;</span><br><span class="line"> <span class="comment">// 创建游戏对象</span></span><br><span class="line"> <span class="type">Game</span> <span class="variable">game</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Game</span>();</span><br><span class="line"> <span class="comment">// 创建游戏管理对象</span></span><br><span class="line"> <span class="type">GameCareTaker</span> <span class="variable">careTaker</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">GameCareTaker</span>();</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 进行游戏,修改状态</span></span><br><span class="line"> game.play(); </span><br><span class="line"> <span class="comment">// 保存当前游戏状态到存档</span></span><br><span class="line"> careTaker.saveState(game.save());</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 再次进行游戏,修改状态</span></span><br><span class="line"> game.play();</span><br><span class="line"> <span class="comment">// 保存当前游戏状态到存档</span></span><br><span class="line"> careTaker.saveState(game.save());</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 恢复第一个存档</span></span><br><span class="line"> <span class="type">GameState</span> <span class="variable">savedState1</span> <span class="operator">=</span> careTaker.restoreState(<span class="number">0</span>);</span><br><span class="line"> <span class="keyword">if</span> (savedState1 != <span class="literal">null</span>) &#123;</span><br><span class="line"> game.restore(savedState1);</span><br><span class="line"> game.printState(); <span class="comment">// 打印恢复后的游戏状态</span></span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 恢复第二个存档</span></span><br><span class="line"> <span class="type">GameState</span> <span class="variable">savedState2</span> <span class="operator">=</span> careTaker.restoreState(<span class="number">1</span>);</span><br><span class="line"> <span class="keyword">if</span> (savedState2 != <span class="literal">null</span>) &#123;</span><br><span class="line"> game.restore(savedState2);</span><br><span class="line"> game.printState(); <span class="comment">// 打印恢复后的游戏状态</span></span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
]]></content>
<categories>
<category>代码美学</category>
</categories>
<tags>
<tag>设计模式</tag>
</tags>
</entry>
<entry>
<title>Java 虚拟机</title>
<url>/2023/05/16/jvm/</url>
<content><![CDATA[<p>注:栈内存、堆内存诊断都没学</p>
<h1 id="概念"><a href="#概念" class="headerlink" title="概念"></a>概念</h1><h2 id="定义"><a href="#定义" class="headerlink" title="定义"></a>定义</h2><p>java二进制字节码的运行环境</p>
<h2 id="好处"><a href="#好处" class="headerlink" title="好处"></a>好处</h2><ul>
<li><p>一次编写,到处运行 </p>
</li>
<li><ul>
<li>消除了字节码和底层操作系统之间的的差异 </li>
<li>对外提供了一个一致的运行环境 </li>
<li>jvm可以用解释的方法来执行二进制字节码来达到代码的平台无关性</li>
</ul>
</li>
<li><p>自动内存管理,垃圾回收功能 </p>
</li>
<li><p>数组下标越界越界检查 </p>
</li>
<li><ul>
<li>不会覆盖其他代码的内存</li>
</ul>
</li>
<li><p>多态 </p>
</li>
<li><ul>
<li>提升扩展性</li>
<li>使用虚方法表的方式实现</li>
</ul>
</li>
</ul>
<h2 id="概念对比"><a href="#概念对比" class="headerlink" title="概念对比"></a>概念对比</h2><p>JVM: Java虚拟机</p>
<p>JRE: Java的运行时环境 &lt;&#x3D; JVM + 基础类库</p>
<p>JDK: JRE基础上加上一些javac,javap,内存监测工具什么的 &lt;&#x3D; JVM + 基础类库 + 编译工具</p>
<h1 id="流程"><a href="#流程" class="headerlink" title="流程"></a>流程</h1><p>先通过类加载器加载到JVM内存结构</p>
<p>JVM内存结构包括:</p>
<ul>
<li><code>方法区</code>: 存放类</li>
<li><code>堆</code>:存放类的实例对象</li>
<li><code>虚拟机栈</code>:实例对象调用方法时用到</li>
<li><code>程序计数器</code>:实例对象调用方法时用到</li>
<li><code>本地方法栈</code>:实例对象调用方法时用到</li>
</ul>
<p>接下来到了执行引擎:</p>
<ul>
<li>方法执行时,每行代码由<code>解释器</code>进行逐行执行 </li>
<li>方法内的热点代码,也就是被频繁调用的代码,会被<code>即时编译器</code>进行编译。</li>
</ul>
<p>可以竟是看做为优化代码</p>
<ul>
<li><code>GC垃圾回收</code>将<code>堆</code>里面不再被引用的模块进行垃圾回收</li>
</ul>
<p>还有一些是Java代码无法实现,需要调用底层代码的功能:</p>
<ul>
<li>通过<code>本地方法接口</code>调用操作系统的一些功能方法</li>
</ul>
<h1 id="内存结构"><a href="#内存结构" class="headerlink" title="内存结构"></a>内存结构</h1><h2 id="程序计数器"><a href="#程序计数器" class="headerlink" title="程序计数器"></a>程序计数器</h2><h3 id="引言"><a href="#引言" class="headerlink" title="引言"></a>引言</h3><p>JVM指令 &#x3D;&gt; 解释器 &#x3D;&gt; 机器码 &#x3D;&gt; CPU</p>
<ul>
<li>Java源代码经过编译,成为<code>二进制字节码(JVM指令)</code>,所有平台下都是一致的 </li>
<li>接下来每一条<code>JVM指令</code>都被<code>解释器</code>(java虚拟机执行引擎的一个组件)解释成一条<code>机器码</code> </li>
<li>CPU拿着<code>机器码</code>执行命令</li>
</ul>
<p>CPU只认机器码</p>
<h3 id="作用"><a href="#作用" class="headerlink" title="作用"></a>作用</h3><p>程序计数器负责在一些JVM指令的执行过程中,记住下一条JVM指令的执行地址</p>
<p>当JVM指令最后变成机器码被CPU执行后,解释器就去程序计数器找要执行的指令地址</p>
<p>在<strong>物理上</strong>程序计数器是通过一个叫<strong>寄存器</strong>的东西实现的,程序计数器是对寄存器的一些屏蔽和抽象</p>
<p>寄存器是整个CPU读取速度最快的单元(因为读取非常频繁)</p>
<p>使用Java虚拟机在手机的时候,就将CPU的寄存器作为程序计数器来存储地址</p>
<h3 id="特点"><a href="#特点" class="headerlink" title="特点"></a>特点</h3><ul>
<li>线程私有</li>
</ul>
<p>在多个线程运行的时候,CPU会有一个调度器组件来给每个线程分配时间片</p>
<p>如果线程1的代码在时间片内没有执行完,线程1的代码就会被暂存,CPU资源切换到线程2</p>
<p>线程2的时间片用完了再切换</p>
<p>在线程切换的改成中,如果要记住下一条指令执行到哪里了,就要用到程序计数器</p>
<p>程序计数器现在就属于被暂存的线程</p>
<p>每个线程都有自己的程序计数器()因为各自执行的地址是不一样的</p>
<ul>
<li>JVM规范中唯一一个不会存在内存溢出的区</li>
</ul>
<h2 id="虚拟机栈"><a href="#虚拟机栈" class="headerlink" title="虚拟机栈"></a>虚拟机栈</h2><h3 id="作用-1"><a href="#作用-1" class="headerlink" title="作用"></a>作用</h3><ul>
<li>每个线程运行时所需要的内存,称为虚拟机栈</li>
</ul>
<p>一个栈内由多个栈帧组成</p>
<p>一个栈帧对应一次方法的调用</p>
<p>线程执行代码,代码由多个方法组成,每个方法运行需要的内存就是栈帧</p>
<p>栈帧的内存存着方法参数,局部变量,返回地址</p>
<p>方法1被调用,分配栈帧1,栈帧1入栈;方法1调用方法2,分配栈帧2,栈帧2入栈…方法2执行完,栈帧2出栈。出栈就是释放内存</p>
<ul>
<li>每个栈由多个栈帧组成,对应着每次方法调用时所占用的内存 </li>
<li>每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法</li>
</ul>
<h3 id="注意点"><a href="#注意点" class="headerlink" title="注意点"></a>注意点</h3><ul>
<li><p>出栈自动回收内存,不需要用到GC,GC是堆用的</p>
</li>
<li><p>栈内存Linux、macOS、Oracle Solaris默认1m</p>
</li>
<li><p>栈内存不是越大越好,因为物理内存是固定的,单个栈内存大了,可以容纳的线程数量就少了</p>
</li>
<li><p>局部变量是线程安全的,因为俩线程调用一个方法,各自都有各自的局部变量</p>
</li>
<li><p>但是全局变量(<code>static</code>)对各个方法是共享的。静态变量值的改变发生在方法调用完毕之后。当方法内部进行静态变量的修改时,实际上只是修改了该方法中对静态变量的局部变量,这并不会影响在内存中存储的静态变量值。当方法执行完毕后,再次访问该静态变量时,才能看到被更改的值。因此,静态变量值的改变情况都发生在方法调用完毕之后。</p>
</li>
<li><p>判断方法内局部变量是否线程安全:</p>
</li>
<li><ul>
<li>没有进出:不是通过参数引用进来,没有return出去</li>
<li>基本类型当然是线程安全的</li>
</ul>
</li>
</ul>
<h3 id="栈内存溢出的场景"><a href="#栈内存溢出的场景" class="headerlink" title="栈内存溢出的场景"></a>栈内存溢出的场景</h3><ul>
<li>栈帧过多,只入不出(错误的递归调用)</li>
</ul>
<p>比方说一个部门类和员工类,部门类有一个员工类的List,员工类有一个自己所属部门的属性</p>
<p>转换成json时便会出现循环依赖</p>
<p>此时可以在员工类的部门字段上加上<code>@JsonIgnore</code>来忽略这个属性</p>
<ul>
<li>栈帧过大(理论上才会存在?)</li>
</ul>
<h2 id="本地方法栈"><a href="#本地方法栈" class="headerlink" title="本地方法栈"></a>本地方法栈</h2><p>分配给不是由Java编写的代码</p>
<h2 id="堆"><a href="#堆" class="headerlink" title="堆"></a>堆</h2><h3 id="作用-2"><a href="#作用-2" class="headerlink" title="作用"></a>作用</h3><p>通过new关键字,创建对象都会使用堆内存</p>
<h3 id="特点-1"><a href="#特点-1" class="headerlink" title="特点"></a>特点</h3><ul>
<li>它是线程共享的,堆中对象都需要考虑线程安全的问题</li>
<li>有垃圾回收机制</li>
</ul>
<h3 id="堆内存溢出"><a href="#堆内存溢出" class="headerlink" title="堆内存溢出"></a>堆内存溢出</h3><ul>
<li>集合内元素在集合作用范围内无法被垃圾回收,如果元素过多过大会导致堆内存溢出</li>
<li>垃圾回收后,内存占用仍然很高</li>
</ul>
<h2 id="方法区"><a href="#方法区" class="headerlink" title="方法区"></a>方法区</h2><p>存储类的信息</p>
<h3 id="内存溢出"><a href="#内存溢出" class="headerlink" title="内存溢出"></a>内存溢出</h3><ul>
<li>1.8以前会导致永久代内存溢出</li>
<li>1.8之后会导致元空间内存溢出</li>
</ul>
]]></content>
<categories>
<category>Java 八股文</category>
</categories>
<tags>
<tag>JVM</tag>
</tags>
</entry>
<entry>
<title>(一)Go基本语法</title>
<url>/2023/07/25/go-note1/</url>
<content><![CDATA[<h1 id="变量"><a href="#变量" class="headerlink" title="变量"></a>变量</h1><h2 id="变量声明"><a href="#变量声明" class="headerlink" title="变量声明"></a>变量声明</h2><h3 id="显式声明"><a href="#显式声明" class="headerlink" title="显式声明"></a>显式声明</h3><ul>
<li><p>如果我们未为一个变量初始化,则必须显式指定变量类型</p>
</li>
<li><p>如果声明时不指定具体的值,则会被赋值为默认值。</p>
</li>
</ul>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> str <span class="type">string</span> <span class="comment">// str的值为&quot;&quot;(空字符串)</span></span><br><span class="line"><span class="keyword">var</span> num <span class="type">int</span> <span class="comment">// num的值为0</span></span><br></pre></td></tr></table></figure>
<h3 id="快速声明赋值"><a href="#快速声明赋值" class="headerlink" title="快速声明赋值"></a>快速声明赋值</h3><ul>
<li><p>Go 支持变量类型自动推断,也就是说,当我们立即为一个变量进行初始化时,其类型是可以省略的</p>
</li>
<li><p>可以通过 <code>:=</code> 符号以一种简单的方式(也是实际上最常用的方式)声明一个变量</p>
</li>
</ul>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line">a, b := <span class="number">1</span>, <span class="number">2</span><span class="comment">// Go 支持多个变量在同一行同时声明</span></span><br><span class="line">str := <span class="string">&quot;我是字符串。&quot;</span></span><br></pre></td></tr></table></figure>
<h2 id="常量声明"><a href="#常量声明" class="headerlink" title="常量声明"></a>常量声明</h2><p>可以使用 <code>const</code> 关键字代替 <code>var</code> 关键字来创建一个常量(不可变变量)</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> str <span class="type">string</span> = <span class="string">&quot;我是字符串&quot;</span></span><br><span class="line"><span class="keyword">const</span> num <span class="type">int</span> = <span class="number">1</span></span><br><span class="line"><span class="keyword">const</span> a, b = <span class="number">3</span>, <span class="number">4</span></span><br><span class="line"></span><br><span class="line">num = <span class="number">2</span> <span class="comment">// 若更改会报错</span></span><br></pre></td></tr></table></figure>
<h1 id="流程控制"><a href="#流程控制" class="headerlink" title="流程控制"></a>流程控制</h1><h2 id="if"><a href="#if" class="headerlink" title="if"></a>if</h2><ul>
<li><code>Go</code>的<code>if</code>语句和<code>C、Java</code>类似,只不过<code>if</code>后面没有括号,但是加上括号也能正确运行</li>
<li><code>if</code>后面的开始大括号必须跟if在同一行,不能换行(会报错)。</li>
</ul>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line">num1, num2 := <span class="number">1</span>, <span class="number">2</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> num1 &lt; num2 &#123;</span><br><span class="line"> fmt.Println(<span class="string">&quot;num1 &lt; num2&quot;</span>)</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> fmt.Println(<span class="string">&quot;num1 &gt;= num2&quot;</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><code>if</code>语句后面也可以定义变量,但只能用一个分号。也支持<code>if-else-if</code>语句。</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">if</span> num1, num2 := <span class="number">1</span>, <span class="number">2</span>; num1 &lt; num2 &#123;</span><br><span class="line"> fmt.Println(<span class="string">&quot;num1 &lt; num2&quot;</span>)</span><br><span class="line">&#125; <span class="keyword">else</span> <span class="keyword">if</span> num1 &gt; num2 &#123;</span><br><span class="line"> fmt.Println(<span class="string">&quot;num1 &lt; num2&quot;</span>)</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> fmt.Println(<span class="string">&quot;num1 = num2&quot;</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="for"><a href="#for" class="headerlink" title="for"></a>for</h2><p>在<code>Go</code>里没有<code>while</code>和<code>do-while</code>循环,只有<code>for</code>循环。<code>Go</code>的<code>for</code>循环包含了<code>for</code>和<code>while</code>的功能。</p>
<p>用<code>for</code>定义死循环,相当于<code>while(true)</code>。</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">for</span> &#123;</span><br><span class="line"> fmt.Println(<span class="string">&quot;loop&quot;</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="switch"><a href="#switch" class="headerlink" title="switch"></a>switch</h2><p><code>switch</code>语句也和<code>C、Java</code>的&#96;&#96;switch<code>语句类似,只不过</code>switch<code>后面不需要加括号,</code>case<code>里也不需要加</code>break<code>,</code>Go<code>的</code>switch <code>默认情况下 </code>case<code>最后自带</code>break<code>语句,匹配成功后就不会执行其他</code>case<code>,如果我们需要执行后面的 </code>case<code>,可以使用 </code>fallthrough&#96; 。</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line">num := <span class="number">2</span></span><br><span class="line"><span class="keyword">switch</span> num &#123;</span><br><span class="line"><span class="keyword">case</span> <span class="number">1</span>:</span><br><span class="line"> fmt.Println(<span class="string">&quot;one&quot;</span>)</span><br><span class="line"><span class="keyword">case</span> <span class="number">2</span>, <span class="number">3</span>:</span><br><span class="line"> fmt.Println(<span class="string">&quot;two or three&quot;</span>)</span><br><span class="line"><span class="keyword">default</span>:</span><br><span class="line"> fmt.Println(<span class="string">&quot;other&quot;</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><code>Go</code>的<code>switch</code>可以匹配任意类型,也可以实现<code>if-else</code>的功能,在<code>switch</code>后面不加匹配条件,可以在<code>case</code>里进行匹配。</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line">t := time.Now()</span><br><span class="line"><span class="keyword">switch</span> &#123;</span><br><span class="line"><span class="keyword">case</span> t.Hour() &lt; <span class="number">12</span>:</span><br><span class="line"> fmt.Println(<span class="string">&quot;It&#x27;s before noon&quot;</span>)</span><br><span class="line"><span class="keyword">default</span>:</span><br><span class="line"> fmt.Println(<span class="string">&quot;It&#x27;s after noon&quot;</span>)</span><br><span class="line">&#125;</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 go"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 声明一个长度为5的数组</span></span><br><span class="line"><span class="keyword">var</span> a [<span class="number">5</span>]<span class="type">int</span></span><br><span class="line">a[<span class="number">4</span>] = <span class="number">100</span></span><br><span class="line">fmt.Println(<span class="string">&quot;get:&quot;</span>, a[<span class="number">2</span>])</span><br><span class="line">fmt.Println(<span class="string">&quot;len:&quot;</span>, <span class="built_in">len</span>(a))</span><br><span class="line"></span><br><span class="line"><span class="comment">// 声明并初始化</span></span><br><span class="line">b := [<span class="number">5</span>]<span class="type">int</span>&#123;<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>&#125;</span><br><span class="line">fmt.Println(b)</span><br></pre></td></tr></table></figure>
<h2 id="多维数组"><a href="#多维数组" class="headerlink" title="多维数组"></a>多维数组</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> twoD [<span class="number">2</span>][<span class="number">3</span>]<span class="type">int</span></span><br><span class="line"><span class="keyword">for</span> i := <span class="number">0</span>; i &lt; <span class="number">2</span>; i++ &#123;</span><br><span class="line"> <span class="keyword">for</span> j := <span class="number">0</span>; j &lt; <span class="number">3</span>; j++ &#123;</span><br><span class="line"> twoD[i][j] = i + j</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line">fmt.Println(<span class="string">&quot;2d: &quot;</span>, twoD)</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><ul>
<li><p>有点像<code>Java</code>的<code>List</code></p>
</li>
<li><p>切片声明需要通过<code>make</code>来进行,需要指定切片的类型以及长度</p>
</li>
</ul>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 声明一个空切片,长度为3,容量为10</span></span><br><span class="line">s1 := <span class="built_in">make</span>([]<span class="type">string</span>, <span class="number">3</span>, <span class="number">10</span>)</span><br><span class="line"><span class="comment">// 声明并初始化</span></span><br><span class="line">s2 :=[] <span class="type">int</span> &#123;<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span>&#125; </span><br></pre></td></tr></table></figure>
<h2 id="操作"><a href="#操作" class="headerlink" title="操作"></a>操作</h2><p>对切片元素的操作,可以像数组一样操作:</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line">s := <span class="built_in">make</span>([]<span class="type">string</span>, <span class="number">3</span>)</span><br><span class="line">s[<span class="number">0</span>] = <span class="string">&quot;a&quot;</span></span><br><span class="line">s[<span class="number">1</span>] = <span class="string">&quot;b&quot;</span></span><br><span class="line">s[<span class="number">2</span>] = <span class="string">&quot;c&quot;</span></span><br></pre></td></tr></table></figure>
<h2 id="添加"><a href="#添加" class="headerlink" title="添加"></a>添加</h2><p>可以通过<code>append</code>方法向切片中添加元素,因为切片会涉及到扩容,所以需要一个变量来接收返回值</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line">s = <span class="built_in">append</span>(s, <span class="string">&quot;d&quot;</span>)</span><br><span class="line">s = <span class="built_in">append</span>(s, <span class="string">&quot;e&quot;</span>, <span class="string">&quot;f&quot;</span>)</span><br></pre></td></tr></table></figure>
<h2 id="复制"><a href="#复制" class="headerlink" title="复制"></a>复制</h2><p>可以通过<code>copy</code>方法对切片进行复制,复制后是两个独立的切片,更改元素值相互不影响。</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">go</span>复制代码c := <span class="built_in">make</span>([]<span class="type">string</span>, <span class="built_in">len</span>(s))</span><br><span class="line"><span class="built_in">copy</span>(c, s)</span><br><span class="line">fmt.Println(c) <span class="comment">// [a b c d e f]</span></span><br></pre></td></tr></table></figure>
<h2 id="切片-1"><a href="#切片-1" class="headerlink" title="切片"></a>切片</h2><p>相当于<code>Java</code>中的<code>substring</code></p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">go</span>复制代码<span class="comment">// 返回下标2到4的值</span></span><br><span class="line">fmt.Println(s[<span class="number">2</span>:<span class="number">5</span>]) <span class="comment">// [c d e]</span></span><br><span class="line"><span class="comment">// 返回下标0到4的值</span></span><br><span class="line">fmt.Println(s[:<span class="number">5</span>]) <span class="comment">// [a b c d e]</span></span><br><span class="line"><span class="comment">// 返回下标2到最后一个元素</span></span><br><span class="line">fmt.Println(s[<span class="number">2</span>:]) <span class="comment">// [c d e f]</span></span><br></pre></td></tr></table></figure>
<h1 id="Map"><a href="#Map" class="headerlink" title="Map"></a>Map</h1><p>无序</p>
<h2 id="声明-1"><a href="#声明-1" class="headerlink" title="声明"></a>声明</h2><p>声明一个<code>key</code>为<code>string</code>,<code>value</code>为<code>int</code>的空<code>Map</code></p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line">m := <span class="built_in">make</span>(<span class="keyword">map</span>[<span class="type">string</span>]<span class="type">int</span>)</span><br></pre></td></tr></table></figure>
<p>声明的同时对其进行初始化操作:</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line">m2 := <span class="keyword">map</span>[<span class="type">string</span>]<span class="type">int</span>&#123;<span class="string">&quot;one&quot;</span>: <span class="number">1</span>, <span class="string">&quot;two&quot;</span>: <span class="number">2</span>&#125;</span><br></pre></td></tr></table></figure>
<h2 id="访问和操作"><a href="#访问和操作" class="headerlink" title="访问和操作"></a>访问和操作</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line">m[<span class="string">&quot;one&quot;</span>] = <span class="number">1</span></span><br><span class="line">m[<span class="string">&quot;two&quot;</span>] = <span class="number">2</span></span><br><span class="line">fmt.Println(m[<span class="string">&quot;one&quot;</span>]) <span class="comment">// 1</span></span><br><span class="line">fmt.Println(m[<span class="string">&quot;unknow&quot;</span>]) <span class="comment">// 0</span></span><br></pre></td></tr></table></figure>
<p><code>Go</code>在查询<code>key</code>时返回了两个值,一个是<code>value</code>,另一个是<code>bool</code>类型的值代表是否存在</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line">r, ok := m[<span class="string">&quot;unknow&quot;</span>]</span><br><span class="line">fmt.Println(r, ok) <span class="comment">// 0 false</span></span><br></pre></td></tr></table></figure>
<p>可以使用<code>delete</code>函数来删除<code>Map</code>中的键值对</p>
<p><code>Goland</code>中可以通过<code>map.delete</code>快速生成</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="built_in">delete</span>(m, <span class="string">&quot;one&quot;</span>)</span><br></pre></td></tr></table></figure>
<h1 id="range"><a href="#range" class="headerlink" title="range"></a>range</h1><p>有点像<code>JavaScript</code>的解构赋值</p>
<p>数组、切片、<code>Map</code>都可以使用<code>range</code>来遍历,通过<code>range</code>可以得到索引和对应的值</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line">nums := []<span class="type">int</span>&#123;<span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>&#125;</span><br><span class="line">sum := <span class="number">0</span></span><br><span class="line"><span class="comment">// 遍历切片</span></span><br><span class="line"><span class="keyword">for</span> i, num := <span class="keyword">range</span> nums &#123;</span><br><span class="line"> fmt.Println(<span class="string">&quot;index:&quot;</span>, i, <span class="string">&quot;num:&quot;</span>, num)</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 遍历Map</span></span><br><span class="line">m := <span class="keyword">map</span>[<span class="type">string</span>]<span class="type">string</span>&#123;<span class="string">&quot;a&quot;</span>: <span class="string">&quot;A&quot;</span>, <span class="string">&quot;b&quot;</span>: <span class="string">&quot;B&quot;</span>&#125;</span><br><span class="line"><span class="keyword">for</span> k, v := <span class="keyword">range</span> m &#123;</span><br><span class="line"> fmt.Println(k, v) <span class="comment">// b 8; a A</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 只获取key</span></span><br><span class="line"><span class="keyword">for</span> k := <span class="keyword">range</span> m &#123;</span><br><span class="line"> fmt.Println(<span class="string">&quot;key&quot;</span>, k) <span class="comment">// key a; key b</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 只获取value</span></span><br><span class="line"><span class="keyword">for</span> _, v := <span class="keyword">range</span> m &#123;</span><br><span class="line"> fmt.Println(<span class="string">&quot;value&quot;</span>, v) <span class="comment">// value A; value B</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h1 id="函数"><a href="#函数" class="headerlink" title="函数"></a>函数</h1><p><code>Go</code>函数不同于<code>Java</code>,<strong>返回值写在最后面</strong>,而且可以同时<strong>返回多个值</strong></p>
<p>一般来说,第一个是函数的返回值,第二个是错误信息</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">exists</span><span class="params">(m <span class="keyword">map</span>[<span class="type">string</span>]<span class="type">string</span>, k <span class="type">string</span>)</span></span> (v <span class="type">string</span>, ok <span class="type">bool</span>) &#123;</span><br><span class="line"> v, ok = m[k]</span><br><span class="line"> <span class="keyword">return</span> v, ok</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h1 id="指针"><a href="#指针" class="headerlink" title="指针"></a>指针</h1><p><code>Go</code>支持指针,相对于<code>C语言</code>,<code>Go</code>的指针功能十分简单,当然功能也很有限,主要用途就是对函数传入的参数进行修改。</p>
<p>默认情况下,<code>Go</code>函数的参数传参是值传递,也就是拷贝了一个副本,对其修改不会影响原来的值,如果要对原来的值也要变的话,需要使用指针。</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">add2</span><span class="params">(n <span class="type">int</span>)</span></span> &#123;</span><br><span class="line"> n += <span class="number">2</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">add2ptr</span><span class="params">(n *<span class="type">int</span>)</span></span> &#123;</span><br><span class="line"> *n += <span class="number">2</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line"> n := <span class="number">5</span></span><br><span class="line"> add2(n)</span><br><span class="line"> fmt.Println(n) <span class="comment">// 5</span></span><br><span class="line"> add2ptr(&amp;n)</span><br><span class="line"> fmt.Println(n) <span class="comment">// 7</span></span><br><span class="line">&#125;</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>和<code>C</code>语法差不多,相当于<code>Java</code>的对象</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">type</span> user <span class="keyword">struct</span> &#123;</span><br><span class="line"> name <span class="type">string</span></span><br><span class="line"> password <span class="type">string</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>结构体的初始化,可以指定字段初始化,也可以全部初始化。</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line">a := user&#123;name: <span class="string">&quot;wang&quot;</span>, password: <span class="string">&quot;1024&quot;</span>&#125;</span><br><span class="line">b := user&#123;<span class="string">&quot;wang&quot;</span>, <span class="string">&quot;1024&quot;</span>&#125;</span><br><span class="line">c := user&#123;name: <span class="string">&quot;wang&quot;</span>&#125;</span><br></pre></td></tr></table></figure>
<p>对于结构体内部成员的访问可以通过<code>.</code>操作符来进行访问或赋值。</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> d user</span><br><span class="line">d.name = <span class="string">&quot;wang&quot;</span></span><br><span class="line">d.password = <span class="string">&quot;1024&quot;</span></span><br><span class="line">fmt.Println(d.password)</span><br></pre></td></tr></table></figure>
<h2 id="结构体方法"><a href="#结构体方法" class="headerlink" title="结构体方法"></a>结构体方法</h2><p>相当于<code>Java</code>类的成员函数</p>
<p>在一个普通方法的<code>func</code>关键字后面加上对应结构体信息,就代表这个方法是属于结构体的。</p>
<p>为<code>user</code>结构体添加一个检查密码的函数:</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(u user)</span></span> checkPassword(password <span class="type">string</span>) <span class="type">bool</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> u.password == password</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>为<code>user</code>结构体添加一个重置密码的函数,因为要修改结构体内部的值,所以要使用指针类型:</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(u *user)</span></span> resetPassword(password <span class="type">string</span>) &#123;</span><br><span class="line"> u.password = password</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>结构体方法的调用:</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line">a := user&#123;name: <span class="string">&quot;wang&quot;</span>, password: <span class="string">&quot;1024&quot;</span>&#125;</span><br><span class="line">a.resetPassword(<span class="string">&quot;2048&quot;</span>)</span><br><span class="line">fmt.Println(a.checkPassword(<span class="string">&quot;2048&quot;</span>)) <span class="comment">// true</span></span><br></pre></td></tr></table></figure>
<h1 id="错误处理"><a href="#错误处理" class="headerlink" title="错误处理"></a>错误处理</h1><p><code>Go</code>内置的错误接口提供了非常简单的错误处理机制,<code>error</code>类型是一个接口类型,其定义如下:</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">type</span> <span class="type">error</span> <span class="keyword">interface</span> &#123;</span><br><span class="line"> Error() <span class="type">string</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><code>Go</code>函数可以返回多个值,通常函数的最后一个值用来代表错误信息,在函数内部可以使用<code>errors.New </code>可返回一个错误信息:</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">findUser</span><span class="params">(users []user, name <span class="type">string</span>)</span></span> (v *user, err <span class="type">error</span>) &#123;</span><br><span class="line"> <span class="keyword">for</span> _, u := <span class="keyword">range</span> users &#123;</span><br><span class="line"> <span class="keyword">if</span> u.name == name &#123;</span><br><span class="line"> <span class="keyword">return</span> &amp;u, <span class="literal">nil</span></span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">nil</span>, errors.New(<span class="string">&quot;not found&quot;</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>如果函数不出现错误的话,可以直接返回<code>nil</code>。</p>
<p>在调用函数的时候,需要用多个变量来接收函数的返回值,我们可以通过函数的返回值来判断是否发生了异常。</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line">u, err := findUser([]user&#123;&#123;<span class="string">&quot;wang&quot;</span>, <span class="string">&quot;1024&quot;</span>&#125;&#125;, <span class="string">&quot;wang&quot;</span>)</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line"> fmt.Println(err)</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h1 id="标准库"><a href="#标准库" class="headerlink" title="标准库"></a>标准库</h1><p><code>Go</code>内置了非常丰富的标准库工具,包括字符串操作、字符串格式化、<code>json</code>处理、时间处理等</p>
<h2 id="字符串操作"><a href="#字符串操作" class="headerlink" title="字符串操作"></a>字符串操作</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line">a := <span class="string">&quot;hello&quot;</span></span><br><span class="line"><span class="comment">// 是否包含</span></span><br><span class="line">fmt.Println(strings.Contains(a, <span class="string">&quot;ll&quot;</span>)) <span class="comment">// true</span></span><br><span class="line"><span class="comment">// 字符统计</span></span><br><span class="line">fmt.Println(strings.Count(a, <span class="string">&quot;l&quot;</span>)) <span class="comment">// 2</span></span><br><span class="line"><span class="comment">// 判断字符串开头</span></span><br><span class="line">fmt.Println(strings.HasPrefix(a, <span class="string">&quot;he&quot;</span>)) <span class="comment">// true</span></span><br><span class="line"><span class="comment">// 判断字符串结尾</span></span><br><span class="line">fmt.Println(strings.HasSuffix(a, <span class="string">&quot;llo&quot;</span>)) <span class="comment">// true</span></span><br><span class="line"><span class="comment">// 查找字符串</span></span><br><span class="line">fmt.Println(strings.Index(a, <span class="string">&quot;ll&quot;</span>)) <span class="comment">// 2</span></span><br><span class="line"><span class="comment">// 字符串拼接</span></span><br><span class="line">fmt.Println(strings.Join([]<span class="type">string</span>&#123;<span class="string">&quot;he&quot;</span>, <span class="string">&quot;llo&quot;</span>&#125;, <span class="string">&quot;-&quot;</span>)) <span class="comment">// he-llo</span></span><br><span class="line"><span class="comment">// 复制字符串指定次数</span></span><br><span class="line">fmt.Println(strings.Repeat(a, <span class="number">2</span>)) <span class="comment">// hellohello</span></span><br><span class="line"><span class="comment">// 字符串替换</span></span><br><span class="line">fmt.Println(strings.Replace(a, <span class="string">&quot;e&quot;</span>, <span class="string">&quot;E&quot;</span>, <span class="number">-1</span>)) <span class="comment">// hEllo</span></span><br><span class="line"><span class="comment">// 字符串分割</span></span><br><span class="line">fmt.Println(strings.Split(<span class="string">&quot;a-b-c&quot;</span>, <span class="string">&quot;-&quot;</span>)) <span class="comment">// [a b c]</span></span><br><span class="line"><span class="comment">// 转为小写</span></span><br><span class="line">fmt.Println(strings.ToLower(a)) <span class="comment">// hello</span></span><br><span class="line"><span class="comment">// 转为大写</span></span><br><span class="line">fmt.Println(strings.ToUpper(a)) <span class="comment">// HELLO</span></span><br><span class="line"><span class="comment">// 字符串长度</span></span><br><span class="line">fmt.Println(<span class="built_in">len</span>(a)) <span class="comment">// 5</span></span><br></pre></td></tr></table></figure>
<h3 id="字符串格式化"><a href="#字符串格式化" class="headerlink" title="字符串格式化"></a>字符串格式化</h3><p><code>Println</code>最为常用作用为打印并换行,<code>Printf</code>可以按指定格式打印字符串。</p>
<p><code>+v</code>可以打印字段和值详细信息。</p>
<p><code>#v</code>可以打印出整个结构体的构造以及详细信息。</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">go</span>复制代码s := <span class="string">&quot;hello&quot;</span></span><br><span class="line">n := <span class="number">123</span></span><br><span class="line">p := point&#123;<span class="number">1</span>, <span class="number">2</span>&#125;</span><br><span class="line">fmt.Println(s, n) <span class="comment">// hello 123</span></span><br><span class="line">fmt.Println(p) <span class="comment">// &#123;1 2&#125;</span></span><br><span class="line"></span><br><span class="line">fmt.Printf(<span class="string">&quot;s=%v\n&quot;</span>, s) <span class="comment">// s=hello</span></span><br><span class="line">fmt.Printf(<span class="string">&quot;n=%v\n&quot;</span>, n) <span class="comment">// n=123</span></span><br><span class="line">fmt.Printf(<span class="string">&quot;p=%v\n&quot;</span>, p) <span class="comment">// p=&#123;1 2&#125;</span></span><br><span class="line">fmt.Printf(<span class="string">&quot;p=%+v\n&quot;</span>, p) <span class="comment">// p=&#123;x:1 y:2&#125;</span></span><br><span class="line">fmt.Printf(<span class="string">&quot;p=%#v\n&quot;</span>, p) <span class="comment">// p=main.point&#123;x:1, y:2&#125;</span></span><br><span class="line"></span><br><span class="line">f := <span class="number">3.141592653</span></span><br><span class="line">fmt.Println(f) <span class="comment">// 3.141592653</span></span><br><span class="line">fmt.Printf(<span class="string">&quot;%.2f\n&quot;</span>, f) <span class="comment">// 3.14</span></span><br></pre></td></tr></table></figure>
<h3 id="json处理"><a href="#json处理" class="headerlink" title="json处理"></a>json处理</h3><p><code>Go</code>在处理json时十分简单,只需要将结构体中的字段第一个字母变成大写就能用内置的<code>JSON</code>工具进行处理。</p>
<p><code>Go</code>中一些特殊的类型,比如<code>Channel</code>、<code>complex</code>、<code>function</code>是不能被解析成<code>JSON</code>的。</p>
<p><code>Go</code>中<code>JSON</code>对象只支持<code>string</code>作为<code>key</code>,对于<code>map</code>,那么必须是<code>map[string]T</code>这种类型,<code>T</code>可以是<code>Go</code>语言中任意的类型。</p>
<p><code>Go</code>的<code>json</code>处理是通过<code>Marshal</code>、<code>Unmarshal</code>方法来进行处理的。</p>
<p><code>Marshal</code> 用于自定义编码<code>json</code>的方法,也就是将变量、对象转成<code>json</code>,转换之后需要用<code>string</code>方法强转一下,否则会打印出的是16进制字符串。</p>
<p><code>Unmarshal</code>用于自定义解码<code>json</code>方法,也就是将<code>json</code>转为对象。</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">type</span> userInfo <span class="keyword">struct</span> &#123;</span><br><span class="line"> Name <span class="type">string</span></span><br><span class="line"> Age <span class="type">int</span> <span class="string">`json:&quot;age&quot;`</span> <span class="comment">// 自定义json输出的字段</span></span><br><span class="line"> Hobby []<span class="type">string</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line"> a := userInfo&#123;Name: <span class="string">&quot;wang&quot;</span>, Age: <span class="number">18</span>, Hobby: []<span class="type">string</span>&#123;<span class="string">&quot;Golang&quot;</span>, <span class="string">&quot;TypeScript&quot;</span>&#125;&#125;</span><br><span class="line"> buf, err := json.Marshal(a)</span><br><span class="line"> <span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line"> <span class="built_in">panic</span>(err)</span><br><span class="line"> &#125;</span><br><span class="line"> fmt.Println(buf) <span class="comment">// [123 34 78 97...]</span></span><br><span class="line"> fmt.Println(<span class="type">string</span>(buf)) <span class="comment">// &#123;&quot;Name&quot;:&quot;wang&quot;,&quot;age&quot;:18,&quot;Hobby&quot;:[&quot;Golang&quot;,&quot;TypeScript&quot;]&#125;</span></span><br><span class="line"></span><br><span class="line"> buf, err = json.MarshalIndent(a, <span class="string">&quot;&quot;</span>, <span class="string">&quot;\t&quot;</span>)</span><br><span class="line"> <span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line"> <span class="built_in">panic</span>(err)</span><br><span class="line"> &#125;</span><br><span class="line"> fmt.Println(<span class="type">string</span>(buf))</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> b userInfo</span><br><span class="line"> err = json.Unmarshal(buf, &amp;b)</span><br><span class="line"> <span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line"> <span class="built_in">panic</span>(err)</span><br><span class="line"> &#125;</span><br><span class="line"> fmt.Printf(<span class="string">&quot;%#v\n&quot;</span>, b) <span class="comment">// main.userInfo&#123;Name:&quot;wang&quot;, Age:18, Hobby:[]string&#123;&quot;Golang&quot;, &quot;TypeScript&quot;&#125;&#125;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="时间处理"><a href="#时间处理" class="headerlink" title="时间处理"></a>时间处理</h3><p><code>Go</code>提供了很多常用的时间处理函数,例如Now、解析字符串、转字符串、获取时间戳等。在操作时间相关的方法时,需要导入<code>time</code>包。</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 获取当前时间</span></span><br><span class="line">now := time.Now()</span><br><span class="line">fmt.Println(now) <span class="comment">// 2022-03-27 18:04:59.433297 +0800 CST m=+0.000087933</span></span><br><span class="line">t := time.Date(<span class="number">2022</span>, <span class="number">3</span>, <span class="number">27</span>, <span class="number">1</span>, <span class="number">25</span>, <span class="number">36</span>, <span class="number">0</span>, time.UTC)</span><br><span class="line">t2 := time.Date(<span class="number">2022</span>, <span class="number">3</span>, <span class="number">27</span>, <span class="number">2</span>, <span class="number">30</span>, <span class="number">36</span>, <span class="number">0</span>, time.UTC)</span><br><span class="line">fmt.Println(t) <span class="comment">// 2022-03-27 01:25:36 +0000 UTC</span></span><br><span class="line"><span class="comment">// 获取时间的年月日</span></span><br><span class="line">fmt.Println(t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute()) <span class="comment">// 2022 March 27 1 25</span></span><br><span class="line"><span class="comment">// 时间转字符串</span></span><br><span class="line">fmt.Println(t.Format(<span class="string">&quot;2006-01-02 15:04:05&quot;</span>)) <span class="comment">// 2022-03-27 01:25:36</span></span><br><span class="line"><span class="comment">// 获取时间差</span></span><br><span class="line">diff := t2.Sub(t)</span><br><span class="line">fmt.Println(diff) <span class="comment">// 1h5m0s</span></span><br><span class="line">fmt.Println(diff.Minutes(), diff.Seconds()) <span class="comment">// 65 3900</span></span><br><span class="line"><span class="comment">// 解析字符串</span></span><br><span class="line">t3, err := time.Parse(<span class="string">&quot;2006-01-02 15:04:05&quot;</span>, <span class="string">&quot;2022-03-27 01:25:36&quot;</span>)</span><br><span class="line"><span class="keyword">if</span> err != <span class="literal">nil</span> &#123;</span><br><span class="line"> <span class="built_in">panic</span>(err)</span><br><span class="line">&#125;</span><br><span class="line">fmt.Println(t3 == t) <span class="comment">// true</span></span><br><span class="line"><span class="comment">// 获取时间戳</span></span><br><span class="line">fmt.Println(now.Unix()) <span class="comment">// 1648738080</span></span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category>以Java视角学Go</category>
</categories>
<tags>
<tag>Go</tag>
<tag>字节跳动青训营</tag>
</tags>
</entry>
<entry>
<title>Golang 备忘清单</title>
<url>/2023/07/13/go-guide/</url>
<content><![CDATA[<blockquote>
<p>本文为转载</p>
<p>原出处:<a href="http://bbs.laoleng.vip/reference/docs/golang.html">http://bbs.laoleng.vip/reference/docs/golang.html</a></p>
<p>仓库地址:<a href="https://github.com/jaywcjlove/reference">https://github.com/jaywcjlove/reference</a></p>
</blockquote>
<p>该备忘单提供了帮助您使用 <a href="https://golang.org/">Golang</a> 的基本语法和方法。</p>
<h1 id="入门"><a href="#入门" class="headerlink" title="入门"></a>入门</h1><h2 id="hello-go"><a href="#hello-go" class="headerlink" title="hello.go"></a>hello.go</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"><span class="keyword">import</span> <span class="string">&quot;fmt&quot;</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line"> fmt.Println(<span class="string">&quot;Hello, world!&quot;</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>直接运行</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line"><span class="meta prompt_">$ </span><span class="language-bash">go run hello.go</span></span><br><span class="line">Hello, world!</span><br></pre></td></tr></table></figure>
<p>或者在 <a href="https://repl.it/languages/go">Go repl</a> 中尝试一,<code>go</code> 命令<a href="#go-%E5%91%BD%E4%BB%A4">参考</a></p>
<h2 id="变量"><a href="#变量" class="headerlink" title="变量"></a>变量</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> s1 <span class="type">string</span></span><br><span class="line">s1 = <span class="string">&quot;Learn Go!&quot;</span></span><br><span class="line"><span class="comment">// 一次声明多个变量</span></span><br><span class="line"><span class="keyword">var</span> b, c <span class="type">int</span> = <span class="number">1</span>, <span class="number">2</span></span><br><span class="line"><span class="keyword">var</span> d = <span class="literal">true</span></span><br><span class="line"><span class="comment">// 匿名赋值</span></span><br><span class="line">_ , e = <span class="number">10</span>, <span class="number">20</span> </span><br></pre></td></tr></table></figure>
<p>简短声明</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line">s1 := <span class="string">&quot;Learn Go!&quot;</span> <span class="comment">// string</span></span><br><span class="line">b, c := <span class="number">1</span>, <span class="number">2</span> <span class="comment">// int</span></span><br><span class="line">d := <span class="literal">true</span> <span class="comment">// bool</span></span><br></pre></td></tr></table></figure>
<p>参见:<a href="#golang-%E5%9F%BA%E6%9C%AC%E7%B1%BB%E5%9E%8B">基本类型</a></p>
<h2 id="函数"><a href="#函数" class="headerlink" title="函数"></a>函数</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"><span class="keyword">import</span> <span class="string">&quot;fmt&quot;</span></span><br><span class="line"><span class="comment">// 程序的入口点</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line"> fmt.Println(<span class="string">&quot;Hello world!&quot;</span>)</span><br><span class="line"> say(<span class="string">&quot;Hello Go!&quot;</span>)</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">say</span><span class="params">(message <span class="type">string</span>)</span></span> &#123;</span><br><span class="line"> fmt.Println(<span class="string">&quot;You said: &quot;</span>, message)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>参见:<a href="#golang-%E5%87%BD%E6%95%B0">函数(Functions)</a></p>
<h2 id="注释"><a href="#注释" class="headerlink" title="注释"></a>注释</h2><figure class="highlight go"><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></pre></td></tr></table></figure>
<h2 id="if-语句"><a href="#if-语句" class="headerlink" title="if 语句"></a>if 语句</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">if</span> <span class="literal">true</span> &#123;</span><br><span class="line"> fmt.Println(<span class="string">&quot;Yes!&quot;</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>参见:<a href="#golang-%E6%9D%A1%E4%BB%B6%E6%8E%A7%E5%88%B6">条件控制</a></p>
<h1 id="Golang-基本类型"><a href="#Golang-基本类型" class="headerlink" title="Golang 基本类型"></a>Golang 基本类型</h1><h2 id="字符串-Strings"><a href="#字符串-Strings" class="headerlink" title="字符串 Strings"></a>字符串 Strings</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line">s1 := <span class="string">&quot;Hello&quot;</span> + <span class="string">&quot;World&quot;</span></span><br><span class="line">s2 := <span class="string">`A &quot;raw&quot; string literal</span></span><br><span class="line"><span class="string">can include line breaks.`</span></span><br><span class="line"><span class="comment">// 输出:10</span></span><br><span class="line">fmt.Println(<span class="built_in">len</span>(s1))</span><br><span class="line"><span class="comment">// 输出:Hello</span></span><br><span class="line">fmt.Println(<span class="type">string</span>(s1[<span class="number">0</span>:<span class="number">5</span>]))</span><br></pre></td></tr></table></figure>
<p>字符串的类型为 <code>字符串</code></p>
<h2 id="数字-Numbers"><a href="#数字-Numbers" class="headerlink" title="数字 Numbers"></a>数字 Numbers</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line">num := <span class="number">3</span> <span class="comment">// int</span></span><br><span class="line">num := <span class="number">3.</span> <span class="comment">// float64</span></span><br><span class="line">num := <span class="number">3</span> + <span class="number">4i</span> <span class="comment">// complex128</span></span><br><span class="line">num := <span class="type">byte</span>(<span class="string">&#x27;a&#x27;</span>) <span class="comment">// byte (alias: uint8)</span></span><br><span class="line"><span class="keyword">var</span> u <span class="type">uint</span> = <span class="number">7</span> <span class="comment">// uint (unsigned)</span></span><br><span class="line"><span class="keyword">var</span> p <span class="type">float32</span> = <span class="number">22.7</span> <span class="comment">// 32-bit float</span></span><br></pre></td></tr></table></figure>
<h3 id="操作符-Operators"><a href="#操作符-Operators" class="headerlink" title="操作符 Operators"></a>操作符 Operators</h3><figure class="highlight go"><table><tr><td class="code"><pre><span class="line">x := <span class="number">5</span></span><br><span class="line">x++</span><br><span class="line">fmt.Println(<span class="string">&quot;x + 4 =&quot;</span>, x + <span class="number">4</span>)</span><br><span class="line">fmt.Println(<span class="string">&quot;x * 4 =&quot;</span>, x * <span class="number">4</span>) </span><br></pre></td></tr></table></figure>
<p>参见:<a href="#%E8%BF%90%E7%AE%97%E7%AC%A6%E5%92%8C%E6%A0%87%E7%82%B9%E7%AC%A6%E5%8F%B7">更多操作符</a></p>
<h2 id="布尔值-Booleans"><a href="#布尔值-Booleans" class="headerlink" title="布尔值 Booleans"></a>布尔值 Booleans</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line">isTrue := <span class="literal">true</span></span><br><span class="line">isFalse := <span class="literal">false</span></span><br></pre></td></tr></table></figure>
<h3 id="操作符"><a href="#操作符" class="headerlink" title="操作符"></a>操作符</h3><figure class="highlight go"><table><tr><td class="code"><pre><span class="line">fmt.Println(<span class="literal">true</span> &amp;&amp; <span class="literal">true</span>) <span class="comment">// true </span></span><br><span class="line">fmt.Println(<span class="literal">true</span> &amp;&amp; <span class="literal">false</span>) <span class="comment">// false</span></span><br><span class="line">fmt.Println(<span class="literal">true</span> || <span class="literal">true</span>) <span class="comment">// true</span></span><br><span class="line">fmt.Println(<span class="literal">true</span> || <span class="literal">false</span>) <span class="comment">// true</span></span><br><span class="line">fmt.Println(!<span class="literal">true</span>) <span class="comment">// false</span></span><br></pre></td></tr></table></figure>
<p>参见:<a href="#%E8%BF%90%E7%AE%97%E7%AC%A6%E5%92%8C%E6%A0%87%E7%82%B9%E7%AC%A6%E5%8F%B7">更多操作符</a></p>
<h2 id="数组-Arrays"><a href="#数组-Arrays" class="headerlink" title="数组 Arrays"></a>数组 Arrays</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line">┌────┬────┬────┬────┬─────┬─────┐</span><br><span class="line">| <span class="number">2</span> | <span class="number">3</span> | <span class="number">5</span> | <span class="number">7</span> | <span class="number">11</span> | <span class="number">13</span> |</span><br><span class="line">└────┴────┴────┴────┴─────┴─────┘</span><br><span class="line"> <span class="number">0</span> <span class="number">1</span> <span class="number">2</span> <span class="number">3</span> <span class="number">4</span> <span class="number">5</span></span><br></pre></td></tr></table></figure>
<hr>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line">primes := [...]<span class="type">int</span>&#123;<span class="number">2</span>, <span class="number">3</span>, <span class="number">5</span>, <span class="number">7</span>, <span class="number">11</span>, <span class="number">13</span>&#125;</span><br><span class="line">fmt.Println(<span class="built_in">len</span>(primes)) <span class="comment">// =&gt; 6</span></span><br><span class="line"><span class="comment">// 输出:[2 3 5 7 11 13]</span></span><br><span class="line">fmt.Println(primes)</span><br><span class="line"><span class="comment">// 与 [:3] 相同,输出:[2 3 5]</span></span><br><span class="line">fmt.Println(primes[<span class="number">0</span>:<span class="number">3</span>])</span><br></pre></td></tr></table></figure>
<hr>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> a [<span class="number">2</span>]<span class="type">string</span></span><br><span class="line">a[<span class="number">0</span>] = <span class="string">&quot;Hello&quot;</span></span><br><span class="line">a[<span class="number">1</span>] = <span class="string">&quot;World&quot;</span></span><br><span class="line">fmt.Println(a[<span class="number">0</span>], a[<span class="number">1</span>]) <span class="comment">//=&gt; Hello World</span></span><br><span class="line">fmt.Println(a) <span class="comment">// =&gt; [Hello World]</span></span><br></pre></td></tr></table></figure>
<h3 id="2d-array"><a href="#2d-array" class="headerlink" title="2d array"></a>2d array</h3><figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> twoDimension [<span class="number">2</span>][<span class="number">3</span>]<span class="type">int</span></span><br><span class="line"><span class="keyword">for</span> i := <span class="number">0</span>; i &lt; <span class="number">2</span>; i++ &#123;</span><br><span class="line"> <span class="keyword">for</span> j := <span class="number">0</span>; j &lt; <span class="number">3</span>; j++ &#123;</span><br><span class="line"> twoDimension[i][j] = i + j</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// =&gt; 2d: [[0 1 2] [1 2 3]]</span></span><br><span class="line">fmt.Println(<span class="string">&quot;2d: &quot;</span>, twoDimension)</span><br></pre></td></tr></table></figure>
<h2 id="指针-Pointers"><a href="#指针-Pointers" class="headerlink" title="指针(Pointers)"></a>指针(Pointers)</h2><!--rehype:wrap-class=col-span-2-->
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span> <span class="params">()</span></span> &#123;</span><br><span class="line"> b := *getPointer()</span><br><span class="line"> fmt.Println(<span class="string">&quot;Value is&quot;</span>, b)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">getPointer</span> <span class="params">()</span></span> (myPointer *<span class="type">int</span>) &#123;</span><br><span class="line"> a := <span class="number">234</span></span><br><span class="line"> <span class="keyword">return</span> &amp;a</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//申明指针的时候,如果没有指向某个变量,默认值为nil</span></span><br><span class="line"><span class="comment">//不能直接进行操作,包括读写</span></span><br><span class="line"><span class="keyword">var</span> p *<span class="type">int</span></span><br><span class="line">*p = <span class="number">123</span> <span class="comment">// panic nil pointer</span></span><br></pre></td></tr></table></figure>
<hr>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="comment">//而用new返回的是有默认值的指针, 为数据类型的默认值</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span>&#123;</span><br><span class="line"> <span class="comment">//有一块内存存放了10,它的地址由系统自动分配,别名是a</span></span><br><span class="line"> a := <span class="number">10</span></span><br><span class="line"> <span class="comment">//内存存放的10变成了20</span></span><br><span class="line"> a = <span class="number">20</span></span><br><span class="line"> <span class="keyword">var</span> p *<span class="type">int</span></span><br><span class="line"> p = &amp;a <span class="comment">//或者直接写 p := &amp;a</span></span><br><span class="line"> <span class="comment">//上面的p是一个指针,通过 *p 的方式同样可以访问 变量a指向 的内存</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">/*当你动态申请内存的时候,指针的存在意义之一就被体现出来了*/</span> </span><br><span class="line"> ptr := <span class="built_in">new</span>(<span class="type">int</span>) </span><br><span class="line"> <span class="comment">//申请了一块内存空间,没有办法指定别名,new()返回内存地址,用指针接收</span></span><br><span class="line"> <span class="comment">//此时并没有变量能直接指向这块内存,所以只能通过内存地址来访问</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>参见:<a href="https://tour.golang.org/moretypes/1">指针(Pointers)</a></p>
<h2 id="切片-Slices"><a href="#切片-Slices" class="headerlink" title="切片(Slices)"></a>切片(Slices)</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line">s := <span class="built_in">make</span>([]<span class="type">string</span>, <span class="number">3</span>)</span><br><span class="line">s[<span class="number">0</span>] = <span class="string">&quot;a&quot;</span></span><br><span class="line">s[<span class="number">1</span>] = <span class="string">&quot;b&quot;</span></span><br><span class="line">s = <span class="built_in">append</span>(s, <span class="string">&quot;d&quot;</span>)</span><br><span class="line">s = <span class="built_in">append</span>(s, <span class="string">&quot;e&quot;</span>, <span class="string">&quot;f&quot;</span>)</span><br><span class="line">fmt.Println(s)</span><br><span class="line">fmt.Println(s[<span class="number">1</span>])</span><br><span class="line">fmt.Println(<span class="built_in">len</span>(s))</span><br><span class="line">fmt.Println(s[<span class="number">1</span>:<span class="number">3</span>])</span><br><span class="line">slice := []<span class="type">int</span>&#123;<span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>&#125;</span><br></pre></td></tr></table></figure>
<p>另见:<a href="https://gobyexample.com/slices">切片示例</a></p>
<h2 id="常量-Constants"><a href="#常量-Constants" class="headerlink" title="常量(Constants)"></a>常量(Constants)</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> s <span class="type">string</span> = <span class="string">&quot;constant&quot;</span></span><br><span class="line"><span class="keyword">const</span> Phi = <span class="number">1.618</span></span><br><span class="line"><span class="keyword">const</span> n = <span class="number">500000000</span></span><br><span class="line"><span class="keyword">const</span> d = <span class="number">3e20</span> / n</span><br></pre></td></tr></table></figure>
<p>常量声明可以使用 iota常量生成器 初始化,它用于<br>生成一组以相似规则初始化的常量,但是不用每行都<br>写一遍初始化表达式。<br>注意:</p>
<ol>
<li>在一个const声明语句中,在第一个声明的常量所在的行,iota被置为0,然后在每一个有常量声明的行加一。</li>
<li>写在同一行的值是相同的</li>
</ol>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> (</span><br><span class="line"> a = <span class="literal">iota</span></span><br><span class="line"> b</span><br><span class="line"> c</span><br><span class="line">)</span><br><span class="line"><span class="comment">// a = 0, b = 1, c = 2</span></span><br></pre></td></tr></table></figure>
<h2 id="类型转换"><a href="#类型转换" class="headerlink" title="类型转换"></a>类型转换</h2><p>Go语言中不允许隐式转换,所有类型转换必须显式声明(强制转换),而且转换只能发生在两种相互兼容的类型之间。</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line">i := <span class="number">90</span></span><br><span class="line">f := <span class="type">float64</span>(i)</span><br><span class="line">u := <span class="type">uint</span>(i)</span><br><span class="line"><span class="comment">// 将等于字符Z</span></span><br><span class="line">s := <span class="type">string</span>(i)</span><br></pre></td></tr></table></figure>
<h3 id="如何获取int字符串?"><a href="#如何获取int字符串?" class="headerlink" title="如何获取int字符串?"></a>如何获取int字符串?</h3><figure class="highlight go"><table><tr><td class="code"><pre><span class="line">i := <span class="number">90</span></span><br><span class="line"><span class="comment">// 需要导入“strconv”</span></span><br><span class="line">s := strconv.Itoa(i)</span><br><span class="line">fmt.Println(s) <span class="comment">// Outputs: 90</span></span><br></pre></td></tr></table></figure>
<h1 id="Golang-字符串"><a href="#Golang-字符串" class="headerlink" title="Golang 字符串"></a>Golang 字符串</h1><h2 id="字符串函数"><a href="#字符串函数" class="headerlink" title="字符串函数"></a>字符串函数</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">&quot;fmt&quot;</span></span><br><span class="line"> s <span class="string">&quot;strings&quot;</span></span><br><span class="line">)</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line"> <span class="comment">/* 需要将字符串导入为 s */</span></span><br><span class="line"> fmt.Println(s.Contains(<span class="string">&quot;test&quot;</span>, <span class="string">&quot;e&quot;</span>))</span><br><span class="line"> <span class="comment">/* 内置 */</span></span><br><span class="line"> fmt.Println(<span class="built_in">len</span>(<span class="string">&quot;hello&quot;</span>)) <span class="comment">// =&gt; 5</span></span><br><span class="line"> <span class="comment">// 输出: 101</span></span><br><span class="line"> fmt.Println(<span class="string">&quot;hello&quot;</span>[<span class="number">1</span>])</span><br><span class="line"> <span class="comment">// 输出: e</span></span><br><span class="line"> fmt.Println(<span class="type">string</span>(<span class="string">&quot;hello&quot;</span>[<span class="number">1</span>]))</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="fmt-Printf"><a href="#fmt-Printf" class="headerlink" title="fmt.Printf"></a>fmt.Printf</h2><!--rehype:wrap-class=row-span-2 col-span-2-->
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">&quot;fmt&quot;</span></span><br><span class="line"> <span class="string">&quot;os&quot;</span></span><br><span class="line">)</span><br><span class="line"><span class="keyword">type</span> point <span class="keyword">struct</span> &#123;</span><br><span class="line"> x, y <span class="type">int</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line"> p := point&#123;<span class="number">1</span>, <span class="number">2</span>&#125;</span><br><span class="line"> fmt.Printf(<span class="string">&quot;%v\n&quot;</span>, p) <span class="comment">// =&gt; &#123;1 2&#125;</span></span><br><span class="line"> fmt.Printf(<span class="string">&quot;%+v\n&quot;</span>, p) <span class="comment">// =&gt; &#123;x:1 y:2&#125;</span></span><br><span class="line"> fmt.Printf(<span class="string">&quot;%#v\n&quot;</span>, p) <span class="comment">// =&gt; main.point&#123;x:1, y:2&#125;</span></span><br><span class="line"> fmt.Printf(<span class="string">&quot;%T\n&quot;</span>, p) <span class="comment">// =&gt; main.point</span></span><br><span class="line"> fmt.Printf(<span class="string">&quot;%t\n&quot;</span>, <span class="literal">true</span>) <span class="comment">// =&gt; TRUE</span></span><br><span class="line"> fmt.Printf(<span class="string">&quot;%d\n&quot;</span>, <span class="number">123</span>) <span class="comment">// =&gt; 123</span></span><br><span class="line"> fmt.Printf(<span class="string">&quot;%b\n&quot;</span>, <span class="number">14</span>) <span class="comment">// =&gt; 1110</span></span><br><span class="line"> fmt.Printf(<span class="string">&quot;%c\n&quot;</span>, <span class="number">33</span>) <span class="comment">// =&gt; !</span></span><br><span class="line"> fmt.Printf(<span class="string">&quot;%x\n&quot;</span>, <span class="number">456</span>) <span class="comment">// =&gt; 1c8</span></span><br><span class="line"> fmt.Printf(<span class="string">&quot;%f\n&quot;</span>, <span class="number">78.9</span>) <span class="comment">// =&gt; 78.9</span></span><br><span class="line"> fmt.Printf(<span class="string">&quot;%e\n&quot;</span>, <span class="number">123400000.0</span>) <span class="comment">// =&gt; 1.23E+08</span></span><br><span class="line"> fmt.Printf(<span class="string">&quot;%E\n&quot;</span>, <span class="number">123400000.0</span>) <span class="comment">// =&gt; 1.23E+08</span></span><br><span class="line"> fmt.Printf(<span class="string">&quot;%s\n&quot;</span>, <span class="string">&quot;\&quot;string\&quot;&quot;</span>) <span class="comment">// =&gt; &quot;string&quot;</span></span><br><span class="line"> fmt.Printf(<span class="string">&quot;%q\n&quot;</span>, <span class="string">&quot;\&quot;string\&quot;&quot;</span>) <span class="comment">// =&gt; &quot;\&quot;string\&quot;&quot;</span></span><br><span class="line"> fmt.Printf(<span class="string">&quot;%x\n&quot;</span>, <span class="string">&quot;hex this&quot;</span>) <span class="comment">// =&gt; 6.86578E+15</span></span><br><span class="line"> fmt.Printf(<span class="string">&quot;%p\n&quot;</span>, &amp;p) <span class="comment">// =&gt; 0xc00002c040</span></span><br><span class="line"> fmt.Printf(<span class="string">&quot;|%6d|%6d|\n&quot;</span>, <span class="number">12</span>, <span class="number">345</span>) <span class="comment">// =&gt; | 12| 345|</span></span><br><span class="line"> fmt.Printf(<span class="string">&quot;|%6.2f|%6.2f|\n&quot;</span>, <span class="number">1.2</span>, <span class="number">3.45</span>) <span class="comment">// =&gt; | 1.20| 3.45|</span></span><br><span class="line"> fmt.Printf(<span class="string">&quot;|%-6.2f|%-6.2f|\n&quot;</span>, <span class="number">1.2</span>, <span class="number">3.45</span>) <span class="comment">// =&gt; |1.20 |3.45 |</span></span><br><span class="line"> fmt.Printf(<span class="string">&quot;|%6s|%6s|\n&quot;</span>, <span class="string">&quot;foo&quot;</span>, <span class="string">&quot;b&quot;</span>) <span class="comment">// =&gt; | foo| b|</span></span><br><span class="line"> fmt.Printf(<span class="string">&quot;|%-6s|%-6s|\n&quot;</span>, <span class="string">&quot;foo&quot;</span>, <span class="string">&quot;b&quot;</span>) <span class="comment">// =&gt; |foo |b |</span></span><br><span class="line"> s := fmt.Sprintf(<span class="string">&quot;a %s&quot;</span>, <span class="string">&quot;string&quot;</span>)</span><br><span class="line"> fmt.Println(s)</span><br><span class="line"> fmt.Fprintf(os.Stderr, <span class="string">&quot;an %s\n&quot;</span>, <span class="string">&quot;error&quot;</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>另见:<a href="https://golang.org/pkg/fmt/">fmt</a></p>
<h2 id="函数实例"><a href="#函数实例" class="headerlink" title="函数实例"></a>函数实例</h2><table>
<thead>
<tr>
<th>实例</th>
<th>Result</th>
</tr>
</thead>
<tbody><tr>
<td>Contains(“test”, “es”)</td>
<td>true</td>
</tr>
<tr>
<td>Count(“test”, “t”)</td>
<td>2</td>
</tr>
<tr>
<td>HasPrefix(“test”, “te”)</td>
<td>true</td>
</tr>
<tr>
<td>HasSuffix(“test”, “st”)</td>
<td>true</td>
</tr>
<tr>
<td>Index(“test”, “e”)</td>
<td>1</td>
</tr>
<tr>
<td>Join([]string{“a”, “b”}, “-“)</td>
<td>a-b</td>
</tr>
<tr>
<td>Repeat(“a”, 5)</td>
<td>aaaaa</td>
</tr>
<tr>
<td>Replace(“foo”, “o”, “0”, -1)</td>
<td>f00</td>
</tr>
<tr>
<td>Replace(“foo”, “o”, “0”, 1)</td>
<td>f0o</td>
</tr>
<tr>
<td>Split(“a-b-c-d-e”, “-“)</td>
<td>[a b c d e]</td>
</tr>
<tr>
<td>ToLower(“TEST”)</td>
<td>test</td>
</tr>
<tr>
<td>ToUpper(“test”)</td>
<td>TEST</td>
</tr>
</tbody></table>
<h1 id="Golang-条件控制"><a href="#Golang-条件控制" class="headerlink" title="Golang 条件控制"></a>Golang 条件控制</h1><h2 id="有条件的"><a href="#有条件的" class="headerlink" title="有条件的"></a>有条件的</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line">a := <span class="number">10</span></span><br><span class="line"><span class="keyword">if</span> a &gt; <span class="number">20</span> &#123;</span><br><span class="line"> fmt.Println(<span class="string">&quot;&gt;&quot;</span>)</span><br><span class="line">&#125; <span class="keyword">else</span> <span class="keyword">if</span> a &lt; <span class="number">20</span> &#123;</span><br><span class="line"> fmt.Println(<span class="string">&quot;&lt;&quot;</span>)</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> fmt.Println(<span class="string">&quot;=&quot;</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="if-中的语句"><a href="#if-中的语句" class="headerlink" title="if 中的语句"></a>if 中的语句</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line">x := <span class="string">&quot;hello go!&quot;</span></span><br><span class="line"><span class="keyword">if</span> count := <span class="built_in">len</span>(x); count &gt; <span class="number">0</span> &#123;</span><br><span class="line"> fmt.Println(<span class="string">&quot;Yes&quot;</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<hr>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">if</span> _, err := doThing(); err != <span class="literal">nil</span> &#123;</span><br><span class="line"> fmt.Println(<span class="string">&quot;Uh oh&quot;</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="Switch"><a href="#Switch" class="headerlink" title="Switch"></a>Switch</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line">x := <span class="number">42.0</span></span><br><span class="line"><span class="keyword">switch</span> x &#123;</span><br><span class="line"> <span class="keyword">case</span> <span class="number">0</span>:</span><br><span class="line"> <span class="keyword">case</span> <span class="number">1</span>, <span class="number">2</span>:</span><br><span class="line"> fmt.Println(<span class="string">&quot;Multiple matches&quot;</span>)</span><br><span class="line"> <span class="keyword">case</span> <span class="number">42</span>: <span class="comment">// Don&#x27;t &quot;fall through&quot;.</span></span><br><span class="line"> fmt.Println(<span class="string">&quot;reached&quot;</span>)</span><br><span class="line"> <span class="keyword">case</span> <span class="number">43</span>:</span><br><span class="line"> fmt.Println(<span class="string">&quot;Unreached&quot;</span>)</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> fmt.Println(<span class="string">&quot;Optional&quot;</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>参见:<a href="https://github.com/golang/go/wiki/Switch">Switch</a></p>
<h2 id="For-loop"><a href="#For-loop" class="headerlink" title="For loop"></a>For loop</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">for</span> i := <span class="number">0</span>; i &lt;= <span class="number">10</span>; i++ &#123;</span><br><span class="line"> fmt.Println(<span class="string">&quot;i: &quot;</span>, i)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="对于-Range-循环"><a href="#对于-Range-循环" class="headerlink" title="对于 Range 循环"></a>对于 Range 循环</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line">nums := []<span class="type">int</span>&#123;<span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>&#125;</span><br><span class="line">sum := <span class="number">0</span></span><br><span class="line"><span class="keyword">for</span> _, num := <span class="keyword">range</span> nums &#123;</span><br><span class="line"> sum += num</span><br><span class="line">&#125;</span><br><span class="line">fmt.Println(<span class="string">&quot;sum:&quot;</span>, sum)</span><br></pre></td></tr></table></figure>
<h2 id="For-循环"><a href="#For-循环" class="headerlink" title="For 循环"></a>For 循环</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line">i := <span class="number">1</span></span><br><span class="line"><span class="keyword">for</span> i &lt;= <span class="number">3</span> &#123;</span><br><span class="line"> fmt.Println(i)</span><br><span class="line"> i++</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="Continue-关键字"><a href="#Continue-关键字" class="headerlink" title="Continue 关键字"></a>Continue 关键字</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">for</span> i := <span class="number">0</span>; i &lt;= <span class="number">5</span>; i++ &#123;</span><br><span class="line"> <span class="keyword">if</span> i % <span class="number">2</span> == <span class="number">0</span> &#123;</span><br><span class="line"> <span class="keyword">continue</span></span><br><span class="line"> &#125;</span><br><span class="line"> fmt.Println(i)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="Break-关键字"><a href="#Break-关键字" class="headerlink" title="Break 关键字"></a>Break 关键字</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">for</span> &#123;</span><br><span class="line"> fmt.Println(<span class="string">&quot;loop&quot;</span>)</span><br><span class="line"> <span class="keyword">break</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h1 id="Golang-结构和Maps"><a href="#Golang-结构和Maps" class="headerlink" title="Golang 结构和Maps"></a>Golang 结构和Maps</h1><h2 id="定义"><a href="#定义" class="headerlink" title="定义"></a>定义</h2><!--rehype:wrap-class=row-span-2-->
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">&quot;fmt&quot;</span></span><br><span class="line">)</span><br><span class="line"><span class="keyword">type</span> Vertex <span class="keyword">struct</span> &#123;</span><br><span class="line"> X <span class="type">int</span></span><br><span class="line"> Y <span class="type">int</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line"> v := Vertex&#123;<span class="number">1</span>, <span class="number">2</span>&#125;</span><br><span class="line"> v.X = <span class="number">4</span></span><br><span class="line"> fmt.Println(v.X, v.Y) <span class="comment">// =&gt; 4 2</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>参见:<a href="https://tour.golang.org/moretypes/2">结构(Structs)</a></p>
<h2 id="字面量"><a href="#字面量" class="headerlink" title="字面量"></a>字面量</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line">v := Vertex&#123;X: <span class="number">1</span>, Y: <span class="number">2</span>&#125;</span><br><span class="line"><span class="comment">// Field names can be omitted</span></span><br><span class="line">v := Vertex&#123;<span class="number">1</span>, <span class="number">2</span>&#125;</span><br><span class="line"><span class="comment">// Y is implicit</span></span><br><span class="line">v := Vertex&#123;X: <span class="number">1</span>&#125;</span><br></pre></td></tr></table></figure>
<p>您还可以输入字段名</p>
<h2 id="Maps"><a href="#Maps" class="headerlink" title="Maps"></a>Maps</h2><!--rehype:wrap-class=row-span-2-->
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line">m := <span class="built_in">make</span>(<span class="keyword">map</span>[<span class="type">string</span>]<span class="type">int</span>)</span><br><span class="line">m[<span class="string">&quot;k1&quot;</span>] = <span class="number">7</span></span><br><span class="line">m[<span class="string">&quot;k2&quot;</span>] = <span class="number">13</span></span><br><span class="line">fmt.Println(m) <span class="comment">// =&gt; map[k1:7 k2:13]</span></span><br><span class="line">v1 := m[<span class="string">&quot;k1&quot;</span>]</span><br><span class="line">fmt.Println(v1) <span class="comment">// =&gt; 7</span></span><br><span class="line">fmt.Println(<span class="built_in">len</span>(m)) <span class="comment">// =&gt; 2</span></span><br><span class="line"><span class="built_in">delete</span>(m, <span class="string">&quot;k2&quot;</span>)</span><br><span class="line">fmt.Println(m) <span class="comment">// =&gt; map[k1:7]</span></span><br><span class="line">_, prs := m[<span class="string">&quot;k2&quot;</span>]</span><br><span class="line">fmt.Println(prs) <span class="comment">// =&gt; false</span></span><br><span class="line">n := <span class="keyword">map</span>[<span class="type">string</span>]<span class="type">int</span>&#123;<span class="string">&quot;foo&quot;</span>: <span class="number">1</span>, <span class="string">&quot;bar&quot;</span>: <span class="number">2</span>&#125;</span><br><span class="line">fmt.Println(n) <span class="comment">// =&gt; map[bar:2 foo:1]</span></span><br></pre></td></tr></table></figure>
<h2 id="指向结构的指针"><a href="#指向结构的指针" class="headerlink" title="指向结构的指针"></a>指向结构的指针</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line">v := &amp;Vertex&#123;<span class="number">1</span>, <span class="number">2</span>&#125;</span><br><span class="line">v.X = <span class="number">2</span></span><br></pre></td></tr></table></figure>
<p>Doing <code>v.X</code> is the same as doing <code>(*v).X</code>, when <code>v</code> is a pointer.</p>
<h1 id="Golang-函数"><a href="#Golang-函数" class="headerlink" title="Golang 函数"></a>Golang 函数</h1><h2 id="多个参数"><a href="#多个参数" class="headerlink" title="多个参数"></a>多个参数</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">plus</span><span class="params">(a <span class="type">int</span>, b <span class="type">int</span>)</span></span> <span class="type">int</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> a + b</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">plusPlus</span><span class="params">(a, b, c <span class="type">int</span>)</span></span> <span class="type">int</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> a + b + c</span><br><span class="line">&#125;</span><br><span class="line">fmt.Println(plus(<span class="number">1</span>, <span class="number">2</span>))</span><br><span class="line">fmt.Println(plusPlus(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>))</span><br></pre></td></tr></table></figure>
<h2 id="多返回值"><a href="#多返回值" class="headerlink" title="多返回值"></a>多返回值</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">vals</span><span class="params">()</span></span> (<span class="type">int</span>, <span class="type">int</span>) &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">3</span>, <span class="number">7</span></span><br><span class="line">&#125;</span><br><span class="line">a, b := vals()</span><br><span class="line">fmt.Println(a) <span class="comment">// =&gt; 3</span></span><br><span class="line">fmt.Println(b) <span class="comment">// =&gt; 7</span></span><br></pre></td></tr></table></figure>
<h2 id="匿名函数"><a href="#匿名函数" class="headerlink" title="匿名函数"></a>匿名函数</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line">r1, r2 := <span class="function"><span class="keyword">func</span><span class="params">()</span></span> (<span class="type">string</span>, <span class="type">string</span>) &#123;</span><br><span class="line"> x := []<span class="type">string</span>&#123;<span class="string">&quot;hello&quot;</span>, <span class="string">&quot;world&quot;</span>&#125;</span><br><span class="line"> <span class="keyword">return</span> x[<span class="number">0</span>], x[<span class="number">1</span>]</span><br><span class="line">&#125;()</span><br><span class="line"><span class="comment">// =&gt; hello world</span></span><br><span class="line">fmt.Println(r1, r2)</span><br></pre></td></tr></table></figure>
<h2 id="命名返回值"><a href="#命名返回值" class="headerlink" title="命名返回值"></a>命名返回值</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">split</span><span class="params">(sum <span class="type">int</span>)</span></span> (x, y <span class="type">int</span>) &#123;</span><br><span class="line"> x = sum * <span class="number">4</span> / <span class="number">9</span></span><br><span class="line"> y = sum - x</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line">&#125;</span><br><span class="line">x, y := split(<span class="number">17</span>)</span><br><span class="line">fmt.Println(x) <span class="comment">// =&gt; 7</span></span><br><span class="line">fmt.Println(y) <span class="comment">// =&gt; 10</span></span><br></pre></td></tr></table></figure>
<h2 id="可变参数函数"><a href="#可变参数函数" class="headerlink" title="可变参数函数"></a>可变参数函数</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">sum</span><span class="params">(nums ...<span class="type">int</span>)</span></span> &#123;</span><br><span class="line"> fmt.Print(nums, <span class="string">&quot; &quot;</span>)</span><br><span class="line"> total := <span class="number">0</span></span><br><span class="line"> <span class="keyword">for</span> _, num := <span class="keyword">range</span> nums &#123;</span><br><span class="line"> total += num</span><br><span class="line"> &#125;</span><br><span class="line"> fmt.Println(total)</span><br><span class="line">&#125;</span><br><span class="line">sum(<span class="number">1</span>, <span class="number">2</span>) <span class="comment">// =&gt; [1 2] 3</span></span><br><span class="line">sum(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>) <span class="comment">// =&gt; [1 2 3] 6</span></span><br><span class="line">nums := []<span class="type">int</span>&#123;<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>&#125;</span><br><span class="line">sum(nums...) <span class="comment">// =&gt; [1 2 3 4] 10</span></span><br><span class="line"><span class="comment">// 不定参在内存中是连续存储的</span></span><br><span class="line"><span class="comment">// 不定参内部再传递的时候,参数也要是不定的</span></span><br></pre></td></tr></table></figure>
<h2 id="初始化函数"><a href="#初始化函数" class="headerlink" title="初始化函数"></a>初始化函数</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> --&gt; <span class="keyword">const</span> --&gt; <span class="keyword">var</span> --&gt; init()</span><br></pre></td></tr></table></figure>
<hr>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> num = setNumber()</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">setNumber</span><span class="params">()</span></span> <span class="type">int</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">42</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">init</span><span class="params">()</span></span> &#123;</span><br><span class="line"> num = <span class="number">0</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line"> fmt.Println(num) <span class="comment">// =&gt; 0</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="作为值的函数"><a href="#作为值的函数" class="headerlink" title="作为值的函数"></a>作为值的函数</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line"> <span class="comment">// 将函数赋给名称</span></span><br><span class="line"> add := <span class="function"><span class="keyword">func</span><span class="params">(a, b <span class="type">int</span>)</span></span> <span class="type">int</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> a + b</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="comment">// 使用名称调用函数</span></span><br><span class="line"> fmt.Println(add(<span class="number">3</span>, <span class="number">4</span>)) <span class="comment">// =&gt; 7</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="闭包"><a href="#闭包" class="headerlink" title="闭包"></a>闭包</h2><!--rehype:wrap-class=col-span-2 row-span-2-->
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">outer</span><span class="params">()</span></span> (<span class="function"><span class="keyword">func</span><span class="params">()</span></span> <span class="type">int</span>, <span class="type">int</span>) &#123;</span><br><span class="line"> outer_var := <span class="number">2</span></span><br><span class="line"> inner := <span class="function"><span class="keyword">func</span><span class="params">()</span></span> <span class="type">int</span> &#123;</span><br><span class="line"> outer_var += <span class="number">99</span></span><br><span class="line"> <span class="keyword">return</span> outer_var</span><br><span class="line"> &#125;</span><br><span class="line"> inner()</span><br><span class="line"> <span class="keyword">return</span> inner, outer_var</span><br><span class="line">&#125;</span><br><span class="line">inner, val := outer()</span><br><span class="line">fmt.Println(val)</span><br><span class="line"><span class="comment">// =&gt; 101</span></span><br><span class="line">fmt.Println(inner())</span><br><span class="line"><span class="comment">// =&gt; 200,这里涉及到golang中闭包和内存逃逸的概念,inner()实际上执行了两次,outer()中一次,fmt又一次,</span></span><br><span class="line"><span class="comment">//但为什么是200呢,编译器不能确定outer_var在后续会不会使用,</span></span><br><span class="line"><span class="comment">//所以outer_var不会随着outer()结束而释放它的栈(Stack)空间,</span></span><br><span class="line"><span class="comment">//而会‘逃逸到’堆(Heap)上,那么第二次的inner()中outer_var就会是101。</span></span><br></pre></td></tr></table></figure>
<h2 id="关闭-1"><a href="#关闭-1" class="headerlink" title="关闭 1"></a>关闭 1</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">scope</span><span class="params">()</span></span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> <span class="type">int</span>&#123;</span><br><span class="line"> outer_var := <span class="number">2</span></span><br><span class="line"> foo := <span class="function"><span class="keyword">func</span><span class="params">()</span></span> <span class="type">int</span> &#123;<span class="keyword">return</span> outer_var&#125;</span><br><span class="line"> <span class="keyword">return</span> foo</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// Outpus: 2</span></span><br><span class="line">fmt.Println(scope()())</span><br></pre></td></tr></table></figure>
<h1 id="Golang-包-Packages"><a href="#Golang-包-Packages" class="headerlink" title="Golang 包(Packages)"></a>Golang 包(Packages)</h1><h2 id="导入"><a href="#导入" class="headerlink" title="导入"></a>导入</h2><!--rehype:wrap-class=row-span-2-->
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="string">&quot;fmt&quot;</span></span><br><span class="line"><span class="keyword">import</span> <span class="string">&quot;math/rand&quot;</span></span><br></pre></td></tr></table></figure>
<h3 id="等同于"><a href="#等同于" class="headerlink" title="等同于"></a>等同于</h3><figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">&quot;fmt&quot;</span> <span class="comment">// 给 fmt.Println</span></span><br><span class="line"> <span class="string">&quot;math/rand&quot;</span> <span class="comment">// 给 rand.Intn</span></span><br><span class="line">)</span><br></pre></td></tr></table></figure>
<p>另见:<a href="https://tour.golang.org/basics/1">导入</a></p>
<h2 id="别名"><a href="#别名" class="headerlink" title="别名"></a>别名</h2><!--rehype:wrap-class=row-span-2-->
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> r <span class="string">&quot;math/rand&quot;</span></span><br></pre></td></tr></table></figure>
<hr>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">&quot;fmt&quot;</span></span><br><span class="line"> r <span class="string">&quot;math/rand&quot;</span></span><br><span class="line">)</span><br></pre></td></tr></table></figure>
<hr>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line">r.Intn()</span><br></pre></td></tr></table></figure>
<h2 id="Packages"><a href="#Packages" class="headerlink" title="Packages"></a>Packages</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">package</span> main</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">package</span> internal</span><br></pre></td></tr></table></figure>
<p>另见:<a href="https://go.dev/doc/go1.4#internalpackages">内部包</a></p>
<h2 id="导出名称"><a href="#导出名称" class="headerlink" title="导出名称"></a>导出名称</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 以大写字母开头</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">Hello</span> <span class="params">()</span></span> &#123;</span><br><span class="line"> ···</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>另见:<a href="https://tour.golang.org/basics/3">导出的名称</a></p>
<h1 id="Golang-并发"><a href="#Golang-并发" class="headerlink" title="Golang 并发"></a>Golang 并发</h1><h2 id="协程"><a href="#协程" class="headerlink" title="协程"></a>协程</h2><!--rehype:wrap-class=row-span-2-->
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">&quot;fmt&quot;</span></span><br><span class="line"> <span class="string">&quot;time&quot;</span></span><br><span class="line">)</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">f</span><span class="params">(from <span class="type">string</span>)</span></span> &#123;</span><br><span class="line"> <span class="keyword">for</span> i := <span class="number">0</span>; i &lt; <span class="number">3</span>; i++ &#123;</span><br><span class="line"> fmt.Println(from, <span class="string">&quot;:&quot;</span>, i)</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line"> f(<span class="string">&quot;direct&quot;</span>)</span><br><span class="line"> <span class="keyword">go</span> f(<span class="string">&quot;goroutine&quot;</span>)</span><br><span class="line"> <span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">(msg <span class="type">string</span>)</span></span> &#123;</span><br><span class="line"> fmt.Println(msg)</span><br><span class="line"> &#125;(<span class="string">&quot;going&quot;</span>)</span><br><span class="line"> time.Sleep(time.Second)</span><br><span class="line"> fmt.Println(<span class="string">&quot;done&quot;</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>参见:<a href="https://tour.golang.org/concurrency/1">Goroutines</a>, <a href="https://tour.golang.org/concurrency/2">Channels</a></p>
<h2 id="WaitGroup"><a href="#WaitGroup" class="headerlink" title="WaitGroup"></a>WaitGroup</h2><!--rehype:wrap-class=row-span-2-->
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">&quot;fmt&quot;</span></span><br><span class="line"> <span class="string">&quot;sync&quot;</span></span><br><span class="line"> <span class="string">&quot;time&quot;</span></span><br><span class="line">)</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">w</span><span class="params">(id <span class="type">int</span>, wg *sync.WaitGroup)</span></span> &#123;</span><br><span class="line"> <span class="keyword">defer</span> wg.Done()</span><br><span class="line"> fmt.Printf(<span class="string">&quot;%d starting\n&quot;</span>, id)</span><br><span class="line"> time.Sleep(time.Second)</span><br><span class="line"> fmt.Printf(<span class="string">&quot;%d done\n&quot;</span>, id)</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line"> <span class="keyword">var</span> wg sync.WaitGroup</span><br><span class="line"> <span class="keyword">for</span> i := <span class="number">1</span>; i &lt;= <span class="number">5</span>; i++ &#123;</span><br><span class="line"> wg.Add(<span class="number">1</span>)</span><br><span class="line"> <span class="keyword">go</span> w(i, &amp;wg)</span><br><span class="line"> &#125;</span><br><span class="line"> wg.Wait()</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>参见:<a href="https://golang.org/pkg/sync/#WaitGroup">WaitGroup</a></p>
<h2 id="Closing-channels"><a href="#Closing-channels" class="headerlink" title="Closing channels"></a>Closing channels</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line">ch &lt;- <span class="number">1</span></span><br><span class="line">ch &lt;- <span class="number">2</span></span><br><span class="line">ch &lt;- <span class="number">3</span></span><br><span class="line"><span class="built_in">close</span>(ch) <span class="comment">// 关闭频道</span></span><br></pre></td></tr></table></figure>
<hr>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 迭代通道直到关闭</span></span><br><span class="line"><span class="keyword">for</span> i := <span class="keyword">range</span> ch &#123;</span><br><span class="line"> ···</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<hr>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="comment">// Closed if `ok == false`</span></span><br><span class="line">v, ok := &lt;- ch</span><br></pre></td></tr></table></figure>
<p>参见:<a href="https://tour.golang.org/concurrency/4">范围和关闭</a></p>
<h2 id="缓冲通道"><a href="#缓冲通道" class="headerlink" title="缓冲通道"></a>缓冲通道</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line">ch := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">int</span>, <span class="number">2</span>)</span><br><span class="line">ch &lt;- <span class="number">1</span></span><br><span class="line">ch &lt;- <span class="number">2</span></span><br><span class="line">ch &lt;- <span class="number">3</span></span><br><span class="line"><span class="comment">// 致命错误:</span></span><br><span class="line"><span class="comment">// 所有 goroutine 都处于休眠状态 - 死锁</span></span><br></pre></td></tr></table></figure>
<p>参见:<a href="https://tour.golang.org/concurrency/3">缓冲通道</a></p>
<h1 id="Golang-错误控制"><a href="#Golang-错误控制" class="headerlink" title="Golang 错误控制"></a>Golang 错误控制</h1><h2 id="延迟函数"><a href="#延迟函数" class="headerlink" title="延迟函数"></a>延迟函数</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line"> <span class="keyword">defer</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> &#123;</span><br><span class="line"> fmt.Println(<span class="string">&quot;Done&quot;</span>)</span><br><span class="line"> &#125;()</span><br><span class="line"> fmt.Println(<span class="string">&quot;Working...&quot;</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="Lambda-defer"><a href="#Lambda-defer" class="headerlink" title="Lambda defer"></a>Lambda defer</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line"> <span class="keyword">var</span> d = <span class="type">int64</span>(<span class="number">0</span>)</span><br><span class="line"> <span class="keyword">defer</span> <span class="function"><span class="keyword">func</span><span class="params">(d *<span class="type">int64</span>)</span></span> &#123;</span><br><span class="line"> fmt.Printf(<span class="string">&quot;&amp; %v Unix Sec\n&quot;</span>, *d)</span><br><span class="line"> &#125;(&amp;d)</span><br><span class="line"> fmt.Print(<span class="string">&quot;Done &quot;</span>)</span><br><span class="line"> d = time.Now().Unix()</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><code>defer</code> 函数使用当前值<code>d</code>,除非我们使用指针在 <code>main</code> 末尾获取最终值</p>
<h2 id="Defer"><a href="#Defer" class="headerlink" title="Defer"></a>Defer</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line"> <span class="keyword">defer</span> fmt.Println(<span class="string">&quot;Done&quot;</span>)</span><br><span class="line"> fmt.Println(<span class="string">&quot;Working...&quot;</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>参见:<a href="https://blog.golang.org/defer-panic-and-recover">Defer, panic and recover</a></p>
<h1 id="Golang-方法-Methods"><a href="#Golang-方法-Methods" class="headerlink" title="Golang 方法(Methods)"></a>Golang 方法(Methods)</h1><!--rehype:body-class=cols-2-->
<h2 id="接收器"><a href="#接收器" class="headerlink" title="接收器"></a>接收器</h2><!--rehype:wrap-class=row-span-2-->
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="comment">//Go语言中的方法(Method)是一种作用于特定类型变量的函数。</span></span><br><span class="line"><span class="comment">//这种特定类型变量叫做接收者(Receiver)。</span></span><br><span class="line"><span class="comment">//接收者的概念就类似于其他语言中的 this 或者 self。</span></span><br><span class="line"><span class="comment">//方法的定义格式如下:</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(接收者变量 接收者类型)</span></span> 方法名(参数列表) (返回参数) &#123;</span><br><span class="line"> 函数体</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 其中,</span></span><br><span class="line"><span class="comment">// 1.接收者变量:接收者中的参数变量名在命名时,官方建议使用接收者类型名</span></span><br><span class="line"><span class="comment">//的第一个小写字母,而不是self、this之类的命名。例如,Person类型的接收者变量</span></span><br><span class="line"><span class="comment">// 应该命名为 p,Connector类型的接收者变量应该命名为c等。</span></span><br><span class="line"><span class="comment">// 2.接收者类型:接收者类型和参数类似,可以是指针类型和非指针类型。</span></span><br><span class="line"><span class="comment">// 3.方法名、参数列表、返回参数:具体格式与函数定义相同。</span></span><br><span class="line"><span class="keyword">type</span> Vertex <span class="keyword">struct</span> &#123;</span><br><span class="line"> X, Y <span class="type">float64</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(v Vertex)</span></span> Abs() <span class="type">float64</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> math.Sqrt(v.X * v.X + v.Y * v.Y)</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(v Vertex)</span></span> valuechange() <span class="type">float64</span> &#123;</span><br><span class="line"> v.X += <span class="number">1</span></span><br><span class="line"> <span class="keyword">return</span> v.X</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(v *Vertex)</span></span> pointerchange() <span class="type">float64</span> &#123;</span><br><span class="line"> v.X += <span class="number">1</span></span><br><span class="line"> <span class="keyword">return</span> v.X</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line"> v := Vertex&#123;<span class="number">1</span>, <span class="number">2</span>&#125;</span><br><span class="line"> v.Abs()</span><br><span class="line"></span><br><span class="line"> v = Vertex&#123;<span class="number">1</span>, <span class="number">2</span>&#125;</span><br><span class="line"> fmt.Println(v.valuechange()) <span class="comment">// 2</span></span><br><span class="line"> fmt.Println(v) <span class="comment">// &#123;1 2&#125;</span></span><br><span class="line"></span><br><span class="line"> v = Vertex&#123;<span class="number">1</span>, <span class="number">2</span>&#125;</span><br><span class="line"> fmt.Println(v.pointerchange())<span class="comment">// 2</span></span><br><span class="line"> fmt.Println(v) <span class="comment">// &#123;2 2&#125;</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//如果在方法里修改receiver的值要对caller生效,使用 pointer receiver。</span></span><br></pre></td></tr></table></figure>
<p>参见:<a href="https://tour.golang.org/methods/1">Methods</a>,<a href="https://tour.golang.org/methods/4">指针接收器</a></p>
<h2 id="方法表达式"><a href="#方法表达式" class="headerlink" title="方法表达式"></a>方法表达式</h2><p>方法表达式相当于提供一种语法将类型方法调用显式地转换为函数调用,接收者(receiver)必须显式地传递进去。</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(t T)</span></span> Get()&#123;</span><br><span class="line"> <span class="keyword">return</span> t.a</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(t *T)</span></span> Set(i <span class="type">int</span>)&#123;</span><br><span class="line"> t.a = i</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//表达式`T.Get`和`(*T).Set`被称为方法表达式,</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">type</span> Data <span class="keyword">struct</span>&#123;&#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(Data)</span></span> TestValue () &#123;&#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(*Data)</span></span> TestPointer () &#123;&#125; </span><br><span class="line"><span class="comment">//声明一个类型变量a</span></span><br><span class="line"><span class="keyword">var</span> a Data= <span class="keyword">struct</span>&#123;&#125;&#123;&#125;</span><br><span class="line"><span class="comment">//表达式调用编译器不会进行自动转换</span></span><br><span class="line">Data.TestValue(a) </span><br><span class="line"><span class="comment">//Data.TestValue(&amp;a) </span></span><br><span class="line">(*Data).TestPointer (&amp;a) </span><br><span class="line"><span class="comment">//Data.TestPointer(&amp;a) //type Data has no method TestPointer </span></span><br><span class="line"><span class="comment">//值调用编译器会进行自动转换</span></span><br><span class="line">y : = (&amp;a).TestValue <span class="comment">//编译器帮助转换a.TestValue</span></span><br><span class="line">g : = a.TestPointer <span class="comment">//会转换为(&amp;a).TestPointer </span></span><br></pre></td></tr></table></figure>
<h3 id="组合结构的方法集"><a href="#组合结构的方法集" class="headerlink" title="组合结构的方法集"></a>组合结构的方法集</h3><p>内嵌字段的访问不需要使用全路径,只要保证命名是唯一的就可以,尽量避免同名。如果外层字段和内层字段有相同的方法,则使用简化模式访问外层方法会覆盖内层的方法。</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line">x : = X&#123;a: <span class="number">1</span>&#125; </span><br><span class="line">y : = Y&#123; </span><br><span class="line"> X : x , </span><br><span class="line"> b : <span class="number">2</span> , </span><br><span class="line">&#125;</span><br><span class="line">z : = z &#123; </span><br><span class="line"> Y : y , </span><br><span class="line"> c : <span class="number">3</span> ,</span><br><span class="line">&#125;<span class="comment">//组合结构,内嵌字段</span></span><br></pre></td></tr></table></figure>
<p>组合结构的方法集有如下规则:</p>
<ul>
<li>若类型 T 包含匿名字段 S ,则 T 的方法集包含S的方法集</li>
<li>若类型 T 包含匿名字段 <em>S ,则 T 的方法集包含 S 和</em>S方法集</li>
<li>不管类型 T 中嵌入的匿名字段是 S 还是 <em>S ,</em>T 方法集总是包含 S 和 *S 方法集</li>
</ul>
<h1 id="Golang-接口-Interfaces"><a href="#Golang-接口-Interfaces" class="headerlink" title="Golang 接口(Interfaces)"></a>Golang 接口(Interfaces)</h1><!--rehype:body-class=cols-2-->
<h2 id="基本接口-Interfaces"><a href="#基本接口-Interfaces" class="headerlink" title="基本接口(Interfaces)"></a>基本接口(Interfaces)</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">type</span> Shape <span class="keyword">interface</span> &#123;</span><br><span class="line"> Area() <span class="type">float64</span></span><br><span class="line"> Perimeter() <span class="type">float64</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="结构-Struct"><a href="#结构-Struct" class="headerlink" title="结构(Struct)"></a>结构(Struct)</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">type</span> Rectangle <span class="keyword">struct</span> &#123;</span><br><span class="line"> Length, Width <span class="type">float64</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>结构 <code>Rectangle</code> 通过实现其所有方法隐式实现接口 <code>Shape</code></p>
<h2 id="方法-Methods"><a href="#方法-Methods" class="headerlink" title="方法(Methods)"></a>方法(Methods)</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(r Rectangle)</span></span> Area() <span class="type">float64</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> r.Length * r.Width</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(r Rectangle)</span></span> Perimeter() <span class="type">float64</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">2</span> * (r.Length + r.Width)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>在 <code>Shape</code> 中定义的方法在<code>Rectangle</code>中实现</p>
<h2 id="接口实例"><a href="#接口实例" class="headerlink" title="接口实例"></a>接口实例</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line"> <span class="keyword">var</span> r Shape = Rectangle&#123;Length: <span class="number">3</span>, Width: <span class="number">4</span>&#125;</span><br><span class="line"> fmt.Printf(<span class="string">&quot;Type of r: %T, Area: %v, Perimeter: %v.&quot;</span>, r, r.Area(), r.Perimeter())</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<!--rehype:className=wrap-text -->
<h1 id="Golang-Embed-Go-version-1-16"><a href="#Golang-Embed-Go-version-1-16" class="headerlink" title="Golang Embed (Go version &gt;&#x3D; 1.16)"></a>Golang Embed (Go version &gt;&#x3D; 1.16)</h1><h2 id="嵌入为string"><a href="#嵌入为string" class="headerlink" title="嵌入为string"></a>嵌入为string</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> _ <span class="string">&quot;embed&quot;</span></span><br><span class="line"> <span class="string">&quot;fmt&quot;</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment">//go:embed version.txt</span></span><br><span class="line"><span class="keyword">var</span> version <span class="type">string</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line"> fmt.Printf(<span class="string">&quot;version %q\n&quot;</span>, version)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="嵌入为-byte"><a href="#嵌入为-byte" class="headerlink" title="嵌入为[]byte"></a>嵌入为[]byte</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> _ <span class="string">&quot;embed&quot;</span></span><br><span class="line"> <span class="string">&quot;fmt&quot;</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment">//go:embed version.txt</span></span><br><span class="line"><span class="keyword">var</span> versionByte []<span class="type">byte</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line"> fmt.Printf(<span class="string">&quot;version %q\n&quot;</span>, <span class="type">string</span>(versionByte))</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="嵌入为embed-FS"><a href="#嵌入为embed-FS" class="headerlink" title="嵌入为embed.FS"></a>嵌入为embed.FS</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="comment">//go:embed hello.txt</span></span><br><span class="line"><span class="keyword">var</span> f embed.FS</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line"> data, _ := f.ReadFile(<span class="string">&quot;hello.txt&quot;</span>)</span><br><span class="line"> fmt.Println(<span class="type">string</span>(data))</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="嵌入多个文件"><a href="#嵌入多个文件" class="headerlink" title="嵌入多个文件"></a>嵌入多个文件</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="comment">//go:embed hello.txt</span></span><br><span class="line"><span class="comment">//go:embed hello2.txt</span></span><br><span class="line"><span class="keyword">var</span> f embed.FS</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line"> data, _ := f.ReadFile(<span class="string">&quot;hello.txt&quot;</span>)</span><br><span class="line"> fmt.Println(<span class="type">string</span>(data))</span><br><span class="line"> data, _ = f.ReadFile(<span class="string">&quot;hello2.txt&quot;</span>)</span><br><span class="line"> fmt.Println(<span class="type">string</span>(data))</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="嵌入子文件夹下的文件"><a href="#嵌入子文件夹下的文件" class="headerlink" title="嵌入子文件夹下的文件"></a>嵌入子文件夹下的文件</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="comment">//go:embed p/hello.txt p/hello2.txt</span></span><br><span class="line"><span class="keyword">var</span> f embed.FS</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line"> data, _ := f.ReadFile(<span class="string">&quot;p/hello.txt&quot;</span>)</span><br><span class="line"> fmt.Println(<span class="type">string</span>(data))</span><br><span class="line"> data, _ = f.ReadFile(<span class="string">&quot;p/hello2.txt&quot;</span>)</span><br><span class="line"> fmt.Println(<span class="type">string</span>(data))</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="同一个文件嵌入为多个变量"><a href="#同一个文件嵌入为多个变量" class="headerlink" title="同一个文件嵌入为多个变量"></a>同一个文件嵌入为多个变量</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="comment">//go:embed hello.txt</span></span><br><span class="line"><span class="keyword">var</span> s <span class="type">string</span></span><br><span class="line"><span class="comment">//go:embed hello.txt</span></span><br><span class="line"><span class="keyword">var</span> s2 <span class="type">string</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line"> fmt.Println(s)</span><br><span class="line"> fmt.Println(s2)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="匹配模式"><a href="#匹配模式" class="headerlink" title="匹配模式"></a>匹配模式</h2><figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="comment">//go:embed p/*</span></span><br><span class="line"><span class="keyword">var</span> f embed.FS</span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> &#123;</span><br><span class="line"> data, _ := f.ReadFile(<span class="string">&quot;p/.hello.txt&quot;</span>)</span><br><span class="line"> fmt.Println(<span class="type">string</span>(data))</span><br><span class="line"> data, _ = f.ReadFile(<span class="string">&quot;p/q/.hi.txt&quot;</span>) <span class="comment">// 没有嵌入 p/q/.hi.txt</span></span><br><span class="line"> fmt.Println(<span class="type">string</span>(data))</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h1 id="Golang-泛型-Go-version-1-18"><a href="#Golang-泛型-Go-version-1-18" class="headerlink" title="Golang 泛型 (Go version &gt;&#x3D; 1.18)"></a>Golang 泛型 (Go version &gt;&#x3D; 1.18)</h1><h2 id="泛型类型"><a href="#泛型类型" class="headerlink" title="泛型类型"></a>泛型类型</h2><!--rehype:wrap-class=row-span-1-->
<figure class="highlight text"><table><tr><td class="code"><pre><span class="line">type S[T int|float32|float64 ] []T</span><br><span class="line"> ┬ ────────┬──────── </span><br><span class="line"> ┆ ╰─── 2. 类型约束</span><br><span class="line"> ╰────────────── 1. 类型形参</span><br></pre></td></tr></table></figure>
<!--rehype:className=wrap-text -->
<p>可以使用类型实参 int 或 string 实例化</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">type</span> MyMap[K <span class="type">int</span>|<span class="type">string</span>, V <span class="type">float32</span> | <span class="type">float64</span>] <span class="keyword">map</span>[K]V</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> a MyMap[<span class="type">string</span>, <span class="type">float64</span>] = <span class="keyword">map</span>[<span class="type">string</span>]<span class="type">float64</span>&#123;</span><br><span class="line"> <span class="string">&quot;jack_score&quot;</span>: <span class="number">9.6</span>,</span><br><span class="line"> <span class="string">&quot;bob_score&quot;</span>: <span class="number">8.4</span>,</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<!--rehype:className=wrap-text -->
<ul>
<li><strong>匿名结构体不支持泛型</strong><!--rehype:style=color: #b43c29;--></li>
<li><strong>匿名函数不支持泛型</strong><!--rehype:style=color: #b43c29;--></li>
</ul>
<h2 id="泛型函数"><a href="#泛型函数" class="headerlink" title="泛型函数"></a>泛型函数</h2><!--rehype:wrap-class=row-span-1-->
<p>任意类型</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">Add</span>[<span class="title">T</span> <span class="title">any</span>]<span class="params">(a,b T)</span></span> T &#123;</span><br><span class="line"> <span class="keyword">return</span> a+b</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>对类型进行约束</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">Add</span>[<span class="title">T</span> <span class="title">string</span> | <span class="title">int</span> | <span class="title">int8</span>]<span class="params">(a,b T)</span></span> T &#123;</span><br><span class="line"> <span class="keyword">return</span> a+b</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>类型嵌套</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">type</span> WowStruct[T <span class="type">int</span> | <span class="type">float32</span>, S []T] <span class="keyword">struct</span> &#123;</span><br><span class="line"> Data S</span><br><span class="line"> MaxValue T</span><br><span class="line"> MinValue T</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> ws WowStruct[<span class="type">int</span>, []<span class="type">int</span>] </span><br></pre></td></tr></table></figure>
<!--rehype:className=wrap-text -->
<p>泛型函数中进行类型声明 (go version &gt;&#x3D; 1.20)</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">F</span>[<span class="title">T1</span> <span class="title">any</span>]<span class="params">()</span></span> &#123;</span><br><span class="line"> <span class="keyword">type</span> x <span class="keyword">struct</span>&#123;&#125; </span><br><span class="line"> <span class="keyword">type</span> y = x </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="泛型约束"><a href="#泛型约束" class="headerlink" title="泛型约束"></a>泛型约束</h2><!--rehype:wrap-class=row-span-2-->
<p>通过接口实现</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">type</span> Addable <span class="keyword">interface</span>&#123;</span><br><span class="line"> <span class="keyword">type</span> <span class="type">int</span>, <span class="type">int8</span>, <span class="type">int16</span>, <span class="type">int32</span>, <span class="type">int64</span>, <span class="type">uint</span>, <span class="type">uint8</span>, <span class="type">uint16</span>, <span class="type">uint32</span>, <span class="type">uint64</span>, <span class="type">uintptr</span>, <span class="type">float32</span>, <span class="type">float64</span>, <span class="type">complex64</span>, <span class="type">complex128</span>, <span class="type">string</span> </span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">Add</span>[<span class="title">T</span> <span class="title">Addable</span>]<span class="params">(a,b T)</span></span> T &#123;</span><br><span class="line"> <span class="keyword">return</span> a+b</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<!--rehype:className=wrap-text -->
<p>使用 ~ 符号</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">type</span> Int <span class="keyword">interface</span> &#123;</span><br><span class="line"> ~<span class="type">int</span> | ~<span class="type">int8</span> | ~<span class="type">int16</span> | ~<span class="type">int32</span> | ~<span class="type">int64</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Uint <span class="keyword">interface</span> &#123;</span><br><span class="line"> ~<span class="type">uint</span> | ~<span class="type">uint8</span> | ~<span class="type">uint16</span> | ~<span class="type">uint32</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">type</span> Float <span class="keyword">interface</span> &#123;</span><br><span class="line"> ~<span class="type">float32</span> | ~<span class="type">float64</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Slice[T Int | Uint | Float] []T </span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> s Slice[<span class="type">int</span>] <span class="comment">// 正确</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> MyInt <span class="type">int</span></span><br><span class="line"><span class="keyword">var</span> s2 Slice[MyInt] <span class="comment">// MyInt底层类型是int,所以可以用于实例化</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> MyMyInt MyInt</span><br><span class="line"><span class="keyword">var</span> s3 Slice[MyMyInt] <span class="comment">// 正确。MyMyInt 虽然基于 MyInt ,但底层类型也是int,所以也能用于实例化</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> MyFloat32 <span class="type">float32</span> <span class="comment">// 正确</span></span><br><span class="line"><span class="keyword">var</span> s4 Slice[MyFloat32]</span><br></pre></td></tr></table></figure>
<!--rehype:className=wrap-text -->
<p>使用 ~ 时的限制:</p>
<!--rehype:style=color: #b43c29;-->
<ol>
<li><p>~后面的类型不能为接口</p>
</li>
<li><p>~后面的类型必须为基本类型</p>
<!--rehype:style=color: #b43c29;--></li>
</ol>
<h2 id="泛型-Receiver"><a href="#泛型-Receiver" class="headerlink" title="泛型 Receiver"></a>泛型 Receiver</h2><!--rehype:wrap-class=row-span-1-->
<p>定义普通类型支持泛型</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">type</span> MySlice[T <span class="type">int</span> | <span class="type">float32</span>] []T</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(s MySlice[T])</span></span> Sum() T &#123;</span><br><span class="line"> <span class="keyword">var</span> sum T</span><br><span class="line"> <span class="keyword">for</span> _, value := <span class="keyword">range</span> s &#123;</span><br><span class="line"> sum += value</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">return</span> sum</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<!--rehype:className=wrap-text -->
<p>结构体支持泛型</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">type</span> A[T <span class="type">int</span> | <span class="type">float32</span> | <span class="type">float64</span>] <span class="keyword">struct</span> &#123;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(receiver A[T])</span></span> Add(a T, b T) T &#123;</span><br><span class="line"> <span class="keyword">return</span> a + b</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<!--rehype:className=wrap-text -->
<h2 id="泛型接口"><a href="#泛型接口" class="headerlink" title="泛型接口"></a>泛型接口</h2><!--rehype:wrap-class=row-span-1-->
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="keyword">type</span> Uint <span class="keyword">interface</span> &#123; <span class="comment">// 接口 Uint 中有类型,所以是一般接口</span></span><br><span class="line"> ~<span class="type">uint</span> | ~<span class="type">uint8</span> | ~<span class="type">uint16</span> | ~<span class="type">uint32</span> | ~<span class="type">uint64</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> ReadWriter <span class="keyword">interface</span> &#123; <span class="comment">// ReadWriter 接口既有方法也有类型,所以是一般接口</span></span><br><span class="line"> ~<span class="type">string</span> | ~[]<span class="type">rune</span></span><br><span class="line"></span><br><span class="line"> Read(p []<span class="type">byte</span>) (n <span class="type">int</span>, err <span class="type">error</span>)</span><br><span class="line"> Write(p []<span class="type">byte</span>) (n <span class="type">int</span>, err <span class="type">error</span>)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<!--rehype:className=wrap-text -->
<p>一般接口类型不能用来定义变量,只能用于泛型的类型约束中</p>
<!--rehype:style=color: #b43c29;-->
<h1 id="杂项"><a href="#杂项" class="headerlink" title="杂项"></a>杂项</h1><h2 id="关键字-Keywords"><a href="#关键字-Keywords" class="headerlink" title="关键字(Keywords)"></a>关键字(Keywords)</h2><ul>
<li><p>break</p>
</li>
<li><p>default</p>
</li>
<li><p>func</p>
</li>
<li><p>interface</p>
</li>
<li><p>select</p>
</li>
<li><p>case</p>
</li>
<li><p>defer</p>
</li>
<li><p>go</p>
</li>
<li><p>map</p>
</li>
<li><p>struct</p>
</li>
<li><p>chan</p>
</li>
<li><p>else</p>
</li>
<li><p>goto</p>
</li>
<li><p>package</p>
</li>
<li><p>switch</p>
</li>
<li><p>const</p>
</li>
<li><p>fallthrough</p>
</li>
<li><p>if</p>
</li>
<li><p>range</p>
</li>
<li><p>type</p>
</li>
<li><p>continue</p>
</li>
<li><p>for</p>
</li>
<li><p>import</p>
</li>
<li><p>return</p>
</li>
<li><p>var</p>
<!--rehype:className=cols-3 style-none--></li>
</ul>
<h2 id="运算符和标点符号"><a href="#运算符和标点符号" class="headerlink" title="运算符和标点符号"></a>运算符和标点符号</h2><table>
<thead>
<tr>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody><tr>
<td><code>+</code></td>
<td><code>&amp;</code></td>
<td><code>+=</code></td>
<td><code>&amp;=</code></td>
<td><code>&amp;&amp;</code></td>
<td><code>==</code></td>
<td><code>!=</code></td>
<td><code>(</code></td>
<td><code>)</code></td>
</tr>
<tr>
<td><code>-</code></td>
<td><code>|</code></td>
<td><code>-=</code></td>
<td><code>|=</code></td>
<td><code>||</code></td>
<td><code>&lt;</code></td>
<td><code>&lt;=</code></td>
<td><code>[</code></td>
<td><code>]</code></td>
</tr>
<tr>
<td><code>*</code></td>
<td><code>^</code></td>
<td><code>*=</code></td>
<td><code>^=</code></td>
<td><code>&lt;-</code></td>
<td><code>&gt;</code></td>
<td><code>&gt;=</code></td>
<td><code>&#123;</code></td>
<td><code>&#125;</code></td>
</tr>
<tr>
<td><code>/</code></td>
<td><code>&lt;&lt;</code></td>
<td><code>/=</code></td>
<td><code>&lt;&lt;=</code></td>
<td><code>++</code></td>
<td><code>=</code></td>
<td><code>:=</code></td>
<td><code>,</code></td>
<td><code>;</code></td>
</tr>
<tr>
<td><code>%</code></td>
<td><code>&gt;&gt;</code></td>
<td><code>%=</code></td>
<td><code>&gt;&gt;=</code></td>
<td><code>--</code></td>
<td><code>!</code></td>
<td><code>...</code></td>
<td><code>.</code></td>
<td><code>:</code></td>
</tr>
<tr>
<td></td>
<td><code>&amp;^</code></td>
<td><code>&amp;^=</code></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody></table>
<h1 id="Go-命令"><a href="#Go-命令" class="headerlink" title="Go 命令"></a>Go 命令</h1><h2 id="Go-编译器命令"><a href="#Go-编译器命令" class="headerlink" title="Go 编译器命令"></a>Go 编译器命令</h2><table>
<thead>
<tr>
<th align="left">:-</th>
<th>–</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><code>go command [参数]</code></td>
<td>go 命令 [参数]</td>
</tr>
<tr>
<td align="left"><code>go build</code></td>
<td>编译包和依赖包</td>
</tr>
<tr>
<td align="left"><code>go clean</code></td>
<td>移除对象和缓存文件</td>
</tr>
<tr>
<td align="left"><code>go doc</code></td>
<td>显示包的文档</td>
</tr>
<tr>
<td align="left"><code>go env</code></td>
<td>打印go的环境变量信息</td>
</tr>
<tr>
<td align="left"><code>go bug</code></td>
<td>报告bug</td>
</tr>
<tr>
<td align="left"><code>go fix</code></td>
<td>更新包使用新的api</td>
</tr>
<tr>
<td align="left"><code>go fmt</code></td>
<td>格式规范化代码</td>
</tr>
<tr>
<td align="left"><code>go generate</code></td>
<td>通过处理资源生成go文件</td>
</tr>
<tr>
<td align="left"><code>go get</code></td>
<td>下载并安装包及其依赖</td>
</tr>
<tr>
<td align="left"><code>go install</code></td>
<td>编译和安装包及其依赖</td>
</tr>
<tr>
<td align="left"><code>go list</code></td>
<td>列出所有包</td>
</tr>
<tr>
<td align="left"><code>go run</code></td>
<td>编译和运行go程序</td>
</tr>
<tr>
<td align="left"><code>go test</code></td>
<td>测试</td>
</tr>
<tr>
<td align="left"><code>go tool</code></td>
<td>运行给定的go工具</td>
</tr>
<tr>
<td align="left"><code>go version</code></td>
<td>显示go当前版本</td>
</tr>
<tr>
<td align="left"><code>go vet</code></td>
<td>发现代码中可能的错误</td>
</tr>
</tbody></table>
<h2 id="ENV"><a href="#ENV" class="headerlink" title="ENV"></a>ENV</h2><table>
<thead>
<tr>
<th align="left">:-</th>
<th>–</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><code>GOOS</code></td>
<td>编译系统</td>
</tr>
<tr>
<td align="left"><code>GOARCH</code></td>
<td>编译arch</td>
</tr>
<tr>
<td align="left"><code>GO111MODULE</code></td>
<td>gomod开关</td>
</tr>
<tr>
<td align="left"><code>GOPROXY</code></td>
<td>go代理 <a href="https://goproxy.io/">https://goproxy.io</a> <a href="https://goproxy.cn/">https://goproxy.cn</a> <a href="https://mirrors.aliyun.com/goproxy/">https://mirrors.aliyun.com/goproxy/</a></td>
</tr>
<tr>
<td align="left"><code>GOSSAFUNC</code></td>
<td>生成 <code>SSA.html</code> 文件,展示代码优化的每一步 <code>GOSSAFUNC=func_name go build</code></td>
</tr>
</tbody></table>
<!--rehype:className=style-list-arrow-->
<h2 id="Module"><a href="#Module" class="headerlink" title="Module"></a>Module</h2><table>
<thead>
<tr>
<th align="left">:-</th>
<th>–</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><code>go mod init</code></td>
<td>初始化当前文件夹,创建go.mod文件</td>
</tr>
<tr>
<td align="left"><code>go mod download</code></td>
<td>下载依赖的module到本地</td>
</tr>
<tr>
<td align="left"><code>go mod tidy</code></td>
<td>增加缺少的module,删除无用的module</td>
</tr>
<tr>
<td align="left"><code>go mod vendor</code></td>
<td>将依赖复制到vendor下</td>
</tr>
<tr>
<td align="left">文件 <code>go.mod</code></td>
<td>依赖列表和版本约束</td>
</tr>
<tr>
<td align="left">文件 <code>go.sum</code></td>
<td>记录 <code>module</code> 文件 <code>hash</code> 值,用于安全校验</td>
</tr>
</tbody></table>
<!--rehype:className=style-list-arrow-->
<h1 id="另见"><a href="#另见" class="headerlink" title="另见"></a>另见</h1><ul>
<li><a href="https://devhints.io/go">Devhints</a> <em>(devhints.io)</em></li>
<li><a href="https://tour.golang.org/welcome/1">A tour of Go</a> <em>(tour.golang.org)</em></li>
<li><a href="https://github.com/golang/go/wiki/">Golang wiki</a> <em>(github.com)</em></li>
<li><a href="https://golang.org/doc/effective_go.html">Effective Go</a> <em>(golang.org)</em></li>
<li><a href="https://gobyexample.com/">Go by Example</a> <em>(gobyexample.com)</em></li>
<li><a href="https://awesome-go.com/">Awesome Go</a> <em>(awesome-go.com)</em></li>
<li><a href="https://www.youtube.com/channel/UC_BzFbxG2za3bp5NRRRXJSw">JustForFunc Youtube</a> <em>(youtube.com)</em></li>
<li><a href="https://github.com/golang/go/wiki/CodeReviewComments">Style Guide</a> <em>(github.com)</em></li>
</ul>
]]></content>
<categories>
<category>语言指南</category>
</categories>
<tags>
<tag>Golang</tag>
</tags>
</entry>
<entry>
<title>Lombok常用注解</title>
<url>/2023/07/06/lombok/</url>
<content><![CDATA[<h1 id="RequiredArgsConstructor"><a href="#RequiredArgsConstructor" class="headerlink" title="@RequiredArgsConstructor"></a><code>@RequiredArgsConstructor</code></h1><p><code>@RequiredArgsConstructor</code> 注解是 Lombok 提供的一个功能注解,它可以为包含 <code>final</code> 或 <code>@NonNull</code> 修饰的字段生成构造函数参数。</p>
<p>使用 <code>@RequiredArgsConstructor</code> 注解时,Lombok 会根据类中的字段自动生成一个构造函数,该构造函数只接受被修饰字段作为参数,并将这些参数用于初始化对象的对应字段。通过自动生成的构造函数,可以方便地为带有 <code>final</code> 或 <code>@NonNull</code> 修饰的字段赋值,避免了手动编写构造函数的繁琐工作。</p>
<p>需要注意的是,<code>@RequiredArgsConstructor</code> 注解不会为非 <code>final</code> 或无 <code>@NonNull</code> 修饰的字段生成构造函数参数,也不会为静态字段生成构造函数参数。</p>
<p>以下是使用 <code>@RequiredArgsConstructor</code> 注解的示例:</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> lombok.RequiredArgsConstructor;</span><br><span class="line"></span><br><span class="line"><span class="meta">@RequiredArgsConstructor</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyClass</span> &#123;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">int</span> id;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> String name;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="type">int</span> age;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">printInfo</span><span class="params">()</span> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;ID: &quot;</span> + id);</span><br><span class="line"> System.out.println(<span class="string">&quot;Name: &quot;</span> + name);</span><br><span class="line"> System.out.println(<span class="string">&quot;Age: &quot;</span> + age);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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_">MyClass</span><span class="params">(<span class="type">int</span> id, String name)</span> &#123;</span><br><span class="line"> <span class="built_in">this</span>.id = id;</span><br><span class="line"> <span class="built_in">this</span>.name = name;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>在上面的示例中,使用 <code>@RequiredArgsConstructor</code> 注解后,自动生成了一个构造函数,接受 <code>id</code> 和 <code>name</code> 字段作为参数,并将它们赋值给相应的字段。</p>
<p>需要注意的是,为了正确使用 <code>@RequiredArgsConstructor</code> 注解,请确保类中的字段被声明为实例变量,并且具有 <code>final</code> 或 <code>@NonNull</code> 修饰。</p>
<h1 id="EqualsAndHashCode-callSuper-true"><a href="#EqualsAndHashCode-callSuper-true" class="headerlink" title="@EqualsAndHashCode(callSuper = true)"></a><code>@EqualsAndHashCode(callSuper = true)</code></h1><p><code>@EqualsAndHashCode(callSuper = true)</code> 和不加 <code>callSuper</code> 的区别在于生成的 <code>equals()</code> 和 <code>hashCode()</code> 方法是否包含父类的字段。</p>
<p>当你在一个子类中使用 <code>@EqualsAndHashCode</code> 注解时,默认情况下只会生成处理当前类的字段的 <code>equals()</code> 和 <code>hashCode()</code> 方法。这意味着父类的字段不会被考虑在内。</p>
<p>然而,如果你将 <code>callSuper</code> 参数设置为 <code>true</code>,生成的方法将会调用父类中的对应方法,并包括父类的字段。这样做可以确保生成的方法同时包含父类和子类的字段。</p>
<p>下面是一个示例来说明两者之间的区别:</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Parent</span> &#123;</span><br><span class="line"> <span class="keyword">private</span> <span class="type">int</span> parentId;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// getter, setter, 其他方法...</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Child</span> <span class="keyword">extends</span> <span class="title class_">Parent</span> &#123;</span><br><span class="line"> <span class="keyword">private</span> <span class="type">int</span> childId;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// getter, setter, 其他方法...</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="type">boolean</span> <span class="title function_">equals</span><span class="params">(Object o)</span> &#123;</span><br><span class="line"> <span class="comment">// 手动编写 equals() 方法,不调用父类方法</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">this</span> == o) <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> <span class="keyword">if</span> (o == <span class="literal">null</span> || getClass() != o.getClass()) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> <span class="type">Child</span> <span class="variable">child</span> <span class="operator">=</span> (Child) o;</span><br><span class="line"> <span class="keyword">return</span> childId == child.childId;</span><br><span class="line"> &#125;</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_">hashCode</span><span class="params">()</span> &#123;</span><br><span class="line"> <span class="comment">// 手动编写 hashCode() 方法,不调用父类方法</span></span><br><span class="line"> <span class="keyword">return</span> Objects.hash(childId);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>在上面的例子中,<code>Child</code> 类手动编写了 <code>equals()</code> 和 <code>hashCode()</code> 方法,并且只考虑了子类的字段 <code>childId</code>。父类的字段 <code>parentId</code> 没有被包含在内。</p>
<p>相比之下,在使用 <code>@EqualsAndHashCode(callSuper = true)</code> 注解的情况下,生成的方法将同时处理父类和子类的字段。</p>
<p>总结来说,<code>@EqualsAndHashCode(callSuper = true)</code> 会自动生成的 <code>equals()</code> 和 <code>hashCode()</code> 方法将同时包含父类和子类的字段;而默认情况下不带 <code>callSuper</code> 的 <code>@EqualsAndHashCode</code> 注解只会处理当前类的字段。</p>
]]></content>
<categories>
<category>实用工具</category>
</categories>
<tags>
<tag>Lombok</tag>
</tags>
</entry>
<entry>
<title>Guava常用工具类总结</title>
<url>/2023/09/15/guava-guide/</url>
<content><![CDATA[<blockquote>
<p>原文链接:<a href="https://www.cnblogs.com/cuzzz/p/16609775.html">https://www.cnblogs.com/cuzzz/p/16609775.html</a></p>
</blockquote>
<blockquote>
<ul>
<li>“我想写得更优雅,可是没人告诉我怎么写得更优雅” </li>
<li>“Null的含糊语义让人很不舒服。Null很少可以明确地表示某种语义,例如,Map.get(key)返回Null时,可能表示map中的值是null,亦或map中没有key对应的值。Null可以表示失败、成功或几乎任何情况。使用Null以外的特定值,会让你的逻辑描述变得更清晰。”</li>
</ul>
<p>此文档只是Guava最常用工具介绍,guava存在更多本文档没有介绍的api</p>
</blockquote>
<h1 id="Optional"><a href="#Optional" class="headerlink" title="Optional"></a>Optional</h1><p><strong>Optional</strong>类是Java8为了解决null值判断问题,借鉴google guava类库的Optional类而引入的一个同名Optional类,使用Optional类可以避免显式的null值判断(null的防御性检查),避免null导致的NPE(NullPointerException)。</p>
<blockquote>
<p>这里讲的optional 也是指jdk中的optional,其实二者类似,<br>但是编码使用gauva的optional,阿里巴巴编程规范会提醒换成jdk自带的optional</p>
</blockquote>
<h2 id="不要用isPressent判断一个对象是否为空"><a href="#不要用isPressent判断一个对象是否为空" class="headerlink" title="不要用isPressent判断一个对象是否为空"></a>不要用isPressent判断一个对象是否为空</h2><p>这种用法不但没有减少null的防御性检查,而且增加了Optional包装的过程,违背了Optional设计的初衷,因此开发中要避免这种糟糕的使用</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_">TestEnum</span> &#123;</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"> a(<span class="string">&quot;aaa&quot;</span>, <span class="string">&quot;aname&quot;</span>),</span><br><span class="line"> b(<span class="string">&quot;bbbb&quot;</span>, <span class="string">&quot;bname&quot;</span>),</span><br><span class="line"> c(<span class="string">&quot;cccc&quot;</span>, <span class="string">&quot;cname&quot;</span>),</span><br><span class="line"> ;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Getter</span></span><br><span class="line"> <span class="keyword">private</span> String code;</span><br><span class="line"> <span class="meta">@Getter</span></span><br><span class="line"> <span class="keyword">private</span> String name;</span><br><span class="line"></span><br><span class="line"> TestEnum(String code, String name) &#123;</span><br><span class="line"> <span class="built_in">this</span>.code = code;</span><br><span class="line"> <span class="built_in">this</span>.name = name;</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> String <span class="title function_">getNameByCode</span><span class="params">(String code)</span> &#123;</span><br><span class="line"> Optional&lt;String&gt; nameOfCode = Arrays.stream(TestEnum.values())</span><br><span class="line"> .filter(item -&gt; StringUtils.equals(item.code, code))</span><br><span class="line"> .map(TestEnum::getName)</span><br><span class="line"> .findFirst();</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 不要这样使用optional 这样optional的意义:优雅的处理空指针 就不存在了</span></span><br><span class="line"> <span class="keyword">if</span> (!nameOfCode.isPresent()) &#123;</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(<span class="string">&quot;不存在&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 请使用orElseThrow 和orElse</span></span><br><span class="line"> <span class="keyword">return</span> nameOfCode.orElseThrow(() -&gt; <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(<span class="string">&quot;不存在&quot;</span>));</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="考虑让方法返回optional"><a href="#考虑让方法返回optional" class="headerlink" title="考虑让方法返回optional"></a>考虑让方法返回optional</h2><p>《Effective Java》中对方法返回Optional的一些观点</p>
<ul>
<li>容器(包括,集合,映射,数组,stream,optional)都不应该包装在Optional进行返回,返回空的容器能让客户端免于处理一个Optional</li>
<li>如果无法返回结果,且没有返回结果客户端必须进行特殊的处理,那么就应该声明返回optional</li>
<li>返回optional并不是一个不需要成本的操作,无论返回空,还是非空,使用optional作为返回值的方法都是需要初始化的,所以optional在看重性能的情况下使用不当是一种性能的浪费</li>
<li>永远不要返回基本类型对于包装类型的Optional,这需要进行基本类型-&gt;包装类型-&gt;optional的三层包装,<strong>可以使用OptionalInt,optionallong等</strong></li>
</ul>
<h1 id="PreConditions前置条件检查"><a href="#PreConditions前置条件检查" class="headerlink" title="PreConditions前置条件检查"></a>PreConditions前置条件检查</h1><p>前置条件:让方法调用的前置条件判断更简单。api比较见名知意,根据参数分为三种:</p>
<ul>
<li>没有额外参数:抛出的异常中没有错误消息;</li>
<li>有一个Object对象作为额外参数:抛出的异常使用Object.toString() 作为错误消息;</li>
<li>有一个String对象作为额外参数,并且有一组任意数量的附加Object对象:这个变种处理异常消息的方式有点类似printf,但考虑GWT的兼容性和效率,只支持%s指示符。</li>
</ul>
<p>例子:</p>
<ul>
<li>test1 缺点:if看起来臃肿,优点:可用抛出我们系统的自定义异常便于前端反馈</li>
<li>test2 优点:简单直接,缺点:抛出的都是jdk中的异常,通一异常处理可能无法返回正确提示的通一结果集给前端</li>
</ul>
<p>我们可用写一个带异常Class的工具类或者直接代理guava中的Predition 加一层 try-catch 使我其抛出我们系统的自定义异常</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title function_">test1</span><span class="params">(Integer index, List&lt;String&gt; list)</span> &#123;</span><br><span class="line"> <span class="keyword">if</span> (Objects.isNull(index)) &#123;</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(<span class="string">&quot;index不可以为空&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">if</span> (CollectionUtils.isEmpty(list)) &#123;</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(<span class="string">&quot;list不可以为空&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">if</span> (index &lt; <span class="number">0</span> || index &gt;= list.size()) &#123;</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(String.format(<span class="string">&quot;越界无法获取,下标%S&quot;</span>, index));</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">return</span> list.get(index);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title function_">test2</span><span class="params">(Integer index, List&lt;String&gt; list)</span> &#123;</span><br><span class="line"> Preconditions.checkNotNull(index, <span class="string">&quot;index不可以为空&quot;</span>);</span><br><span class="line"> Preconditions.checkNotNull(list, <span class="string">&quot;list不可以为空&quot;</span>);</span><br><span class="line"> Preconditions.checkElementIndex(index, list.size(), String.format(<span class="string">&quot;越界无法获取,下标%S&quot;</span>, index));</span><br><span class="line"> Preconditions.checkArgument(index &gt;= <span class="number">0</span> &amp;&amp; index &lt; list.size(), <span class="string">&quot;越界无法获取,下标%S&quot;</span>, index);</span><br><span class="line"> <span class="keyword">return</span> list.get(index);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h1 id="ComparisonChain和Ordering"><a href="#ComparisonChain和Ordering" class="headerlink" title="ComparisonChain和Ordering"></a>ComparisonChain和Ordering</h1><p>想象一个场景,人先根据age排序后根据height排序</p>
<h2 id="实现comparable"><a href="#实现comparable" class="headerlink" title="实现comparable"></a>实现comparable</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="meta">@Data</span></span><br><span class="line"><span class="meta">@Builder</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_">People</span> <span class="keyword">implements</span> <span class="title class_">Comparable</span>&lt;People&gt; &#123;</span><br><span class="line"> <span class="keyword">private</span> Integer age;</span><br><span class="line"> <span class="keyword">private</span> Integer height;</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_">compareTo</span><span class="params">(People o)</span> &#123;</span><br><span class="line"> <span class="keyword">if</span> (!Objects.equals(<span class="built_in">this</span>.age, o.age)) &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">this</span>.age - o.age;</span><br><span class="line"> &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">this</span>.height - o.height;</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</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_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line"> <span class="type">People</span> <span class="variable">p1</span> <span class="operator">=</span> People.builder().build();</span><br><span class="line"> <span class="type">People</span> <span class="variable">p2</span> <span class="operator">=</span> People.builder().build();</span><br><span class="line"> List&lt;People&gt; list = Arrays.asList(p1, p2);</span><br><span class="line"> Collections.sort(list);</span><br><span class="line"> System.out.println(list);</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 更高级的实现方法</span></span><br><span class="line"> <span class="comment">// @Override</span></span><br><span class="line"> <span class="comment">// public int compareTo(People o) &#123;</span></span><br><span class="line"> <span class="comment">// int ageCompare = Ints.compare(this.age, o.age);</span></span><br><span class="line"> <span class="comment">// if (ageCompare == 0) &#123;</span></span><br><span class="line"> <span class="comment">// return Ints.compare(this.height, o.height);</span></span><br><span class="line"> <span class="comment">// &#125; else &#123;</span></span><br><span class="line"> <span class="comment">// return ageCompare;</span></span><br><span class="line"> <span class="comment">// &#125;</span></span><br><span class="line"> <span class="comment">// &#125;</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>缺点:</p>
<ul>
<li>写法繁琐</li>
<li>忽略了空指针, return this.age - o.age; 这一句存在空指针的情况,对null进行拆箱直接NPE</li>
<li>维护复杂,再加一个存款,加逻辑复杂</li>
</ul>
<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">@Override</span></span><br><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">compareTo</span><span class="params">(People o)</span> &#123;</span><br><span class="line"> <span class="type">int</span> <span class="variable">thisAge</span> <span class="operator">=</span> Optional.ofNullable(<span class="built_in">this</span>.age).orElse(Integer.MAX_VALUE);</span><br><span class="line"> <span class="type">int</span> <span class="variable">thisHeight</span> <span class="operator">=</span> Optional.ofNullable(<span class="built_in">this</span>.height).orElse(Integer.MAX_VALUE);</span><br><span class="line"> <span class="type">int</span> <span class="variable">oAge</span> <span class="operator">=</span> Optional.ofNullable(o.age).orElse(Integer.MAX_VALUE);</span><br><span class="line"> <span class="type">int</span> <span class="variable">oHeight</span> <span class="operator">=</span> Optional.ofNullable(o.height).orElse(Integer.MAX_VALUE);</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (thisAge != oAge) &#123;</span><br><span class="line"> <span class="keyword">return</span> thisAge - oAge;</span><br><span class="line"> &#125; <span class="keyword">else</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> thisHeight - oHeight;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>解决了空指针,还实现了Null指在最前或者最后</p>
<p>缺点:</p>
<ul>
<li>繁琐</li>
<li>语法化不明显,看懂代码太累了</li>
<li>维护复杂,再加一个存款,加逻辑复杂</li>
</ul>
<h3 id="写法3"><a href="#写法3" class="headerlink" title="写法3"></a>写法3</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">return</span> ComparisonChain.start()</span><br><span class="line"> .compare(<span class="built_in">this</span>.age, o.age, Ordering.natural().nullsFirst())</span><br><span class="line"> .compare(<span class="built_in">this</span>.height, o.height,Ordering.natural().nullsFirst())</span><br><span class="line"> <span class="comment">//让True在最前面,但是无法处理空的BoolBean</span></span><br><span class="line"> .compareTrueFirst(Optional.ofNullable(<span class="built_in">this</span>.sex).orElse(<span class="literal">false</span>),</span><br><span class="line"> Optional.ofNullable(o.sex).orElse(<span class="literal">false</span>))</span><br><span class="line"> .result();</span><br><span class="line"></span><br><span class="line"><span class="comment">//想一下这个需求你自己写,代码会多么繁琐</span></span><br></pre></td></tr></table></figure>
<p>优点:</p>
<ul>
<li>优雅的处理空指针,传入比较器 Ordering.natural().nullsFirst() 让null在最前面</li>
<li>链式调用yyds</li>
<li>语义化明显:先比较age 后比较 height吗,null在最前面</li>
<li>更易于维护,只需要加一行</li>
</ul>
<h2 id="使用Comparator"><a href="#使用Comparator" class="headerlink" title="使用Comparator"></a>使用Comparator</h2><blockquote>
<p>典型的策略模式,将算法和业务分离开</p>
</blockquote>
<p>大概基础的写法和上述的写法1、2差不多,不做过多赘述,直接上干货</p>
<h3 id="请使用Ordering"><a href="#请使用Ordering" class="headerlink" title="请使用Ordering"></a>请使用Ordering</h3><table>
<thead>
<tr>
<th align="left"><strong>方法</strong></th>
<th align="left"><strong>描述</strong></th>
</tr>
</thead>
<tbody><tr>
<td align="left"><a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Ordering.html#natural()"><code>natural()</code></a></td>
<td align="left">对可排序类型做自然排序,如数字按大小,日期按先后排序</td>
</tr>
<tr>
<td align="left"><a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Ordering.html#usingToString()"><code>usingToString()</code></a></td>
<td align="left">按对象的字符串形式做字典排序[lexicographical ordering]</td>
</tr>
<tr>
<td align="left"><a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Ordering.html#from(java.util.Comparator)"><code>from(Comparator)</code></a></td>
<td align="left">把给定的Comparator转化为排序器</td>
</tr>
<tr>
<td align="left"><a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Ordering.html#reverse()"><code>reverse()</code></a></td>
<td align="left">获取语义相反的排序器</td>
</tr>
<tr>
<td align="left"><a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Ordering.html#nullsFirst()"><code>nullsFirst()</code></a></td>
<td align="left">使用当前排序器,但额外把null值排到最前面。</td>
</tr>
<tr>
<td align="left"><a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Ordering.html#nullsLast()"><code>nullsLast()</code></a></td>
<td align="left">使用当前排序器,但额外把null值排到最后面。</td>
</tr>
<tr>
<td align="left"><a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/Ordering.html#compound(java.util.Comparator)"><code>compound(Comparator)</code></a></td>
<td align="left">合成另一个比较器,以处理当前排序器中的相等情况。</td>
</tr>
</tbody></table>
<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> &#123;</span><br><span class="line"> <span class="type">People</span> <span class="variable">p1</span> <span class="operator">=</span> People.builder().build();</span><br><span class="line"> <span class="type">People</span> <span class="variable">p2</span> <span class="operator">=</span> People.builder().age(<span class="number">1000</span>).height(<span class="number">2</span>).build();</span><br><span class="line"> </span><br><span class="line"> List&lt;People&gt; list = Arrays.asList(p1, p2,<span class="literal">null</span>);</span><br><span class="line"> Comparator&lt;People&gt; ageComparator = (o1, o2) -&gt; ComparisonChain.start().compare(o1.age, o2.age, Ordering.natural().nullsFirst()).result();</span><br><span class="line"> Comparator&lt;People&gt; heightComparator = (o1, o2) -&gt; ComparisonChain.start().compare(o1.height, o2.height, Ordering.natural().nullsFirst()).result();</span><br><span class="line"> </span><br><span class="line"> Ordering&lt;People&gt; compound = Ordering.natural().nullsFirst().compound(ageComparator).compound(heightComparator);</span><br><span class="line"> list.sort(compound);</span><br><span class="line"> System.out.println(<span class="string">&quot;源list 排序之后&quot;</span>);</span><br><span class="line"> System.out.println(list);</span><br><span class="line"> <span class="comment">//深拷贝返回一个排序后的people</span></span><br><span class="line"> List&lt;People&gt; peopleList = compound.sortedCopy(list);</span><br><span class="line"> System.out.println(<span class="string">&quot;深拷贝一个list结果&quot;</span>);</span><br><span class="line"> System.out.println(peopleList);</span><br><span class="line"></span><br><span class="line"> Ordering&lt;People&gt; withDefault = compound.onResultOf(<span class="keyword">new</span> <span class="title class_">Function</span>&lt;People, People&gt;() &#123;</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="meta">@Nullable</span> People <span class="title function_">apply</span><span class="params">(<span class="meta">@Nullable</span> People input)</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> Optional.ofNullable(input).orElse(<span class="keyword">new</span> <span class="title class_">People</span>(<span class="number">21</span>, <span class="number">175</span>, <span class="literal">false</span>));</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;);</span><br><span class="line"> Collections.shuffle(peopleList);</span><br><span class="line"> System.out.println(<span class="string">&quot;打乱深拷贝的list&quot;</span>);</span><br><span class="line"> System.out.println(<span class="string">&quot;深拷贝一个list结果&quot;</span>);</span><br><span class="line"> System.out.println(peopleList);</span><br><span class="line"> System.out.println(<span class="string">&quot;源list&quot;</span>);</span><br><span class="line"> System.out.println(list);</span><br><span class="line"> System.out.println(<span class="string">&quot;使用带默认值的排序结果&quot;</span>);</span><br><span class="line"> peopleList.sort(withDefault);</span><br><span class="line"> System.out.println(<span class="string">&quot;深拷贝一个list结果&quot;</span>);</span><br><span class="line"> System.out.println(peopleList);</span><br><span class="line"> <span class="comment">//这句会显示废弃 很巧妙</span></span><br><span class="line"> <span class="comment">// Ordering.from(Ordering.natural())</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h1 id="Throwables"><a href="#Throwables" class="headerlink" title="Throwables"></a>Throwables</h1><p>Throwables 可用简化异常和错误的传播与检查,什么叫错误的传播——不捕获异常向上抛出,什么是异常的检查——多个catch,catch不同类型的异常进行不同的处理</p>
<blockquote>
<p>&#x2F;&#x2F;好用,但是差点意思</p>
</blockquote>
<table>
<thead>
<tr>
<th>方法</th>
<th>含义</th>
</tr>
</thead>
<tbody><tr>
<td>Throwables.throwIfInstanceOf(e, FileNotFoundException.class);</td>
<td>如果异常属于第二个参数的类型,那么抛出</td>
</tr>
<tr>
<td>Throwables.throwIfUnchecked(e);</td>
<td>如果这个异常是非<strong>Unchecked Exception</strong>(即运行时异常)那么抛出</td>
</tr>
<tr>
<td>Throwable getRootCause(Throwable</td>
<td></td>
</tr>
<tr>
<td>List getCausalChain(Throwable</td>
<td></td>
</tr>
<tr>
<td>String getStackTraceAsString(Throwable</td>
<td></td>
</tr>
</tbody></table>
<p>例子:比较test1 和test2 使用Throwables似乎能让代码更加简洁</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_">ThrowablesLearn</span> &#123;</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> Integer <span class="title function_">doSomeThing</span><span class="params">(<span class="type">int</span> num)</span> <span class="keyword">throws</span> FileNotFoundException, SQLException &#123;</span><br><span class="line"> <span class="comment">//想象这是你的封装的方法,不同的情况你需要抛出不同的异常</span></span><br><span class="line"> <span class="keyword">if</span> (num % <span class="number">2</span> == <span class="number">0</span>) &#123;</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">FileNotFoundException</span>(<span class="string">&quot;FileNotFoundException&quot;</span>);</span><br><span class="line"> &#125; <span class="keyword">else</span> <span class="keyword">if</span> (num % <span class="number">3</span> == <span class="number">0</span>) &#123;</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(<span class="string">&quot;RuntimeException&quot;</span>);</span><br><span class="line"> &#125; <span class="keyword">else</span> <span class="keyword">if</span> (num % <span class="number">5</span> == <span class="number">0</span>) &#123;</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">SQLException</span>(<span class="string">&quot;SQLException&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line"> List&lt;String&gt; integers = Arrays.asList(<span class="string">&quot;1&quot;</span>, <span class="string">&quot;2&quot;</span>, <span class="string">&quot;3&quot;</span>, <span class="string">&quot;4&quot;</span>, <span class="string">&quot;5&quot;</span>, <span class="string">&quot;6&quot;</span>, <span class="string">&quot;7aa&quot;</span>, <span class="string">&quot;8&quot;</span>, <span class="string">&quot;9&quot;</span>);</span><br><span class="line"> <span class="comment">//输入7这里抛出NumberFormatException 因为 7aa 不能转换为数字</span></span><br><span class="line"> <span class="keyword">return</span> Integer.valueOf(integers.get(num - <span class="number">1</span>));</span><br><span class="line"> &#125;</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_">test1</span><span class="params">()</span> <span class="keyword">throws</span> FileNotFoundException &#123;</span><br><span class="line"> <span class="comment">//想象这是你业务代码 调用上面方法</span></span><br><span class="line"> <span class="keyword">try</span> &#123;</span><br><span class="line"> <span class="comment">//这里输入不是数字也会抛出异常</span></span><br><span class="line"> <span class="type">int</span> <span class="variable">num</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Scanner</span>(System.in).nextInt();</span><br><span class="line"> System.out.println(doSomeThing(num));</span><br><span class="line"> <span class="comment">// 你只想 抛出FileNotFoundException</span></span><br><span class="line"> <span class="comment">// ,SQLExceptionn包装成runtimeException异常</span></span><br><span class="line"> <span class="comment">// 其他的异常你打印日志就可</span></span><br><span class="line"> &#125; <span class="keyword">catch</span> (FileNotFoundException e) &#123;</span><br><span class="line"> <span class="keyword">throw</span> e;</span><br><span class="line"> &#125; <span class="keyword">catch</span> (SQLException e) &#123;</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line"> &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;操作失败 失败原因&quot;</span>);</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</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_">test2</span><span class="params">()</span> <span class="keyword">throws</span> FileNotFoundException &#123;</span><br><span class="line"> <span class="comment">//想象这是你业务代码 调用上面方法</span></span><br><span class="line"> <span class="keyword">try</span> &#123;</span><br><span class="line"> <span class="comment">//这里输入不是数字也会抛出异常</span></span><br><span class="line"> <span class="type">int</span> <span class="variable">num</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Scanner</span>(System.in).nextInt();</span><br><span class="line"> System.out.println(doSomeThing(num));</span><br><span class="line"> &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line"> <span class="comment">//你只想 抛出FileNotFoundException SQLException,其他的异常你打印日志就可</span></span><br><span class="line"> Throwables.throwIfInstanceOf(e, FileNotFoundException.class);</span><br><span class="line"> Throwables.throwIfUnchecked(e);</span><br><span class="line"> System.out.println(<span class="string">&quot;操作失败 失败原因&quot;</span>);</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</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_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> FileNotFoundException &#123;</span><br><span class="line"> test2();</span><br><span class="line"> System.out.println(<span class="string">&quot;testEnd&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h1 id="不可变集合"><a href="#不可变集合" class="headerlink" title="不可变集合"></a>不可变集合</h1><ul>
<li>使用场景:</li>
<li>如定义一系列状态比如吃饭,睡觉,过马路,需要根据这个状态判断是否可以玩手机,可以在类中定义集合包装这个三个状态,如果当前状态属于三个之一那么不可以玩手机,你可以使用基本的hashset,但是hashset的元素可以被更改,导致可能方法的判断和原本的语义出现出入</li>
<li>优点<ul>
<li>当对象被不可信的库调用时,不可变形式是安全的;</li>
<li>不可变对象被多个线程调用时,不存在竞态条件问题</li>
<li>不可变集合不需要考虑变化,因此可以节省时间和空间。所有不可变的集合都比它们的可变形式有更好的内存利用率(分析和测试细节);</li>
<li>不可变对象因为有固定不变,可以作为常量来安全使用。</li>
</ul>
</li>
</ul>
<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_">ImmutableLearn</span> &#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> Set&lt;String&gt;statusCannotPlayWithPhone;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">ImmutableLearn</span><span class="params">(Set&lt;String&gt; statusCannotPlayWithPhone)</span> &#123;</span><br><span class="line"> <span class="built_in">this</span>.statusCannotPlayWithPhone = statusCannotPlayWithPhone;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">canPlayPhoneOrNot</span><span class="params">(String status)</span>&#123;</span><br><span class="line"> <span class="keyword">return</span> !statusCannotPlayWithPhone.contains(status);</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">showWhyShouldUseImmutable</span><span class="params">()</span>&#123;</span><br><span class="line"> <span class="type">ImmutableLearn</span> <span class="variable">immutableLearn</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ImmutableLearn</span>(Sets.newHashSet(<span class="string">&quot;sleep&quot;</span>, <span class="string">&quot;eat&quot;</span>, <span class="string">&quot;crossTheStreet&quot;</span>));</span><br><span class="line"> System.out.println(immutableLearn.canPlayPhoneOrNot(<span class="string">&quot;sleep&quot;</span>));</span><br><span class="line"> immutableLearn.statusCannotPlayWithPhone.remove(<span class="string">&quot;sleep&quot;</span>);</span><br><span class="line"> System.out.println(immutableLearn.canPlayPhoneOrNot(<span class="string">&quot;sleep&quot;</span>));</span><br><span class="line"></span><br><span class="line"> <span class="type">ImmutableLearn</span> <span class="variable">immutableLearn1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ImmutableLearn</span>(ImmutableSet.copyOf(Lists.newArrayList(<span class="string">&quot;sleep&quot;</span>, <span class="string">&quot;eat&quot;</span>, <span class="string">&quot;crossTheStreet&quot;</span>)));</span><br><span class="line"> System.out.println(immutableLearn.canPlayPhoneOrNot(<span class="string">&quot;sleep&quot;</span>));</span><br><span class="line"> immutableLearn.statusCannotPlayWithPhone.remove(<span class="string">&quot;sleep&quot;</span>);</span><br><span class="line"> System.out.println(immutableLearn.canPlayPhoneOrNot(<span class="string">&quot;sleep&quot;</span>));</span><br><span class="line"> &#125;</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_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line"> <span class="comment">//创建不可变集合的三种繁琐</span></span><br><span class="line"> ImmutableSet&lt;Integer&gt; set1 = ImmutableSet.of(<span class="number">1</span>);</span><br><span class="line"> ImmutableSet&lt;Integer&gt; set2 = ImmutableSet.copyOf(Lists.newArrayList(<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>));</span><br><span class="line"> Set&lt;Object&gt; set3 = ImmutableSet.builder()</span><br><span class="line"> .add(<span class="number">1</span>)</span><br><span class="line"> .addAll(set2)</span><br><span class="line"> .addAll(set1)</span><br><span class="line"> .add(<span class="number">10</span>)</span><br><span class="line"> .build();</span><br><span class="line"> System.out.println(set1);</span><br><span class="line"> System.out.println(set2);</span><br><span class="line"> System.out.println(set3);</span><br><span class="line"> <span class="comment">//不允许操作</span></span><br><span class="line"> set3.add(<span class="number">1</span>);</span><br><span class="line"> set3.remove(<span class="number">1</span>);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h1 id="新集合类型"><a href="#新集合类型" class="headerlink" title="新集合类型"></a>新集合类型</h1><h2 id="Multiset"><a href="#Multiset" class="headerlink" title="Multiset"></a>Multiset</h2><h3 id="可以用两种方式看待Multiset"><a href="#可以用两种方式看待Multiset" class="headerlink" title="可以用两种方式看待Multiset"></a>可以用两种方式看待Multiset</h3><ul>
<li>没有元素顺序限制的ArrayList当把Multiset看成普通的Collection时,它表现得就像无序的ArrayList:add(E)添加单个给定元素iterator()返回一个迭代器,包含Multiset的所有元素(包括重复的元素)size()返回所有元素的总个数(包括重复的元素)</li>
<li>Map&lt;E, Integer&gt;,键为元素,值为计数<ul>
<li>Multiset看作Map&lt;E, Integer&gt;时,它也提供了符合性能期望的查询操作:<ul>
<li>count(Object)返回给定元素的计数。HashMultiset.count的复杂度为O(1),TreeMultiset.count的复杂度为O(log n)。</li>
<li>entrySet()返回Set&lt;Multiset.Entry&gt;,和Map的entrySet类似。</li>
<li>elementSet()返回所有不重复元素的Set,和Map的keySet()类似。</li>
<li>所有Multiset实现的内存消耗随着不重复元素的个数线性增长。</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="使用Multiset例子"><a href="#使用Multiset例子" class="headerlink" title="使用Multiset例子"></a>使用Multiset例子</h3><p>1.统计一个list中单词出现的次数</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_">MultiSetLearn</span> &#123;</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 class="doctag">@param</span> list</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="keyword">static</span> Map&lt;String, Integer&gt; <span class="title function_">statisticsWordCount1</span><span class="params">(List&lt;String&gt; list)</span> &#123;</span><br><span class="line"> Map&lt;String, Integer&gt; countMap = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line"> <span class="keyword">if</span> (list == <span class="literal">null</span> || list.size() == <span class="number">0</span>) &#123;</span><br><span class="line"> <span class="keyword">return</span> countMap;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">for</span> (String s : list) &#123;</span><br><span class="line"> <span class="type">int</span> <span class="variable">nowCount</span> <span class="operator">=</span> countMap.getOrDefault(s, <span class="number">0</span>);</span><br><span class="line"> countMap.put(s, nowCount + <span class="number">1</span>);</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">return</span> countMap;</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 学了java 8新特性我会这么写</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> list</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="keyword">static</span> Map&lt;String, Integer&gt; <span class="title function_">statisticsWordCount2</span><span class="params">(List&lt;String&gt; list)</span> &#123;</span><br><span class="line"> list = Optional.ofNullable(list).orElse(Collections.emptyList());</span><br><span class="line"> <span class="keyword">return</span> list.stream()</span><br><span class="line"> .collect(</span><br><span class="line"> Collectors.groupingBy(t -&gt; t, Collectors.reducing(<span class="number">0</span>, num -&gt; <span class="number">1</span>, Integer::sum))</span><br><span class="line"> );</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 学了guava api</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> list</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="keyword">static</span> Map&lt;String, Integer&gt; <span class="title function_">statisticsWordCount3</span><span class="params">(List&lt;String&gt; list)</span> &#123;</span><br><span class="line"> list = Optional.ofNullable(list).orElse(Collections.emptyList());</span><br><span class="line"> HashMultiset&lt;String&gt; multiset = HashMultiset.create(list);</span><br><span class="line"> <span class="comment">//可以直接返回HashMultiset 那这个方法可以更加简单</span></span><br><span class="line"> <span class="comment">//但是对不熟悉的guava的同事的有点痛苦</span></span><br><span class="line"> System.out.println(multiset);</span><br><span class="line"> <span class="keyword">return</span> multiset.stream()</span><br><span class="line"> .collect(Collectors.toMap(item -&gt; item, multiset::count, BinaryOperator.maxBy(Ordering.natural())));</span><br><span class="line"> &#125;</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_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line"> Map&lt;String, Integer&gt; map = statisticsWordCount3(Arrays.asList(<span class="string">&quot;a&quot;</span>, <span class="string">&quot;b&quot;</span>, <span class="string">&quot;a&quot;</span>, <span class="string">&quot;c&quot;</span>));</span><br><span class="line"> map.forEach((k, v) -&gt; System.out.println(k + <span class="string">&quot;-&quot;</span> + v));</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="SortedMultiset"><a href="#SortedMultiset" class="headerlink" title="SortedMultiset"></a>SortedMultiset</h3><blockquote>
<p>&#x2F;&#x2F;就这?</p>
</blockquote>
<p>Multiset 接口的变种,它支持高效地获取指定范围的子集。</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_">SortMultiSetLearn</span> &#123;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 在不改变源money 元素的情况下 统计介于min和max间的钱</span></span><br><span class="line"><span class="comment"> * money中的null 视为0</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> List&lt;Float&gt; <span class="title function_">findMoneyBetween1</span><span class="params">(<span class="type">float</span> min, <span class="type">float</span> max, List&lt;Float&gt; money)</span> &#123;</span><br><span class="line"> money = Optional.ofNullable(money).orElse(Collections.emptyList());</span><br><span class="line"> <span class="keyword">return</span> money.stream().map(item -&gt; Optional.ofNullable(item).orElse(<span class="number">0F</span>))</span><br><span class="line"> .filter(item -&gt; item &gt; min &amp;&amp; item &lt; max)</span><br><span class="line"> .collect(Collectors.toList());</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 在不改变源money 元素的情况下 统计介于min和max间的钱</span></span><br><span class="line"><span class="comment"> * money中的null 视为0</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> List&lt;Float&gt; <span class="title function_">findMoneyBetween2</span><span class="params">(<span class="type">float</span> min, <span class="type">float</span> max, List&lt;Float&gt; money)</span> &#123;</span><br><span class="line"> money = Optional.ofNullable(money).orElse(Collections.emptyList());</span><br><span class="line"> TreeMultiset&lt;Float&gt; treeMultiset = TreeMultiset.create(Ordering.&lt;Float&gt;natural().onResultOf(f1 -&gt; Optional.ofNullable(f1).orElse(<span class="number">0F</span>)));</span><br><span class="line"> treeMultiset.addAll(money);</span><br><span class="line"> <span class="keyword">return</span> Lists.newLinkedList(treeMultiset.subMultiset(min, BoundType.CLOSED, max, BoundType.CLOSED));</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="Multimap"><a href="#Multimap" class="headerlink" title="Multimap"></a>Multimap</h2><p>Guava的 <a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/Multimap.html">Multimap</a>可以很容易地把一个键映射到多个值。换句话说,Multimap是把键映射到任意多个值的一般方式</p>
<h3 id="合并两个map"><a href="#合并两个map" class="headerlink" title="合并两个map"></a>合并两个map</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> Map&lt;String, Collection&lt;Integer&gt;&gt; <span class="title function_">mergeMap1</span><span class="params">(Map&lt;String, Integer&gt; map1, Map&lt;String, Integer&gt; map2)</span> &#123;</span><br><span class="line"> map1 = Optional.ofNullable(map1).orElse(Collections.emptyMap());</span><br><span class="line"> map2 = Optional.ofNullable(map2).orElse(Collections.emptyMap());</span><br><span class="line"> Set&lt;String&gt; allKey = Stream.of(map1.keySet(), map2.keySet()).flatMap(Collection::stream).collect(Collectors.toSet());</span><br><span class="line"> Map&lt;String, Collection&lt;Integer&gt;&gt; resMap = Maps.newHashMap();</span><br><span class="line"> <span class="keyword">for</span> (String key : allKey) &#123;</span><br><span class="line"> <span class="type">Integer</span> <span class="variable">integer1</span> <span class="operator">=</span> map1.get(key);</span><br><span class="line"> <span class="type">Integer</span> <span class="variable">integer2</span> <span class="operator">=</span> map2.get(key);</span><br><span class="line"> Set&lt;Integer&gt; tempSet = Sets.newHashSet();</span><br><span class="line"> <span class="keyword">if</span> (Objects.nonNull(integer1)) &#123;</span><br><span class="line"> tempSet.add(integer1);</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">if</span> (Objects.nonNull(integer2)) &#123;</span><br><span class="line"> tempSet.add(integer2);</span><br><span class="line"> &#125;</span><br><span class="line"> resMap.put(key, tempSet);</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">return</span> resMap;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> Map&lt;String, Collection&lt;Integer&gt;&gt; <span class="title function_">mergeMap2</span><span class="params">(Map&lt;String, Integer&gt; map1, Map&lt;String, Integer&gt; map2)</span> &#123;</span><br><span class="line"> map1 = Optional.ofNullable(map1).orElse(Collections.emptyMap());</span><br><span class="line"> map2 = Optional.ofNullable(map2).orElse(Collections.emptyMap());</span><br><span class="line"> Multimap&lt;String, Integer&gt; multimap = HashMultimap.create();</span><br><span class="line"> map1.forEach(multimap::put);</span><br><span class="line"> map2.forEach(multimap::put);</span><br><span class="line"> <span class="keyword">return</span> multimap.asMap();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="BiMap"><a href="#BiMap" class="headerlink" title="BiMap"></a>BiMap</h2><p><a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/BiMap.html">BiMap</a>是特殊的Map:</p>
<ul>
<li>可以用 <a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/BiMap.html#inverse()">inverse()</a>反转BiMap&lt;K, V&gt;的键值映射</li>
<li>保证值是唯一的,因此 <a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/collect/BiMap.html#values()">values()</a>返回Set而不是普通的Collection</li>
<li>在BiMap中,如果你想把键映射到已经存在的值,会抛出IllegalArgumentException异常。如果对特定值,你想要强制替换它的键,请使用 <a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/BiMap.html#forcePut(java.lang.Object,java.lang.Object)">BiMap.forcePut(key, value)</a>。</li>
</ul>
<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_">BiMapLearn</span> &#123;</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_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line"> <span class="comment">// 想象这是用户id 和用户名对应map</span></span><br><span class="line"> <span class="comment">// 你需要根据id查询用户名。根据用户名查询id(用户名同样不可以重复)</span></span><br><span class="line"> HashBiMap&lt;String, String&gt; userNameAndIdMap = HashBiMap.create();</span><br><span class="line"> userNameAndIdMap.put(<span class="string">&quot;2017015600&quot;</span>,<span class="string">&quot;陈兴cupk&quot;</span>);</span><br><span class="line"> userNameAndIdMap.put(<span class="string">&quot;80309525&quot;</span>,<span class="string">&quot;陈兴cmbnk&quot;</span>);</span><br><span class="line"> <span class="comment">// 重复value 会抛出异常ava.lang.IllegalArgumentException:</span></span><br><span class="line"> <span class="comment">// value already present: 陈兴cmbnk</span></span><br><span class="line"> <span class="comment">// userNameAndIdMap.put(&quot;309525&quot;,&quot;陈兴cmbnk&quot;);</span></span><br><span class="line"> <span class="comment">// forcePut 可以强制替换 key -value 组合</span></span><br><span class="line"> <span class="comment">// userNameAndIdMap.forcePut(&quot;309525&quot;,&quot;陈兴cmbnk&quot;);</span></span><br><span class="line"> System.out.println(userNameAndIdMap.get(<span class="string">&quot;2017015600&quot;</span>));</span><br><span class="line"> System.out.println(userNameAndIdMap.inverse().get(<span class="string">&quot;陈兴cmbnk&quot;</span>));</span><br><span class="line"> System.out.println(userNameAndIdMap.get(<span class="string">&quot;80309525&quot;</span>));</span><br><span class="line"> <span class="comment">// set类型的key value</span></span><br><span class="line"> Set&lt;String&gt; strings = userNameAndIdMap.keySet();</span><br><span class="line"> Set&lt;String&gt; values = userNameAndIdMap.values();</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="Table"><a href="#Table" class="headerlink" title="Table"></a>Table</h2><ul>
<li><p>使用场景:当你需要多个字段作为key时,你可能为这个key编写一个类,重写equals和hashMap。或者使用形同Map&lt;FirstName, Map&lt;LastName, Person&gt;&gt;的map结构,前者编码繁琐,后者使用不友好(第一个get后判空,后才能左第二次get)</p>
</li>
<li><p>Guava为此提供了新集合类型<a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/Table.html">Table</a>,它有两个支持所有类型的键:”行”和”列”。Table提供多种视图,以便你从各种角度使用它:</p>
<ul>
<li><a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/Table.html#rowMap()">rowMap()</a>:用Map&lt;R, Map&lt;C, V&gt;&gt;表现Table&lt;R, C, V&gt;。同样的, <a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/Table.html#rowKeySet()">rowKeySet()</a>返回”行”的集合Set。</li>
<li><a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/Table.html#row(R)">row(r)</a> :用Map&lt;C, V&gt;返回给定”行”的所有列,对这个map进行的写操作也将写入Table中。</li>
<li>类似的列访问方法:<a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/Table.html#columnMap()">columnMap()</a>、<a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/Table.html#columnKeySet()">columnKeySet()</a>、<a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/Table.html#column(C)">column(c)</a>。(基于列的访问会比基于的行访问稍微低效点)</li>
<li><a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/Table.html#cellSet()">cellSet()</a>:用元素类型为<a href="http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/Table.Cell.html">Table.Cell</a>的Set表现Table&lt;R, C, V&gt;。Cell类似于Map.Entry,但它是用行和列两个键区分的。</li>
</ul>
</li>
<li><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_">TableLearn</span> &#123;</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_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line"> System.out.println(getNameByAgeAndNo1(<span class="number">17</span>, <span class="string">&quot;201715600&quot;</span>));</span><br><span class="line"> System.out.println(getNameByAgeAndNo2(<span class="number">17</span>, <span class="string">&quot;201715600&quot;</span>));</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="comment">//根据年龄和编号 获取名字,编写KeyOfAgeAndNo 重写equals hashcode</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> String <span class="title function_">getNameByAgeAndNo1</span><span class="params">(<span class="type">int</span> age,String no)</span>&#123;</span><br><span class="line"> HashMap&lt;KeyOfAgeAndNo, String&gt; memory = Maps.newHashMap();</span><br><span class="line"> memory.put(KeyOfAgeAndNo.of(<span class="number">17</span>,<span class="string">&quot;201715600&quot;</span>),<span class="string">&quot;大一的陈兴&quot;</span>);</span><br><span class="line"> memory.put(KeyOfAgeAndNo.of(<span class="number">14</span>,<span class="string">&quot;0929&quot;</span>),<span class="string">&quot;高一的陈兴&quot;</span>);</span><br><span class="line"> memory.put(KeyOfAgeAndNo.of(<span class="number">20</span>,<span class="string">&quot;80303697&quot;</span>),<span class="string">&quot;实习的陈兴&quot;</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> Optional.ofNullable(memory.get(KeyOfAgeAndNo.of(age, no)))</span><br><span class="line"> .orElseThrow(() -&gt; <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(<span class="string">&quot;查无此人&quot;</span>));</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="comment">//编写KeyOfAgeAndNo 重写equals hashcode</span></span><br><span class="line"> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">KeyOfAgeAndNo</span>&#123;</span><br><span class="line"> Integer age;</span><br><span class="line"> String no;</span><br><span class="line"> <span class="keyword">static</span> KeyOfAgeAndNo <span class="title function_">of</span><span class="params">( Integer age,String no)</span>&#123;</span><br><span class="line"> <span class="type">KeyOfAgeAndNo</span> <span class="variable">res</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">KeyOfAgeAndNo</span>();</span><br><span class="line"> res.age=age;</span><br><span class="line"> res.no=no;</span><br><span class="line"> <span class="keyword">return</span> res;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">equals</span><span class="params">(Object o)</span> &#123;</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">this</span> == o) <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> <span class="keyword">if</span> (!(o <span class="keyword">instanceof</span> KeyOfAgeAndNo)) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> <span class="type">KeyOfAgeAndNo</span> <span class="variable">that</span> <span class="operator">=</span> (KeyOfAgeAndNo) o;</span><br><span class="line"> <span class="keyword">return</span> Objects.equals(age, that.age) &amp;&amp;</span><br><span class="line"> Objects.equals(no, that.no);</span><br><span class="line"> &#125;</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_">hashCode</span><span class="params">()</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> Objects.hash(age, no);</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="comment">//使用table </span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> String <span class="title function_">getNameByAgeAndNo2</span><span class="params">(<span class="type">int</span> age,String no)</span>&#123;</span><br><span class="line"> HashBasedTable&lt;Integer ,String,String&gt;table=HashBasedTable.create();</span><br><span class="line"> table.put(<span class="number">17</span>,<span class="string">&quot;201715600&quot;</span>,<span class="string">&quot;大一的陈兴&quot;</span>);</span><br><span class="line"> table.put(<span class="number">14</span>,<span class="string">&quot;0929&quot;</span>,<span class="string">&quot;高一的陈兴&quot;</span>);</span><br><span class="line"> table.put(<span class="number">20</span>,<span class="string">&quot;80303697&quot;</span>,<span class="string">&quot;实习的陈兴&quot;</span>);</span><br><span class="line"> <span class="keyword">return</span> Optional.ofNullable(table.get(age, no))</span><br><span class="line"> .orElseThrow(() -&gt; <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(<span class="string">&quot;查无此人&quot;</span>));</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
</ul>
<h2 id="ClassToInstanceMap"><a href="#ClassToInstanceMap" class="headerlink" title="ClassToInstanceMap"></a>ClassToInstanceMap</h2><ul>
<li><p>使用场景,类型指向实例,使用普通map需要</p>
</li>
<li><p>示例</p>
<blockquote>
<p>getInstanceByClass1需要进行强转因为map get方法返回object类型,不能限制key的类型<br>getInstanceByClass2则没有这种需要,且可以限定key的类型</p>
</blockquote>
<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_">ClassToInstanceMapLearn</span> &#123;</span><br><span class="line"> <span class="comment">//静态内部类实现单例 和ClassToInstanceMap 使用没有必要关系</span></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> &#123;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> ClassToInstanceMapLearn INSTANCE;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">static</span> &#123;</span><br><span class="line"> INSTANCE = <span class="keyword">new</span> <span class="title class_">ClassToInstanceMapLearn</span>();</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="title function_">ClassToInstanceMapLearn</span><span class="params">()</span> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;ClassToInstanceMapLearn Constructor&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> ClassToInstanceMapLearn <span class="title function_">newInstance</span><span class="params">()</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> SingletonHolder.INSTANCE;</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> Map&lt;Class&lt;?&gt;, ? <span class="built_in">super</span> ClassToInstanceMapLearn&gt; Memory1 = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">static</span> &#123;</span><br><span class="line"> Memory1.put(ClassToInstanceMapLearn.class, ClassToInstanceMapLearn.newInstance());</span><br><span class="line"> <span class="comment">//加入从 简单工场拿SingletonHolder实例 强转化 将抛出异常</span></span><br><span class="line"> Memory1.put(SingletonHolder.class, ClassToInstanceMapLearn.newInstance());</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> &lt;T <span class="keyword">extends</span> <span class="title class_">ClassToInstanceMapLearn</span>&gt; T <span class="title function_">getInstanceByClass1</span><span class="params">(Class&lt;T&gt; clazz)</span> &#123;</span><br><span class="line"> <span class="comment">//需要强转需要去判断 是否是clazz的实例 错误写法</span></span><br><span class="line"> <span class="keyword">return</span> (T) Optional.ofNullable(Memory1.get(clazz))</span><br><span class="line"> .orElseThrow(() -&gt; <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(<span class="string">&quot;不存在&quot;</span>));</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> ClassToInstanceMap&lt;? <span class="built_in">super</span> ClassToInstanceMapLearn&gt; Memory2 = MutableClassToInstanceMap.create();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">static</span> &#123;</span><br><span class="line"> Memory2.putInstance(ClassToInstanceMapLearn.class, ClassToInstanceMapLearn.newInstance());</span><br><span class="line"> <span class="comment">//无法加入</span></span><br><span class="line"> <span class="comment">// Memory2.put(SingletonHolder.class, ClassToInstanceMapLearn.newInstance());</span></span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> &lt;T <span class="keyword">extends</span> <span class="title class_">ClassToInstanceMapLearn</span>&gt; T <span class="title function_">getInstanceByClass2</span><span class="params">(Class&lt;T&gt; clazz)</span> &#123;</span><br><span class="line"> <span class="comment">//不需要强转</span></span><br><span class="line"> <span class="keyword">return</span> Optional.ofNullable(Memory2.getInstance(clazz))</span><br><span class="line"> .orElseThrow(() -&gt; <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(<span class="string">&quot;不存在&quot;</span>));</span><br><span class="line"> &#125;</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_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line"> System.out.println(getInstanceByClass1(ClassToInstanceMapLearn.class));</span><br><span class="line"> System.out.println(getInstanceByClass2(ClassToInstanceMapLearn.class));</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
</ul>
<h1 id="集合工具类"><a href="#集合工具类" class="headerlink" title="集合工具类"></a>集合工具类</h1><ul>
<li><p>guava 中的集合工具常常以集合名称加s出现</p>
<ul>
<li>Collections2 因为java存在Collections guava加了2</li>
<li>Lists</li>
<li>Maps</li>
<li>Sets</li>
<li>等等 上面介绍的新集合类型也存在对应的工具类</li>
</ul>
</li>
<li><p>这些工具类的共性</p>
<ul>
<li><p>都存在静态工厂方法</p>
<ul>
<li><p>为什么要使用静态工厂方法,它相比于构造方法(这里的静态工厂方法不是指,设计模式中的工厂模式)</p>
<p>《Effective Java》第一条 使用静态工厂方法代替构造器,给予了解答</p>
<ul>
<li><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="type">BigInteger</span> <span class="variable">big1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BigInteger</span>(<span class="number">10</span>, <span class="number">100</span>, <span class="keyword">new</span> <span class="title class_">Random</span>(<span class="number">10</span>));</span><br><span class="line">System.out.println(big1);</span><br><span class="line"><span class="comment">//这一句又是什么意思</span></span><br><span class="line"><span class="type">BigInteger</span> <span class="variable">big2</span> <span class="operator">=</span> BigInteger.probablePrime(<span class="number">10</span>, <span class="keyword">new</span> <span class="title class_">Random</span>(<span class="number">10</span>));</span><br><span class="line">System.out.println(big2);</span><br></pre></td></tr></table></figure>
</li>
<li><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="type">boolean</span> <span class="variable">flag</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Random</span>().nextInt() % <span class="number">2</span> == <span class="number">0</span>;</span><br><span class="line"><span class="comment">//每次生成一个新对象</span></span><br><span class="line"><span class="type">Boolean</span> <span class="variable">b1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Boolean</span>(flag);</span><br><span class="line"><span class="comment">//不会生成新对象</span></span><br><span class="line"><span class="type">Boolean</span> <span class="variable">b2</span> <span class="operator">=</span> Boolean.valueOf(flag);</span><br></pre></td></tr></table></figure>
</li>
<li><p>静态工厂方法可以返回任何原返回类型的子类型,如guava中的api</p>
</li>
<li><p>静态工厂的返回对象的类可也随着每次调用而变化,取决于入参类似于简单工厂模式</p>
</li>
<li><p>静态工厂方法返回的对象所属的类可以在,在编写百行该静态工厂方法的类时不存在,如JDBC数据库连接</p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="Collections2"><a href="#Collections2" class="headerlink" title="Collections2"></a>Collections2</h2><ul>
<li><p>过滤</p>
<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_">filterLearn</span><span class="params">()</span> &#123;</span><br><span class="line"> List&lt;Integer&gt; list = Arrays.asList(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="literal">null</span>);</span><br><span class="line"> Collection&lt;Integer&gt; filter = Collections2.filter(list, Objects::nonNull);</span><br><span class="line"> System.out.println(list);</span><br><span class="line"> System.out.println(filter);</span><br><span class="line"> <span class="comment">//返回一个继承了AbstractCollection的集合</span></span><br><span class="line"> System.out.println(filter.getClass());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><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_">Collections2Learn</span> &#123;</span><br><span class="line"> <span class="keyword">private</span> Integer nums;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">Collections2Learn</span><span class="params">(Integer nums)</span> &#123;</span><br><span class="line"> <span class="built_in">this</span>.nums = nums;</span><br><span class="line"> &#125;</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_">transformLearn</span><span class="params">()</span> &#123;</span><br><span class="line"> com.cuzz.miscellaneous.guava.collectionutils.<span class="type">Collections2Learn</span> <span class="variable">c1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">com</span>.cuzz.miscellaneous.guava.collectionutils.Collections2Learn(<span class="number">1</span>);</span><br><span class="line"> com.cuzz.miscellaneous.guava.collectionutils.<span class="type">Collections2Learn</span> <span class="variable">c2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">com</span>.cuzz.miscellaneous.guava.collectionutils.Collections2Learn(<span class="number">2</span>);</span><br><span class="line"> com.cuzz.miscellaneous.guava.collectionutils.<span class="type">Collections2Learn</span> <span class="variable">c3</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">com</span>.cuzz.miscellaneous.guava.collectionutils.Collections2Learn(<span class="number">3</span>);</span><br><span class="line"> List&lt;com.cuzz.miscellaneous.guava.collectionutils.Collections2Learn&gt; list = Arrays.asList(c1, c2, c3);</span><br><span class="line"> Collection&lt;Integer&gt; transform = Collections2.transform(list,</span><br><span class="line"> t -&gt; Optional.ofNullable(t)</span><br><span class="line"> .orElse(<span class="keyword">new</span> <span class="title class_">com</span>.cuzz.miscellaneous.guava.collectionutils.Collections2Learn(<span class="number">0</span>)).nums);</span><br><span class="line"> System.out.println(transform);</span><br><span class="line"> System.out.println(transform.getClass());</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
</li>
<li><p>全排列</p>
<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> &#123;</span><br><span class="line"> ArrayList&lt;Integer&gt; list = Lists.newArrayList(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>);</span><br><span class="line"> Collection&lt;List&lt;Integer&gt;&gt; lists = Collections2.orderedPermutations(list);</span><br><span class="line"> lists.forEach(System.out::println);</span><br><span class="line"> Collection&lt;List&lt;Integer&gt;&gt; permutations = Collections2.permutations(list);</span><br><span class="line"> System.out.println(<span class="string">&quot;====&quot;</span>);</span><br><span class="line"> permutations.forEach(System.out::println);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
</ul>
<h2 id="lists"><a href="#lists" class="headerlink" title="lists"></a>lists</h2><ul>
<li><p>切割</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">//获取一个字符串中的全部字符,返回不可变集合</span></span><br><span class="line">ImmutableList&lt;Character&gt; chars = Lists.charactersOf(<span class="string">&quot;123&quot;</span>);</span><br><span class="line">System.out.println(chars);</span><br><span class="line"><span class="comment">//按照大小分割list</span></span><br><span class="line">ArrayList&lt;Integer&gt; intList = Lists.newArrayList(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>);</span><br><span class="line">List&lt;List&lt;Integer&gt;&gt; partitionList = Lists.partition(intList, <span class="number">2</span>);</span><br><span class="line">System.out.println(partitionList);</span><br></pre></td></tr></table></figure></li>
</ul>
<h2 id="sets"><a href="#sets" class="headerlink" title="sets"></a>sets</h2><ul>
<li><p>交集</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">HashSet&lt;Integer&gt; set1 = Sets.newHashSet(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>);</span><br><span class="line">HashSet&lt;Integer&gt; set2 = Sets.newHashSet(<span class="number">1</span>, <span class="number">2</span>, <span class="number">4</span>,<span class="number">5</span>);</span><br><span class="line"><span class="comment">//返回交集</span></span><br><span class="line">Sets.SetView&lt;Integer&gt; intersection = Sets.intersection(set1, set2);</span><br><span class="line">System.out.println(intersection);</span><br></pre></td></tr></table></figure>
</li>
<li><p>差集</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">//返回set1中存在 s2中不存在的元素</span></span><br><span class="line">System.out.println(Sets.difference(set1, set2));</span><br><span class="line">System.out.println(Sets.difference(set2, set1));</span><br></pre></td></tr></table></figure>
</li>
<li><p>并集</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">//返回并集</span></span><br><span class="line">Sets.SetView&lt;Integer&gt; union = Sets.union(set1, set2);</span><br><span class="line">System.out.println(union);</span><br></pre></td></tr></table></figure>
</li>
<li><p>过滤</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">System.out.println(Sets.filter(union, t -&gt; t % <span class="number">2</span> == <span class="number">0</span>));</span><br></pre></td></tr></table></figure></li>
</ul>
<h2 id="maps"><a href="#maps" class="headerlink" title="maps"></a>maps</h2><ul>
<li><p>uniqueIndex 根据传入的function生成map</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">ArrayList&lt;Integer&gt; list1 = Lists.newArrayList(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">6</span>);</span><br><span class="line"><span class="comment">//传入function根据function生成map 要求 key 不可重复</span></span><br><span class="line">ImmutableMap&lt;String, Integer&gt; integerImmutableMap = Maps.uniqueIndex(list1, String::valueOf);</span><br></pre></td></tr></table></figure>
</li>
<li><p>获取两个map的不同</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">//如果你预计hashMap的大小请使用这个方法</span></span><br><span class="line">HashMap&lt;String, Integer&gt; map1 = Maps.newHashMapWithExpectedSize(<span class="number">3</span>);</span><br><span class="line">map1.put(<span class="string">&quot;1&quot;</span>, <span class="number">1</span>);</span><br><span class="line">map1.put(<span class="string">&quot;2&quot;</span>, <span class="number">2</span>);</span><br><span class="line">map1.put(<span class="string">&quot;3&quot;</span>, <span class="number">3</span>);</span><br><span class="line">map1.put(<span class="string">&quot;4&quot;</span>, <span class="number">3</span>);</span><br><span class="line">map1.put(<span class="string">&quot;5&quot;</span>, <span class="number">5</span>);</span><br><span class="line">ArrayList&lt;Integer&gt; list1 = Lists.newArrayList(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">6</span>);</span><br><span class="line"><span class="comment">//传入function根据function生成map 要求 key 不可重复</span></span><br><span class="line">ImmutableMap&lt;String, Integer&gt; integerImmutableMap = Maps.uniqueIndex(list1, String::valueOf);</span><br><span class="line">MapDifference&lt;String, Integer&gt; difference = Maps.difference(map1, integerImmutableMap);</span><br><span class="line"><span class="comment">//左边独有key</span></span><br><span class="line">Map&lt;String, Integer&gt; mapLeft = difference.entriesOnlyOnLeft();</span><br><span class="line"><span class="comment">//右边独有key</span></span><br><span class="line">Map&lt;String, Integer&gt; mapRight = difference.entriesOnlyOnRight();</span><br><span class="line"><span class="comment">//两个map相同key 但是不同value</span></span><br><span class="line">Map&lt;String, MapDifference.ValueDifference&lt;Integer&gt;&gt; valueDifferenceMap = difference.entriesDiffering();</span><br><span class="line"><span class="comment">//左边map的值 有边map的值</span></span><br><span class="line">System.out.println(valueDifferenceMap.get(<span class="string">&quot;4&quot;</span>).rightValue());</span><br><span class="line">System.out.println(valueDifferenceMap.get(<span class="string">&quot;4&quot;</span>).leftValue());</span><br></pre></td></tr></table></figure>
</li>
<li><p>过滤</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">//过滤map 中的Entries</span></span><br><span class="line">Map&lt;String, Integer&gt; filterEntriesMap = Maps.filterEntries(map1, e -&gt; &#123;</span><br><span class="line"> <span class="keyword">assert</span> e != <span class="literal">null</span>;</span><br><span class="line"> <span class="keyword">return</span> StringUtils.equals(e.getKey(), String.valueOf(e.getValue()));</span><br><span class="line">&#125;);</span><br><span class="line"><span class="comment">//过滤key</span></span><br><span class="line">Map&lt;String, Integer&gt; filterKeysMap = Maps.filterKeys(map1, StringUtils::isNotBlank);</span><br><span class="line"><span class="comment">//过滤value</span></span><br><span class="line">Map&lt;String, Integer&gt; filterValuesMap = Maps.filterValues(map1, v -&gt; &#123;</span><br><span class="line"> <span class="keyword">assert</span> v != <span class="literal">null</span>;</span><br><span class="line"> <span class="keyword">return</span> v % <span class="number">2</span> == <span class="number">0</span>;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>
</li>
<li><p>根据map构造转换器</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">HashBiMap&lt;String, Integer&gt; biMapForConverter = HashBiMap.create(integerImmutableMap);</span><br><span class="line">Converter&lt;String, Integer&gt; converter = Maps.asConverter(biMapForConverter);</span><br><span class="line">System.out.println(converter.convert(<span class="string">&quot;1&quot;</span>));</span><br><span class="line">Iterable&lt;Integer&gt; convertRes = converter.convertAll(Arrays.asList(<span class="string">&quot;1&quot;</span>, <span class="string">&quot;2&quot;</span>));</span><br></pre></td></tr></table></figure>
</li>
<li><p>转换</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">Map&lt;String, String&gt; transformEntriesMap = Maps.transformEntries(map1, (key, value) -&gt; String.valueOf(map1.get(key)));</span><br><span class="line"><span class="comment">//同样还存在</span></span><br><span class="line"><span class="comment">// Maps.transformValues()</span></span><br></pre></td></tr></table></figure></li>
</ul>
<h1 id="字符串处理"><a href="#字符串处理" class="headerlink" title="字符串处理"></a>字符串处理</h1><h2 id="连接器Joiner"><a href="#连接器Joiner" class="headerlink" title="连接器Joiner"></a>连接器Joiner</h2><ul>
<li><p>连接任何实现了Iterable结果的类型</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">List&lt;Integer&gt; list = Arrays.asList(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="literal">null</span>, <span class="number">5</span>, <span class="literal">null</span>);</span><br><span class="line"><span class="comment">//跳过null</span></span><br><span class="line"><span class="type">String</span> <span class="variable">str1</span> <span class="operator">=</span> Joiner.on(<span class="string">&quot;-&quot;</span>).skipNulls().join(list);</span><br><span class="line">System.out.println(str1);</span><br><span class="line"><span class="comment">//用NNNN代替空</span></span><br><span class="line"><span class="type">String</span> <span class="variable">str2</span> <span class="operator">=</span> Joiner.on(<span class="string">&quot;-&quot;</span>).useForNull(<span class="string">&quot;NNNN&quot;</span>).join(list);</span><br><span class="line">System.out.println(str2);</span><br><span class="line"><span class="comment">//空指针</span></span><br><span class="line">String str3= Joiner.on(<span class="string">&quot;-&quot;</span>).join(list);</span><br><span class="line">System.out.println(str3);</span><br></pre></td></tr></table></figure>
</li>
<li><p>连接map</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">HashMap&lt;String, String&gt; map = Maps.newHashMap();</span><br><span class="line">map.put(<span class="string">&quot;a&quot;</span>,<span class="string">&quot;1&quot;</span>);</span><br><span class="line">map.put(<span class="string">&quot;b&quot;</span>,<span class="string">&quot;2&quot;</span>);</span><br><span class="line"><span class="comment">//每一个k-v连接方式为\n kv连接方式为-&gt;</span></span><br><span class="line"><span class="type">String</span> <span class="variable">str1</span> <span class="operator">=</span> Joiner.on(<span class="string">&quot;\n&quot;</span>).withKeyValueSeparator(<span class="string">&quot;-&gt;&quot;</span>).join(map);</span><br><span class="line">System.out.println(str1);</span><br></pre></td></tr></table></figure>
</li>
<li><p>连接实现了Appendable的任何类型</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="type">StringBuilder</span> <span class="variable">str3</span> <span class="operator">=</span> Joiner.on(<span class="string">&quot;-&quot;</span>).appendTo(<span class="keyword">new</span> <span class="title class_">StringBuilder</span>(), Arrays.asList(<span class="string">&quot;1&quot;</span>, <span class="string">&quot;a&quot;</span>,<span class="string">&quot;2&quot;</span>));</span><br><span class="line">System.out.println(str3);</span><br></pre></td></tr></table></figure></li>
</ul>
<h2 id="分割器"><a href="#分割器" class="headerlink" title="分割器"></a>分割器</h2><blockquote>
<p>JDK内建的字符串拆分工具有一些古怪的特性。比如,String.split悄悄丢弃了尾部的分隔符。 问题:”,a,,b,”.split(“,”)返回?</p>
<p>1.“”, “a”, “”, “b”, “”<br>2.null, “a”, null, “b”, null<br>3.“a”, null, “b”<br>4.“a”, “b”<br>5.以上都不对<br>正确答案是5:””, “a”, “”, “b”。只有尾部的空字符串被忽略了。 Splitter使用令人放心的、直白的流畅API模式对这些混乱的特性作了完全的掌控。</p>
</blockquote>
<ul>
<li><p>分割成list</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">String str=<span class="string">&quot;1-2 -3 - 4- - - &quot;</span>;</span><br><span class="line">List&lt;String&gt; list1 = Splitter.fixedLength(<span class="number">2</span>).splitToList(str);</span><br><span class="line">System.out.println(list1);</span><br><span class="line">List&lt;String&gt; list2 = Splitter.on(<span class="string">&quot;-&quot;</span>).splitToList(str);</span><br><span class="line">System.out.println(list2);</span><br><span class="line">List&lt;String&gt; list3 = Splitter.on(<span class="string">&quot;-&quot;</span>).trimResults().splitToList(str);</span><br><span class="line">System.out.println(list3);</span><br></pre></td></tr></table></figure>
</li>
<li><p>分割成map</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">String str2=<span class="string">&quot;1#2-2#3-3#1&quot;</span>;</span><br><span class="line"><span class="comment">//每一组entry使用的是-分割 k和v使用的#分割</span></span><br><span class="line">Map&lt;String, String&gt; map = Splitter.on(<span class="string">&quot;-&quot;</span>).withKeyValueSeparator(<span class="string">&quot;#&quot;</span>).split(str2);</span><br><span class="line">map.forEach((k,v)-&gt; System.out.println(k+<span class="string">&quot;-&gt;&quot;</span>+v));</span><br></pre></td></tr></table></figure>
</li>
<li><p>分割成Iterable</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">Iterable&lt;String&gt; stringIterable = Splitter.on(<span class="string">&quot;-&quot;</span>).split(str);</span><br><span class="line">stringIterable.iterator().forEachRemaining(System.out::println);</span><br></pre></td></tr></table></figure></li>
</ul>
<h2 id="字符匹配器"><a href="#字符匹配器" class="headerlink" title="字符匹配器"></a>字符匹配器</h2><table>
<thead>
<tr>
<th align="left"><strong>方法</strong></th>
<th align="left"><strong>描述</strong></th>
</tr>
</thead>
<tbody><tr>
<td align="left"><a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/CharMatcher.html#anyOf(java.lang.CharSequence)"><code>anyOf(CharSequence)</code></a></td>
<td align="left">枚举匹配字符。如CharMatcher.anyOf(“aeiou”)匹配小写英语元音</td>
</tr>
<tr>
<td align="left"><a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/CharMatcher.html#is(char)"><code>is(char)</code></a></td>
<td align="left">给定单一字符匹配。</td>
</tr>
<tr>
<td align="left">[<code>inRange(char, char)</code>](<a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/CharMatcher.html#inRange">http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/CharMatcher.html#inRange</a>(char, char))</td>
<td align="left">给定字符范围匹配,如CharMatcher.inRange(‘a’, ‘z’)</td>
</tr>
</tbody></table>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="comment">//删除字符</span></span><br><span class="line"><span class="type">String</span> <span class="variable">str</span> <span class="operator">=</span> <span class="string">&quot;/1/2/3/4&quot;</span>;</span><br><span class="line"><span class="type">String</span> <span class="variable">str1</span> <span class="operator">=</span> CharMatcher.is(<span class="string">&#x27;/&#x27;</span>).removeFrom(str);</span><br><span class="line">System.out.println(str1);</span><br><span class="line"><span class="type">String</span> <span class="variable">str2</span> <span class="operator">=</span> CharMatcher.anyOf(<span class="string">&quot;/1&quot;</span>).removeFrom(str);</span><br><span class="line">System.out.println(str2);</span><br><span class="line"><span class="type">String</span> <span class="variable">str3</span> <span class="operator">=</span> CharMatcher.noneOf(<span class="string">&quot;12/&quot;</span>).removeFrom(str);</span><br><span class="line">System.out.println(str3);</span><br><span class="line"></span><br><span class="line"><span class="type">String</span> <span class="variable">str4</span> <span class="operator">=</span> CharMatcher.inRange(<span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;9&#x27;</span>).removeFrom(str);</span><br><span class="line">System.out.println(str4);</span><br><span class="line"><span class="comment">//替换</span></span><br><span class="line"><span class="type">String</span> <span class="variable">str5</span> <span class="operator">=</span> CharMatcher.inRange(<span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;9&#x27;</span>).replaceFrom(<span class="string">&quot;a1b2c3&quot;</span>, <span class="string">&quot;.&quot;</span>);</span><br><span class="line">System.out.println(str5);</span><br><span class="line"><span class="comment">//裁剪</span></span><br><span class="line"><span class="type">String</span> <span class="variable">str6</span> <span class="operator">=</span> CharMatcher.inRange(<span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;9&#x27;</span>).trimTrailingFrom(<span class="string">&quot;a1b2c3&quot;</span>);</span><br><span class="line">System.out.println(str6);</span><br><span class="line"><span class="comment">//比对</span></span><br><span class="line">System.out.println(CharMatcher.inRange(<span class="string">&#x27;1&#x27;</span>, <span class="string">&#x27;9&#x27;</span>).matchesAllOf(<span class="string">&quot;1b2&quot;</span>));</span><br></pre></td></tr></table></figure>
<h2 id="字符集和大小写格式"><a href="#字符集和大小写格式" class="headerlink" title="字符集和大小写格式"></a>字符集和大小写格式</h2><ul>
<li><a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/Charsets.html"><code>Charsets</code></a>针对所有Java平台都要保证支持的六种字符集提供了常量引用。尝试使用这些常量,而不是通过名称获取字符集实例。</li>
<li>CaseFormat</li>
</ul>
<table>
<thead>
<tr>
<th align="left"><strong>格式</strong></th>
<th align="left"><strong>范例</strong></th>
</tr>
</thead>
<tbody><tr>
<td align="left"><a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/CaseFormat.html#LOWER_CAMEL"><code>LOWER_CAMEL</code></a></td>
<td align="left">lowerCamel</td>
</tr>
<tr>
<td align="left"><a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/CaseFormat.html#LOWER_HYPHEN"><code>LOWER_HYPHEN</code></a></td>
<td align="left">lower-hyphen</td>
</tr>
<tr>
<td align="left"><a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/CaseFormat.html#LOWER_UNDERSCORE"><code>LOWER_UNDERSCORE</code></a></td>
<td align="left">lower_underscore</td>
</tr>
<tr>
<td align="left"><a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/CaseFormat.html#UPPER_CAMEL"><code>UPPER_CAMEL</code></a></td>
<td align="left">UpperCamel</td>
</tr>
<tr>
<td align="left"><a href="http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/CaseFormat.html#UPPER_UNDERSCORE"><code>UPPER_UNDERSCORE</code></a></td>
<td align="left">UPPER_UNDERSCORE</td>
</tr>
</tbody></table>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL, <span class="string">&quot;caseFormat&quot;</span>)</span><br></pre></td></tr></table></figure>]]></content>
<categories>
<category>实用工具</category>
</categories>
<tags>
<tag>代码美学</tag>
<tag>Guava</tag>
</tags>
</entry>
<entry>
<title>(二)搭完架子串珠子——流程引擎</title>
<url>/2023/09/03/process-engine/</url>
<content><![CDATA[<h1 id="单用模板方法带来的问题"><a href="#单用模板方法带来的问题" class="headerlink" title="单用模板方法带来的问题"></a>单用模板方法带来的问题</h1><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> UserInfoDTO <span class="title function_">getUserInfo</span><span class="params">(<span class="meta">@RequestParam(&quot;userId&quot;)</span> String userId)</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> (<span class="keyword">new</span> <span class="title class_">ServiceTemplate</span>&lt;String&gt;() &#123;</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_">validParam</span><span class="params">(String request)</span> &#123;</span><br><span class="line"> <span class="comment">// 1. 校验入参</span></span><br><span class="line"> <span class="keyword">if</span> (userId == <span class="literal">null</span> || userId.isEmpty()) &#123;</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(<span class="string">&quot;param UserId is invalid&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</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> UserInfoDTO <span class="title function_">doProcess</span><span class="params">(String request)</span> &#123;</span><br><span class="line"> <span class="comment">// 2. 获取用户基础信息</span></span><br><span class="line"> <span class="type">UserBaseInfoVO</span> <span class="variable">userBaseInfoVO</span> <span class="operator">=</span> userBaseInfoRepository.getUserBaseInfo(userId);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 3. 获取用户特殊信息</span></span><br><span class="line"> <span class="type">UserSpecialInfoVO</span> <span class="variable">userSpecialInfoVO</span> <span class="operator">=</span> UserSpecialInfoRepository.getSpecialInfoVO(userId);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 4. 获取用户余额</span></span><br><span class="line"> <span class="type">UserMoneyVO</span> <span class="variable">userMoneyVO</span> <span class="operator">=</span> userMoneyRepository.getUserMoneyVO(userId);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 5. 获取用户消费记录</span></span><br><span class="line"> List&lt;ConsumeRecordVO&gt; consumeRecordVOList = userConsumeRepository.getConsumeRecordVOList(userId);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 6. 计算最贵一次消费</span></span><br><span class="line"> Optional&lt;ConsumeRecordVO&gt; max = consumeRecordVOList.stream()</span><br><span class="line"> .max(Comparator.comparing(ConsumeRecordVO::getAmount));</span><br><span class="line"> <span class="type">ConsumeDTO</span> <span class="variable">maxAmountConsume</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line"> <span class="keyword">if</span> (max.isPresent()) &#123;</span><br><span class="line"> <span class="type">ConsumeRecordVO</span> <span class="variable">consumeRecordVO</span> <span class="operator">=</span> max.get();</span><br><span class="line"> maxAmountConsume = ConsumeDTO.builder()</span><br><span class="line"> .amount(consumeRecordVO.getAmount())</span><br><span class="line"> .date(consumeRecordVO.getDate())</span><br><span class="line"> .build();</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 7. 计算用户充值总金额</span></span><br><span class="line"> <span class="type">DoubleSummaryStatistics</span> <span class="variable">total</span> <span class="operator">=</span> consumeRecordVOList.stream()</span><br><span class="line"> .collect(Collectors.summarizingDouble(ConsumeRecordVO::getAmount));</span><br><span class="line"> <span class="type">double</span> <span class="variable">totalMoney</span> <span class="operator">=</span> userMoneyVO.getMoney() + total.getSum();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> UserInfoDTO.builder()</span><br><span class="line"> .userName(userBaseInfoVO.getUserName())</span><br><span class="line"> .vipLevel(userBaseInfoVO.getVipLevel())</span><br><span class="line"> .maxAmountConsume(maxAmountConsume)</span><br><span class="line"> .totalMoney(totalMoney)</span><br><span class="line"> .build();</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;).process(userId);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><a href="https://orionli.gitee.io/blog/2023/09/02/template-method/">上节</a>说到,虽然我们在架构设计上没啥大问题了,但当我们:</p>
<ul>
<li><strong>加逻辑:</strong><code>getUserlnfo</code>新增加用户优惠券信息</li>
<li><strong>改逻辑:</strong>最贵一次消费记录要改成近一个月的</li>
<li><strong>增加复杂判断:</strong>如果是授权了的用户,才查询余额,没有授权不能查</li>
<li><strong>外部需要实现类中某一块代码</strong>:在另一个接口也需要查询消费记录并计算最大的一次消费</li>
<li><strong>重复修改:</strong>在第四点基础上,所有查消费记录的方式需要换一个接口,请求和返回模型均发生变化</li>
</ul>
<p>我们的代码都在一个文件里加,会导致代码:</p>
<ul>
<li><strong>腕肿难懂:</strong>看起来费劲,而且不同同学代码风格不一致,导致注释还可能难懂和有歧义</li>
<li><strong>容易相互影响:</strong>太多行,一不小心改错了;又或者有依赖顺序、数据结构不能有变动</li>
<li><strong>不好测试:</strong>为了测方法中的某一处逻辑,不得不在方法入参上引入大量无关数据进行mock。而且万一中途某一块其他逻辑判断非常复杂,那mock难度就是指数级上升</li>
<li><strong>相同逻辑重复实现:</strong>如果想调用实现类中某处逻辑,只能将这部分代码复制粘贴。如果后续还要修改调用接口,可能会导致代码可能错、漏、不一致。修改工作量翻倍</li>
</ul>
<p>说白了,现在业务代码都写在一起,牵一发而动全身,也就是<strong>高耦合</strong>了!</p>
<h1 id="怎么解决耦合"><a href="#怎么解决耦合" class="headerlink" title="怎么解决耦合"></a>怎么解决耦合</h1><h2 id="先来看一个生活中的例子"><a href="#先来看一个生活中的例子" class="headerlink" title="先来看一个生活中的例子"></a>先来看一个生活中的例子</h2><p>我们家里有很多螺丝刀,但是</p>
<ul>
<li>我们很少用它们——<strong>使用场景少</strong></li>
<li>而且各个螺丝刀尺寸和类型(一字头,十字头等等)不一致——<strong>坏了的话整个螺丝刀就没用了</strong></li>
<li>体积大、类型多——<strong>带着费劲</strong></li>
</ul>
<p>那维修工是怎么解决的呢?</p>
<p><strong>只用一个螺丝刀柄</strong>,通过<strong>更换不同的螺丝刀头</strong>实现</p>
<ul>
<li>什么螺丝都能拧——<strong>通用</strong></li>
<li>坏了一个换一个——<strong>易更换</strong></li>
<li><strong>方便携带(体积小)</strong></li>
<li><strong>省成本</strong></li>
<li><strong>定制轻松</strong></li>
</ul>
<p>总结下来,就是我们要把<strong>功能拆细(造珠子)、组装便捷(串珠子)</strong></p>
<h1 id="流程引擎模式"><a href="#流程引擎模式" class="headerlink" title="流程引擎模式"></a>流程引擎模式</h1><p>造珠子 + 串珠子 &#x3D;&gt; 责任链模式(非典型) &#x3D;&gt; 流程引擎</p>
<p>流程引擎的核心思想是:<strong>将要执行的逻辑看成是一个个步骤的串接,由统一的角色来管理步骤的执行顺序,这个角色就是流程引擎</strong>。</p>
<p>我们用两张图来对比下使用流程引擎和常规瀑布式编码的不同。</p>
<p><img src="https://mmbiz.qpic.cn/mmbiz_jpg/KExqX4mSOW8aPTcctrttFQyeNI2kqcsGptrDDxMAxZ4ib1ib9a490VCKbBrDW5IWsBSdUWeHMa9Isfp2yxaUxLHw/640?wx_fmt=jpeg&wxfrom=5&wx_lazy=1&wx_co=1"></p>
<p>【瀑布式编码】就是从上往下按照步骤把业务逻辑写完。</p>
<p>【流程式编码】是先把可以独立的功能抽成一个个<strong>执行器</strong>。不同的服务根据自己功能的需求来串接这些执行器。</p>
<p>两者对比,流程式编码有这样一些好处:</p>
<p><strong>【避免冗余】</strong>:同样的业务逻辑只有一份代码。</p>
<p><strong>【最小修改】</strong>:如果需要加一个环节,只需要新增一个处理器,并且编排到流程中即可,对已有代码没有任何侵入。</p>
<p><strong>【方便追踪】</strong>:我们可以在每一个节点执行完以后,在流程引擎中添加一些日志,以此来追踪执行过程。例如在哪里中断了?哪个执行器耗时最长?</p>
<p><strong>【利于分工】</strong>:每个处理器约定好职责就可以独立开发,并且可以独立测试。</p>
<p><strong>【可读性好】</strong>:流程式代码往往在一处编辑所有的步骤,代码可读性佳。看到一个流程由哪些节点组成,基本上就了解大概的逻辑了。</p>
<p><strong>【灵活多变】</strong>:流程式编程还可以支持各个处理器以分支和循环的方式组合。</p>
<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">interface</span> <span class="title class_">Processor</span> &#123;</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="comment"> * <span class="doctag">@param</span> request 初始请求,跟随责任链一路传下去</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> context 该处理器的输出结果,存储责任链的中间结果或返回给外层的最终结果</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> true 如果需要执行,否则返回 false</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="type">boolean</span> <span class="title function_">needExecute</span><span class="params">(ProcessRequest request, ProcessContext context)</span>;</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="comment"> * <span class="doctag">@param</span> request 初始请求,跟随责任链一路传下去</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> context 该处理器的输出结果,存储责任链的中间结果或返回给外层的最终结果</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">void</span> <span class="title function_">execute</span><span class="params">(ProcessRequest request, ProcessContext context)</span>;</span><br><span class="line">&#125;</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="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="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserInfoQueryProcessor</span> <span class="keyword">implements</span> <span class="title class_">Processor</span> &#123;</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> UserBaseInfoRepository userBaseInfoRepository;</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> UserSpecialInfoRepository userSpecialInfoRepository;</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">boolean</span> <span class="title function_">needExecute</span><span class="params">(ProcessRequest request, ProcessContext context)</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> &#125;</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_">execute</span><span class="params">(ProcessRequest request, ProcessContext context)</span> &#123;</span><br><span class="line"> <span class="type">UserBaseInfoVo</span> <span class="variable">userBaseInfoVo</span> <span class="operator">=</span> userBaseInfoRepository.getUserBaseInfo(request.getUserId());</span><br><span class="line"> <span class="type">UserSpecialInfoVo</span> <span class="variable">userSpecialInfoVo</span> <span class="operator">=</span> userSpecialInfoRepository.getUserSpecialInfo(request.getUserId());</span><br><span class="line"> context.setUserBaseInfoVo(userBaseInfoVo);</span><br><span class="line"> context.setUserSpecialInfoVo(userSpecialInfoVo);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<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="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MoneyProcessor</span> <span class="keyword">implements</span> <span class="title class_">Processor</span> &#123;</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> UserMoneyRepository userMoneyRepository;</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> UserConsumeRepository userConsumeRepository;</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">boolean</span> <span class="title function_">needExecute</span><span class="params">(ProcessRequest request, ProcessContext context)</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> &#125;</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_">execute</span><span class="params">(ProcessRequest request, ProcessContext context)</span> &#123;</span><br><span class="line"> <span class="comment">// 1. 查询用户余额</span></span><br><span class="line"> <span class="type">UserMoneyVo</span> <span class="variable">userMoneyVo</span> <span class="operator">=</span> userMoneyRepository.getUserMoneyVo(request.getUserId());</span><br><span class="line"> context.setUserMoneyVo(userMoneyVo);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 2. 获取所有消费记录</span></span><br><span class="line"> List&lt;ConsumeRecordVo&gt; consumeRecordVoList = userConsumeRepository.getConsumeRecordVoList(request.getUserId());</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 3. 计算用户充值总金额</span></span><br><span class="line"> <span class="type">DoubleSummaryStatistics</span> <span class="variable">total</span> <span class="operator">=</span> consumeRecordVoList.stream()</span><br><span class="line"> .collect(Collectors.summarizingDouble(ConsumeRecordVo::getAmount));</span><br><span class="line"> <span class="type">Double</span> <span class="variable">totalMoney</span> <span class="operator">=</span> userMoneyVo.getMoney() + total.getSum();</span><br><span class="line"> </span><br><span class="line"> context.setTotalMoney(totalMoney);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<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="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ConsumeRecordProcessor</span> <span class="keyword">implements</span> <span class="title class_">Processor</span> &#123;</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> UserConsumeRepository userConsumeRepository;</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">boolean</span> <span class="title function_">needExecute</span><span class="params">(ProcessRequest request, ProcessContext context)</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> &#125;</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_">execute</span><span class="params">(ProcessRequest request, ProcessContext context)</span> &#123;</span><br><span class="line"> <span class="comment">// 获取所有消费记录</span></span><br><span class="line"> List&lt;ConsumeRecordVo&gt; consumeRecordVoList = userConsumeRepository.getConsumeRecordVoList(request.getUserId());</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 从中找出最大一笔消费</span></span><br><span class="line"> Optional&lt;ConsumeRecordVo&gt; max = consumeRecordVoList.stream()</span><br><span class="line"> .max(Comparator.comparing(ConsumeRecordVo::getAmount));</span><br><span class="line"> max.ifPresent(context::setMaxConsume);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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">interface</span> <span class="title class_">ProcessEngine</span> &#123;</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="comment"> * <span class="doctag">@param</span> request 初始请求,跟随责任链一路传下去</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> context 该处理器的输出结果,存储责任链的中间结果或返回给外层的最终结果</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">void</span> <span class="title function_">start</span><span class="params">(ProcessRequest request, ProcessContext context)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="实现流程引擎"><a href="#实现流程引擎" class="headerlink" title="实现流程引擎"></a>实现流程引擎</h2><p>流程引擎只有一个<code>start</code>接口用来启动流程。</p>
<p>以下是流程引擎抽象类。抽象类除了实现对处理器执行的控制外,还可以包括日志打印、异常处理等操作。</p>
<p>流程引擎需要执行哪些处理器由子类决定,子类通过实现<code>getProcessors()</code>抽象方法来指定使用的处理器。(又是模板模式)</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_">AbstractProcessEngineImpl</span> <span class="keyword">implements</span> <span class="title class_">ProcessEngine</span> &#123;</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> Logger logger;</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> ApplicationContext applicationContext;</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_">start</span><span class="params">(ProcessRequest request, ProcessContext context)</span> &#123;</span><br><span class="line"> <span class="comment">// 1. 打印引擎开始日志</span></span><br><span class="line"> logger.info(<span class="string">&quot;processEngine start, request:&quot;</span> + request);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 2. 获取执行器列表</span></span><br><span class="line"> List&lt;ProcessNameEnum&gt; processors = getProcessors();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">try</span> &#123;</span><br><span class="line"> <span class="comment">// 3. 依次运行执行器</span></span><br><span class="line"> processors.forEach(processorName -&gt; &#123;</span><br><span class="line"> <span class="type">Object</span> <span class="variable">bean</span> <span class="operator">=</span> applicationContext.getBean(processorName.getName());</span><br><span class="line"> <span class="keyword">if</span> (!(bean <span class="keyword">instanceof</span> Processor)) &#123;</span><br><span class="line"> logger.error(<span class="string">&quot;processor:&quot;</span> + processorName + <span class="string">&quot; not exist or type is incorrect&quot;</span>);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 3.1 执行器开始日志标注</span></span><br><span class="line"> logger.info(<span class="string">&quot;processor:&quot;</span> + processorName + <span class="string">&quot; start&quot;</span>);</span><br><span class="line"> <span class="type">Processor</span> <span class="variable">processor</span> <span class="operator">=</span> (Processor) bean;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 3.2 判断执行器是否符合执行条件,子类实现</span></span><br><span class="line"> <span class="keyword">if</span> (!processor.needExecute(request, context)) &#123;</span><br><span class="line"> logger.info(<span class="string">&quot;processor:&quot;</span> + processorName + <span class="string">&quot; skipped&quot;</span>);</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 3.3 执行器执行</span></span><br><span class="line"> processor.execute(request, context);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 3.4 执行器结束日志标注</span></span><br><span class="line"> logger.info(<span class="string">&quot;processor:&quot;</span> + processorName + <span class="string">&quot; end&quot;</span>);</span><br><span class="line"> &#125;);</span><br><span class="line"> &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line"> <span class="comment">// 执行异常中断日志打印</span></span><br><span class="line"> logger.error(<span class="string">&quot;processEngine interrupted, e:&quot;</span> + Arrays.toString(e.getStackTrace()));</span><br><span class="line"> <span class="comment">// 继续抛出异常</span></span><br><span class="line"> <span class="keyword">throw</span> e;</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 4. 打印引擎执行完成日志</span></span><br><span class="line"> logger.info(<span class="string">&quot;processEngine end, context:&quot;</span> + context);</span><br><span class="line"> &#125;</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">protected</span> <span class="keyword">abstract</span> List&lt;ProcessNameEnum&gt; <span class="title function_">getProcessors</span><span class="params">()</span>;</span><br><span class="line">&#125;</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="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserInfoQueryProcessEngine</span> <span class="keyword">extends</span> <span class="title class_">AbstractProcessEngineImpl</span> &#123;</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> List&lt;ProcessNameEnum&gt; processorList = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">static</span> &#123;</span><br><span class="line"> processorList.add(ProcessNameEnum.USER_INFO_QUERY_PROCESSOR);</span><br><span class="line"> processorList.add(ProcessNameEnum.MONEY_PROCESSOR);</span><br><span class="line"> processorList.add(ProcessNameEnum.CONSUME_RECORD_PROCESSOR);</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">protected</span> List&lt;ProcessNameEnum&gt; <span class="title function_">getProcessors</span><span class="params">()</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> processorList;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>引擎子类实现<code>getProcessors()</code>方法即可。此方法就是告诉流程引擎具体要执行的执行器列表及执行顺序。</p>
<p>如果你走读代码到这里,看到<code>list</code>里放的三个处理器名称,你基本上就知道“用户查询接口”提供了怎样的功能。这就是良好的可读性。</p>
<p>试想,如果有一天,一个流程中需要新增一个逻辑,我们可以包装一个新的处理器,然后添加到上图中的<code>processorList</code>中即可。</p>
<p>每个接口都可以实现一个如上截图的引擎子类,用以编排需要执行的处理器。</p>
<h2 id="特点:逻辑拆细,便捷组装"><a href="#特点:逻辑拆细,便捷组装" class="headerlink" title="特点:逻辑拆细,便捷组装"></a>特点:逻辑拆细,便捷组装</h2><ul>
<li>改一个处理器,直接在全部地方生效</li>
<li>处理器添加、调用和测试方便</li>
</ul>
<h1 id="典型责任链模式"><a href="#典型责任链模式" class="headerlink" title="典型责任链模式"></a>典型责任链模式</h1><p>之前我们提到流程引擎是非典型的责任链模式,那什么是一个典型的责任链模式?</p>
<p>对于一个典型的责任链</p>
<ul>
<li>它的执行流程是<code>node1 or node2 or node3</code>,只要有一个节点处理就可以了。不处理就给下一个,处理完了直接返回,有些像<code>else-if</code>的逻辑</li>
<li>每个节点自己指定了它的下一个节点</li>
</ul>
<p>而对于流程引擎来说</p>
<ul>
<li>它的执行流程是<code>node1 and node2 and node3</code>,所有节点都要执行(<code>needExecute</code>方法本质上也是走到了节点里面)</li>
<li>引擎驱动,开发者编排执行流程</li>
</ul>
<h1 id="流程引擎中的SOLID"><a href="#流程引擎中的SOLID" class="headerlink" title="流程引擎中的SOLID"></a>流程引擎中的SOLID</h1><ul>
<li>S(单一职责原则):每个“珠子”职责清晰</li>
<li>O(开闭原则):业务逻辑新增则新增“珠子”</li>
<li>D(依赖倒置原则):流程引擎执行的是抽象的“珠子接口”,具体“珠子”是使用时注入</li>
</ul>
<h1 id="拆的太猛过犹不及"><a href="#拆的太猛过犹不及" class="headerlink" title="拆的太猛过犹不及"></a>拆的太猛过犹不及</h1><p>如果拆分过细,会导致编排复杂、难以管理。而且某些子逻辑很可能会被重复实现</p>
<p>我们很难清楚我们的系统会不会过度设计,毕竟那是未来的事。</p>
<p>所以我们可以<strong>尽量先不拆的那么细,但是要让系统保持继续拆分的灵活性</strong></p>
<h1 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h1><p>[^1]: <a href="https://www.bilibili.com/video/BV1bV411K7Z2">【学架构也可以很有趣】【“趣”学架构】- 3.搭完架子串珠子</a><br>[^2]: <a href="https://mp.weixin.qq.com/s?__biz=MzkzMDI0ODg4NQ==&mid=2247499724&idx=1&sn=ef8b60d910d5a3e9924c681f764138eb&chksm=c27f9e16f5081700783ec85baa0aaa157439f3bdbc90089f348df7888416a25a97b2e67bfbc6&scene=178&cur_album_id=2247053463681564673#rd">【成为架构师】8. 成为工程师 - 搭建系统先搭建框架</a></p>
]]></content>
<categories>
<category>架构学习</category>
</categories>
<tags>
<tag>架构设计</tag>
<tag>代码美学</tag>
</tags>
</entry>
<entry>
<title>Markdown 备忘清单</title>
<url>/2023/07/06/md-guide/</url>
<content><![CDATA[<blockquote>
<p>本文为转载</p>
<p>原出处:<a href="http://bbs.laoleng.vip/reference/docs/markdown.html">http://bbs.laoleng.vip/reference/docs/markdown.html</a></p>
<p>仓库地址:<a href="https://github.com/jaywcjlove/reference">https://github.com/jaywcjlove/reference</a></p>
</blockquote>
<p>这是 Markdown 语法的快速参考备忘单。</p>
<h1 id="标题-atx-风格"><a href="#标题-atx-风格" class="headerlink" title="标题 (atx 风格)"></a>标题 (atx 风格)</h1><figure class="highlight markdown"><table><tr><td class="code"><pre><span class="line"><span class="section"># h1</span></span><br><span class="line"><span class="section">## h2</span></span><br><span class="line"><span class="section">### h3</span></span><br><span class="line"><span class="section">#### h4</span></span><br><span class="line"><span class="section">##### h5</span></span><br><span class="line"><span class="section">###### h6</span></span><br></pre></td></tr></table></figure>
<h1 id="块引用"><a href="#块引用" class="headerlink" title="块引用"></a>块引用</h1><figure class="highlight markdown"><table><tr><td class="code"><pre><span class="line"><span class="quote">&gt; 这是一个</span></span><br><span class="line"><span class="quote">&gt; 块引用</span></span><br><span class="line"><span class="quote">&gt;</span></span><br><span class="line"><span class="quote">&gt; &gt; 嵌套</span></span><br><span class="line"><span class="quote">&gt; &gt; 块引用</span></span><br></pre></td></tr></table></figure>
<h1 id="无序列表"><a href="#无序列表" class="headerlink" title="无序列表"></a>无序列表</h1><!--rehype:wrap-class=row-span-3-->
<figure class="highlight markdown"><table><tr><td class="code"><pre><span class="line"><span class="bullet">*</span> Item 1</span><br><span class="line"><span class="bullet">*</span> Item 2</span><br><span class="line"><span class="bullet"> *</span> item 3a</span><br><span class="line"><span class="bullet"> *</span> item 3b</span><br></pre></td></tr></table></figure>
<p>或者</p>
<figure class="highlight markdown"><table><tr><td class="code"><pre><span class="line"><span class="bullet">-</span> Item 1</span><br><span class="line"><span class="bullet">-</span> Item 2</span><br></pre></td></tr></table></figure>
<p>或者</p>
<figure class="highlight markdown"><table><tr><td class="code"><pre><span class="line"><span class="bullet">+</span> Item 1</span><br><span class="line"><span class="bullet">+</span> Item 2</span><br></pre></td></tr></table></figure>
<p>或者<strong>任务</strong>列表</p>
<figure class="highlight markdown"><table><tr><td class="code"><pre><span class="line"><span class="bullet">-</span> [ ] Checkbox off</span><br><span class="line"><span class="bullet">-</span> [x] Checkbox on</span><br></pre></td></tr></table></figure>
<h1 id="有序列表"><a href="#有序列表" class="headerlink" title="有序列表"></a>有序列表</h1><figure class="highlight markdown"><table><tr><td class="code"><pre><span class="line"><span class="bullet">1.</span> Item 1</span><br><span class="line"><span class="bullet">2.</span> Item 2</span><br><span class="line"><span class="code"> a. item 3a</span></span><br><span class="line"><span class="code"> b. item 3b</span></span><br></pre></td></tr></table></figure>
<h1 id="链接"><a href="#链接" class="headerlink" title="链接"></a>链接</h1><figure class="highlight markdown"><table><tr><td class="code"><pre><span class="line">[<span class="string">link</span>](<span class="link">http://google.com</span>)</span><br><span class="line"></span><br><span class="line">[<span class="string">link</span>][<span class="symbol">google</span>]</span><br><span class="line">[<span class="symbol">google</span>]: <span class="link">http://google.com</span></span><br><span class="line"></span><br><span class="line"><span class="language-xml">&lt;http://google.com&gt;</span></span><br></pre></td></tr></table></figure>
<h1 id="强调"><a href="#强调" class="headerlink" title="强调"></a>强调</h1><figure class="highlight markdown"><table><tr><td class="code"><pre><span class="line"><span class="emphasis">*斜体*</span> <span class="emphasis">_斜体_</span> <span class="strong">**粗体**</span> <span class="strong">__粗体__</span></span><br><span class="line"></span><br><span class="line"><span class="code">`内联代码`</span> ~~删除~~</span><br></pre></td></tr></table></figure>
<h1 id="水平线"><a href="#水平线" class="headerlink" title="水平线"></a>水平线</h1><!--rehype:wrap-class=row-span-2-->
<p>连字符</p>
<figure class="highlight markdown"><table><tr><td class="code"><pre><span class="line">---</span><br></pre></td></tr></table></figure>
<p>星号</p>
<figure class="highlight markdown"><table><tr><td class="code"><pre><span class="line"><span class="strong">**<span class="emphasis">*</span></span></span><br></pre></td></tr></table></figure>
<p>下划线</p>
<figure class="highlight markdown"><table><tr><td class="code"><pre><span class="line"><span class="strong">__<span class="emphasis">_</span></span></span><br></pre></td></tr></table></figure>
<h1 id="换行"><a href="#换行" class="headerlink" title="换行"></a>换行</h1><figure class="highlight markdown"><table><tr><td class="code"><pre><span class="line">在当前行的结尾加 2 个空格··</span><br><span class="line">这行就会新起一行\</span><br><span class="line">反斜杠也可以换行</span><br></pre></td></tr></table></figure>
<p>尾部添加两个空格,或者添加 <code>\</code> 反斜杠</p>
<h1 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h1><figure class="highlight markdown"><table><tr><td class="code"><pre><span class="line"><span class="code">```javascript</span></span><br><span class="line"><span class="code">console.log(&quot;This is a block code&quot;)</span></span><br></pre></td></tr></table></figure>
<figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">```markdown</span><br><span class="line">~~~css</span><br><span class="line">.button &#123; border: none; &#125;</span><br><span class="line">~~~</span><br></pre></td></tr></table></figure>
<figure class="highlight markdown"><table><tr><td class="code"><pre><span class="line">4 空格缩进做一个代码块</span><br></pre></td></tr></table></figure>
<h2 id="内联代码"><a href="#内联代码" class="headerlink" title="内联代码"></a>内联代码</h2><figure class="highlight markdown"><table><tr><td class="code"><pre><span class="line"><span class="code">`Inline code`</span> 周围有反引号</span><br></pre></td></tr></table></figure>
<h1 id="表格"><a href="#表格" class="headerlink" title="表格"></a>表格</h1><figure class="highlight markdown"><table><tr><td class="code"><pre><span class="line">| 左栏 | 中间栏 | 右栏 |</span><br><span class="line">| -------- | -------- | ----- |</span><br><span class="line">| 单元格 1 | 居中 | $1600 |</span><br><span class="line">| 单元格 2 | 单元格 3 | $12 |</span><br></pre></td></tr></table></figure>
<p>简单的风格</p>
<figure class="highlight markdown"><table><tr><td class="code"><pre><span class="line">左栏 | 中间栏 | 右栏 </span><br><span class="line">-------- | -------- | -----</span><br><span class="line">单元格 1 | 居中 | $1600</span><br><span class="line">单元格 2 | 单元格 3 | $12 </span><br></pre></td></tr></table></figure>
<p>增加 <code>:</code> 改变文字对齐方式</p>
<figure class="highlight markdown"><table><tr><td class="code"><pre><span class="line">左栏 | 中间栏 | 右栏 </span><br><span class="line">:------- | :------: | -----: </span><br><span class="line">左对齐 | 居中 | 右对齐</span><br></pre></td></tr></table></figure>
<p>Markdown 表格生成器:<a href="https://tableconvert.com/">tableconvert.com</a></p>
<h1 id="脚注-Footnotes"><a href="#脚注-Footnotes" class="headerlink" title="脚注 (Footnotes)"></a>脚注 (Footnotes)</h1><figure class="highlight markdown"><table><tr><td class="code"><pre><span class="line">这是一个简单的脚注[^1]。</span><br><span class="line"></span><br><span class="line">一个脚注也可以有多行[^2]。</span><br><span class="line"></span><br><span class="line">你也可以使用文字,更贴合你的写作风格[^note]。</span><br><span class="line"></span><br><span class="line">[^1]:我的参考。</span><br><span class="line">[^2]:每个新行都应以 2 个空格为前缀。</span><br><span class="line"> 这允许你有一个多行的脚注。</span><br><span class="line">[^note]:</span><br><span class="line"><span class="code"> 推荐使用数字命名脚注,但文本更容易识别和链接。</span></span><br><span class="line"><span class="code"> 脚注使用了不同的语法,使用 4 个空格作为新行。</span></span><br></pre></td></tr></table></figure>
<h1 id="图片"><a href="#图片" class="headerlink" title="图片"></a>图片</h1><!--rehype:wrap-class=col-span-2-->
<figure class="highlight markdown"><table><tr><td class="code"><pre><span class="line">![<span class="string">图片名称</span>](<span class="link">http://图片网址/images/logo.png</span>)</span><br><span class="line"></span><br><span class="line">![<span class="string">替代文字</span>](<span class="link">url</span>)</span><br></pre></td></tr></table></figure>
<h2 id="带链接的图片"><a href="#带链接的图片" class="headerlink" title="带链接的图片"></a>带链接的图片</h2><figure class="highlight markdown"><table><tr><td class="code"><pre><span class="line">[<span class="string">![GitHub Logo</span>](<span class="link">/images/logo.png</span>)](<span class="link">https://github.com/</span>)</span><br><span class="line"></span><br><span class="line">[<span class="string">![替代文字</span>](<span class="link">image_url</span>)](<span class="link">link_url</span>)</span><br></pre></td></tr></table></figure>
<h2 id="参考风格"><a href="#参考风格" class="headerlink" title="参考风格"></a>参考风格</h2><figure class="highlight markdown"><table><tr><td class="code"><pre><span class="line">![<span class="string">替代文字</span>][<span class="symbol">logo</span>]</span><br><span class="line"></span><br><span class="line">[<span class="symbol">logo</span>]: <span class="link">/images/logo.png &quot;Logo Title&quot;</span></span><br></pre></td></tr></table></figure>
<h1 id="反斜杠转义"><a href="#反斜杠转义" class="headerlink" title="反斜杠转义"></a>反斜杠转义</h1><!--rehype:wrap-class=row-span-2-->
<table>
<thead>
<tr>
<th>字符</th>
<th>转义</th>
<th>描述</th>
</tr>
</thead>
<tbody><tr>
<td><pur>\</pur></td>
<td>\\</td>
<td>backslash 反斜杠</td>
</tr>
<tr>
<td><pur>`</pur></td>
<td>\`</td>
<td>backtick 反引号</td>
</tr>
<tr>
<td><pur>*</pur></td>
<td>\*</td>
<td>asterisk 星号</td>
</tr>
<tr>
<td><pur>_</pur></td>
<td>\_</td>
<td>underscore 下划线</td>
</tr>
<tr>
<td><pur>{}</pur></td>
<td>\{}</td>
<td>curly braces 花括号</td>
</tr>
<tr>
<td><pur>[]</pur></td>
<td>\[]</td>
<td>square brackets 方括号</td>
</tr>
<tr>
<td><pur>()</pur></td>
<td>\()</td>
<td>parentheses 圆括号</td>
</tr>
<tr>
<td><pur>#</pur></td>
<td>\#</td>
<td>hash mark 哈希标记</td>
</tr>
<tr>
<td><pur>+</pur></td>
<td>\+</td>
<td>plus sign 加号</td>
</tr>
<tr>
<td><pur>-</pur></td>
<td>\-</td>
<td>minus sign (hyphen) 减号(连字符)</td>
</tr>
<tr>
<td><pur>.</pur></td>
<td>\.</td>
<td>dot 点</td>
</tr>
<tr>
<td><pur>!</pur></td>
<td>\!</td>
<td>exclamation mark 感叹号</td>
</tr>
</tbody></table>
<h1 id="行内-HTML-元素"><a href="#行内-HTML-元素" class="headerlink" title="行内 HTML 元素"></a>行内 HTML 元素</h1><!--rehype:wrap-class=col-span-2-->
<figure class="highlight html"><table><tr><td class="code"><pre><span class="line">目前只支持部分段内 HTML 元素效果,包括 <span class="tag">&lt;<span class="name">kbd</span>&gt;</span>, <span class="tag">&lt;<span class="name">b</span>&gt;</span>, <span class="tag">&lt;<span class="name">i</span>&gt;</span>, <span class="tag">&lt;<span class="name">em</span>&gt;</span>, <span class="tag">&lt;<span class="name">sup</span>&gt;</span>, <span class="tag">&lt;<span class="name">sub</span>&gt;</span>, <span class="tag">&lt;<span class="name">br</span>&gt;</span></span><br></pre></td></tr></table></figure>
<h1 id="另见"><a href="#另见" class="headerlink" title="另见"></a>另见</h1><ul>
<li><a href="https://github.github.com/gfm/">GitHub 风格的 Markdown 规范</a> <em>(github.com)</em></li>
</ul>
]]></content>
<categories>
<category>语言指南</category>
</categories>
<tags>
<tag>Markdown</tag>
</tags>
</entry>
<entry>
<title>软件设计原则</title>
<url>/2023/07/06/software-design-principles/</url>
<content><![CDATA[<h1 id="开闭原则"><a href="#开闭原则" class="headerlink" title="开闭原则"></a>开闭原则</h1><p><strong>对扩展开放,对修改关闭</strong>。在程序需要进行拓展的时候,不能去修改原有的代码,而是要实现一个热插拔的效果。简言之,是为了使程序的扩展性好,易于维护和升级</p>
<p>想要达到这样的效果,我们需要使用接口和抽象类。</p>
<h2 id="示例:计算面积"><a href="#示例:计算面积" class="headerlink" title="示例:计算面积"></a>示例:计算面积</h2><p>假设有一个图形类(Shape)和两个具体的图形子类:矩形(Rectangle)和圆形(Circle)。现在,我们要为这些图形添加一个计算面积的功能,并且希望在未来能够扩展更多的图形</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">abstract</span> <span class="keyword">class</span> <span class="title class_">Shape</span> &#123;</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">abstract</span> <span class="type">double</span> <span class="title function_">calculateArea</span><span class="params">()</span>;</span><br><span class="line">&#125;</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_">Rectangle</span> <span class="keyword">extends</span> <span class="title class_">Shape</span> &#123;</span><br><span class="line"> <span class="keyword">private</span> <span class="type">double</span> width;</span><br><span class="line"> <span class="keyword">private</span> <span class="type">double</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">double</span> width, <span class="type">double</span> height)</span> &#123;</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"> &#125;</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">double</span> <span class="title function_">calculateArea</span><span class="params">()</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> width * height;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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_">Circle</span> <span class="keyword">extends</span> <span class="title class_">Shape</span> &#123;</span><br><span class="line"> <span class="keyword">private</span> <span class="type">double</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">double</span> radius)</span> &#123;</span><br><span class="line"> <span class="built_in">this</span>.radius = radius;</span><br><span class="line"> &#125;</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">double</span> <span class="title function_">calculateArea</span><span class="params">()</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> Math.PI * radius * radius;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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_">AreaCalculator</span> &#123;</span><br><span class="line"> <span class="keyword">public</span> <span class="type">double</span> <span class="title function_">calculateTotalArea</span><span class="params">(Shape[] shapes)</span> &#123;</span><br><span class="line"> <span class="type">double</span> <span class="variable">totalArea</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (Shape shape : shapes) &#123;</span><br><span class="line"> totalArea += shape.calculateArea();</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">return</span> totalArea;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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_">OCPDemo</span> &#123;</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> &#123;</span><br><span class="line"> Shape[] shapes = &#123;<span class="keyword">new</span> <span class="title class_">Rectangle</span>(<span class="number">4</span>, <span class="number">5</span>), <span class="keyword">new</span> <span class="title class_">Circle</span>(<span class="number">3</span>)&#125;;</span><br><span class="line"> <span class="type">AreaCalculator</span> <span class="variable">calculator</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AreaCalculator</span>();</span><br><span class="line"> <span class="type">double</span> <span class="variable">totalArea</span> <span class="operator">=</span> calculator.calculateTotalArea(shapes);</span><br><span class="line"> System.out.println(<span class="string">&quot;Total area: &quot;</span> + totalArea);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h1 id="里氏代换原则"><a href="#里氏代换原则" class="headerlink" title="里氏代换原则"></a>里氏代换原则</h1><p>任何基类可以出现的地方,子类一定可以出现。通俗理解:子类可以扩展父类的功能,但不能改变父类原有的功能。换句话说,子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法。</p>
<p>里氏代换原则可以扩展代码的功能而不破坏原有结构,同时提高类型检查的准确性</p>
<h1 id="依赖倒转原则"><a href="#依赖倒转原则" class="headerlink" title="依赖倒转原则"></a>依赖倒转原则</h1><p>高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。</p>
<h2 id="示例:电脑组装"><a href="#示例:电脑组装" class="headerlink" title="示例:电脑组装"></a>示例:电脑组装</h2><p>现要组装一台电脑,需要配件cpu,硬盘,内存条。只有这些配置都有了,计算机才能正常的运行。选择cpu有很多选择,如Intel,AMD等,硬盘可以选择希捷,西数等,内存条可以选择金士顿,海盗船等。</p>
<p><strong>电脑(Computer):</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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Computer</span> &#123;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> HardDisk hardDisk;</span><br><span class="line"> <span class="keyword">private</span> Cpu cpu;</span><br><span class="line"> <span class="keyword">private</span> Memory memory;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;计算机工作&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><strong>硬盘接口(HardDisk):</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_">HardDisk</span> &#123;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">void</span> <span class="title function_">save</span><span class="params">(String data)</span>;</span><br><span class="line"></span><br><span class="line"> String <span class="title function_">get</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><strong>希捷硬盘类(XiJieHardDisk):</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_">XiJieHardDisk</span> <span class="keyword">implements</span> <span class="title class_">HardDisk</span> &#123;</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_">save</span><span class="params">(String data)</span> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;使用希捷硬盘存储数据&quot;</span> + data);</span><br><span class="line"> &#125;</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_">get</span><span class="params">()</span> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;使用希捷希捷硬盘取数据&quot;</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="string">&quot;数据&quot;</span>;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><strong>西数硬盘类(XiShuHardDisk):</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_">XiShuHardDisk</span> <span class="keyword">implements</span> <span class="title class_">HardDisk</span> &#123;</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_">save</span><span class="params">(String data)</span> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;使用西数硬盘存储数据&quot;</span> + data);</span><br><span class="line"> &#125;</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_">get</span><span class="params">()</span> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;使用西数希捷硬盘取数据&quot;</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="string">&quot;数据&quot;</span>;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>CPU、内存各自的接口和实现类同理</p>
<p><strong>测试类(TestComputer):</strong></p>
<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_">TestComputer</span> &#123;</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> &#123;</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"> computer.setHardDisk(<span class="keyword">new</span> <span class="title class_">XiJieHardDisk</span>()); <span class="comment">// new XiShuHardDisk() 也能运行,可以替换,下面同理</span></span><br><span class="line"> computer.setCpu(<span class="keyword">new</span> <span class="title class_">IntelCpu</span>());</span><br><span class="line"> computer.setMemory(<span class="keyword">new</span> <span class="title class_">KingstonMemory</span>());</span><br><span class="line"></span><br><span class="line"> computer.run();</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h1 id="接口隔离原则"><a href="#接口隔离原则" class="headerlink" title="接口隔离原则"></a>接口隔离原则</h1><p>客户端不应该被迫依赖于它不使用的方法;一个类对另一个类的依赖应该建立在最小的接口上。</p>
<h2 id="示例:安全门"><a href="#示例:安全门" class="headerlink" title="示例:安全门"></a>示例:安全门</h2><p>我们需要创建一个安全门,该安全门具有防火、防水、防盗的功能。可以将防火,防水,防盗功能提取成一个接口,形成一套规范。</p>
<p>现在如果我们还需要再创建一个只具有防盗、防水功能,那创建起来就相当方便了</p>
<p><strong>防盗接口(AntiTheft):</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_">AntiTheft</span> &#123;</span><br><span class="line"> <span class="keyword">void</span> <span class="title function_">antiTheft</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><strong>防火接口(Fireproof):</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_">Fireproof</span> &#123;</span><br><span class="line"> <span class="keyword">void</span> <span class="title function_">fireproof</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><strong>防水接口(Waterproof):</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_">Waterproof</span> &#123;</span><br><span class="line"> <span class="keyword">void</span> <span class="title function_">waterproof</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><strong>SafetyDoor(类):</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_">SafetyDoor</span> <span class="keyword">implements</span> <span class="title class_">AntiTheft</span>,Fireproof,Waterproof &#123;</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">antiTheft</span><span class="params">()</span> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;防盗&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">fireproof</span><span class="params">()</span> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;防火&quot;</span>);</span><br><span class="line"> &#125;</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_">waterproof</span><span class="params">()</span> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;防水&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><strong>LiteSafetyDoor(类):</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_">LiteSafetyDoor</span> <span class="keyword">implements</span> <span class="title class_">AntiTheft</span>,Fireproof &#123;</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">antiTheft</span><span class="params">()</span> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;防盗&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">fireproof</span><span class="params">()</span> &#123;</span><br><span class="line"> System.out.println(<span class="string">&quot;防火&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h1 id="迪米特法则"><a href="#迪米特法则" class="headerlink" title="迪米特法则"></a>迪米特法则</h1><p>迪米特法则又叫最少知识原则。</p>
<p>只和你的直接朋友交谈,不跟“陌生人”说话(Talk only to your immediate friends and not to strangers)。</p>
<p>其含义是:如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。</p>
<p>迪米特法则中的“朋友”是指:当前对象本身、当前对象的成员对象、当前对象所创建的对象、当前对象的方法参数等,这些对象同当前对象存在关联、聚合或组合关系,可以直接访问这些对象的方法。</p>
<h2 id="示例:明星与经纪人"><a href="#示例:明星与经纪人" class="headerlink" title="示例:明星与经纪人"></a>示例:明星与经纪人</h2><p>明星由于全身心投入艺术,所以许多日常事务由经纪人负责处理,如和粉丝的见面会,和媒体公司的业务洽淡等。这里的经纪人是明星的朋友,而粉丝和媒体公司是陌生人,所以适合使用迪米特法则。</p>
<p><strong>明星类(Star)</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">@RequiredArgsConstructor</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Star</span> &#123;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> String name;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><strong>粉丝类(Fans)</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">@RequiredArgsConstructor</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Fans</span> &#123;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> String name;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><strong>媒体公司类(Company)</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">@RequiredArgsConstructor</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Company</span> &#123;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> String name;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><strong>经纪人类(Agent)</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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Agent</span> &#123;</span><br><span class="line"> <span class="keyword">private</span> Star star;</span><br><span class="line"> <span class="keyword">private</span> Fans fans;</span><br><span class="line"> <span class="keyword">private</span> Company company;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">meeting</span><span class="params">()</span> &#123;</span><br><span class="line"> System.out.println(fans.getName() + <span class="string">&quot;与明星&quot;</span> + star.getName() + <span class="string">&quot;见面了。&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">business</span><span class="params">()</span> &#123;</span><br><span class="line"> System.out.println(company.getName() + <span class="string">&quot;与明星&quot;</span> + star.getName() + <span class="string">&quot;洽谈业务。&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h1 id="合成复用原则"><a href="#合成复用原则" class="headerlink" title="合成复用原则"></a>合成复用原则</h1><p>合成复用原则是指:尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。</p>
<p>通常类的复用分为继承复用和合成复用两种。</p>
<p>继承复用虽然有简单和易实现的优点,但它也存在以下缺点:</p>
<ol>
<li>继承复用破坏了类的封装性。因为继承会将父类的实现细节暴露给子类,父类对子类是透明的,所以这种复用又称为“白箱”复用。</li>
<li>子类与父类的耦合度高。父类的实现的任何改变都会导致子类的实现发生变化,这不利于类的扩展与维护。</li>
<li>它限制了复用的灵活性。从父类继承而来的实现是静态的,在编译时已经定义,所以在运行时不可能发生变化。</li>
</ol>
<p>采用组合或聚合复用时,可以将已有对象纳入新对象中,使之成为新对象的一部分,新对象可以调用已有对象的功能,它有以下优点:</p>
<ol>
<li>它维持了类的封装性。因为成分对象的内部细节是新对象看不见的,所以这种复用又称为“黑箱”复用。</li>
<li>对象间的耦合度低。可以在类的成员位置声明抽象。</li>
<li>复用的灵活性高。这种复用可以在运行时动态进行,新对象可以动态地引用与成分对象类型相同的对象。</li>
</ol>
<h2 id="示例:汽车颜色和动力源"><a href="#示例:汽车颜色和动力源" class="headerlink" title="示例:汽车颜色和动力源"></a>示例:汽车颜色和动力源</h2><p>汽车按“动力源”划分可分为汽油汽车、电动汽车等;按“颜色”划分可分为白色汽车、黑色汽车和红色汽车等。如果同时考虑这两种分类,其组合就很多。</p>
<p>当使用继承复用时,我们可能会先让电车和油车继承抽象父类Car,然后电车油车各自再细分子类红电车、蓝电车、黑油车……继承复用会产生很多子类</p>
<p>但如果我们将继承复用改为聚合复用就会简单很多:</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> lombok.Getter;</span><br><span class="line"><span class="keyword">import</span> lombok.Setter;</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_">PowerSource</span> &#123;</span><br><span class="line"> <span class="comment">// 声明动力源相关的方法</span></span><br><span class="line">&#125;</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_">Color</span> &#123;</span><br><span class="line"> <span class="comment">// 声明颜色相关的方法</span></span><br><span class="line">&#125;</span><br><span class="line"></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_">Car</span> &#123;</span><br><span class="line"> <span class="keyword">private</span> PowerSource powerSource;</span><br><span class="line"> <span class="keyword">private</span> Color color;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 其他属性和方法</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">Car</span><span class="params">(PowerSource powerSource, Color color)</span> &#123;</span><br><span class="line"> <span class="built_in">this</span>.powerSource = powerSource;</span><br><span class="line"> <span class="built_in">this</span>.color = color;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</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_">Main</span> &#123;</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> &#123;</span><br><span class="line"> <span class="type">PowerSource</span> <span class="variable">gasolinePower</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">GasolinePower</span>();</span><br><span class="line"> <span class="type">Color</span> <span class="variable">whiteColor</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">WhiteColor</span>();</span><br><span class="line"></span><br><span class="line"> <span class="type">Car</span> <span class="variable">car1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Car</span>(gasolinePower, whiteColor);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 如果有新的动力源或者新的颜色,只需要定义新的类并组合即可</span></span><br><span class="line"> <span class="type">PowerSource</span> <span class="variable">electricPower</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ElectricPower</span>();</span><br><span class="line"> <span class="type">Color</span> <span class="variable">redColor</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">RedColor</span>();</span><br><span class="line"></span><br><span class="line"> <span class="type">Car</span> <span class="variable">car2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Car</span>(electricPower, redColor);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 其他操作和逻辑</span></span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
]]></content>
<categories>
<category>代码美学</category>
</categories>
<tags>
<tag>软件设计原则</tag>
</tags>
</entry>
<entry>
<title>(一)搭系统先搭架子——模板方法模式</title>
<url>/2023/09/02/template-method/</url>
<content><![CDATA[<h1 id="引例——抽取工具类"><a href="#引例——抽取工具类" class="headerlink" title="引例——抽取工具类"></a>引例——抽取工具类</h1><p>先来看看这段代码:</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@GetMapping(path = &quot;/getUserInfo&quot;)</span></span><br><span class="line"><span class="keyword">public</span> UserInfoDTO <span class="title function_">getUserInfo</span><span class="params">(<span class="meta">@RequestParam(&quot;userId&quot;)</span> String userId)</span> &#123;</span><br><span class="line"> <span class="comment">// 1. 打印入口日志</span></span><br><span class="line"> logger.info(<span class="string">&quot;userId: &quot;</span> + userId + <span class="string">&quot; visit path: /getUserInfo&quot;</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">try</span> &#123;</span><br><span class="line"> <span class="comment">// 2. 检验入参合理性,userId是否以字母&#x27;u&#x27;开头。正确的uid示例:u001</span></span><br><span class="line"> <span class="keyword">if</span> (userId == <span class="literal">null</span> || !userId.startsWith(<span class="string">&quot;u&quot;</span>)) &#123;</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(<span class="string">&quot;param userId is invalid&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 3. 获取用户基础信息</span></span><br><span class="line"> <span class="type">UserBaseInfoVO</span> <span class="variable">userBaseInfoVO</span> <span class="operator">=</span> userBaseInfoRepository.getUserBaseInfo(userId);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 4. 获取用户特殊信息</span></span><br><span class="line"> <span class="type">UserSpecialInfoVO</span> <span class="variable">userSpecialInfoVO</span> <span class="operator">=</span> userSpecialInfoRepository.getUserSpecialInfo(userId);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 5. 获取用户余额</span></span><br><span class="line"> <span class="type">UserMoneyVO</span> <span class="variable">userMoneyVO</span> <span class="operator">=</span> UserMoneyRepository.getUserMoney(userId);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 6. 获取用户消费记录</span></span><br><span class="line"> List&lt;ConsumeRecordVO&gt; consumeRecordVOList = userConsumeRepository.getConsumeRecordVOList(userId);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 7. 计算最贵一次消费</span></span><br><span class="line"> Optional&lt;ConsumeRecordVO&gt; max = consumeRecordVOList.stream()</span><br><span class="line"> .max(Comparator.comparing(ConsumeRecordVO::getAmount));</span><br><span class="line"> <span class="type">ConsumeDTO</span> <span class="variable">maxAmountConsume</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line"> <span class="keyword">if</span> (max.isPresent()) &#123;</span><br><span class="line"> <span class="type">ConsumeRecordVO</span> <span class="variable">consumeRecordVO</span> <span class="operator">=</span> max.get();</span><br><span class="line"> maxAmountConsume = ConsumeDTO.builder()</span><br><span class="line"> .amount(consumeRecordVO.getAmount())</span><br><span class="line"> .date(consumeRecordVO.getDate())</span><br><span class="line"> .build();</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 8. 计算用户充值总金额</span></span><br><span class="line"> <span class="type">DoubleSummaryStatistics</span> <span class="variable">total</span> <span class="operator">=</span> consumeRecordVOList.stream()</span><br><span class="line"> .collect(Collectors.summarizingDouble(ConsumeRecordVO::getAmount));</span><br><span class="line"> <span class="type">Double</span> <span class="variable">totalMoney</span> <span class="operator">=</span> userMoneyVO.getMoney() + total.getSum();</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 9. 打印访问成功日志</span></span><br><span class="line"> logger.info(<span class="string">&quot;userId: &quot;</span> + userId + <span class="string">&quot; visit path: /getUserInfo success&quot;</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> UserInfoDTO.builder()</span><br><span class="line"> .userName(userBaseInfoVO.getUserName())</span><br><span class="line"> .vipLevel(userBaseInfoVO.getVipLevel())</span><br><span class="line"> .maxAmountConsume(maxAmountConsume)</span><br><span class="line"> .totalMoney(totalMoney)</span><br><span class="line"> .build();</span><br><span class="line"> &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line"> <span class="comment">// 打印访问失败日志</span></span><br><span class="line"> logger.error(<span class="string">&quot;userId: &quot;</span> + userId + <span class="string">&quot; visit path: /getUserInfo fail. exception message = &quot;</span> + Arrays.toString(e.getStackTrace()));</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>看上去不错,对吧</p>
<p>但是当我们有两个这种接口</p>
<div class="mermaid-wrap"><pre class="mermaid-src" hidden>
graph TD
subgraph getOtherInfo
A1[打印入参]
A3[校验入参与执行业务逻辑]
A4[打印出参]
A5[处理异常]
A6[返回结果]
A1 --&gt; A3
A3 --&gt; A4
A4 --&gt; A6
A3 --&gt;|校验失败或业务逻辑异常| A5
A5 --&gt; A6
end
subgraph getUserInfo
B1[打印入参]
B3[校验入参与执行业务逻辑]
B4[打印出参]
B5[处理异常]
B6[返回结果]
B1 --&gt; B3
B3 --&gt; B4
B4 --&gt; B6
B3 --&gt;|校验失败或业务逻辑异常| B5
B5 --&gt; B6
end
</pre></div>
<p>是不是看出来问题了?</p>
<h2 id="问题:相同能力没有复用"><a href="#问题:相同能力没有复用" class="headerlink" title="问题:相同能力没有复用"></a>问题:相同能力没有复用</h2><p>我们会发现他们逻辑不一致的地方只有校验入参和业务逻辑,其他相似或相同逻辑的地方没有复用</p>
<ul>
<li>如果新增接口,所有的日志打印要冗余写一遍,包括入口日志、出口日志、异常日志</li>
<li>如果新增接口,try-catch的异常处理逻辑也需要冗余重写</li>
<li>如果新增一个只获取用户金额信息的接口,需要冗余复制上述代码中和金额相关的部分</li>
<li>如果接口需要修改,返回新的信息,那就需要往这个代码里添加新的业务逻辑。而这个类一旦有变化,就涉及对这个类的回归验证</li>
</ul>
<p>对于这种情况,我们可能会给他抽出一个工具类</p>
<div class="mermaid-wrap"><pre class="mermaid-src" hidden>
graph TD
subgraph 接口A
打印入参 --&gt; A校验入参和执行业务逻辑
A校验入参和执行业务逻辑 --&gt;|校验失败或业务异常| 处理异常
A校验入参和执行业务逻辑 --&gt; 打印出参
处理异常 --&gt; 返回结果
end
subgraph 接口B
打印入参 --&gt; B校验入参和执行业务逻辑
B校验入参和执行业务逻辑 --&gt;|校验失败或业务异常| 处理异常
B校验入参和执行业务逻辑 --&gt; 打印出参
打印出参 --&gt; 返回结果
end
</pre></div>
<h2 id="紧接着带来的问题"><a href="#紧接着带来的问题" class="headerlink" title="紧接着带来的问题"></a>紧接着带来的问题</h2><ol>
<li>你怎么保证别人以后一定用工具类?</li>
<li>你怎么保证别人以后一定正确使用工具类?(如顺序有误)</li>
<li>用的人怎么确定需要用哪些工具类?(线上系统共同逻辑很多,难以定位需要的工具类)</li>
</ol>
<h1 id="正解——模板方法"><a href="#正解——模板方法" class="headerlink" title="正解——模板方法"></a>正解——模板方法</h1><p>抽象一个模板,规定顺序是打印入参、校验入参、业务逻辑、打印出参。</p>
<ul>
<li>打印入参和打印出参有默认实现</li>
<li>校验入参、业务逻辑是抽象的,接口A、接口B各自实现</li>
<li>统一异常处理</li>
</ul>
<div class="mermaid-wrap"><pre class="mermaid-src" hidden>
graph LR
subgraph 模板
打印入参 --&gt; 校验入参
校验入参 --&gt; 业务逻辑
业务逻辑 --&gt; 打印出参
打印出参 --&gt; 结束
end
subgraph 接口A
校验入参A --&gt;|实现| 校验入参
业务逻辑A --&gt;|实现| 业务逻辑
end
subgraph 接口B
校验入参B --&gt;|实现| 校验入参
业务逻辑B --&gt;|实现| 业务逻辑
end
</pre></div>
<h2 id="特点:统一逻辑,标准化流程"><a href="#特点:统一逻辑,标准化流程" class="headerlink" title="特点:统一逻辑,标准化流程"></a>特点:统一逻辑,标准化流程</h2><p>从系统的设计上</p>
<ul>
<li>每个接口都不用担心忘了执行必要的公共逻辑,例如打印日志、异常处理。</li>
<li>不用担心接口有遗漏步骤及搞错步骤顺序,例如入参校验在执行业务流程之前。</li>
<li>接口只需要关心自己业务逻辑的实现即可。</li>
<li>所有接口打印的日志及异常处理方式确保是一致的,方便监控和定位问题。</li>
<li>如果需要增加一些公用的能力,例如埋点上报某个统计平台,只需要在框架中添加逻辑,所有接口都直接生效。</li>
</ul>
<p>简单来说,就是<strong>统一逻辑,标准化流程</strong></p>
<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">abstract</span> <span class="keyword">class</span> <span class="title class_">ServiceTemplate</span>&lt;T, R&gt; &#123;</span><br><span class="line"> <span class="keyword">private</span> <span class="type">Logger</span> <span class="variable">logger</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">LoggerImpl</span>();</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="comment"> * 所谓算法要素:和业务相关的个性化实现。例如不同的接口对入参校验逻辑不同</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">public</span> R <span class="title function_">process</span><span class="params">(T request)</span> &#123;</span><br><span class="line"> <span class="comment">// 1. 打印入口日志</span></span><br><span class="line"> logger.info(<span class="string">&quot;start invoke, request - &quot;</span> + request);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 开始计时,用于日志记录耗时</span></span><br><span class="line"> <span class="type">Stopwatch</span> <span class="variable">stopwatch</span> <span class="operator">=</span> Stopwatch.createStarted();</span><br><span class="line"> <span class="keyword">try</span> &#123;</span><br><span class="line"> <span class="comment">// 2. 校验参数</span></span><br><span class="line"> validParam(request);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 3. 子类实现逻辑</span></span><br><span class="line"> <span class="type">R</span> <span class="variable">response</span> <span class="operator">=</span> doProcess(request);</span><br><span class="line"></span><br><span class="line"> <span class="type">Long</span> <span class="variable">timeCost</span> <span class="operator">=</span> stopwatch.elapsed(TimeUnit.MILLISECONDS);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 4. 打印出口日志</span></span><br><span class="line"> logger.info(<span class="string">&quot;end invoke, response = &quot;</span> + response + <span class="string">&quot;, costTime = &quot;</span> + timeCost);</span><br><span class="line"> <span class="keyword">return</span> response;</span><br><span class="line"> &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line"> <span class="comment">// 打印异常日志</span></span><br><span class="line"> logger.error(<span class="string">&quot;error invoke, exception: &quot;</span> + Arrays.toString(e.getStackTrace()));</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</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">protected</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title function_">validParam</span><span class="params">(T request)</span>;</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">protected</span> <span class="keyword">abstract</span> R <span class="title function_">doProcess</span><span class="params">(T request)</span>;</span><br><span class="line">&#125;</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="meta">@GetMapping(path = &quot;doSomething&quot;)</span></span><br><span class="line"><span class="keyword">public</span> Response <span class="title function_">doSomething</span><span class="params">(Request request)</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> (<span class="keyword">new</span> <span class="title class_">ServiceTemplate</span>&lt;RequestResponse&gt;() &#123;</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_">validParam</span><span class="params">(Request request)</span> &#123;</span><br><span class="line"> <span class="comment">// 对request的校验逻辑,例如非空,字段类型判断等</span></span><br><span class="line"> &#125;</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> Response <span class="title function_">doProcess</span><span class="params">(Request request)</span> &#123;</span><br><span class="line"> <span class="comment">// 执行业务逻辑</span></span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Response</span>();</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;).process(request);</span><br><span class="line">&#125;</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> UserInfoDTO <span class="title function_">getUserInfo</span><span class="params">(<span class="meta">@RequestParam(&quot;userId&quot;)</span> String userId)</span> &#123;</span><br><span class="line"> <span class="keyword">return</span> (<span class="keyword">new</span> <span class="title class_">ServiceTemplate</span>&lt;String&gt;() &#123;</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_">validParam</span><span class="params">(String request)</span> &#123;</span><br><span class="line"> <span class="comment">// 1. 校验入参</span></span><br><span class="line"> <span class="keyword">if</span> (userId == <span class="literal">null</span> || userId.isEmpty()) &#123;</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(<span class="string">&quot;param UserId is invalid&quot;</span>);</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</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> UserInfoDTO <span class="title function_">doProcess</span><span class="params">(String request)</span> &#123;</span><br><span class="line"> <span class="comment">// 2. 获取用户基础信息</span></span><br><span class="line"> <span class="type">UserBaseInfoVO</span> <span class="variable">userBaseInfoVO</span> <span class="operator">=</span> userBaseInfoRepository.getUserBaseInfo(userId);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 3. 获取用户特殊信息</span></span><br><span class="line"> <span class="type">UserSpecialInfoVO</span> <span class="variable">userSpecialInfoVO</span> <span class="operator">=</span> UserSpecialInfoRepository.getSpecialInfoVO(userId);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 4. 获取用户余额</span></span><br><span class="line"> <span class="type">UserMoneyVO</span> <span class="variable">userMoneyVO</span> <span class="operator">=</span> userMoneyRepository.getUserMoneyVO(userId);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 5. 获取用户消费记录</span></span><br><span class="line"> List&lt;ConsumeRecordVO&gt; consumeRecordVOList = userConsumeRepository.getConsumeRecordVOList(userId);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 6. 计算最贵一次消费</span></span><br><span class="line"> Optional&lt;ConsumeRecordVO&gt; max = consumeRecordVOList.stream()</span><br><span class="line"> .max(Comparator.comparing(ConsumeRecordVO::getAmount));</span><br><span class="line"> <span class="type">ConsumeDTO</span> <span class="variable">maxAmountConsume</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line"> <span class="keyword">if</span> (max.isPresent()) &#123;</span><br><span class="line"> <span class="type">ConsumeRecordVO</span> <span class="variable">consumeRecordVO</span> <span class="operator">=</span> max.get();</span><br><span class="line"> maxAmountConsume = ConsumeDTO.builder()</span><br><span class="line"> .amount(consumeRecordVO.getAmount())</span><br><span class="line"> .date(consumeRecordVO.getDate())</span><br><span class="line"> .build();</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 7. 计算用户充值总金额</span></span><br><span class="line"> <span class="type">DoubleSummaryStatistics</span> <span class="variable">total</span> <span class="operator">=</span> consumeRecordVOList.stream()</span><br><span class="line"> .collect(Collectors.summarizingDouble(ConsumeRecordVO::getAmount));</span><br><span class="line"> <span class="type">double</span> <span class="variable">totalMoney</span> <span class="operator">=</span> userMoneyVO.getMoney() + total.getSum();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> UserInfoDTO.builder()</span><br><span class="line"> .userName(userBaseInfoVO.getUserName())</span><br><span class="line"> .vipLevel(userBaseInfoVO.getVipLevel())</span><br><span class="line"> .maxAmountConsume(maxAmountConsume)</span><br><span class="line"> .totalMoney(totalMoney)</span><br><span class="line"> .build();</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;).process(userId);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="问题"><a href="#问题" class="headerlink" title="问题"></a>问题</h2><p>即使这样,系统还不够完美,当我们:</p>
<ul>
<li><strong>加逻辑:</strong><code>getUserlnfo</code>新增加用户优惠券信息</li>
<li><strong>改逻辑:</strong>最贵一次消费记录要改成近一个月的</li>
<li><strong>增加复杂判断:</strong>如果是授权了的用户,才查询余额,没有授权不能查</li>
</ul>
<p>我们的代码都在一个文件里加,会导致代码:</p>
<ul>
<li><strong>腕肿难懂:</strong>看起来费劲,而且不同同学代码风格不一致,导致注释还可能难懂和有歧义</li>
<li><strong>容易相互影响:</strong>太多行,一不小心改错了;又或者有依赖顺序、数据结构不能有变动</li>
<li><strong>不好测试:</strong>为了测方法中的某一处逻辑,不得不在方法入参上引入大量无关数据进行mock。而且万一中途某一块其他逻辑判断非常复杂,那mock难度就是指数级上升</li>
</ul>
<p>那么应该怎么解决呢?详情请看<a href="https://orionli.gitee.io/blog/2023/09/03/process-engine/">下节</a></p>
<h1 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h1><p>[^1]: <a href="https://www.bilibili.com/video/BV1GF41197iR">【学架构也可以很有趣】【“趣”学架构】- 2.搭系统得先搭架子</a><br>[^2]: <a href="https://mp.weixin.qq.com/s?__biz=MzkzMDI0ODg4NQ==&mid=2247499724&idx=1&sn=ef8b60d910d5a3e9924c681f764138eb&chksm=c27f9e16f5081700783ec85baa0aaa157439f3bdbc90089f348df7888416a25a97b2e67bfbc6&scene=178&cur_album_id=2247053463681564673#rd">【成为架构师】8. 成为工程师 - 搭建系统先搭建框架</a></p>
]]></content>
<categories>
<category>架构学习</category>
</categories>
<tags>
<tag>架构设计</tag>
<tag>代码美学</tag>
<tag>设计模式</tag>
</tags>
</entry>
<entry>
<title>图灵考核路线</title>
<url>/2023/09/10/turing-assessment/</url>
<content><![CDATA[<h1 id="时间安排"><a href="#时间安排" class="headerlink" title="时间安排"></a>时间安排</h1><ul>
<li>宣讲会:9月17日</li>
<li>面试:十月上旬至中旬</li>
<li>一轮考核:十一月初左右</li>
</ul>
<h1 id="考核内容"><a href="#考核内容" class="headerlink" title="考核内容"></a>考核内容</h1><p>前端为机试,注重抽查前端部分</p>
<p>后端、AI方向(CV、NLP)为笔试,主要抽查算法</p>
<p>UI方向考核,请加群获取学习路线,考核内容会一并在群里公布</p>
<p>算法难度参考<a href="https://www.luogu.com.cn/training/list">洛谷:https://www.luogu.com.cn/training/list</a></p>
<ul>
<li><p>范围:入门1—入门6</p>
</li>
<li><p>难度:普及-及以下</p>
</li>
<li><p>主要抽查内容:</p>
<ul>
<li>基础:顺序结构、分支、循环、数组、字符串、结构体(面向过程)| 对象(面向对象)、函数、模拟、排序</li>
<li>进阶(附加):递归、贪心、动态规划</li>
</ul>
</li>
</ul>
<blockquote>
<p> 学有余力的同学可以挑战更高难度的题目</p>
</blockquote>
<h1 id="前端"><a href="#前端" class="headerlink" title="前端"></a>前端</h1><p>考核内容:前端基础、算法</p>
<p>教程链接:</p>
<ul>
<li>HTML+CSS:<a href="https://www.bilibili.com/video/BV1p84y1P7Z5/?spm_id_from=333.337.search-card.all.click&vd_source=baa49ed64521e4f4dbe682477dd594ea">尚硅谷前端html+css零基础教程:https://www.bilibili.com/video/BV1p84y1P7Z5/?spm_id_from&#x3D;333.337.search-card.all.click&amp;vd_source&#x3D;baa49ed64521e4f4dbe682477dd594ea</a></li>
<li>JavaScript:<a href="https://www.bilibili.com/video/BV1Y84y1L7Nn/?spm_id_from=333.337.search-card.all.click&vd_source=baa49ed64521e4f4dbe682477dd594ea">黑马程序员前端JavaScript入门到精通全套教程:https://www.bilibili.com/video/BV1Y84y1L7Nn/?spm_id_from&#x3D;333.337.search-card.all.click&amp;vd_source&#x3D;baa49ed64521e4f4dbe682477dd594ea</a></li>
<li>算法部分:语言不限,如C、C++、Java、Python、JavaScript等</li>
</ul>
<p>一轮考核范围:</p>
<ul>
<li>前端:HTML、CSS</li>
<li>算法:参考上方考核内容</li>
</ul>
<blockquote>
<p>主要抽查前端内容,算法部分占30%左右,一轮考核为机试</p>
</blockquote>
<h1 id="后端"><a href="#后端" class="headerlink" title="后端"></a>后端</h1><p>考核内容:Java</p>
<p>教程链接:<a href="https://www.bilibili.com/video/BV1Cv411372m/">Java入门基础视频教程:https://www.bilibili.com/video/BV1Cv411372m/</a></p>
<blockquote>
<p>有语言基础的可以看此文档:<a href="https://www.liaoxuefeng.com/wiki/1252599548343744/1255876875896416">https://www.liaoxuefeng.com/wiki/1252599548343744/1255876875896416</a></p>
</blockquote>
<p>一轮考核范围:视频教程P1-P112、算法</p>
<p>洛谷题解参考以下博客:</p>
<ul>
<li>入门1:<a href="https://blog.csdn.net/lonely__snow/article/details/131622459">https://blog.csdn.net/lonely__snow/article/details/131622459</a></li>
<li>入门2:<a href="https://blog.csdn.net/lonely__snow/article/details/131754462">https://blog.csdn.net/lonely__snow/article/details/131754462</a></li>
<li>入门3:<a href="https://blog.csdn.net/lonely__snow/article/details/131852281">https://blog.csdn.net/lonely__snow/article/details/131852281</a></li>
<li>入门4:<a href="https://blog.csdn.net/lonely__snow/article/details/132124077">https://blog.csdn.net/lonely__snow/article/details/132124077</a></li>
<li>入门5:<a href="https://blog.csdn.net/qq_43751506/article/details/104916010">https://blog.csdn.net/qq_43751506/article/details/104916010</a></li>
<li>入门6:<a href="https://blog.csdn.net/qq_43751506/article/details/104936728?spm=1001.2014.3001.5502">https://blog.csdn.net/qq_43751506/article/details/104936728?spm=1001.2014.3001.5502</a></li>
</ul>
<h1 id="CV-NLP"><a href="#CV-NLP" class="headerlink" title="CV &amp; NLP"></a>CV &amp; NLP</h1><p>考核内容:Python</p>
<p>教程链接: </p>
<ul>
<li><a href="https://www.bilibili.com/video/BV19P411Q7eA/?spm_id_from=333.788&vd_source=b75b865340d3eff147772aacfdcd5b0a">【戴师兄】历时两年半,Python自学课终于更新辣!:https://www.bilibili.com/video/BV19P411Q7eA/?spm_id_from&#x3D;333.788&amp;vd_source&#x3D;b75b865340d3eff147772aacfdcd5b0a</a></li>
<li>全球最强python教程–mosh大神的python从入门到精通:<a href="https://www.bilibili.com/video/BV1ng4y1i7Uk?p=13&vd_source=87ca73aed428e31176ed964dd32c8a60">13- 使用Python进行机器学习:https://www.bilibili.com/video/BV1ng4y1i7Uk?p=13&vd_source&#x3D;87ca73aed428e31176ed964dd32c8a60</a></li>
</ul>
<blockquote>
<p>有语言基础的可以看此文档:<a href="https://www.runoob.com/python3/python3-tutorial.html">https://www.runoob.com/python3/python3-tutorial.html</a></p>
</blockquote>
<p>一轮范围:第一个视频教程全部 或 第二个教程(P1-P10)、算法</p>
<p>学有余力的同学可以学习:<a href="https://www.bilibili.com/video/BV1Xa411L7xS/?vd_source=87ca73aed428e31176ed964dd32c8a60">numpy库</a></p>
<p>洛谷题解参考以下博客:</p>
<ul>
<li>入门1:<a href="https://blog.csdn.net/l18339702017/article/details/122461303?spm=1001.2014.3001.5502">https://blog.csdn.net/l18339702017/article/details/122461303?spm=1001.2014.3001.5502</a></li>
<li>入门2:<a href="https://blog.csdn.net/l18339702017/article/details/122461466?spm=1001.2014.3001.5502">https://blog.csdn.net/l18339702017/article/details/122461466?spm=1001.2014.3001.5502</a></li>
<li>入门3:<a href="https://blog.csdn.net/l18339702017/article/details/122477823?spm=1001.2014.3001.5502">https://blog.csdn.net/l18339702017/article/details/122477823?spm=1001.2014.3001.5502</a></li>
<li>入门4:<a href="https://blog.csdn.net/l18339702017/article/details/122580573?spm=1001.2014.3001.5502">https://blog.csdn.net/l18339702017/article/details/122580573?spm=1001.2014.3001.5502</a></li>
<li>入门5:<a href="https://blog.csdn.net/l18339702017/article/details/122580937?spm=1001.2014.3001.5502">https://blog.csdn.net/l18339702017/article/details/122580937?spm=1001.2014.3001.5502</a></li>
<li>入门6:<a href="https://blog.csdn.net/l18339702017/article/details/122585414?spm=1001.2014.3001.5502">https://blog.csdn.net/l18339702017/article/details/122585414?spm=1001.2014.3001.5502</a></li>
</ul>
<h1 id="UI"><a href="#UI" class="headerlink" title="UI"></a>UI</h1><img src="./../img/1-1694350827613-2.jpg" alt="1" style="zoom:25%;" />
<blockquote>
<p>学习路线通过上方二维码加群获取!</p>
<p>欢迎22级&amp;23级对UI方向感兴趣的同学加群咨询</p>
</blockquote>
]]></content>
<categories>
<category>学习路线</category>
</categories>
<tags>
<tag>学习路线</tag>
</tags>
</entry>
<entry>
<title>Protobuf3 语言指南</title>
<url>/2023/06/21/protobuf3-guide/</url>
<content><![CDATA[<blockquote>
<p>英文文档:<a href="https://developers.google.com/protocol-buffers/docs/proto3?hl=zh-cn#generating">Language Guide(proto3)</a></p>
<p>汉化文档参考:<a href="https://github.com/lixiangyun/protobuf_doc_ZH_CN">https://github.com/lixiangyun/protobuf_doc_ZH_CN</a></p>
<p>本文基于汉化文档魔改,用一些简单的代码示例替代冗长的表述,降低学习protobuf3的时间成本</p>
</blockquote>
<h1 id="为什么使用protobuf"><a href="#为什么使用protobuf" class="headerlink" title="为什么使用protobuf"></a>为什么使用protobuf</h1><p>新人在使用gRPC的时候,常常会有以下疑问</p>
<p>Q: 欸?我直接写Request和Response类不好吗,为啥要用protobuf定义消息类型并编译</p>
<p>A: 使用.proto文件定义消息结构,然后通过protobuf编译器生成对应的Java类或其他编程语言的类,是为了方便不同编程语言之间的数据传输和解析。</p>
<p><strong>protobuf所采用的二进制格式非常紧凑,而且具有高效的序列化和反序列化性能。因此,可以将使用不同编程语言开发的应用程序之间的数据传输格式标准化,从而实现跨语言的数据交换。</strong></p>
<p>此外,使用生成的Java类可以更方便地操作protobuf消息对象及其中的字段。protobuf编译器生成的Java类包含了许多有用的方法,如setter和getter方法,以及对protobuf消息对象的序列化和反序列化方法等,这些方法和操作简单方便,可以提高开发效率。</p>
<p>因此,通过protobuf编译器生成Java类或其他类,可以将消息结构定义与底层编码和解码逻辑分离,使得代码更加清晰、易读和易维护,同时也方便了跨语言数据传输。</p>
<p><strong>简单来说,在proto文件定义完编译生成的类只是方便对应语言的开发者开发服务的,消息传输还是用的protobuf,就像广东的内部交流可能说粤语,重庆的内部交流可能说重庆话,但当重庆人要和广东人交流,那毫无疑问还是用统一标准的普通话好</strong></p>
<h1 id="定义一个消息类型"><a href="#定义一个消息类型" class="headerlink" title="定义一个消息类型"></a>定义一个消息类型</h1><p>我们上面讲到:<strong>在生成的Java代码中,每个消息类型对应一个Java类,其中每个字段都有对应的getter和setter方法。同时,Java类还提供了对应的构造函数、toString方法等,方便开发者快速处理消息。</strong></p>
<p>先来看一个非常简单的例子。假设你想定义一个“搜索请求”的消息格式,每一个请求含有一个查询字符串、你感兴趣的查询结果所在的页数,以及每一页多少条查询结果。可以采用如下的方式来定义消息类型的.proto文件了:</p>
<figure class="highlight protobuf"><table><tr><td class="code"><pre><span class="line">syntax = <span class="string">&quot;proto3&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">message </span><span class="title class_">SearchRequest</span> &#123;</span><br><span class="line"> <span class="type">string</span> query = <span class="number">1</span>;</span><br><span class="line"> <span class="type">int32</span> page_number = <span class="number">2</span>;</span><br><span class="line"> <span class="type">int32</span> result_per_page = <span class="number">3</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<ul>
<li>文件的第一行指定了你正在使用proto3语法:如果你没有指定这个,编译器会使用proto2。这个指定语法行必须是文件的非空非注释的第一个行。</li>
<li><code>SearchRequest</code>消息格式有3个字段,在消息中承载的数据分别对应于每一个字段。其中每个字段都有一个名字和一种类型。</li>
</ul>
<h2 id="指定字段类型"><a href="#指定字段类型" class="headerlink" title="指定字段类型"></a>指定字段类型</h2><p>在上面的例子中,所有字段都是标量类型:两个整型(<code>page_number</code>和<code>result_per_page</code>),一个string类型(<code>query</code>)。当然,你也可以为字段指定其他的合成类型,包括枚举(enumerations)或其他消息类型。</p>
<h2 id="分配标识号"><a href="#分配标识号" class="headerlink" title="分配标识号"></a>分配标识号</h2><p>正如你所见,在消息定义中,每个字段都有唯一的一个数字标识符。这些标识符是用来在消息的二进制格式中识别各个字段的,一旦开始使用就不能够再改变。注:[1,15]之内的标识号在编码的时候会占用一个字节。[16,2047]之内的标识号则占用2个字节。所以应该为那些频繁出现的消息元素保留 [1,15]之内的标识号。切记:要为将来有可能添加的、频繁出现的标识号预留一些标识号。</p>
<p>最小的标识号可以从1开始,最大到2^29 - 1, or 536,870,911。不可以使用其中的[19000-19999]( (从FieldDescriptor::kFirstReservedNumber 到 FieldDescriptor::kLastReservedNumber))的标识号, Protobuf协议实现中对这些进行了预留。如果非要在.proto文件中使用这些预留标识号,编译时就会报警。同样你也不能使用早期保留的标识号。</p>
<h2 id="指定字段规则"><a href="#指定字段规则" class="headerlink" title="指定字段规则"></a>指定字段规则</h2><blockquote>
<p>所指定的消息字段修饰符必须是如下之一:</p>
<ul>
<li><code>singular</code>(默认):一个格式良好的消息应该有0个或者1个这种字段(但是不能超过1个)</li>
<li><code>repeated</code>:在一个格式良好的消息中,这种字段可以重复任意多次(包括0次)。重复的值的顺序会被保留。</li>
</ul>
<p>在proto3中,repeated的标量域默认情况下使用packed。</p>
</blockquote>
<p><code>repeated</code>字段在Protocol Buffers中对应了Java中的<code>List</code>类型。它允许在消息中存储多个相同类型的值,并以列表形式进行操作。</p>
<p>当使用Protocol Buffers生成的Java类时,<code>repeated</code>字段会被映射为一个<code>List</code>类型的属性。您可以通过调用该属性的方法来添加、获取、修改和删除其中的元素。</p>
<p>例如,在示例代码中的<code>UserList</code>消息类型中,<code>repeated User users</code>字段会被映射为一个<code>List&lt;User&gt;</code>类型的属性。您可以使用<code>getUsersList()</code>方法获取用户列表,然后使用<code>add()</code>方法添加新的用户,或者使用下标索引对特定位置的用户进行读取或写入操作。</p>
<figure class="highlight protobuf"><table><tr><td class="code"><pre><span class="line">syntax = <span class="string">&quot;proto3&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">message </span><span class="title class_">User</span> &#123;</span><br><span class="line"> <span class="type">string</span> name = <span class="number">1</span>;</span><br><span class="line"> <span class="type">int32</span> age = <span class="number">2</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">message </span><span class="title class_">UserList</span> &#123;</span><br><span class="line"> <span class="keyword">repeated</span> User users = <span class="number">1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>我们可以假设生成的Java代码包名为<code>com.example.demo</code>,生成的类为<code>User</code>和<code>UserList</code>。</p>
<p>下面是一个简单的使用示例:</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> com.example.demo.UserList;</span><br><span class="line"></span><br><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserController</span> &#123;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@PostMapping(&quot;/users&quot;)</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">createUser</span><span class="params">(<span class="meta">@RequestBody</span> User user)</span> &#123;</span><br><span class="line"> <span class="comment">// 处理用户创建逻辑</span></span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@GetMapping(&quot;/users&quot;)</span></span><br><span class="line"> <span class="keyword">public</span> UserList <span class="title function_">getUsers</span><span class="params">()</span> &#123;</span><br><span class="line"> <span class="comment">// 模拟从数据库中获取用户列表</span></span><br><span class="line"> List&lt;User&gt; userList = Arrays.asList(</span><br><span class="line"> User.newBuilder().setName(<span class="string">&quot;张三&quot;</span>).setAge(<span class="number">18</span>).build(),</span><br><span class="line"> User.newBuilder().setName(<span class="string">&quot;李四&quot;</span>).setAge(<span class="number">20</span>).build()</span><br><span class="line"> );</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 创建UserList对象,并设置用户列表</span></span><br><span class="line"> <span class="type">UserList</span> <span class="variable">userListObj</span> <span class="operator">=</span> UserList.newBuilder().addAllUsers(userList).build();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> userListObj;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>在上面的示例中,我们通过<code>@RequestBody</code>注解接收请求体中的User对象,并执行相应的业务逻辑。在返回用户列表时,我们首先模拟获取用户列表的过程,然后使用<code>UserList</code>对象来封装用户列表,并将其返回给客户端。</p>
<h2 id="添加更多消息类型"><a href="#添加更多消息类型" class="headerlink" title="添加更多消息类型"></a>添加更多消息类型</h2><p>在一个.proto文件中可以定义多个消息类型。在定义多个相关的消息的时候,这一点特别有用——例如,如果想定义与SearchResponse消息类型对应的回复消息格式的话,你可以将它添加到相同的.proto文件中,如:</p>
<figure class="highlight protobuf"><table><tr><td class="code"><pre><span class="line"><span class="keyword">message </span><span class="title class_">SearchRequest</span> &#123;</span><br><span class="line"> <span class="type">string</span> query = <span class="number">1</span>;</span><br><span class="line"> <span class="type">int32</span> page_number = <span class="number">2</span>;</span><br><span class="line"> <span class="type">int32</span> result_per_page = <span class="number">3</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">message </span><span class="title class_">SearchResponse</span> &#123;</span><br><span class="line"> ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="添加注释"><a href="#添加注释" class="headerlink" title="添加注释"></a>添加注释</h2><p>向.proto文件添加注释,可以使用C&#x2F;C++&#x2F;java风格的双斜杠(&#x2F;&#x2F;) 语法格式,如:</p>
<figure class="highlight protobuf"><table><tr><td class="code"><pre><span class="line"><span class="keyword">message </span><span class="title class_">SearchRequest</span> &#123;</span><br><span class="line"> <span class="type">string</span> query = <span class="number">1</span>;</span><br><span class="line"> <span class="type">int32</span> page_number = <span class="number">2</span>; <span class="comment">// Which page number do we want?</span></span><br><span class="line"> <span class="type">int32</span> result_per_page = <span class="number">3</span>; <span class="comment">// Number of results to return per page.</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="保留标识符(Reserved)"><a href="#保留标识符(Reserved)" class="headerlink" title="保留标识符(Reserved)"></a>保留标识符(Reserved)</h2><p>简单来说,这个就是用来占位的,比方说:</p>
<ul>
<li>我先把3, 4标识符先占了</li>
<li><code>age</code>的字段我也占了</li>
</ul>
<figure class="highlight protobuf"><table><tr><td class="code"><pre><span class="line">syntax = <span class="string">&quot;proto3&quot;</span>;</span><br><span class="line"><span class="keyword">package</span> com.example.demo;</span><br><span class="line"></span><br><span class="line"><span class="keyword">message </span><span class="title class_">Person</span> &#123;</span><br><span class="line"> <span class="type">int32</span> id = <span class="number">1</span>;</span><br><span class="line"> <span class="type">string</span> name = <span class="number">2</span>;</span><br><span class="line"> reserved <span class="number">3</span>, <span class="number">4</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>那后面的人就不能用你占了的这些字段和标识符了。现在如果我想更新<code>Person</code>这个消息类型,把3号位给<code>age</code>只需要</p>
<figure class="highlight protobuf"><table><tr><td class="code"><pre><span class="line">syntax = <span class="string">&quot;proto3&quot;</span>;</span><br><span class="line"><span class="keyword">package</span> com.example.demo;</span><br><span class="line"></span><br><span class="line"><span class="keyword">message </span><span class="title class_">Person</span> &#123;</span><br><span class="line"> <span class="type">int32</span> id = <span class="number">1</span>;</span><br><span class="line"> <span class="type">string</span> name = <span class="number">2</span>;</span><br><span class="line"> <span class="type">int32</span> age = <span class="number">3</span>;</span><br><span class="line"> reserved <span class="number">4</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>注:不要在同一行reserved声明中同时声明域名字和标识号</p>
<h2 id="从-proto文件生成了什么?"><a href="#从-proto文件生成了什么?" class="headerlink" title="从.proto文件生成了什么?"></a>从.proto文件生成了什么?</h2><p>当用protocol buffer编译器来运行.proto文件时,编译器将生成所选择语言的代码,这些代码可以操作在.proto文件中定义的消息类型,包括获取、设置字段值,将消息序列化到一个输出流中,以及从一个输入流中解析消息。</p>
<ul>
<li>对C++来说,编译器会为每个.proto文件生成一个.h文件和一个.cc文件,.proto文件中的每一个消息有一个对应的类。</li>
<li>对Java来说,编译器为每一个消息类型生成了一个.java文件,以及一个特殊的Builder类(该类是用来创建消息类接口的)。</li>
<li>对Python来说,有点不太一样——Python编译器为.proto文件中的每个消息类型生成一个含有静态描述符的模块,,该模块与一个元类(metaclass)在运行时(runtime)被用来创建所需的Python数据访问类。</li>
<li>对go来说,编译器会位每个消息类型生成了一个.pd.go文件。</li>
<li>对于Ruby来说,编译器会为每个消息类型生成了一个.rb文件。</li>
<li>javaNano来说,编译器输出类似域java但是没有Builder类</li>
<li>对于Objective-C来说,编译器会为每个消息类型生成了一个pbobjc.h文件和pbobjcm文件,.proto文件中的每一个消息有一个对应的类。</li>
<li>对于C#来说,编译器会为每个消息类型生成了一个.cs文件,.proto文件中的每一个消息有一个对应的类。</li>
</ul>
<p>你可以从如下的文档链接中获取每种语言更多API(proto3版本的内容很快就公布)。<a href="https://developers.google.com/protocol-buffers/docs/reference/overview?hl=zh-cn">API Reference</a></p>
<h1 id="标量数值类型"><a href="#标量数值类型" class="headerlink" title="标量数值类型"></a>标量数值类型</h1><p>一个标量消息字段可以含有一个如下的类型——该表格展示了定义于.proto文件中的类型,以及与之对应的、在自动生成的访问类中定义的类型:</p>
<table>
<thead>
<tr>
<th>.proto Type</th>
<th>Notes</th>
<th>C++ Type</th>
<th>Java Type</th>
<th>Python Type[2]</th>
<th>Go Type</th>
<th>Ruby Type</th>
<th>C# Type</th>
<th>PHP Type</th>
</tr>
</thead>
<tbody><tr>
<td>double</td>
<td></td>
<td>double</td>
<td>double</td>
<td>float</td>
<td>float64</td>
<td>Float</td>
<td>double</td>
<td>float</td>
</tr>
<tr>
<td>float</td>
<td></td>
<td>float</td>
<td>float</td>
<td>float</td>
<td>float32</td>
<td>Float</td>
<td>float</td>
<td>float</td>
</tr>
<tr>
<td>int32</td>
<td>使用变长编码,对于负值的效率很低,如果你的域有可能有负值,请使用sint64替代</td>
<td>int32</td>
<td>int</td>
<td>int</td>
<td>int32</td>
<td>Fixnum 或者 Bignum(根据需要)</td>
<td>int</td>
<td>integer</td>
</tr>
<tr>
<td>uint32</td>
<td>使用变长编码</td>
<td>uint32</td>
<td>int</td>
<td>int&#x2F;long</td>
<td>uint32</td>
<td>Fixnum 或者 Bignum(根据需要)</td>
<td>uint</td>
<td>integer</td>
</tr>
<tr>
<td>uint64</td>
<td>使用变长编码</td>
<td>uint64</td>
<td>long</td>
<td>int&#x2F;long</td>
<td>uint64</td>
<td>Bignum</td>
<td>ulong</td>
<td>integer&#x2F;string</td>
</tr>
<tr>
<td>sint32</td>
<td>使用变长编码,这些编码在负值时比int32高效的多</td>
<td>int32</td>
<td>int</td>
<td>int</td>
<td>int32</td>
<td>Fixnum 或者 Bignum(根据需要)</td>
<td>int</td>
<td>integer</td>
</tr>
<tr>
<td>sint64</td>
<td>使用变长编码,有符号的整型值。编码时比通常的int64高效。</td>
<td>int64</td>
<td>long</td>
<td>int&#x2F;long</td>
<td>int64</td>
<td>Bignum</td>
<td>long</td>
<td>integer&#x2F;string</td>
</tr>
<tr>
<td>fixed32</td>
<td>总是4个字节,如果数值总是比总是比228大的话,这个类型会比uint32高效。</td>
<td>uint32</td>
<td>int</td>
<td>int</td>
<td>uint32</td>
<td>Fixnum 或者 Bignum(根据需要)</td>
<td>uint</td>
<td>integer</td>
</tr>
<tr>
<td>fixed64</td>
<td>总是8个字节,如果数值总是比总是比256大的话,这个类型会比uint64高效。</td>
<td>uint64</td>
<td>long</td>
<td>int&#x2F;long</td>
<td>uint64</td>
<td>Bignum</td>
<td>ulong</td>
<td>integer&#x2F;string</td>
</tr>
<tr>
<td>sfixed32</td>
<td>总是4个字节</td>
<td>int32</td>
<td>int</td>
<td>int</td>
<td>int32</td>
<td>Fixnum 或者 Bignum(根据需要)</td>
<td>int</td>
<td>integer</td>
</tr>
<tr>
<td>sfixed64</td>
<td>总是8个字节</td>
<td>int64</td>
<td>long</td>
<td>int&#x2F;long</td>
<td>int64</td>
<td>Bignum</td>
<td>long</td>
<td>integer&#x2F;string</td>
</tr>
<tr>
<td>bool</td>
<td></td>
<td>bool</td>
<td>boolean</td>
<td>bool</td>
<td>bool</td>
<td>TrueClass&#x2F;FalseClass</td>
<td>bool</td>
<td>boolean</td>
</tr>
<tr>
<td>string</td>
<td>一个字符串必须是UTF-8编码或者7-bit ASCII编码的文本。</td>
<td>string</td>
<td>String</td>
<td>str&#x2F;unicode</td>
<td>string</td>
<td>String (UTF-8)</td>
<td>string</td>
<td>string</td>
</tr>
<tr>
<td>bytes</td>
<td>可能包含任意顺序的字节数据。</td>
<td>string</td>
<td>ByteString</td>
<td>str</td>
<td>[]byte</td>
<td>String (ASCII-8BIT)</td>
<td>ByteString</td>
<td>string</td>
</tr>
</tbody></table>
<h1 id="默认值"><a href="#默认值" class="headerlink" title="默认值"></a>默认值</h1><p>当一个消息被解析的时候,如果被编码的信息不包含一个特定的singular元素,被解析的对象锁对应的域被设置位一个默认值,对于不同类型指定如下:</p>
<ul>
<li>对于strings,默认是一个空string</li>
<li>对于bytes,默认是一个空的bytes</li>
<li>对于bools,默认是false</li>
<li>对于数值类型,默认是0</li>
<li>对于枚举,默认是第一个定义的枚举值,必须为0;</li>
<li>对于消息类型(message),域没有被设置,确切的消息是根据语言确定的,详见<a href="https://developers.google.com/protocol-buffers/docs/reference/overview?hl=zh-cn">generated code guide</a>。</li>
</ul>
<p>对于可重复域的默认值是空(通常情况下是对应语言中空列表)。</p>
<p>注:对于标量消息域,一旦消息被解析,就无法判断域释放被设置为默认值(例如,例如boolean值是否被设置为false)还是根本没有被设置。你应该在定义你的消息类型时非常注意。例如,比如你不应该定义boolean的默认值false作为任何行为的触发方式。也应该注意如果一个标量消息域被设置为标志位,这个值不应该被序列化传输。</p>
<p>查看<a href="https://developers.google.com/protocol-buffers/docs/reference/overview?hl=zh-cn">generated code guide</a>选择你的语言的默认值的工作细节。</p>
<h1 id="枚举"><a href="#枚举" class="headerlink" title="枚举"></a>枚举</h1><p>当需要定义一个消息类型的时候,可能想为一个字段指定某“预定义值序列”中的一个值。例如,假设要为每一个SearchRequest消息添加一个 corpus字段,而corpus的值可能是UNIVERSAL,WEB,IMAGES,LOCAL,NEWS,PRODUCTS或VIDEO中的一个。 其实可以很容易地实现这一点:通过向消息定义中添加一个枚举(enum)并且为每个可能的值定义一个常量就可以了。</p>
<p>在下面的例子中,在消息格式中添加了一个叫做Corpus的枚举类型——它含有所有可能的值 ——以及一个类型为Corpus的字段:</p>
<figure class="highlight protobuf"><table><tr><td class="code"><pre><span class="line"><span class="keyword">message </span><span class="title class_">SearchRequest</span> &#123;</span><br><span class="line"> <span class="type">string</span> query = <span class="number">1</span>;</span><br><span class="line"> <span class="type">int32</span> page_number = <span class="number">2</span>;</span><br><span class="line"> <span class="type">int32</span> result_per_page = <span class="number">3</span>;</span><br><span class="line"> <span class="keyword">enum </span><span class="title class_">Corpus</span> &#123;</span><br><span class="line"> UNIVERSAL = <span class="number">0</span>;</span><br><span class="line"> WEB = <span class="number">1</span>;</span><br><span class="line"> IMAGES = <span class="number">2</span>;</span><br><span class="line"> LOCAL = <span class="number">3</span>;</span><br><span class="line"> NEWS = <span class="number">4</span>;</span><br><span class="line"> PRODUCTS = <span class="number">5</span>;</span><br><span class="line"> VIDEO = <span class="number">6</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> Corpus corpus = <span class="number">4</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>如你所见,Corpus枚举的第一个常量映射为0:每个枚举类型必须将其第一个类型映射为0,这是因为:</p>
<ul>
<li>必须有有一个0值,我们可以用这个0值作为默认值。</li>
<li>这个零值必须为第一个元素,为了兼容proto2语义,枚举类的第一个值总是默认值。</li>
</ul>
<p>你可以通过将不同的枚举常量指定为相同的值。如果这样做你需要将<code>allow_alias</code>设定为true,否则编译器会在相同值地方产生一个错误信息。</p>
<p>比如对于下面的代码,编译铁定出错,因为<code>STARTED</code> 和 <code>RUNNING</code> 都被赋予了相同的值 1:</p>
<figure class="highlight protobuf"><table><tr><td class="code"><pre><span class="line"><span class="keyword">enum </span><span class="title class_">EnumNotAllowingAlias</span> &#123;</span><br><span class="line"> UNKNOWN = <span class="number">0</span>;</span><br><span class="line"> STARTED = <span class="number">1</span>;</span><br><span class="line"> RUNNING = <span class="number">1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>但如果加上<code>allow_alias</code>,这意味着你可以使用 <code>STARTED</code> 或 <code>RUNNING</code> 来表示同一个概念</p>
<figure class="highlight protobuf"><table><tr><td class="code"><pre><span class="line"><span class="keyword">enum </span><span class="title class_">EnumAllowingAlias</span> &#123;</span><br><span class="line"> <span class="keyword">option</span> allow_alias = <span class="literal">true</span>;</span><br><span class="line"> UNKNOWN = <span class="number">0</span>;</span><br><span class="line"> STARTED = <span class="number">1</span>;</span><br><span class="line"> RUNNING = <span class="number">1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>枚举常量必须在32位整型值的范围内。因为enum值是使用可变编码方式的,对负数不够高效,因此不推荐在enum中使用负数。如上例所示,可以在 一个消息定义的内部或外部定义枚举——这些枚举可以在.proto文件中的任何消息定义里重用。当然也可以在一个消息中声明一个枚举类型,而在另一个不同 的消息中使用它——采用MessageType.EnumType的语法格式。</p>
<p>当对一个使用了枚举的.proto文件运行protocol buffer编译器的时候,生成的代码中将有一个对应的enum(对Java或C++来说),或者一个特殊的EnumDescriptor类(对 Python来说),它被用来在运行时生成的类中创建一系列的整型值符号常量(symbolic constants)。</p>
<p>在反序列化的过程中,无法识别的枚举值会被保存在消息中,虽然这种表示方式需要依据所使用语言而定。在那些支持开放枚举类型超出指定范围之外的语言中(例如C++和Go),为识别的值会被表示成所支持的整型。在使用封闭枚举类型的语言中(Java),使用枚举中的一个类型来表示未识别的值,并且可以使用所支持整型来访问。在其他情况下,如果解析的消息被序列号,未识别的值将保持原样。</p>
<p>关于如何在你的应用程序的消息中使用枚举的更多信息,请查看所选择的语言<a href="http://code.google.com/intl/zh-CN/apis/protocolbuffers/docs/reference/overview.html%E3%80%82">generated code guide</a></p>
<h1 id="使用其他消息类型-对应Java的嵌套"><a href="#使用其他消息类型-对应Java的嵌套" class="headerlink" title="使用其他消息类型(对应Java的嵌套)"></a>使用其他消息类型(对应Java的嵌套)</h1><p>你可以将其他消息类型用作字段类型。例如,假设在每一个SearchResponse消息中包含Result消息,此时可以在相同的.proto文件中定义一个Result消息类型,然后在SearchResponse消息中指定一个Result类型的字段,如:</p>
<figure class="highlight protobuf"><table><tr><td class="code"><pre><span class="line"><span class="keyword">message </span><span class="title class_">SearchResponse</span> &#123;</span><br><span class="line"> <span class="keyword">repeated</span> Result results = <span class="number">1</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">message </span><span class="title class_">Result</span> &#123;</span><br><span class="line"> <span class="type">string</span> url = <span class="number">1</span>;</span><br><span class="line"> <span class="type">string</span> title = <span class="number">2</span>;</span><br><span class="line"> <span class="keyword">repeated</span> <span class="type">string</span> snippets = <span class="number">3</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="导入定义"><a href="#导入定义" class="headerlink" title="导入定义"></a>导入定义</h2><p>在上面的例子中,Result消息类型与SearchResponse是定义在同一文件中的。如果想要使用的消息类型已经在其他.proto文件中已经定义过了呢?<br>你可以通过导入(importing)其他.proto文件中的定义来使用它们。要导入其他.proto文件的定义,你需要在你的文件中添加一个导入声明,如:</p>
<figure class="highlight protobuf"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="string">&quot;myproject/other_protos.proto&quot;</span>;</span><br></pre></td></tr></table></figure>
<p>默认情况下你只能使用直接导入的.proto文件中的定义. 然而, 有时候你需要移动一个.proto文件到一个新的位置, 可以不直接移动.proto文件, 只需放入一个伪 .proto 文件在老的位置, 然后使用import public转向新的位置。import public 依赖性会通过任意导入包含import public声明的proto文件传递。例如:</p>
<figure class="highlight protobuf"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 这是新的proto</span></span><br><span class="line"><span class="comment">// All definitions are moved here</span></span><br></pre></td></tr></table></figure>
<figure class="highlight protobuf"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 这是旧的proto</span></span><br><span class="line"><span class="comment">// 这是所有客户端正在导入的包</span></span><br><span class="line"><span class="keyword">import</span> public <span class="string">&quot;new.proto&quot;</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="string">&quot;other.proto&quot;</span>;</span><br></pre></td></tr></table></figure>
<figure class="highlight protobuf"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 客户端proto</span></span><br><span class="line"><span class="keyword">import</span> <span class="string">&quot;old.proto&quot;</span>;</span><br><span class="line"><span class="comment">// 现在你可以使用新久两种包的proto定义了。</span></span><br></pre></td></tr></table></figure>
<p>通过在编译器命令行参数中使用-I&#x2F;–proto_pathprotocal 编译器会在指定目录搜索要导入的文件。如果没有给出标志,编译器会搜索编译命令被调用的目录。通常你只要指定proto_path标志为你的工程根目录就好。并且指定好导入的正确名称就好。</p>
<h2 id="使用proto2消息类型"><a href="#使用proto2消息类型" class="headerlink" title="使用proto2消息类型"></a>使用proto2消息类型</h2><p>在你的proto3消息中导入proto2的消息类型也是可以的,反之亦然,然后proto2枚举不可以直接在proto3的标识符中使用(如果仅仅在proto2消息中使用是可以的)。</p>
<h1 id="嵌套类型-对应Java的内部类"><a href="#嵌套类型-对应Java的内部类" class="headerlink" title="嵌套类型(对应Java的内部类)"></a>嵌套类型(对应Java的内部类)</h1><p>你可以在其他消息类型中定义、使用消息类型,在下面的例子中,Result消息就定义在SearchResponse消息内,如:</p>
<figure class="highlight protobuf"><table><tr><td class="code"><pre><span class="line"><span class="keyword">message </span><span class="title class_">SearchResponse</span> &#123;</span><br><span class="line"> <span class="keyword">message </span><span class="title class_">Result</span> &#123;</span><br><span class="line"> <span class="type">string</span> url = <span class="number">1</span>;</span><br><span class="line"> <span class="type">string</span> title = <span class="number">2</span>;</span><br><span class="line"> <span class="keyword">repeated</span> <span class="type">string</span> snippets = <span class="number">3</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">repeated</span> Result results = <span class="number">1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>如果你想在它的父消息类型的外部重用这个消息类型,你需要以Parent.Type的形式使用它,如:</p>
<figure class="highlight protobuf"><table><tr><td class="code"><pre><span class="line"><span class="keyword">message </span><span class="title class_">SomeOtherMessage</span> &#123;</span><br><span class="line"> SearchResponse.Result result = <span class="number">1</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>当然,你也可以将消息嵌套任意多层,如:</p>
<figure class="highlight protobuf"><table><tr><td class="code"><pre><span class="line"><span class="keyword">message </span><span class="title class_">Outer</span> &#123; <span class="comment">// Level 0</span></span><br><span class="line"> <span class="keyword">message </span><span class="title class_">MiddleAA</span> &#123; <span class="comment">// Level 1</span></span><br><span class="line"> <span class="keyword">message </span><span class="title class_">Inner</span> &#123; <span class="comment">// Level 2</span></span><br><span class="line"> <span class="type">int64</span> ival = <span class="number">1</span>;</span><br><span class="line"> <span class="type">bool</span> booly = <span class="number">2</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line"> <span class="keyword">message </span><span class="title class_">MiddleBB</span> &#123; <span class="comment">// Level 1</span></span><br><span class="line"> <span class="keyword">message </span><span class="title class_">Inner</span> &#123; <span class="comment">// Level 2</span></span><br><span class="line"> <span class="type">int32</span> ival = <span class="number">1</span>;</span><br><span class="line"> <span class="type">bool</span> booly = <span class="number">2</span>;</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h1 id="更新一个消息类型"><a href="#更新一个消息类型" class="headerlink" title="更新一个消息类型"></a>更新一个消息类型</h1><p>如果一个已有的消息格式已无法满足新的需求——如,要在消息中添加一个额外的字段——但是同时旧版本写的代码仍然可用。不用担心!更新消息而不破坏已有代码是非常简单的。在更新时只要记住以下的规则即可。</p>
<blockquote>
<p>不要更改任何已有的字段的数值标识。</p>
</blockquote>
<p>假设我们有一个旧版本的消息类型 <code>Person</code>,其中包含 <code>name</code> 和 <code>age</code> 两个字段:</p>
<p>旧版本代码示例:</p>
<figure class="highlight protobuf"><table><tr><td class="code"><pre><span class="line">syntax = <span class="string">&quot;proto3&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">message </span><span class="title class_">Person</span> &#123;</span><br><span class="line"> <span class="type">string</span> name = <span class="number">1</span>;</span><br><span class="line"> <span class="type">int32</span> age = <span class="number">2</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>现在,我们需要在该消息类型中添加一个新的字段 <code>email</code>,同时遵循规则不更改任何已有字段的数值标识。</p>
<p>更新后的新版本代码示例:</p>
<figure class="highlight protobuf"><table><tr><td class="code"><pre><span class="line">syntax = <span class="string">&quot;proto3&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">message </span><span class="title class_">Person</span> &#123;</span><br><span class="line"> <span class="type">string</span> name = <span class="number">1</span>;</span><br><span class="line"> <span class="type">int32</span> age = <span class="number">2</span>;</span><br><span class="line"> <span class="type">string</span> email = <span class="number">3</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>如上所示,在新版本的代码中,我们增加了一个名为 <code>email</code> 的字段,而没有修改旧字段 <code>name</code> 和 <code>age</code> 的数值标识。</p>
<p>通过这样的更新,旧版本的代码仍然可以正常解析使用旧消息格式的数据,并且只会处理 <code>name</code> 和 <code>age</code> 字段。而新版本的代码可以解析使用新消息格式的数据,并能够同时处理 <code>name</code>、<code>age</code> 和 <code>email</code> 字段。</p>
<blockquote>
<p>如果你增加新的字段,使用旧格式的字段仍然可以被你新产生的代码所解析。你应该记住这些元素的默认值这样你的新代码就可以以适当的方式和旧代码产生的数据交互。相似的,通过新代码产生的消息也可以被旧代码解析:只不过新的字段会被忽视掉。注意,未被识别的字段会在反序列化的过程中丢弃掉,所以如果消息再被传递给新的代码,新的字段依然是不可用的(这和proto2中的行为是不同的,在proto2中未定义的域依然会随着消息被序列化)</p>
</blockquote>
<p>假设我们有一个旧版本的消息类型 <code>Person</code>,包含 <code>name</code> 和 <code>age</code> 字段:</p>
<p>旧版本代码示例:</p>
<figure class="highlight protobuf"><table><tr><td class="code"><pre><span class="line">syntax = <span class="string">&quot;proto3&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">message </span><span class="title class_">Person</span> &#123;</span><br><span class="line"> <span class="type">string</span> name = <span class="number">1</span>;</span><br><span class="line"> <span class="type">int32</span> age = <span class="number">2</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>现在,我们要更新消息类型,在其中添加一个新的字段 <code>email</code>。同时,我们需要确保新代码和旧代码之间可以正确解析消息,并处理默认值和未识别字段的情况。</p>
<p>更新后的新版本代码示例:</p>
<figure class="highlight protobuf"><table><tr><td class="code"><pre><span class="line">syntax = <span class="string">&quot;proto3&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">message </span><span class="title class_">Person</span> &#123;</span><br><span class="line"> <span class="type">string</span> name = <span class="number">1</span>;</span><br><span class="line"> <span class="type">int32</span> age = <span class="number">2</span>;</span><br><span class="line"> <span class="type">string</span> email = <span class="number">3</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>在上述新版本的代码中,我们增加了一个名为 <code>email</code> 的字段。遵循更新消息类型的规则,该字段的数值标识为 3。</p>
<p>通过这样的更新,我们可以实现以下互操作性:</p>
<ul>
<li><p>旧版本代码解析新消息格式的数据:旧版本代码只能访问到 <code>name</code> 和 <code>age</code> 字段,而无法访问 <code>email</code> 字段。它会忽略掉 <code>email</code> 字段,但仍然能够正常解析和处理 <code>name</code> 和 <code>age</code> 的值。</p>
</li>
<li><p>新版本代码解析旧消息格式的数据:新版本代码可以同时访问 <code>name</code>、<code>age</code> 和 <code>email</code> 字段。对于使用旧消息格式的数据,<code>email</code> 字段会使用默认值(在 <code>proto3</code> 中,默认值为空字符串)。新版本代码可以正确处理这种情况。</p>
</li>
</ul>
<p>此外,当使用新代码生成的消息被传递给旧代码进行解析时,旧代码会忽略掉 <code>email</code> 字段,而未被识别的字段会在反序列化过程中被丢弃。因此,新字段并不可用于旧代码。这种行为与 <code>proto2</code> 的行为不同,因为在 <code>proto2</code> 中,未定义的域会随消息一起被序列化。</p>
<p>通过遵循上述步骤,更新消息类型后,我们可以确保新旧代码之间的互操作性,并正确处理默认值和未识别字段的情况。</p>
<blockquote>
<p>非required的字段可以移除——只要它们的标识号在新的消息类型中不再使用(更好的做法可能是重命名那个字段,例如在字段前添加<code>OBSOLETE_</code>前缀,那样的话,使用的.proto文件的用户将来就不会无意中重新使用了那些不该使用的标识号)。</p>
</blockquote>
<p>假设我们有一个旧版本的消息类型 <code>Person</code>,包含 <code>name</code>、<code>age</code> 和 <code>email</code> 字段:</p>
<p>旧版本代码示例:</p>
<figure class="highlight protobuf"><table><tr><td class="code"><pre><span class="line">syntax = <span class="string">&quot;proto3&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">message </span><span class="title class_">Person</span> &#123;</span><br><span class="line"> <span class="type">string</span> name = <span class="number">1</span>;</span><br><span class="line"> <span class="type">int32</span> age = <span class="number">2</span>;</span><br><span class="line"> <span class="type">string</span> email = <span class="number">3</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>现在,我们要更新消息类型,移除 <code>email</code> 字段。根据建议,更好的做法是将该字段重命名为 <code>OBSOLETE_email</code>,以防止其他用户在未来无意中重新使用该字段的标识号。</p>
<p>更新后的新版本代码示例:</p>
<figure class="highlight protobuf"><table><tr><td class="code"><pre><span class="line">syntax = <span class="string">&quot;proto3&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">message </span><span class="title class_">Person</span> &#123;</span><br><span class="line"> <span class="type">string</span> name = <span class="number">1</span>;</span><br><span class="line"> <span class="type">int32</span> age = <span class="number">2</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// OBSOLETE_email field, do not use.</span></span><br><span class="line"> <span class="type">string</span> OBSOLETE_email = <span class="number">3</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>在上述新版本的代码中,我们将要被移除的字段 <code>email</code> 重命名为了 <code>OBSOLETE_email</code>,并添加了注释提示不要使用该字段。通过这样的重命名,我们确保在新消息类型中不再使用原先的标识号(3),从而避免其他用户在未来无意中重新使用该标识号。<strong>对应到Java中,我们可以把它理解为“已弃用的属性”</strong></p>
<p>在这种情况下,通过更新消息类型并移除非 required 的字段,我们可以避免将来的潜在问题,并提供更好的可读性和可维护性。</p>
<blockquote>
<p>int32, uint32, int64, uint64,和bool是全部兼容的,这意味着可以将这些类型中的一个转换为另外一个,而不会破坏向前、 向后的兼容性。如果解析出来的数字与对应的类型不相符,那么结果就像在C++中对它进行了强制类型转换一样(例如,如果把一个64位数字当作int32来 读取,那么它就会被截断为32位的数字)。</p>
</blockquote>
<blockquote>
<p>sint32和sint64是互相兼容的,但是它们与其他整数类型不兼容。</p>
</blockquote>
<blockquote>
<p>string和bytes是兼容的——只要bytes是有效的UTF-8编码。</p>
</blockquote>
<blockquote>
<p>嵌套消息与bytes是兼容的——只要bytes包含该消息的一个编码过的版本。</p>
</blockquote>
<blockquote>
<p>fixed32与sfixed32是兼容的,fixed64与sfixed64是兼容的。</p>
</blockquote>
<blockquote>
<p>枚举类型与int32,uint32,int64和uint64相兼容(注意如果值不相兼容则会被截断),然而在客户端反序列化之后他们可能会有不同的处理方式,例如,未识别的proto3枚举类型会被保留在消息中,但是他的表示方式会依照语言而定。int类型的字段总会保留他们的。</p>
</blockquote>
<p>假设我们有一个旧版本的消息类型 <code>Person</code>,其中包含一个枚举类型的字段 <code>gender</code>:</p>
<p>旧版本代码示例:</p>
<figure class="highlight protobuf"><table><tr><td class="code"><pre><span class="line">syntax = <span class="string">&quot;proto3&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">message </span><span class="title class_">Person</span> &#123;</span><br><span class="line"> <span class="type">string</span> name = <span class="number">1</span>;</span><br><span class="line"> <span class="type">int32</span> age = <span class="number">2</span>;</span><br><span class="line"> Gender gender = <span class="number">3</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">enum </span><span class="title class_">Gender</span> &#123;</span><br><span class="line"> UNKNOWN = <span class="number">0</span>;</span><br><span class="line"> MALE = <span class="number">1</span>;</span><br><span class="line"> FEMALE = <span class="number">2</span>;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>现在,我们要更新消息类型,将 <code>gender</code> 字段的类型从枚举类型改为 int32 类型。根据规范,在更新时需要注意以下几点:</p>
<ul>
<li><p>消息中的已存在的枚举值应该能够使用整型类型进行兼容处理。例如,如果枚举值在新的消息类型中使用了 int32 类型,并且其值在原枚举类型的取值范围内,那么解析时不会出现问题。</p>
</li>
<li><p>对于没有定义的枚举值,旧代码保留未识别的枚举值并将其作为整型类型存储在消息中。但是,它们的表示方式会因语言而异,因此客户端在反序列化后需要注意处理这些未识别值。</p>
</li>
</ul>
<p>更新后的新版本代码示例:</p>
<figure class="highlight protobuf"><table><tr><td class="code"><pre><span class="line">syntax = <span class="string">&quot;proto3&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">message </span><span class="title class_">Person</span> &#123;</span><br><span class="line"> <span class="type">string</span> name = <span class="number">1</span>;</span><br><span class="line"> <span class="type">int32</span> age = <span class="number">2</span>;</span><br><span class="line"> <span class="type">int32</span> gender = <span class="number">3</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>在上述新版本的代码中,我们将 <code>gender</code> 字段的类型由枚举类型改为了 int32 类型。</p>
<p>通过这样的更新,在新消息类型中,整型类型可以与旧消息类型中的枚举类型兼容。已定义的枚举值可以正常解析,并且未定义的枚举值会以整型类型的形式保留在消息中,但是它们的具体表示方式可能因语言而异。</p>
<p>尽管如此,应该注意的是,客户端在反序列化后需要特别处理这些未识别的整型值,以避免出现意外行为。</p>
<h1 id="Any-类似Java的泛型或Object"><a href="#Any-类似Java的泛型或Object" class="headerlink" title="Any(类似Java的泛型或Object)"></a>Any(类似Java的泛型或Object)</h1><p>尽管存在一些类比,但请注意 Any 类型与泛型和 Object 类型之间的差异:</p>
<ul>
<li>Any 类型是一种特殊的消息类型,需要在 .proto 文件中导入 <code>google/protobuf/any.proto</code>,以便使用。</li>
<li>Any 类型通过序列化和反序列化的方式来存储和传递消息。它不仅存储了序列化后的字节,还包括一个 URL 用于解析消息的类型。</li>
<li>Any 类型需要根据 URL 去解析和处理相应的消息类型,而不是简单地将其视为 Object 类型。</li>
</ul>
<p>Any类型消息允许你在没有指定他们的.proto定义的情况下使用消息作为一个嵌套类型。一个Any类型包括一个可以被序列化bytes类型的任意消息,以及一个URL作为一个全局标识符和解析消息类型。为了使用Any类型,你需要导入import google&#x2F;protobuf&#x2F;any.proto</p>
<figure class="highlight protobuf"><table><tr><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="string">&quot;google/protobuf/any.proto&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">message </span><span class="title class_">ErrorStatus</span> &#123;</span><br><span class="line"> <span class="type">string</span> message = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">repeated</span> google.protobuf.Any details = <span class="number">2</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>对于给定的消息类型的默认类型URL是type.googleapis.com&#x2F;packagename.messagename。</p>
<p>不同语言的实现会支持动态库以线程安全的方式去帮助封装或者解封装Any值。例如在java中,Any类型会有特殊的pack()和unpack()访问器,在C++中会有PackFrom()和UnpackTo()方法。</p>
<p>目前,用于Any类型的动态库仍在开发之中<br>如果你已经很熟悉proto2语法,使用Any替换拓展</p>
<h1 id="Oneof"><a href="#Oneof" class="headerlink" title="Oneof"></a>Oneof</h1><p>如果你的消息中有很多可选字段,并且同时最多只会有一个字段被设置,为了节省内存,你可以使用 oneof 特性来增强这种行为。</p>
<p>oneof 字段类似于可选字段,但与可选字段不同的是,它们会共享同一块内存空间,最多只会有一个字段被设置。当设置其中一个字段时,会清除其他已设置的字段。你可以通过使用 <code>case()</code> 或 <code>WhichOneof()</code> 方法(具体取决于你使用的编程语言)来检查哪个 oneof 字段被设置。</p>
<h2 id="使用Oneof"><a href="#使用Oneof" class="headerlink" title="使用Oneof"></a>使用Oneof</h2><p>为了在.proto定义Oneof字段, 你需要在名字前面加上oneof关键字, 比如下面例子的test_oneof:</p>
<figure class="highlight protobuf"><table><tr><td class="code"><pre><span class="line"><span class="keyword">message </span><span class="title class_">SampleMessage</span> &#123;</span><br><span class="line"> <span class="keyword">oneof</span> test_oneof &#123;</span><br><span class="line"> <span class="type">string</span> name = <span class="number">4</span>;</span><br><span class="line"> SubMessage sub_message = <span class="number">9</span>;</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>然后你可以增加oneof字段到 oneof 定义中. 你可以增加任意类型的字段, 但是不能使用repeated 关键字.</p>
<p>在产生的代码中, oneof字段拥有同样的 getters 和setters, 就像正常的可选字段一样. 也有一个特殊的方法来检查到底那个字段被设置. 你可以在相应的语言API指南中找到oneof API介绍.</p>
<h2 id="Oneof-特性"><a href="#Oneof-特性" class="headerlink" title="Oneof 特性"></a>Oneof 特性</h2><ul>
<li>设置oneof会自动清除其它oneof字段的值. 所以设置多次后,只有最后一次设置的字段有值.</li>
</ul>
<figure class="highlight c++"><table><tr><td class="code"><pre><span class="line">SampleMessage message;</span><br><span class="line">message.<span class="built_in">set_name</span>(<span class="string">&quot;name&quot;</span>);</span><br><span class="line"><span class="built_in">CHECK</span>(message.<span class="built_in">has_name</span>());</span><br><span class="line">message.<span class="built_in">mutable_sub_message</span>(); <span class="comment">// Will clear name field.</span></span><br><span class="line"><span class="built_in">CHECK</span>(!message.<span class="built_in">has_name</span>());</span><br></pre></td></tr></table></figure>
<ul>
<li>如果解析器遇到同一个oneof中有多个成员,只有最会一个会被解析成消息。</li>
<li>oneof不支持repeated.</li>
<li>反射API对oneof 字段有效.</li>
<li>如果使用C++,需确保代码不会导致内存泄漏. 下面的代码会崩溃, 因为sub_message 已经通过set_name()删除了</li>
</ul>
<figure class="highlight c++"><table><tr><td class="code"><pre><span class="line">SampleMessage message;</span><br><span class="line">SubMessage* sub_message = message.<span class="built_in">mutable_sub_message</span>();</span><br><span class="line">message.<span class="built_in">set_name</span>(<span class="string">&quot;name&quot;</span>); <span class="comment">// Will delete sub_message</span></span><br><span class="line">sub_message-&gt;set_... <span class="comment">// Crashes here</span></span><br></pre></td></tr></table></figure>
<ul>
<li>在C++中,如果你使用Swap()两个oneof消息,每个消息,两个消息将拥有对方的值,例如在下面的例子中,msg1会拥有sub_message并且msg2会有name。</li>
</ul>
<figure class="highlight c++"><table><tr><td class="code"><pre><span class="line">SampleMessage msg1;</span><br><span class="line">msg1.<span class="built_in">set_name</span>(<span class="string">&quot;name&quot;</span>);</span><br><span class="line">SampleMessage msg2;</span><br><span class="line">msg2.<span class="built_in">mutable_sub_message</span>();</span><br><span class="line">msg1.<span class="built_in">swap</span>(&amp;msg2);</span><br><span class="line"><span class="built_in">CHECK</span>(msg1.<span class="built_in">has_sub_message</span>());</span><br><span class="line"><span class="built_in">CHECK</span>(msg2.<span class="built_in">has_name</span>());</span><br></pre></td></tr></table></figure>
<h2 id="向后兼容性问题"><a href="#向后兼容性问题" class="headerlink" title="向后兼容性问题"></a>向后兼容性问题</h2><p>当增加或者删除oneof字段时一定要小心. 如果检查oneof的值返回None&#x2F;NOT_SET, 它意味着oneof字段没有被赋值或者在一个不同的版本中赋值了。 你不会知道是哪种情况,因为没有办法判断如果未识别的字段是一个oneof字段。</p>
<p>Tage 重用问题:</p>
<ul>
<li>将字段移入或移除oneof:在消息被序列号或者解析后,你也许会失去一些信息(有些字段也许会被清除)</li>
<li>删除一个字段或者加入一个字段:在消息被序列号或者解析后,这也许会清除你现在设置的oneof字段</li>
<li>分离或者融合oneof:行为与移动常规字段相似。</li>
</ul>
<h1 id="映射(Maps)"><a href="#映射(Maps)" class="headerlink" title="映射(Maps)"></a>映射(Maps)</h1><p>如果你希望创建一个关联映射,protocol buffer提供了一种快捷的语法:</p>
<figure class="highlight protobuf"><table><tr><td class="code"><pre><span class="line">map&lt;key_type, value_type&gt; map_field = N;</span><br></pre></td></tr></table></figure>
<p>其中key_type可以是任意Integer或者string类型(所以,除了floating和bytes的任意标量类型都是可以的)value_type可以是任意类型。</p>
<p>例如,如果你希望创建一个project的映射,每个Projecct使用一个string作为key,你可以像下面这样定义:</p>
<figure class="highlight protobuf"><table><tr><td class="code"><pre><span class="line">map&lt;<span class="type">string</span>, Project&gt; projects = <span class="number">3</span>;</span><br></pre></td></tr></table></figure>
<ul>
<li>Map的字段可以是repeated。</li>
<li>序列化后的顺序和map迭代器的顺序是不确定的,所以你不要期望以固定顺序处理Map</li>
<li>当为.proto文件产生生成文本格式的时候,map会按照key 的顺序排序,数值化的key会按照数值排序。</li>
<li>从序列化中解析或者融合时,如果有重复的key则后一个key不会被使用,当从文本格式中解析map时,如果存在重复的key。</li>
</ul>
<p>生成map的API现在对于所有proto3支持的语言都可用了,你可以从API指南找到更多信息。</p>
<h2 id="向后兼容性问题-1"><a href="#向后兼容性问题-1" class="headerlink" title="向后兼容性问题"></a>向后兼容性问题</h2><p>map语法序列化后等同于如下内容,因此即使是不支持map语法的protocol buffer实现也是可以处理你的数据的:</p>
<figure class="highlight protobuf"><table><tr><td class="code"><pre><span class="line"><span class="keyword">message </span><span class="title class_">MapFieldEntry</span> &#123;</span><br><span class="line"> key_type key = <span class="number">1</span>;</span><br><span class="line"> value_type value = <span class="number">2</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">repeated</span> MapFieldEntry map_field = N;</span><br></pre></td></tr></table></figure>
<h1 id="包(Packages)"><a href="#包(Packages)" class="headerlink" title="包(Packages)"></a>包(Packages)</h1><p>当然可以为.proto文件新增一个可选的package声明符,用来防止不同的消息类型有命名冲突。如:</p>
<p>package foo.bar;<br>message Open { … }</p>
<p>在其他的消息格式定义中可以使用包名+消息名的方式来定义域的类型,如:</p>
<figure class="highlight protobuf"><table><tr><td class="code"><pre><span class="line"><span class="keyword">message </span><span class="title class_">Foo</span> &#123;</span><br><span class="line"> ...</span><br><span class="line"> <span class="keyword">required</span> foo.bar.Open open = <span class="number">1</span>;</span><br><span class="line"> ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>包的声明符会根据使用语言的不同影响生成的代码。</p>
<ul>
<li>对于C++,产生的类会被包装在C++的命名空间中,如上例中的Open会被封装在 foo::bar空间中; - 对于Java,包声明符会变为java的一个包,除非在.proto文件中提供了一个明确有java_package;</li>
<li>对于 Python,这个包声明符是被忽略的,因为Python模块是按照其在文件系统中的位置进行组织的。</li>
<li>对于Go,包可以被用做Go包名称,除非你显式的提供一个option go_package在你的.proto文件中。</li>
<li>对于Ruby,生成的类可以被包装在内置的Ruby名称空间中,转换成Ruby所需的大小写样式 (首字母大写;如果第一个符号不是一个字母,则使用PB_前缀),例如Open会在Foo::Bar名称空间中。</li>
<li>对于javaNano包会使用Java包,除非你在你的文件中显式的提供一个option java_package。</li>
<li>对于C#包可以转换为PascalCase后作为名称空间,除非你在你的文件中显式的提供一个option csharp_namespace,例如,Open会在Foo.Bar名称空间中</li>
</ul>
<h2 id="包及名称的解析"><a href="#包及名称的解析" class="headerlink" title="包及名称的解析"></a>包及名称的解析</h2><p>Protocol buffer语言中类型名称的解析与C++是一致的:首先从最内部开始查找,依次向外进行,每个包会被看作是其父类包的内部类。当然对于 (foo.bar.Baz)这样以“.”分隔的意味着是从最外围开始的。</p>
<p>ProtocolBuffer编译器会解析.proto文件中定义的所有类型名。 对于不同语言的代码生成器会知道如何来指向每个具体的类型,即使它们使用了不同的规则。</p>
<h1 id="定义服务-service接口"><a href="#定义服务-service接口" class="headerlink" title="定义服务(service接口)"></a>定义服务(service接口)</h1><p>当然,具体的实现类要我们自己写</p>
<p>如果想要将消息类型用在RPC(远程方法调用)系统中,可以在.proto文件中定义一个RPC服务接口,protocol buffer编译器将会根据所选择的不同语言生成服务接口代码及存根。如,想要定义一个RPC服务并具有一个方法,该方法能够接收 SearchRequest并返回一个SearchResponse,此时可以在.proto文件中进行如下定义:</p>
<figure class="highlight protobuf"><table><tr><td class="code"><pre><span class="line"><span class="keyword">service </span><span class="title class_">SearchService</span> &#123;</span><br><span class="line"> <span class="function"><span class="keyword">rpc</span> Search (SearchRequest) <span class="keyword">returns</span> (SearchResponse)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>最直观的使用protocol buffer的RPC系统是gRPC一个由谷歌开发的语言和平台中的开源的PRC系统,gRPC在使用protocl buffer时非常有效,如果使用特殊的protocol buffer插件可以直接为您从.proto文件中产生相关的RPC代码。</p>
<p>如果你不想使用gRPC,也可以使用protocol buffer用于自己的RPC实现,你可以从proto2语言指南中找到更多信息</p>
<p>还有一些第三方开发的PRC实现使用Protocol Buffer。参考第三方插件wiki查看这些实现的列表。</p>
<h1 id="JSON-映射"><a href="#JSON-映射" class="headerlink" title="JSON 映射"></a>JSON 映射</h1><p>Proto3 支持JSON的编码规范,使他更容易在不同系统之间共享数据,在下表中逐个描述类型。</p>
<p>如果JSON编码的数据丢失或者其本身就是null,这个数据会在解析成protocol buffer的时候被表示成默认值。如果一个字段在protocol buffer中表示为默认值,体会在转化成JSON的时候编码的时候忽略掉以节省空间。具体实现可以提供在JSON编码中可选的默认值。</p>
<table>
<thead>
<tr>
<th>proto3</th>
<th>JSON</th>
<th>JSON示例</th>
<th>注意</th>
</tr>
</thead>
<tbody><tr>
<td>message</td>
<td>object</td>
<td>{“fBar”: v, “g”: null, …}</td>
<td>产生JSON对象,消息字段名可以被映射成lowerCamelCase形式,并且成为JSON对象键,null被接受并成为对应字段的默认值</td>
</tr>
<tr>
<td>enum</td>
<td>string</td>
<td>“FOO_BAR”</td>
<td>枚举值的名字在proto文件中被指定</td>
</tr>
<tr>
<td>map</td>
<td>object</td>
<td>{“k”: v, …}</td>
<td>所有的键都被转换成string</td>
</tr>
<tr>
<td>repeated V</td>
<td>array</td>
<td>[v, …]</td>
<td>null被视为空列表</td>
</tr>
<tr>
<td>bool</td>
<td>true, false</td>
<td>true, false</td>
<td></td>
</tr>
<tr>
<td>string</td>
<td>string</td>
<td>“Hello World!”</td>
<td></td>
</tr>
<tr>
<td>bytes</td>
<td>base64 string</td>
<td>“YWJjMTIzIT8kKiYoKSctPUB+”</td>
<td></td>
</tr>
<tr>
<td>int32, fixed32, uint32</td>
<td>number</td>
<td>1, -10, 0</td>
<td>JSON值会是一个十进制数,数值型或者string类型都会接受</td>
</tr>
<tr>
<td>int64, fixed64, uint64</td>
<td>string</td>
<td>“1”, “-10”</td>
<td>JSON值会是一个十进制数,数值型或者string类型都会接受</td>
</tr>
<tr>
<td>float, double</td>
<td>number</td>
<td>1.1, -10.0, 0, “NaN”, “Infinity”</td>
<td>JSON值会是一个数字或者一个指定的字符串如”NaN”,”infinity”或者”-Infinity”,数值型或者字符串都是可接受的,指数符号也可以接受</td>
</tr>
<tr>
<td>Any</td>
<td>object</td>
<td>{“@type”: “url”, “f”: v, … }</td>
<td>如果一个Any保留一个特上述的JSON映射,则它会转换成一个如下形式:{“@type”: xxx, “value”: yyy}否则,该值会被转换成一个JSON对象,@type字段会被插入所指定的确定的值</td>
</tr>
<tr>
<td>Timestamp</td>
<td>string</td>
<td>“1972-01-01T10:00:20.021Z”</td>
<td>使用RFC 339,其中生成的输出将始终是Z-归一化啊的,并且使用0,3,6或者9位小数</td>
</tr>
<tr>
<td>Duration</td>
<td>string</td>
<td>“1.000340012s”, “1s”</td>
<td>生成的输出总是0,3,6或者9位小数,具体依赖于所需要的精度,接受所有可以转换为纳秒级的精度</td>
</tr>
<tr>
<td>Struct</td>
<td>object</td>
<td>{ … }</td>
<td>任意的JSON对象,见struct.proto</td>
</tr>
<tr>
<td>Wrapper types</td>
<td>various types</td>
<td>2, “2”, “foo”, true, “true”, null, 0, …</td>
<td>包装器在JSON中的表示方式类似于基本类型,但是允许nulll,并且在转换的过程中保留null</td>
</tr>
<tr>
<td>FieldMask</td>
<td>string</td>
<td>“f.fooBar,h”</td>
<td>见fieldmask.proto</td>
</tr>
<tr>
<td>ListValue</td>
<td>array</td>
<td>[foo, bar, …]</td>
<td></td>
</tr>
<tr>
<td>Value</td>
<td>value</td>
<td></td>
<td>任意JSON值</td>
</tr>
<tr>
<td>NullValue</td>
<td>null</td>
<td></td>
<td>JSON null</td>
</tr>
</tbody></table>
<h1 id="选项"><a href="#选项" class="headerlink" title="选项"></a>选项</h1><p>在定义.proto文件时能够标注一系列的options。Options并不改变整个文件声明的含义,但却能够影响特定环境下处理方式。完整的可用选项可以在google&#x2F;protobuf&#x2F;descriptor.proto找到。</p>
<p>一些选项是文件级别的,意味着它可以作用于最外范围,不包含在任何消息内部、enum或服务定义中。一些选项是消息级别的,意味着它可以用在消息定义的内部。当然有些选项可以作用在域、enum类型、enum值、服务类型及服务方法中。到目前为止,并没有一种有效的选项能作用于所有的类型。</p>
<p>如下就是一些常用的选择:</p>
<ul>
<li>java_package (文件选项) :这个选项表明生成java类所在的包。如果在.proto文件中没有明确的声明java_package,就采用默认的包名。当然了,默认方式产生的 java包名并不是最好的方式,按照应用名称倒序方式进行排序的。如果不需要产生java代码,则该选项将不起任何作用。如:</li>
</ul>
<figure class="highlight protobuf"><table><tr><td class="code"><pre><span class="line"><span class="keyword">option</span> java_package = <span class="string">&quot;com.example.foo&quot;</span>;</span><br></pre></td></tr></table></figure>
<ul>
<li>java_outer_classname (文件选项): 该选项表明想要生成Java类的名称。如果在.proto文件中没有明确的java_outer_classname定义,生成的class名称将会根据.proto文件的名称采用驼峰式的命名方式进行生成。如(foo_bar.proto生成的java类名为FooBar.java),如果不生成java代码,则该选项不起任何作用。如:</li>
</ul>
<figure class="highlight protobuf"><table><tr><td class="code"><pre><span class="line"><span class="keyword">option</span> java_outer_classname = <span class="string">&quot;Ponycopter&quot;</span>;</span><br></pre></td></tr></table></figure>
<ul>
<li>optimize_for(文件选项): 可以被设置为 SPEED, CODE_SIZE,或者LITE_RUNTIME。这些值将通过如下的方式影响C++及java代码的生成: <ul>
<li>SPEED (default): protocol buffer编译器将通过在消息类型上执行序列化、语法分析及其他通用的操作。这种代码是最优的。</li>
<li>CODE_SIZE: protocol buffer编译器将会产生最少量的类,通过共享或基于反射的代码来实现序列化、语法分析及各种其它操作。采用该方式产生的代码将比SPEED要少得多, 但是操作要相对慢些。当然实现的类及其对外的API与SPEED模式都是一样的。这种方式经常用在一些包含大量的.proto文件而且并不盲目追求速度的 应用中。</li>
<li>LITE_RUNTIME: protocol buffer编译器依赖于运行时核心类库来生成代码(即采用libprotobuf-lite 替代libprotobuf)。这种核心类库由于忽略了一 些描述符及反射,要比全类库小得多。这种模式经常在移动手机平台应用多一些。编译器采用该模式产生的方法实现与SPEED模式不相上下,产生的类通过实现 MessageLite接口,但它仅仅是Messager接口的一个子集。</li>
</ul>
</li>
</ul>
<figure class="highlight protobuf"><table><tr><td class="code"><pre><span class="line"><span class="keyword">option</span> optimize_for = CODE_SIZE;</span><br></pre></td></tr></table></figure>
<ul>
<li>cc_enable_arenas(文件选项):对于C++产生的代码启用<a href="https://developers.google.com/protocol-buffers/docs/reference/arenas?hl=zh-cn">arena allocation</a>。</li>
<li>objc_class_prefix(文件选项):设置Objective-C类的前缀,添加到所有Objective-C从此.proto文件产生的类和枚举类型。没有默认值,所使用的前缀应该是苹果推荐的3-5个大写字符,注意2个字节的前缀是苹果所保留的。</li>
<li>deprecated(字段选项):如果设置为true则表示该字段已经被废弃,并且不应该在新的代码中使用。在大多数语言中没有实际的意义。在java中,这回变成@Deprecated注释,在未来,其他语言的代码生成器也许会在字标识符中产生废弃注释,废弃注释会在编译器尝试使用该字段时发出警告。如果字段没有被使用你也不希望有新用户使用它,尝试使用保留语句替换字段声明。</li>
</ul>
<figure class="highlight protobuf"><table><tr><td class="code"><pre><span class="line"><span class="type">int32</span> old_field = <span class="number">6</span> [deprecated=<span class="literal">true</span>];</span><br></pre></td></tr></table></figure>
<h2 id="自定义选项"><a href="#自定义选项" class="headerlink" title="自定义选项"></a>自定义选项</h2><p>ProtocolBuffers允许自定义并使用选项。该功能应该属于一个高级特性,对于大部分人是用不到的。如果你的确希望创建自己的选项,请参看<a href="https://developers.google.com/protocol-buffers/docs/proto?hl=zh-cn#customoptions">Proto2 Language Guide</a>。注意创建自定义选项使用了拓展,拓展只在proto3中可用。</p>
<h1 id="生成你的类"><a href="#生成你的类" class="headerlink" title="生成你的类"></a>生成你的类</h1><p>在pom.xml中:</p>
<ol>
<li>引入 <a href="https://mvnrepository.com/artifact/io.grpc/grpc-protobuf"><code>grpc-protobuf</code></a> 依赖,使用 Protobuf 作为序列化库。引入 <a href="https://mvnrepository.com/artifact/io.grpc/grpc-stub"><code>grpc-stub</code></a> 依赖,使用 gRPC Stub 作为客户端。</li>
<li>引入 <a href="https://mvnrepository.com/artifact/kr.motd.maven/os-maven-plugin"><code>os-maven-plugin</code></a> 插件,从 OS 系统中获取参数。因为需要通过它,从 OS 系统中获取 <code>os.detected.classifier</code> 参数,稍后使用到 <code>protobuf-maven-plugin</code> 插件和 OS 系统相关。</li>
<li>引入 <a href="https://mvnrepository.com/artifact/org.xolstice.maven.plugins/protobuf-maven-plugin"><code>protobuf-maven-plugin</code></a> 插件,实现通过 <code>proto</code> 目录下的 protobuf 文件,生成 Service 和 Message 类。</li>
</ol>
<p>然后,我们点击 IDEA 的「compile」按钮,编译该 API 项目,并同时执行 <code>protobuf-maven-plugin</code> 插件进行生成。</p>
<p>具体依赖可以参考<a href="https://github.com/OrionLi/spring-boot-demo/blob/master/demo-rpc-grpc/grpc-userservice-api/pom.xml">我的GitHub中使用gRPC的api模块demo</a></p>
]]></content>
<categories>
<category>语言指南</category>
</categories>
<tags>
<tag>gRPC</tag>
<tag>Protobuf3</tag>
</tags>
</entry>
<entry>
<title>编程入门</title>
<url>/2023/07/06/programming-beginner-guide/</url>
<content><![CDATA[<blockquote>
<p>本文章更适合准大一的同学观看</p>
</blockquote>
<h1 id="导言"><a href="#导言" class="headerlink" title="导言"></a>导言</h1><p>想必很多报考计算机类志愿的高考生在填写志愿的唯一想法就是——<del>我要狠狠赚一笔</del>我对计算机&#x2F;编程感兴趣</p>
<p>当然,绝大多数人在初高中可能从来没接触过编程。那么,对于一个0基础的纯小白来说,常常会有以下问题:</p>
<ul>
<li><p>语言好多啊,我要学哪种,能不能多开</p>
</li>
<li><p>我应该怎么学习编程</p>
</li>
<li><p>我应该选择什么 IDE</p>
</li>
<li><p>有什么推荐的学习工具吗</p>
</li>
</ul>
<p>笔者一年前作为一位同样是0基础,同样有这些想法的小白,在摸索的过程踩了一堆坑,这里进行一些分享</p>
<h1 id="我要学什么语言"><a href="#我要学什么语言" class="headerlink" title="我要学什么语言"></a>我要学什么语言</h1><p>翻阅知乎,哔哩哔哩,你可能会看到以下言论:</p>
<ul>
<li><p>人生苦短,我选 Python</p>
</li>
<li><p>Java 岗位多,薪资也高,现在不学 Java,以后就没机会了,趁着现在的红利,先狠狠赚一笔(手动狗头)</p>
</li>
<li><p>嵌入式才是风口,要学就学C</p>
</li>
<li><p>PHP是最好的语言!</p>
</li>
<li><p>……</p>
</li>
</ul>
<p>在这里,笔者首先推荐学C语言</p>
<h2 id="为什么是C语言?"><a href="#为什么是C语言?" class="headerlink" title="为什么是C语言?"></a>为什么是C语言?</h2><ol>
<li><p>简单易学:C 语言相对于 Java、和 C++ 而言,语法较为简单,没有过多的高级特性和复杂的概念。</p>
</li>
<li><p>Python 和 Java 都是基于C语言开发的,C++ 是基于 C 语言的扩展,在<strong>编程语言基础语法</strong>相差不大的前提下,用 C 语言作为编程入门语言是一个好选择</p>
</li>
<li><p>在 C 和 C++ 中,可以直接使用指针进行编程。理解指针可以帮助你更深入地理解编程语言和计算机底层的工作原理。因为在其他编程语言中或多或少都有指针的概念。</p>
<blockquote>
<p>现在你只需要明白指针的作用很重要就可以了</p>
</blockquote>
</li>
<li><p>使用过面向过程的C语言,学习Java、C++等面向对象的语言可以<strong>更好的理解面向对象的优势</strong>(各种语言专长的领域不同,没有孰优孰劣之分)</p>
</li>
<li><p>C&#x2F;C++算法题的题解多</p>
<blockquote>
<p>因为C&#x2F;C++性能高、运行时间短、内存占用小,所以大多数算法比赛的选手更倾向于选择C&#x2F;C++</p>
</blockquote>
</li>
<li><p>开发环境最容易安装(专指 Visual Studio )</p>
</li>
</ol>
<h1 id="我要选择什么软件来学习C语言"><a href="#我要选择什么软件来学习C语言" class="headerlink" title="我要选择什么软件来学习C语言"></a>我要选择什么软件来学习C语言</h1><p>首先要明确的一点是,我们写C语言常说的 <code>VS (Visual Studio)</code> 和 <code>VS Code (Visual Studio Code)</code> 是完全不同的两个软件</p>
<ul>
<li><p>VS Code 属于编辑器,本质来说就是一个增强的记事本,只不过微软提供了插件可以让你使用它来实现部分 IDE 的功能。尽管它占用内存相对IDE来说小一些,但需要配置环境(配置环境对初学者来说就是噩梦),对小白来说开发体验会比 IDE 差很多,而且学习成本高一些</p>
</li>
<li><p>VS 属于 IDE,IDE(Integrated Development Environment,集成开发环境)简单来说就是把对应语言的开发所需的工具和环境都集成好了,好处就是开箱即用,配置简单,而且使用方便</p>
</li>
<li><p>所以C语言个人推荐VS:<a href="https://www.bilibili.com/video/BV1Xt411g7jT/?share_source=copy_web&vd_source=a509fb44c892c9c55cbccbacad6d249b">VS2022安装教程</a></p>
</li>
<li><p>当然,VS软件本身体积比较大,想节省硬盘空间的同学也可以考虑devc++</p>
</li>
</ul>
<h1 id="怎么做笔记"><a href="#怎么做笔记" class="headerlink" title="怎么做笔记"></a>怎么做笔记</h1><p>拜托,都学计算机了,总不能还手写笔记吧,增删改查知识不累吗</p>
<h2 id="使用-Markdown-语言"><a href="#使用-Markdown-语言" class="headerlink" title="使用 Markdown 语言"></a>使用 Markdown 语言</h2><p>Markdown 简单易上手,很多文档和博客(比如这篇)都会使用 Markdown 语言书写</p>
<p>Markdown 学习可以看这篇:<a href="https://orionli.gitee.io/blog/2023/07/06/Markdown%20%E8%AF%AD%E8%A8%80%E6%8C%87%E5%8D%97/">Markdown 语言指南 | OrionLi’s Blog</a></p>
<h2 id="使用-Typora-记录"><a href="#使用-Typora-记录" class="headerlink" title="使用 Typora 记录"></a>使用 Typora 记录</h2><ol>
<li><a href="https://www.123pan.com/s/HQeA-UX1Sh">点击链接,选择<code>typora_64bit_v1.6.6.0_setup.zip</code>下载压缩包</a></li>
<li>解压后,运行<code>typora_64bit_v1.6.6.0_setup.exe</code></li>
<li>安装完成后把压缩包下的<code>Crack</code>文件夹下的<code>winmm.dll</code>复制到<code>Typora</code>安装目录</li>
<li>完成</li>
</ol>
<h1 id="怎么学习编程"><a href="#怎么学习编程" class="headerlink" title="怎么学习编程"></a>怎么学习编程</h1><h2 id="从哪里学"><a href="#从哪里学" class="headerlink" title="从哪里学"></a>从哪里学</h2><p><strong>绝大多数情况下,视频比书籍更适合小白入门,文档和示例比视频更适合开发者学习</strong></p>
<ul>
<li><p>C语言入门视频推荐翁凯的C语言课程:<a href="https://www.bilibili.com/video/BV1d7411L7jZ/?share_source=copy_web&vd_source=a509fb44c892c9c55cbccbacad6d249b">浙江大学 翁恺C语言入门</a>。最后四集可以不用看,只拿C语言当入门语言的话看完结构体就差不多够了</p>
</li>
<li><p>书籍(尤其是入门的书籍)可以当作工具辅助书。预习、复习和查阅的时候使用是最好的</p>
</li>
<li><p>先把谭浩强的《C程序设计》扔了。这本书在那个时代是好书,但现在来看,代码规范和讲解烂爆了</p>
</li>
<li><p>一个方面<strong>最多</strong>买一本书就行了,不要傻不拉几的买好几本,然后放在角落吃灰</p>
</li>
<li><p>远离清华大学出版社等xx大学出版社出版的书,绝大部分质量极差</p>
</li>
<li><p>推荐异步图书、图灵系列的图书,推荐人民邮电出版社和机械工业出版的图书,推荐外国的书</p>
</li>
</ul>
<h2 id="语言是不是学的越多越好"><a href="#语言是不是学的越多越好" class="headerlink" title="语言是不是学的越多越好"></a>语言是不是学的越多越好</h2><ul>
<li><p>我们前面已经讨论过了<a href="https://orionli.gitee.io/blog/2023/07/06/0%E5%9F%BA%E7%A1%80%E5%B0%8F%E7%99%BD%E6%80%8E%E6%A0%B7%E5%85%A5%E9%97%A8%E7%BC%96%E7%A8%8B/#%E4%B8%BA%E4%BB%80%E4%B9%88%E6%98%AFC%E8%AF%AD%E8%A8%80%EF%BC%9F">为什么要先学C</a></p>
</li>
<li><p>我们学习语言,是因为要用语言对应的技术去开发产品。就目前而言:</p>
<ul>
<li><p>AI方面的基本都使用Python</p>
</li>
<li><p>嵌入式开发使用C语言</p>
<blockquote>
<p>嵌入式系统开发是对于除了电脑之外的所有电子设备上操作系统的开发,开发对象有手机,掌上电脑,机电系统等 (from 百度百科)</p>
</blockquote>
</li>
<li><p>前端先学HTML、CSS、JavaScript三大件(现在还要学TypeScript),然后vue或react。</p>
</li>
<li><p>后端绝大多数为Java,少量大公司如字节跳动、哔哩哔哩、知乎等用Go,也有公司是用Python,不过相对更少。游戏服务器几乎都是C++</p>
</li>
<li><p>大数据领域,Java和Python平分秋色,开发者主要是使用它们来调用另一种语言——SQL。</p>
</li>
</ul>
</li>
<li><p>各方向的详细介绍和分流我们会在团队笔试完进行,到时会有专门的宣讲会进行讲解,在这里只是做简单的介绍。</p>
</li>
</ul>
]]></content>
<categories>
<category>学习路线</category>
</categories>
<tags>
<tag>学习路线</tag>
</tags>
</entry>
</search>
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化