工作六年 我终于学会了这项技能 可惜晚了!!!
在程序員的生涯中,無(wú)論是在跳槽還是晉升的時(shí)候都有遇到各種各樣被考察的情況,隨著軟件開發(fā)工作資歷不斷積累,這些考察要求和條件逐漸變高。
不會(huì)單純用工作年限來(lái)判斷候選人是否合格,因?yàn)楫吘箤懭?CRUD 純業(yè)務(wù)和做三年系統(tǒng)研發(fā)的工作經(jīng)歷和工作質(zhì)量是大不相同的,這樣會(huì)存在不小的問題,尤其是在招聘高級(jí)開發(fā)工程師時(shí)。而算法題面試也一樣,跳槽必刷題,已經(jīng)很難分辨候選人是當(dāng)場(chǎng)想出來(lái)的,還是刷題記住的答案。傳統(tǒng)的八股文、算法、前沿技術(shù)源碼等知識(shí)內(nèi)容已經(jīng)不再新鮮,而一些"軟技能"像系統(tǒng)設(shè)計(jì)面試將會(huì)考察環(huán)節(jié)中較為重要的一環(huán)。
為了能讓面試更有效,采用系統(tǒng)設(shè)計(jì)題來(lái)考察高級(jí)開發(fā)工程師就是很好的選擇。因?yàn)橄到y(tǒng)設(shè)計(jì)題覆蓋面廣,不像算法題那樣在網(wǎng)上四處流傳,候選人想刷也沒法刷,只能拼技術(shù)。
這些軟技能并不會(huì)直接考察所了解到的某個(gè)知識(shí)點(diǎn)或者是某一片知識(shí)點(diǎn)所連接起來(lái)的部分知識(shí)面,而是去查看一位工程師在整體層面和理解應(yīng)用程序設(shè)計(jì)上的功底是否深厚;這類問題不像算法題那樣在網(wǎng)上四處流傳,想刷題也沒法刷,只能拼技術(shù)。雖然不會(huì)牽扯到某個(gè)知識(shí)點(diǎn),比如某個(gè)控件是如何實(shí)現(xiàn)并說(shuō)出內(nèi)部實(shí)現(xiàn)原理,但是會(huì)讓談下這個(gè)控件的優(yōu)點(diǎn)有哪些或如何在原有的基礎(chǔ)上進(jìn)行改進(jìn);
這樣能讓整個(gè)考察環(huán)節(jié)變得更加有效,面試官就能準(zhǔn)確地判斷出候選人的技術(shù)實(shí)力,同時(shí)從而給到合適的待遇和定級(jí)。
在面試系統(tǒng)設(shè)計(jì)題時(shí),又該怎么評(píng)價(jià)候選人呢?對(duì)此,大家可以參考下圖的總結(jié)。
在本文中,我將分享移動(dòng)端中關(guān)于如果被問及到如何設(shè)計(jì)一個(gè)系統(tǒng)的設(shè)計(jì)問題該如何進(jìn)行回答,以及我是如何去解決系統(tǒng)設(shè)計(jì)問題的秘訣。聽起來(lái)很有趣?…… keep reading
?
系統(tǒng)設(shè)計(jì)問題涉及的問題
Eason去觀看了很多不同類型的系統(tǒng)設(shè)計(jì)教程視頻,并做了一些關(guān)于這方面的總結(jié),我覺得這些總結(jié)不僅僅是適用于系統(tǒng)設(shè)計(jì),也是另一種對(duì)自己技術(shù)棧的要求和查漏補(bǔ)缺的方式;下面是羅列出來(lái)程序設(shè)計(jì)中必須要考慮到的方面:
1.功能需求——首先需要定義應(yīng)用的用例和一些功能。
2.非功能性要求——定義性能、經(jīng)驗(yàn)和規(guī)模要求。
3.假設(shè)——定義問題的邊界、任何規(guī)模約束、特征等。
4.客戶端-服務(wù)器通信——定義連接選項(xiàng),如 HTTP 請(qǐng)求、輪詢、服務(wù)器端事件。
5.API 設(shè)計(jì)——定義所構(gòu)建的功能端點(diǎn)。
6.數(shù)據(jù)模型——定義對(duì)象的數(shù)據(jù)模型字段。
7.應(yīng)用流程(用例流程) ——定義并執(zhí)行正在設(shè)計(jì)的功能的用戶流程。
8.性能和工具——定義大家將如何收集數(shù)據(jù)和指標(biāo)以查看應(yīng)用程序的性能包括內(nèi)存利用率、CPU 利用率等。
9.ADA — 確保大家定義了輔助功能以及應(yīng)用程序的可訪問性。
10.國(guó)際化——隨著應(yīng)用程序在國(guó)際上的發(fā)展,需要處理這款系統(tǒng)應(yīng)用的國(guó)際適配。
11.安全性——定義如何去保護(hù)應(yīng)用程序。
?
具體實(shí)現(xiàn)
現(xiàn)在,讓我們通過(guò)一個(gè)例子來(lái)說(shuō)明設(shè)計(jì)一個(gè)系統(tǒng)為何需要以上的這些結(jié)構(gòu):假設(shè)題目是被要求設(shè)計(jì)一個(gè)能展現(xiàn)出日常生活中的趣聞趣事App;
? ?
1. 功能需求
顯示我周圍有趣的地方列表。
向下滾動(dòng)時(shí)加載更多
2. 非功能性需求
該列表應(yīng)該加載非常快或具有低延遲
滾動(dòng)性能
數(shù)據(jù)異步加載且顯示不應(yīng)錯(cuò)位
列表不應(yīng)抖動(dòng)
3.假設(shè)
每天有多少活躍用戶?用戶量級(jí)多少?
API 是否可用?我們需要明智地使用它們
4.客戶端-服務(wù)器通信
常規(guī) HTTP 請(qǐng)求——這是客戶端向服務(wù)器請(qǐng)求數(shù)據(jù)/資源的最通用用例場(chǎng)景。例如,獲取提要中提要項(xiàng)目的列表一般會(huì)采用哪些輪詢方式?
定期輪詢- 客戶端可以不斷向服務(wù)器發(fā)出請(qǐng)求以獲取最新信息,但很多時(shí)間服務(wù)器可能沒有任何更新可提供。這不僅導(dǎo)致大部分時(shí)間響應(yīng)為空,而且在多次設(shè)置 HTTP 連接時(shí)會(huì)浪費(fèi)資源。
長(zhǎng)輪詢——如果我們有一個(gè)用例,我們知道內(nèi)容不會(huì)從服務(wù)器頻繁更新。在這種情況下,我們可以做的就是使用長(zhǎng)輪詢。客戶端會(huì)與服務(wù)器建立 HTTP 連接,并保持與服務(wù)器的連接打開,以便服務(wù)器可以在有任何推送時(shí)向客戶端推送更新。這樣我們就節(jié)省了打開和關(guān)閉與服務(wù)器連接的無(wú)用工作。并需要記住,這個(gè)長(zhǎng)輪詢連接也可能超時(shí),因此在這種情況下需要重新啟動(dòng)連接。
WebSockets——在客戶端和服務(wù)器都可以發(fā)起通信并且客戶端和服務(wù)器之間不斷來(lái)回的情況下,我們可以使用 WebSockets。這個(gè)用例的一個(gè)很好的例子是聊天應(yīng)用程序。
服務(wù)器端事件 (SSE) —客戶端與服務(wù)器建立持久性和長(zhǎng)期連接。服務(wù)器使用此連接發(fā)送數(shù)據(jù)。客戶端處于偵聽模式,因?yàn)橹挥蟹?wù)器才能與客戶端通信。如果客戶端需要與服務(wù)器通信,則需要使用不同的協(xié)議。這個(gè)用例是說(shuō),我們有一個(gè)社交媒體應(yīng)用程序,列出了朋友的提要。但是現(xiàn)在,如果在我們使用應(yīng)用程序時(shí)剛剛發(fā)生了更新,客戶端可以通過(guò)服務(wù)器端事件 (SSE) 協(xié)議輕松獲取這些更新。
5. API 設(shè)計(jì)
API 通過(guò)協(xié)議相互通信。常見的協(xié)議有 SOAP、REST 和 GraphQL。有關(guān)這些的詳細(xì)信息雖然并不是本文的一部分,不過(guò)大家感興趣可以自己去拓展一下。對(duì)于我們這個(gè)展示感興趣內(nèi)容的App,REST 應(yīng)該沒問題,它基本適用于大多數(shù)的場(chǎng)景。根據(jù)我們的要求,需要設(shè)計(jì)以下類型的Api:
a) 我們需要一個(gè)地點(diǎn)列表
b) 由于我們列出了附近的地點(diǎn),因此我們想到的一件事是 API 調(diào)用需要某種位置信息來(lái)返回?cái)?shù)據(jù),且可以是我們的查詢參數(shù)。
c) 分頁(yè)處理。考慮到這是一個(gè)移動(dòng)應(yīng)用程序,我們沒有像臺(tái)式機(jī)或筆記本電腦那樣的無(wú)限資源,如計(jì)算/連接和電源,我們需要小心獲取的數(shù)據(jù)量,不能同時(shí)一次性全部加載出來(lái)或者把數(shù)據(jù)下拉下來(lái)。因此,最好分塊獲取數(shù)據(jù)并在需要時(shí)獲取更多數(shù)據(jù)。所以我們需要有能力分頁(yè)請(qǐng)求數(shù)據(jù),這就是分頁(yè)的用武之地。Offset、KeySet 和 Cursor Based 是不同類型的分頁(yè)服務(wù)器支持。對(duì)于我們的用例,我們可以選擇其中任何一個(gè)。但我個(gè)人更偏向喜歡用偏移分頁(yè)。所以我們會(huì)有一個(gè)頁(yè)碼和頁(yè)數(shù)限制來(lái)限制資源的數(shù)量。考慮到這一點(diǎn),我們的 API 可能看起來(lái)像這樣?
GET — /places?lat={}&long={}&page={}&pageLimit={}
6. 數(shù)據(jù)模型
在設(shè)計(jì)某個(gè)系統(tǒng)模型的時(shí)候,得把基礎(chǔ)架構(gòu)給先定好,好比系統(tǒng)應(yīng)用是高樓,數(shù)據(jù)模型則是地基;在本文中,將向大家介紹如何是高效并有意義的潛在數(shù)據(jù)模型。考慮到整個(gè)需求涉及到的功能,所以將數(shù)據(jù)模型設(shè)計(jì)成下述圖表(簡(jiǎn)化后的模型)👇
7. 應(yīng)用流程
以下是我們可以可能需要用到的任何設(shè)計(jì)架構(gòu)模式。
可以是:
MVC——模型視圖控制器
MVVM——模型視圖視圖模型
MVP——模型視圖Presenter
VIPER等
對(duì)于一般較為簡(jiǎn)單的用例,我采用比較多的是MVVM。大家平時(shí)應(yīng)該使用常用并有意義的模式,并且在工作上也同樣可以采取照搬的方式套用過(guò)去,從而提高工作效率,減少造輪子的時(shí)間。
8. 性能
這節(jié)本來(lái)是可以放在解決非功能性需求和任何其他與性能相關(guān)部分,因?yàn)槭强疾飙h(huán)節(jié)中比較重要的一點(diǎn)所以再次拿出來(lái)展開和大家聊一下,App設(shè)計(jì)中有哪些方法可以用來(lái)提高應(yīng)用程序性能?
布局優(yōu)化:
刪除布局中無(wú)用的控件和層次,其次有選擇地使用性能比較低的ViewGroup。
采用標(biāo)簽,標(biāo)簽,ViewStub.
避免過(guò)度繪制
繪制優(yōu)化:
onDraw中不要?jiǎng)?chuàng)建新的局部對(duì)象。
onDraw方法中不要做耗時(shí)的任務(wù),循環(huán)執(zhí)行方法后十分搶占CPU的時(shí)間片,這會(huì)造成View的繪制過(guò)程不流暢
內(nèi)存泄漏優(yōu)化:
在開發(fā)過(guò)程中避免寫出有內(nèi)存泄漏的代碼
通過(guò)一些分析工具比如MAT來(lái)找出潛在的內(nèi)存泄露,然后解決。
響應(yīng)速度優(yōu)化:響應(yīng)速度優(yōu)化的核心思想就是避免在主線程中做耗時(shí)操作。
ListView/RecycleView及Bitmap優(yōu)化
使用ViewHolder模式來(lái)提高效率
異步加載:耗時(shí)的操作放在異步線程中
ListView/RecycleView的滑動(dòng)時(shí)停止加載和分頁(yè)加載
對(duì)加載圖片進(jìn)行壓縮,避免加載圖片多大導(dǎo)致OOM出現(xiàn)。
線程優(yōu)化:線程優(yōu)化的思想就是采用線程池,避免程序中存在大量的Thread。
其他性能優(yōu)化的建議
① Show Skeleton - 某種東西正在加載的感知概念讓用戶感覺數(shù)據(jù)加載速度更快。在加載數(shù)據(jù)時(shí)顯示一些 Skeleton 會(huì)對(duì)感知的用戶體驗(yàn)產(chǎn)生巨大影響
② 使用具有適當(dāng)尺寸的圖像。如果 API 支持高度和寬度作為查詢參數(shù),那么大家將能拿到對(duì)圖片顯示有意義的結(jié)果。用戶的眼睛無(wú)法分辨圖像大小之間的差異,但它因?yàn)榧虞d的數(shù)據(jù)較少?gòu)亩鴮?duì)應(yīng)用程序的性能產(chǎn)生巨大影響。
③ 使用內(nèi)容交付網(wǎng)絡(luò) (CDN)進(jìn)行靜態(tài)圖像緩存,以幫助更快地交付內(nèi)容。在我們的App中,將 CDN 用于圖像和靜態(tài)內(nèi)容,將地點(diǎn)描述文本和圖像緩存至云端。
④ 在后臺(tái)線程上加載數(shù)據(jù),這樣主線程永遠(yuǎn)不會(huì)被阻塞,UI 永遠(yuǎn)不會(huì)卡住。
以上是一些常見優(yōu)化性能的做法,大家也可以在面試期間去主動(dòng)深入了解其他的方法。
9.可訪問性和動(dòng)態(tài)性
我們始終希望每個(gè)人都使用我們的應(yīng)用程序。為了讓它真正具有包動(dòng)態(tài)性,我們需要在我們的應(yīng)用程序中處理可訪問性。以下是我們可以做的一些事情來(lái)解決可訪問性問題:
確保顏色對(duì)比度正確
每個(gè)目標(biāo)尺寸至少為 44pt
為可訪問性標(biāo)注添加自定義調(diào)用
使用適當(dāng)?shù)呐渖桨?/p>
支持動(dòng)態(tài)字體類型
對(duì)例如GTXilib 的庫(kù)使用自動(dòng)檢查,以便他們可以確保每個(gè) UI 元素都有一個(gè)標(biāo)簽、適當(dāng)?shù)奶卣鳌?biāo)簽不是多余的。
上面提到的做法可以應(yīng)用于任何移動(dòng)應(yīng)用程序。大家也可以在 Google 和Apple網(wǎng)站上閱讀有關(guān)可訪問性的更多信息。
10. 國(guó)際化/本地化
應(yīng)用程序的國(guó)際化為其用戶提供了本地和個(gè)人體驗(yàn),這對(duì)應(yīng)用程序的使用和采用很有幫助。通常,我們可以通過(guò)為應(yīng)用程序中的內(nèi)容/文本使用不同的本地化字符串文件 strings.xml 來(lái)支持這一點(diǎn)。
大家還可以使用高級(jí)技術(shù),例如將設(shè)備區(qū)域設(shè)置發(fā)送到服務(wù)器,然后服務(wù)器返回本地化文件。根據(jù)需要,大家可以采取不同的路線來(lái)實(shí)現(xiàn)本地化,這也是Goolge 官方對(duì)于本地化的指南。
11. 安全
安全是一個(gè)非常廣泛和復(fù)雜的話題。作為移動(dòng)應(yīng)用程序的開發(fā)人員,大家需要在平時(shí)日常工作中學(xué)會(huì)并遵循一些提示和技巧:
使用簽名和加密數(shù)據(jù)
編譯與反編譯
NDK與反匯編
加殼與脫殼
?
結(jié)論
系統(tǒng)設(shè)計(jì)的重點(diǎn)是考察我們作為工程師從整體層面來(lái)看待/理解應(yīng)用程序設(shè)計(jì)的能力,而不僅僅是單個(gè)部分知識(shí)點(diǎn)或知識(shí)面的細(xì)節(jié)。關(guān)于系統(tǒng)設(shè)計(jì)的答案,從來(lái)就不存在最優(yōu)解,畢竟世上本來(lái)就沒有完美的答案
有道無(wú)術(shù),術(shù)可成;有術(shù)無(wú)道,止于術(shù)
歡迎大家關(guān)注Java之道公眾號(hào)
好文章,我在看??
總結(jié)
以上是生活随笔為你收集整理的工作六年 我终于学会了这项技能 可惜晚了!!!的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 学会了CopyOnWriteArrayL
- 下一篇: mysql忘记密码怎末版_mysql忘记