如何寫好程序(下)

開發(fā)出好程序要做的功課

接著前一篇,原則設定好了,接下來還需要能夠實踐,否則就只是空談,而實踐就得依賴開發(fā)人員的素質。要提升開發(fā)人員的素質,有三個重點, “基礎、基礎、基礎” 。沒錯!想要提升素質其實沒有速成的方法,就像功夫電影都會有強調扎馬的橋段,腳力是提供一切招式力道的根源,而再凌厲的招式也可能因為重心不穩(wěn)而失去了威力。開發(fā)的領域中,愈充實的基礎知識在遇到狀況時愈能有效的解決問題,甚至是提早預防。因為在知道有哪些現(xiàn)成選項可以運用,也就不需要重新打造一個輪子,而是站在巨人的肩膀上。

心酸的業(yè)界生態(tài)

身為開發(fā)者必須要很清楚自己設計的每一個環(huán)節(jié)會產生的效果或可能的問題、限制,才能夠貼切地符合需求的內容。這是需要具備一定的經驗和知識,尤其是隨著時間的累進、程序愈來愈精致化,開發(fā)程序時不論是編程語言或是作業(yè)平臺都伴隨著龐大的內容、大量的資訊,需要時間來消化、理解。

入行的條件不再高不可攀

也許是現(xiàn)代開發(fā)工具的強大與坊間的教育機構的普及,很多人經過簡單的訓練就以 “會把網(wǎng)絡上搜尋來的程序片斷兜起來、可以正常運行” 當成是寫程序的全部。但是科班出身的就充滿著光芒嗎?我也看過不少人,不是在課堂上虛擲光陰,就是沒有將所學融會貫通,展現(xiàn)出來的工作水準也只是一般般。

現(xiàn)在寫程序的模式除了有現(xiàn)成的框架、庫可以選用,網(wǎng)絡資訊的發(fā)達讓形形色色的源代碼片段都可以搜尋得到,很多時候只要選好框架、把網(wǎng)絡上的源代碼片段組合起來,就可以有一個能夠運行的程序。當然沒事就沒事,但不幸出事了要算是誰的責任?

在過去的工作經驗里就曾經遇過當程序發(fā)生問題,我問負責的人出問題的源代碼在做什么事,他回答我: “不知道,從網(wǎng)絡上貼來的!” 如果你是老板,遇到這種情況會不會想:“那我應該付錢給原作者?還是負責貼上的人?”

當你不了解程序運行的內容,怎么能保證你的程序能夠穩(wěn)定地運行、沒有安全上的疑慮?的確,在實作上有很多時候需要進階一點的演算法,像是壓縮、圖形運算,非科班出身、沒有數(shù)學基礎的還真沒有辦法理解。有點職業(yè)道德的人會盡可能地做好測試,就算不知道內容,但最少確認運行的結果是自己要的。而不是交差后就當沒事,默默祈求程序不要出問題!

這個情況,讓人聯(lián)想到食安事件。有一部份餐飲業(yè)者沒有具備烹飪的基本功夫,所以很多材料必須要靠外購,對食材的特性也沒有足夠的掌握能力。但在成本的考量下,黑心一點就選擇明知有問題、來路不明但成本低廉的食材。像是用的油出了事、用到了地溝油,就只能推說: “我也是受害者,是那些做地溝油的廠商該死,應該要向地溝油的廠商求償,與我無關?!?/p>

當然,也有負責任的店家并不是用成本低廉的食材但也遭到波及。第一時間會覺得這些人真的很無辜,但進一步想想,要從事餐飲業(yè)是你的選擇,你的客人是信任你的烹飪技巧、口味的掌控、食材挑選能力等等的質量要素,而不是信任你的進貨廠商。出了問題,要說你完全沒有責任并不合理!對消費者來說,合理方式應該是先賠償給消費者,至于和進貨廠商的賠償問題是店家內部的事,這樣對我們這些末端的消費者才公平不是嗎?而且,規(guī)模大的商家更應該要有資源、能力與責任承擔起這樣的標準。

寫程序不再是閉門造車

反觀我們自己理所當然地把別人寫好的東西拿來用,卻鮮少深究內部運作的邏輯,是不是也是一樣的缺乏商業(yè)道德?說起來還挺心虛的,批評地義正嚴詞、擲地有聲,原來比起食品產業(yè)我們高尚不到哪去!用 “我們” 似乎太過一竿子打翻船人,所以我招認最少我自己是。

在社會分工細致化、講求效率的年代,工作當中使用自己不了解的產品成了必要之惡,就像是要求店家所有的食材全部自制一樣,是一種奢求。再加上訊息科技雖然發(fā)展沒幾年,但是已經形成了一個很龐大的知識體系。不一而足的操作系統(tǒng)平臺、程序語言,多如繁星的框架、SDK、庫,各行各業(yè)的領域知識、商業(yè)邏輯,每一個區(qū)塊都形成了一個有高度的門檻和學習障礙。在開發(fā)程序時,如果要完全以自制的模式來進行,不是成本高得嚇人,就是時間長得不能接受,以致于難以實行。

知名廠商的庫、開源的框架比較不可能會是問題的來源,畢竟這些程序都是以一般化的用途為出發(fā)點、能夠適用于大多數(shù)的情況之下,并且經過重重的檢驗、測試。知名的公司也不可能自毀招牌,放任產品中有明顯的瑕疪或安全問題。但由網(wǎng)絡上搜尋到的源代碼往往是示范性質或專為解決特定問題,不見得與程序的需求百分之百的契合,如果一字不改或是對運行的內容一知半解的情況下編入程序內,這樣的做法和之前舉的食安例子中使用來路不明食材的店家有何差別?

那有些小公司或不明來源編譯好的庫豈不是問題潛在的制造者?會不會內藏惡意的源代碼?這個問題對于管理者來說會是一個取決于風險與成本的困難抉擇。端看你的程序對于安全的要求程度,是企業(yè)內部敏感數(shù)據(jù)的系統(tǒng)?還是給一般人使用的系統(tǒng)?打算付出多少的成本?沒有打算付出太多的成本,只能承受較高的風險;不能承受風險的,就得要付出較多的成本,像是有制度一點的應該要成立專職的安全小組對程序做必要的檢測。

如果要問我,所開發(fā)的軟件遇到像食安風暴的情況,會不會采用和那些店家一樣的態(tài)度?我會,同時還會用投名狀中龐青云的語氣說 “裝死無敵!這是規(guī)矩!” 。但這是問題發(fā)生了的假設,在發(fā)生問題之前我會想要盡可能地避免,畢竟程序是自己寫的,光嘴巴上喊沒責任就可以置身事外也不太可能。

追求質量的價值在哪里?

要把這個想法做好,首先會遇到的就是被海量信息淹沒的無助。這是這行從業(yè)人員普遍會有的心酸,要學習的東西愈出愈快、愈來愈多。先不看是不是有足夠的時間吸收,光信息量就已經超出一般人大腦能夠承載的極限。餐飲界里隨處可見幾十年的老味道,能夠傳承好幾代的手藝。反觀軟件界,當年縱橫 COBOL 和 Fortran 語言的系統(tǒng),試問如今安在?姑且不看這種由時代浪潮所帶來的興衰,同一家公司的技術總會一脈相傳、延續(xù)演進吧?過去靠微軟的技術混過飯吃的那段日子,有過不少次被微軟打臉的經驗,那頭才在研討會上大張旗鼓地熱烈推銷,回頭卻是悄悄收攤,不再發(fā)展、更新。收兵尚且要鳴金,有的時候是連鳴金都省了!我的青春、我的人生,就在不斷地用新技術翻修舊系統(tǒng)的循環(huán)中渡過。

信息技術的更替如過江之鯽的現(xiàn)象,再加上公司高層多以利字為先,造就了不少開發(fā)人員速成的心態(tài)。對使用的技術一知半解、得過且過,源代碼能用就好、不求甚解,反正用沒多久又要換一種寫法,復制貼上的時間已經比自己打字的時間還多。而衍生出來的問題是遇到了技術上的狀況,常常磨了很久得到的答案是不可能解決、沒辦法處理。但等到換一個對技術比較自我要求的人去試時卻又不是這么回事,有時解決的方案還直接到不行,可是原本負責的人找答案時又認真到你分不清楚是否是該歸類成偷懶的借口!這樣的情況我遇到的并不是個案,也不是單一個人才有,甚至習以為常到不忍去苛責這個大環(huán)境所形成的共業(yè)。就管理者的角度,迫于專業(yè)分工下的現(xiàn)實,只能盡可能讓開發(fā)人員針對自己擅長的領域做專精,用團隊的力量來截長補短。

既然業(yè)界風氣如此,那一昧的追求開發(fā)人員的素質是否太過矯情?還是個人完美主義在作崇?組合網(wǎng)絡上的源代碼所產出的程序真得有這么不堪、有這么嚴重嗎?就如同做吃的這件事,沒有高超的烹飪技巧、專業(yè)級的食材挑選眼光,就不能煮東西給別人吃了嗎?那這樣豈不是所有家庭的爸爸、媽媽都不能下廚,都只能外食?如果我有一道拿手的菜就不能分享給親戚好友、街坊鄰居?

我個人認為,程序本來就是解決問題的工具,如果寫的人快樂、用的人也快樂,是否得這么吹毛求疪地一定要設定什么門檻或標準?我想并不是這樣的,業(yè)界常常有一句話:“沒有最佳的解決方案,只有最適合的解決方案”。所以在這里完全沒有要貶低不是接受傳統(tǒng)制式訓練的從業(yè)人員,或是對于愿意投入熱情從事程序開發(fā)工作的人有任何不敬的意思。相反地,能夠用程序解決生活中或是專業(yè)領域上遇到的問題,是很值得敬佩的一件事。最少人家有第二專長,而我只能靠人家的第二專長糊口,光這一點就沒什么資格對人說三道四的。

不過,這文章怎么好像愈寫愈沒立場,那還談什么素質的提升?先來看看 Uber 和 Airbnb 的所帶來的爭議,這二個平臺所使用的模式很類似,都是共享經濟的實踐者,分享私人閑置的資產來對大眾提供服務。像是如果我有閑置的車子我可以開著它去載客;如果我有閑置的房間我可以提供給短期需求的人來住宿。聽起來很美好,不但是各取所需,還可以有效利用資源、落實環(huán)保的概念。

但為什么有人要出來反對、抗議,而政府也介入干預?是政府狗拿耗子?那些既得利益者眼紅?如果是眼紅,那出租車司機為什么不叫車隊也搞一個類似的平臺來競爭就好?我用自己的車載朋友也要證照?加入 Uber 載人違法,自己載朋友不違法,差別在哪?是因為沒有從中獲利?朋友請吃飯當謝禮算不算從中得利?我使用的是全新車,車款高級、內裝豪華,載客時也是西裝革履,開車時戰(zhàn)戰(zhàn)競競,車上還有額外的飲水服務,哪一點比出租車差?明明雙方都是你情我愿,又沒礙著別人,這樣也要抗議?那有廚師證的為什么不出來抗議滿街沒證照的街邊小吃?開店做生意的店家為什么不出來抗議擺在路旁的地攤?

上面是延伸前一段的立場只呈現(xiàn)單一面的意見,而實際上兩造的論點各據(jù)一詞、沒有太大的交集。但可以看到一個現(xiàn)象,都是提供服務的二方在爭議,購買服務的第三方卻沒聽到很大的負面評價。我想那是因為還沒有遇到危害切身利益的情況,以服務提供者的角度來看,重點還是那句話:沒事就沒事,但不幸出事了要算是誰的責任?一但牽扯到利益,問題就會變得異常的復雜,不管你是爭議的哪一方、有沒有政府背書的證書、或是你從哪個名校畢業(yè)的。面對的若是親朋好友,可能會看你的情面,不用你開口,就主動放棄權利,也許幫忙出個必要的費用就當沒事。但如果是你不認識的人,你有把握可以說服苦主讓你輕松過關嗎?爛客人事件在新聞中屢見不鮮,當你碰上了一個要跟你走法律程序的對象,你真的有時間、心神應付?

所以,如果你對于寫出好程序還有一點期待,不管你是完美心態(tài)作崇、還是想要明哲保身,提升質量是一項看得見回報的投資,所謂一分耕耘一分收獲,端看你想要先甘后苦還是先苦后甘。也就是說,看你想要先花時間避免未來的損害?還是你想要把時間用在出問題后擦屁股這件事情上?

基礎、基礎、基礎

能做的努力大概就是一開始提到的:基礎,多一分了解才多一分的機會知道怎么逃避責任... 我是說保護自己,就像法律通常都是保護懂法律的人。當然在軟件的工作領域中,很多是商業(yè)機密、只能用不能看,在沒有授權的情況下太 “深入” 是會有觸犯法律的風險。所以這里指的努力,當然就是針對公開的部份,像是文件、開源碼,至少讓自己不是在一知半解的情況下做出錯誤的抉擇。

具備關鍵的知識

基礎中的基礎不外就是在學校中教授的那些課程,像是計算機概論、操作系統(tǒng)、數(shù)據(jù)結構、工程數(shù)學等等。不過,這些內容卻也是在實際從事程序開發(fā)時最不具存在感的內容。現(xiàn)在的程序設計都架構在層層的堆疊與包裝之上,很多底層的問題都被隱藏、處理掉了。像是很多的語言都有提供垃圾回收(GC)的機制,所以 Memory Leak 問題變得不需要花時間處理,原本課堂上學的 Memory Leak 注意事項也無用武之地。

但這些設計上的問題仍然實際存在著,有一些仍然是要靠開發(fā)人員素養(yǎng)來做出適當?shù)木駬?。在選擇變量類型來儲存數(shù)字數(shù)據(jù)時,有些人會想容量應該是愈大愈好,以免數(shù)字不小心超過上限而讓程序出現(xiàn)問題。在沒有接觸過相關的知識會根本沒想到,變量的型別會對運作的效能上產生影響。而造成效能影響的原因,來自變量型別與記憶體長度的關系,是底層一個意想不到的運作規(guī)則。

還有像是之前提到的多線程在設計時會遇到諸多的問題,如果具有相關的知識就會了解系統(tǒng)底層的工作原理、多線程的管理方式、對應處理的機制。數(shù)據(jù)結構可以協(xié)助設計有效率的數(shù)據(jù)處理模式,像是如果要進行排序會有許多的排序演算法可用運用,每一種都有其特性和適用的時機,并且有一套學術上的方法可以預估處理模式在效率上是否符合需求。同時也會知道遞歸很好用,但呼叫階層數(shù)太多程序是會當?shù)舻?。如果要計?1 加到 N 的總合,第一直覺是使用遞歸來寫,寫出來的程序卻有個致命的缺點,N 有上限、超過程序會當?shù)???墒歉挠锰菪蚊娣e公式,程序只要一行、運行時間天差地別,N 也可以理論上達到無限大,最少可以達到變量能儲存總合的最大值。

這些相關的機制大多會被實作在編程語言所搭配的 SDK 上,技術文件中也許只會提到使用的規(guī)格,而不會有詳細的說明來解釋背后的原理。當你擁有相關的背景知識就可以適切的運用這些功能,而不是靠著猜測與試驗來決定要選擇哪一種方案。這些選擇對程序的穩(wěn)定性與運行效率都會有舉足輕重的影響,也決定著程序一但出現(xiàn)問題時初步處理的策略,發(fā)現(xiàn)及解決問題所要耗去的時間。

數(shù)學更是這一些基礎知識最不起眼的,就像我過去在企業(yè) IT 部門服務的經驗里,高階的數(shù)學知識完全束之高閤,大多數(shù)的程序只是單純地要求數(shù)據(jù)庫的 CRUD,數(shù)據(jù)處理的運算需求也頂多是加減乘除。不過,數(shù)學基本上可以訓練邏輯推演、協(xié)助思考,如果能夠融會貫通是有機會可以讓自己增加薪資條件、進入特定的專精領域,像是圖學里的演算多是靠矩陣、向量等運算來進行,這是在影像處理、游戲、3D 應用中必須的。

同時,沒有明顯的助益并不代表就是多余的。這些知識會成為基礎是有其道理,所有的人造物都不會是跳躍式地一下就以復雜的結構出現(xiàn),都會是由簡單的規(guī)則組合并堆砌所演化出來的。這個理論套用在軟件產業(yè)中一樣適用,基于相同底層所發(fā)展出來的系統(tǒng)都會具有一定的相似度。所以吸收的這些知識會內化成為你的工作能力,例如增加技術的敏銳度,提升學習的效率,更容易接納新的技術、縮短上手時間。這就好像如果你有富爸爸,做起事來一定是事半功倍;但反過來如果是白手起家,遇到困難就是只能一步一腳印地走出來了。

掌握編程語言

再接下來的這項基礎效果就會比較明顯一點,那就是編程語言。編程語言是程序開發(fā)的核心,用來編譯成 CPU 可運行機械語言的依據(jù)。而編程語言靠的是保留字來跟編譯器溝通,透過保留字設定好的意義來轉換成機械語言字節(jié)碼。

編程語言是所有人接觸程序的切入點,學習編程語言的脈絡不外是變量宣告、運算符陳述、流程控制,進階的還有面向對象程序設計,但有不少人對于編程語言的學習也就僅此而已。隨著時間的推演,不少的新概念被導入既有的編程語言之中,像是現(xiàn)在最新的是大家都流行要 Lambda 一下,再早一點是使用 Attribute 來提供源代碼在運行時期的附屬資訊,或是利用保留字來簡化多線程環(huán)境的數(shù)據(jù)同步問題。

這些編程語言的新語法如果不會對寫程序造成改變,通常也都不會、畢竟還是要考慮到相容性,很多人的選擇都是視而不見。就算是以喜新厭舊聞名的信息產業(yè),仍存在著為數(shù)不少的堅持不輕易改變的從業(yè)人員。姑且不看新的語法所帶來的撰寫上的彈性、工作效率的增加、運行效能的提升等優(yōu)勢,就算是只安于剪下貼上的工作模式,當網(wǎng)絡上搜到的示范源代碼愈來愈多是以新語法撰寫時,打算如何銜接到所負責的源代碼內?或是有一個對新技術高度狂熱的同事寫的源代碼要讓你接手,這時還能夠以不變應萬變嗎?

只有新玩意才不被人重視?泛型就不是個新玩意,但沒聽過、不知道如何使用還大有人在,不然就是只會依樣畫葫蘆地呼叫以泛型寫的功能,自己要設計卻無從下手、也不了解別人含有泛型設計的源代碼。所以對編程語言不夠了解會導致的下埸就是看不懂別人寫的源代碼,能被分配的工作就只能是只寫不改,可能嗎?

雖說看得懂別人寫的源代碼,是發(fā)現(xiàn)邏輯錯誤或語法問題的第一步,看懂了卻忽略細節(jié)一樣也可能對錯誤視而不見。有一個很古老的案例,以下有一個在 VB 還沒有進入 .NET 的時代的源代碼范例:

Dim A, B, C As Integer

我遇到很多人都以為三個變量全部是以整數(shù)類型儲存在記憶體中,但實際上卻是只有 C 是整數(shù)類型,另外二個是以 Variant 的型別來運作。這個問題嚴重嗎?就 VB 的語言特性來說,平常不會出現(xiàn)什么異樣。只是當問題出在 Variant 上,源代碼的差異一下沒看出來,要確認問題發(fā)生的原因還是挺累人的。

還有一個很基本卻是不少人忽略的細節(jié),那就是變量的生存周期、作用域的議題。看似不經意的變量聲明動作,影響所及是程序會不會虛耗寶貴的記憶體空間、占著毛坑不拉屎;也有可能變量的內容在沒有料想到的惰況下被更改,讓程序時不時地出現(xiàn)莫名其妙的反應,卻又遍尋不著發(fā)生問題的原因。

善用開發(fā)的工具

要開發(fā)程序,絕大多數(shù)的人大概已經脫離不了對 IDE 的依賴,一個整合好開發(fā)所需環(huán)境的工具。從單純的打字、編譯、調試等基本的開發(fā)功能,到源代碼提示、即時語法檢查、源代碼重構、版本控管、源代碼復雜度檢查、效能檢測、測試報告等跟團隊與質量相關的功能。能做的事愈來愈多,但我們在進行開發(fā)工作時對 IDE 提供的功能中能夠掌握運用的比例有多少?

很多跟 Application Lifecycle Management 相關的功能也許不是負責開發(fā)工作的人說要用就用得上,但還有不少是和工作效率習習相關的可以快速發(fā)現(xiàn)問題、縮減開發(fā)工作負擔的功能。曾經就有遇過有人在調試時,仍在使用最基本的左右逼近法,完全不知道有條件斷點可以用。很難想像在遇到大型循環(huán)時,怎么有辦法耐得住性子,一次又一次地按著單步運行的按鈕,直到期望的條件出現(xiàn);又或者是在很多階層的遞歸之間,是如何保持清醒、很明確地知道現(xiàn)在程序走到了第幾層的呼叫?

“工欲善其事,必先利其器” 大家都知道,很多人也羨慕 MacGyver 可以用一把瑞士軍刀就可以解決很多問題,但卻在自己拿到瑞士軍刀時,只知道使用其中一項工具來解決所有問題。有的時候,功能的使用是需要有一點創(chuàng)意才能找到符合自己習慣的模式,但最少應該要先點開來把玩看看,才能借由熟悉找到最合適的使用情境。

熟悉程序運作的環(huán)境

這里的環(huán)境指的是由操作系統(tǒng)內建函式、官方 SDK、各式框架、第三方庫所組成,用來提供程序運行時期一切運作的基礎。既然是基礎,就是在提升開發(fā)人員素質里所要討論的范圍內。

這個部份還真的是知易行難的典范,跟學校教授的課程相比變遷劇烈、跟編程語言的內容相比更加龐雜無邊、跟開發(fā)工具的功能組合相比更是繁復多變。隨便挑一小區(qū)塊的資訊都可能看到成了過去式還看不完,是很心酸,但無奈之余最少應該要對工作所需要的領域有 “基本的認識” 。

過去有接觸過不少有所謂的 “開發(fā) Android 程序” 經驗的人,對談之后發(fā)現(xiàn)這些人對 Intent, Service, Content Provider, Broadcast Receiver 等等基礎的控件完全沒有概念,不要說沒有使用的經驗,甚至連這些名詞都一無所悉。還有的根本不知道自己寫程序用來控制畫面的控件叫 Activity,當然也不用提進階一點的 PendingIntentIntentService。這應該已經不是專不專業(yè),而是合不合格的問題了!

再來看一個架構大一點的例子,在因應 RESTFul 輕量化需求而誕生的 ASP.NET Web API 之前,WCF 一直占據(jù)著微軟遠程過程調用的主要舞臺。WCF 借著簡化的開發(fā)過程、彈性的設定檔調整,讓不具底層知識的開發(fā)人員也可以輕易的建立跨電腦傳遞的功能,成為了企業(yè)內架構分散式系統(tǒng)的好選擇。但隨著移動平臺的堀起,移動裝置要被納入分散式系統(tǒng)成了熱門的需求。這時,不知 HTTP 規(guī)格是何物的開發(fā)人員就踢到了鐵板!要讓沒有 WCF 的移動平臺可以呼叫的情況下,限制服務端只能用 HTTP 通訊是必然的。然而安全性WCF 主打的特色之一,服務端一定設計了相當程度的安全驗證,沒有 WCF 怎么通過驗證?

該想到的情況 WCF 都想到了,沒想到的也保留了接口可以擴充,調整設定參數(shù)就可以完成準備。移動裝置要如何送出驗證信息卻是尚待解決的問題,如果送不了,是不是把服務端的安全機制撤下來?有資安這種單位在,這個想法是不可能的!為了移動平臺再重新打造一組服務?如果有閃電俠在可以考慮一下!這時如果對 HTTP 的規(guī)格有一定的了解、研究過 WCF 設定細節(jié),就可以知道 WCF 要進行何種設定,并且在移動平臺送出要求時要附上什么樣的驗證資訊,以最低的成本來達成任務。

對于所謂 “基本認識” 的作法,每個人天賦不同、做法也因人而異,也許有神人能夠把所有的資訊記憶在腦海之中。我的習慣是最少先瀏覽一遍文件的目錄或大綱讓腦海中有一個印象,然后期望體內的小宇宙能在遇到障礙時,提醒我排除的方法可能存在于文件的某一處。有的時候的確會很神奇的靈光一閃、小有幫助,多數(shù)的時候還是得靠 Google 大神,有道是團結才是力量嘛!只是就算 Google 再神,也得要知道怎么問、下什么關鍵字,這就要看平常閱讀的功夫下得有多深了,也就是一開頭提到的 “基礎、基礎、基礎”。

最后

總算,文章的篇幅很長,大多是過去工作遇到狀況的縮影。叨叨絮絮地打了很多,是希望能讓剛接觸這個行業(yè)的人多些參考。感謝能夠這么耐心地看到最后的各位!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容