設(shè)計(jì)一個(gè)結(jié)構(gòu)合理的下載模塊

有始有終,設(shè)計(jì)一個(gè)結(jié)構(gòu)合理的下載模塊

完成開發(fā)任務(wù)的同時(shí)抡草,我們總希望自己能夠交付高質(zhì)量的代碼。代碼質(zhì)量的測度有很多方法蔗坯,可擴(kuò)展性康震、可復(fù)用性是其中的兩項(xiàng)指標(biāo)。設(shè)計(jì)模式的理論能夠非常有效地指導(dǎo)代碼設(shè)計(jì)宾濒,但是光談這些理論是非常抽象的腿短,本文針對下載這個(gè)場景,結(jié)合設(shè)計(jì)模式的一些理論绘梦,談一談如何設(shè)計(jì)一個(gè)結(jié)構(gòu)較為合理的下載模塊橘忱。

一、明確需求

在著手編碼之前卸奉,先明確功能需求钝诚、技術(shù)需求,然后進(jìn)行初步的思考榄棵。

從目標(biāo)出發(fā)

從目標(biāo)出發(fā)凝颇,能夠幫助明確設(shè)計(jì)過程中的側(cè)重點(diǎn)郎嫁。對于下載這個(gè)場景,很直觀可以想到祈噪,它涉及到的文件操作泽铛、持久化存儲等步驟是會頻繁出現(xiàn)在一個(gè)項(xiàng)目中的。所以我會希望為下載模塊寫的大量代碼能夠被良好復(fù)用辑鲤。同時(shí)可以預(yù)見盔腔,下載這一場景是非常容易出現(xiàn)后續(xù)需求變更或者增加的,沒準(zhǔn)今天只下載視頻月褥,明天又需要添加對音頻弛随、對 zip 文件的支持;對于數(shù)據(jù)庫存儲框架宁赤,可能目前在使用 FMDB舀透,后續(xù)又要更換為 WCDB。所以决左,也對這個(gè)模塊的可擴(kuò)展性愕够、易修改性提出了要求。

結(jié)合一點(diǎn)點(diǎn)理論

設(shè)計(jì)模式中有幾大原則佛猛,剛開始接觸我們總感到難以把握惑芭。因?yàn)樗鼈兒喍痰孟駧鬃终嫜裕鴮?shí)際的場景卻有千千萬萬種继找。那么遂跟,就從最易理解的“單一職責(zé)原則”開始。簡單來說婴渡,一個(gè)單獨(dú)的模塊應(yīng)該只負(fù)責(zé)一個(gè)單獨(dú)的任務(wù)幻锁,任務(wù)的粒度越細(xì),它和其他模塊的耦合性越低边臼,它也越容易被復(fù)用哄尔。而遵循“依賴倒置原則”,則會有效提高代碼的易修改性硼瓣。比如對于數(shù)據(jù)庫模塊究飞,在實(shí)際使用某一數(shù)據(jù)庫框架進(jìn)行存取操作的實(shí)現(xiàn)類之上置谦,再抽象出一層接口類堂鲤。在下載過程中只使用接口類中提供的方法,而接口類中方法的具體實(shí)現(xiàn)媒峡,則由下層的實(shí)現(xiàn)類完成瘟栖。這樣,當(dāng)我們把數(shù)據(jù)庫框架由 FMDB 替換為 WCDB 時(shí)谅阿,只需對實(shí)現(xiàn)類的代碼進(jìn)行修改半哟,修改的目標(biāo)則是使用新框架再次實(shí)現(xiàn)接口類中聲明的方法酬滤,這也就是所謂的“針對接口編程”,而非”針對實(shí)現(xiàn)編程“寓涨。它帶來的好處是顯而易見的:在數(shù)據(jù)庫框架的替換過程中盯串,最上層的業(yè)務(wù)代碼完全無需改動,只需對數(shù)據(jù)庫操作的實(shí)現(xiàn)類進(jìn)行修改即可戒良。

圖一

模塊化的目的

有一件事是需要明確的体捏,我們常談的“模塊化”,并非對所有模塊都追求任意場景下的可復(fù)用糯崎。因?yàn)槟K會分為業(yè)務(wù)模塊和通用模塊几缭,通用模塊力求做到任意場景下的可復(fù)用,而業(yè)務(wù)模塊則專注于完成某一需求場景沃呢。雖然“下載”這個(gè)詞在很多項(xiàng)目中會出現(xiàn)年栓,但不同的項(xiàng)目中對它的定義是不同的。有的“下載”僅僅意指下載單個(gè)的文件薄霜,而有的下載則指的是某一場景下所有內(nèi)容的本地緩存某抓。

在這篇文章中,我預(yù)設(shè)的場景是一個(gè)下載任務(wù)中會包括各種具體的子任務(wù)惰瓜,舉個(gè)例子搪缨,一個(gè)下載任務(wù)可能由三個(gè)視頻文件、兩個(gè)音頻文件鸵熟、三張圖片副编、兩個(gè)網(wǎng)絡(luò)請求的 JSON 格式結(jié)果組成

因此流强,我會把本文所說的“下載”歸入業(yè)務(wù)模塊痹届,它不追求做到任意場景下的可復(fù)用,但它能夠很好地完成這個(gè)較復(fù)雜場景下的下載任務(wù)打月。而這個(gè)業(yè)務(wù)模塊中所包含的文件下載队腐、圖片緩存、文件操作等具體步驟奏篙,其實(shí)是無關(guān)業(yè)務(wù)的柴淘,那么它們便可以歸為通用模塊。在其他進(jìn)行圖片緩存的場景下秘通,可以使用這里的圖片緩存模塊为严,而其他的文件操作場景,也可以使用這里的文件操作模塊肺稀。它們的具體分析會在下文展開第股。

二、給出設(shè)計(jì)方案

結(jié)合文章第一部分的分析话原,著手進(jìn)行方案的設(shè)計(jì)夕吻。

“下載”不是單單一件事

通常意義上的下載诲锹,是指將云端的資源獲取到本地磁盤的過程。對于 iOS 應(yīng)用涉馅,下載的目的多是進(jìn)行某些內(nèi)容的離線展示归园。一個(gè)完整的下載過程,應(yīng)該由以下的步驟組成:

  • 文件操作

對于所下載的文件稚矿,需要確定它在本地的存儲路徑蔓倍;給定某個(gè) key 值,需要獲取對應(yīng)文件的存儲路徑盐捷;對于某個(gè)指定的路徑偶翅,會有檢查文件存在性、完整性等操作碉渡;下載過程中不斷進(jìn)行文件寫入聚谁,刪除已下載內(nèi)容時(shí)涉及文件刪除、目錄刪除滞诺;除此之外形导,還有獲取各個(gè)系統(tǒng)目錄、獲取磁盤空間數(shù)據(jù)等常規(guī)操作习霹。若涉及安全性需求朵耕,還會有文件加密、解密操作淋叶。因此阎曹,將文件操作封裝為一個(gè)單獨(dú)模塊是一個(gè)明智的選擇。文件操作不僅僅會在下載這個(gè)場景中出現(xiàn)煞檩,因此处嫌,在這個(gè)模塊的實(shí)現(xiàn)過程中應(yīng)該盡量剝離業(yè)務(wù)相關(guān)的內(nèi)容,力求成為一個(gè)通用的工具模塊斟湃。

  • 數(shù)據(jù)庫操作

基于文章第一部分中給出的場景熏迹,這里的下載任務(wù)應(yīng)該是結(jié)構(gòu)化的數(shù)據(jù)。無論網(wǎng)絡(luò)狀況是否正常凝赛,已下載的內(nèi)容都能夠正常展示注暗,所以下載記錄應(yīng)該被持久化存儲∧沽裕基于以上兩點(diǎn)捆昏,數(shù)據(jù)庫的使用是自然的選擇。應(yīng)該明確的是陶衅,數(shù)據(jù)庫存儲的是下載任務(wù)記錄屡立,或叫做日志直晨,而非下載的文件搀军∨蚶考慮到 iOS 中數(shù)據(jù)庫框架的多樣性和業(yè)務(wù)方對數(shù)據(jù)庫性能的持續(xù)追求,很容易預(yù)見到數(shù)據(jù)庫框架在未來的替換工作罩句。因此對于這個(gè)模塊焚刺,上文也進(jìn)行了分析,那就是依照依賴倒置原則门烂,分成抽象的接口類和具體的實(shí)現(xiàn)類乳愉。

  • 較大體積文件的下載

在下載的需求中,視頻屯远、音頻蔓姚、zip 文件等體積較大的文件是很常見的。因此一個(gè)只針對較大體積文件的下載模塊模塊必不可少慨丐。它不涉及任何具體的業(yè)務(wù)細(xì)節(jié)坡脐,它的任務(wù)僅僅是根據(jù)給定的文件 url 和本地存儲的路徑,完成該文件的下載房揭。做到這個(gè)模塊的高內(nèi)聚是比較容易的备闲,因此強(qiáng)烈建議將這部分封裝為一個(gè)通用模塊,以滿足任何場景下的文件下載需求捅暴。為減少通用模塊之間的橫向依賴恬砂,一個(gè)思路是本地路徑由上層的業(yè)務(wù)模塊調(diào)用文件操作模塊獲得,然后傳遞給本模塊蓬痒,而非本模塊直接調(diào)用文件操作模塊泻骤;對于文件寫入操作,可直接使用系統(tǒng)的 NSFileManager梧奢。同時(shí)也有另一種思路瞪讼,大文件下載和文件操作之間的依賴是自然、可接受的粹断,允許下載模塊依賴文件操作模塊符欠。這些沒有標(biāo)準(zhǔn)答案,可以自行取舍瓶埋。

  • 圖片的下載

有時(shí)候下載任務(wù)中會包含圖片下載希柿,按照體積來看,將圖片下載歸入文件類型也不為過养筒。但是圖片的緩存在iOS的開發(fā)中是一個(gè)積淀已深的話題曾撤,我們擁有 YYWebImageSDWebImage 等優(yōu)秀的圖片緩存框架晕粪,有什么理由再去重復(fù)造一個(gè)性能未必更優(yōu)的輪子呢挤悉?除此之外,剛剛提到的兩個(gè)圖片框架基本應(yīng)用在了絕大多數(shù)的iOS網(wǎng)絡(luò)應(yīng)用中巫湘,所以很有可能出現(xiàn)的場景是:已經(jīng)下載過的圖片装悲,在項(xiàng)目中的某處不相關(guān)的地方用上述圖片框架進(jìn)行加載昏鹃。如果圖片下載使用這些框架的緩存器來實(shí)現(xiàn),那么在上述場景下诀诊,框架會從本地緩存中尋找到目標(biāo)圖片洞渤,避免重復(fù)的云端下載,達(dá)到了有效且明顯的優(yōu)化效果属瓣≡仄基于局部性原理,這種情景的命中率還是不可忽略的抡蛙。因此护昧,建議將圖片的下載拆分為一個(gè)內(nèi)部實(shí)現(xiàn)使用上述框架的圖片緩存器。

  • 網(wǎng)絡(luò)請求結(jié)果的緩存

有的下載場景中粗截,需要對網(wǎng)絡(luò)請求進(jìn)行緩存捏卓。網(wǎng)絡(luò)請求的結(jié)果多為 JSON 格式的數(shù)據(jù),體積較小慈格,屬于輕量的下載內(nèi)容怠晴。我的實(shí)現(xiàn)是網(wǎng)絡(luò)請求緩存和圖片緩存作為 cache 模塊的一部分,整體封裝一個(gè) cache 模塊浴捆。也可以將這兩者分開模塊化蒜田,視具體業(yè)務(wù)需求靈活決定。

  • 特定場景下載的業(yè)務(wù)模塊

以上列出的模塊选泻,基本都可以向可廣泛復(fù)用的通用模塊努力冲粤。上文提到,模塊化中页眯,也包括專注具體場景的業(yè)務(wù)模塊梯捕。在本文的業(yè)務(wù)場景下,我封裝了一個(gè)業(yè)務(wù)模塊窝撵。它的職責(zé)是:持久化維護(hù)已下載和正在下載任務(wù)的list傀顾;根據(jù)按固定格式提交的下載任務(wù),解析出結(jié)構(gòu)化的任務(wù)結(jié)構(gòu)碌奉;對于不同類型的子任務(wù)短曾,使用上述對應(yīng)的通用模塊完成下載;同時(shí)負(fù)責(zé)協(xié)調(diào)各子任務(wù)之間的同步關(guān)系赐劣;在所有子任務(wù)完成下載后嫉拐,檢查整個(gè)結(jié)構(gòu)的文件完整性;通過完整性校驗(yàn)后魁兼,進(jìn)行數(shù)據(jù)庫存儲操作婉徘,存儲該次下載日志;在整個(gè)活動周期內(nèi),模塊還負(fù)責(zé)下載任務(wù)狀態(tài)的更新盖呼。

模塊整體結(jié)構(gòu)

通過對整個(gè)下載過程的分析儒鹿,我們拆分出了幾個(gè)模塊。依照單一職責(zé)原則塌计,將每個(gè)模塊的職責(zé)劃分到了較為合適的粒度挺身,都能夠做到一定程度上的復(fù)用侯谁。對于其中擴(kuò)展可能較高的模塊锌仅,依照依賴倒置原則,抽象出了一層接口類墙贱,避免了未來底層修改時(shí)對上層業(yè)務(wù)代碼的影響热芹。在模塊化的應(yīng)用上,也做到了目的明確惨撇、合理拆分伊脓。

下圖即是整體的示意圖:

圖二

三、完成具體實(shí)現(xiàn)

其實(shí)寫完第二部分魁衙,本文的寫作目的已經(jīng)差不多達(dá)到报腔。大家從標(biāo)題可以感受到,本文側(cè)重點(diǎn)在于對”下載“這個(gè)場景運(yùn)用一些理論的指導(dǎo)進(jìn)行較為合理的代碼結(jié)構(gòu)設(shè)計(jì)剖淀。不過為做到有始有終——“從理論分析開始纯蛾,用具體實(shí)現(xiàn)來結(jié)尾”,這部分對實(shí)現(xiàn)細(xì)節(jié)進(jìn)行一些討論纵隔,提供一些“干貨”翻诉,這些方案面對不同場景會有不同的優(yōu)劣表現(xiàn),僅供參考捌刮。

  • 文件操作模塊

這部分我的實(shí)現(xiàn)是使用系統(tǒng)的 NSFileManager 進(jìn)行文件存在性判斷等基本操作碰煌。對于本地存儲的目標(biāo)路徑,生成規(guī)則為文件 URL 做 md5 操作绅作,再添加具體的文件類型后綴玷氏。在安全性較高的場景中汪茧,所下載的文件都來自自有的服務(wù)器,那么文件正確性校驗(yàn)可以由后端提供部分支持,如對于每個(gè)文件都返回特定的校驗(yàn)值世落,在本地下載完成后,使用由已下載文件生成的校驗(yàn)值和后端提供的進(jìn)行比對匆背。

  • 數(shù)據(jù)庫模塊

對于數(shù)據(jù)庫中需要存儲什么字段鸣奔,我的意見是這樣的:對于某個(gè)具體的文件,存儲初始 url役拴、文件在本地存儲的路徑糊探、文件大小、更新時(shí)間等基本信息。對于結(jié)構(gòu)化的整條下載記錄科平,則將還原初始下載任務(wù)的所需字段都進(jìn)行存儲褥紫。具體解釋下,初始下載任務(wù)的提交時(shí)多是使用業(yè)務(wù)方的數(shù)據(jù)類型瞪慧,比如一篇微博展示時(shí)的 model 髓考,一篇文章展示時(shí)的 model。而下載任務(wù)提交到下載模塊后弃酌,我們會將初始的數(shù)據(jù)類型轉(zhuǎn)化為下載模塊的規(guī)定的數(shù)據(jù)格式氨菇。若涉及到斷點(diǎn)續(xù)傳等場景,便會存在 app 重啟后妓湘,由從數(shù)據(jù)庫中取得的下載模塊所用數(shù)據(jù)格式向初始業(yè)務(wù)方數(shù)據(jù)格式的逆轉(zhuǎn)化查蓉,這時(shí)就需要初始任務(wù)所有必要的狀態(tài)信息,從而進(jìn)行現(xiàn)場恢復(fù)榜贴,繼續(xù)進(jìn)行下載豌研。

上文說到,下載管理業(yè)務(wù)模塊需要維護(hù)下載中唬党、已下載任務(wù)的 list鹃共,用什么來區(qū)分狀態(tài)呢?我的實(shí)現(xiàn)是為下載記錄添加標(biāo)識是否完成的字段驶拱,這樣當(dāng) app 重啟后霜浴,從數(shù)據(jù)庫中取得所有的下載記錄,若某條記錄被標(biāo)識為未完成屯烦,那么它便是需要還原為初始下載任務(wù)的記錄坷随,被歸入下載中 list。

  • 大體積文件下載模塊

關(guān)于這部分的討論已經(jīng)有很多驻龟,本文不再贅述温眉。值得一提的是,這個(gè)通用組件依然會面臨底層實(shí)現(xiàn)更換或者版本升級的問題翁狐,所以依照依賴倒置抽象出接口層的思路在這里依然適用类溢。

  • 緩存模塊

關(guān)于圖片的緩存在上文已經(jīng)詳細(xì)討論。對于 JSON 格式的網(wǎng)絡(luò)請求結(jié)果露懒,iOS 中一般使用 NSDictionary 存儲闯冷,它支持 NSCoding 協(xié)議,因此 YYCache懈词、EGOCache等緩存框架都是可以使用的蛇耀。這部分的接口設(shè)計(jì)比較直白,為指定 key 對應(yīng)的值進(jìn)行緩存坎弯,根據(jù)給定 key 返回對應(yīng)的緩存值纺涤,以及移除給定 key 對應(yīng)的內(nèi)容译暂。抽象接口層的思路,照例適用撩炊。

  • 下載管理業(yè)務(wù)模塊

在項(xiàng)目的很多地方可能都需要獲知當(dāng)前下載模塊的狀態(tài)外永,所以這里使用單例實(shí)現(xiàn)是一個(gè)比較好的選擇。在整個(gè)下載過程的最初拧咳,它根據(jù)提交的每一個(gè)初始任務(wù)數(shù)據(jù)伯顶,解析出具體的子任務(wù)類型,調(diào)用對應(yīng)的子模塊完成子任務(wù)的下載骆膝。同一下載任務(wù)下的各子任務(wù)之間應(yīng)該是異步的祭衩,所以 dispatch group 是一個(gè)直觀的選擇。順序提交的所有初始任務(wù)之間谭网,則是同步的關(guān)系汪厨,這里可以使用類似隊(duì)列的結(jié)構(gòu)來管理赃春。下面給出一個(gè)示意圖:

圖三

對于下載中愉择、已下載這兩種狀態(tài)的區(qū)分,這里提供一個(gè)改進(jìn)思路:在某個(gè)初始任務(wù)真正開始下載之前织中,就向數(shù)據(jù)庫中插入一條新的下載記錄锥涕,設(shè)置狀態(tài)字段為未完成,當(dāng)所有子任務(wù)均完成且通過完整性校驗(yàn)后狭吼,更新狀態(tài)字段為完成层坠。

最后,為大家提供一個(gè)業(yè)務(wù)模塊的樣例偽代碼刁笙,用以展示整個(gè)下載流程破花。

//下載管理業(yè)務(wù)模塊的接口列表(大意展示)

//業(yè)務(wù)方的model
@class OriginModel;

@interface DownloadManager : NSObject
//獲取下載管理對象(單例)
+ (instancetype)sharedInstance;
//獲取下載中的任務(wù)
- (NSArray<OriginModel *> *)downloadingItems;
//獲取已下載的任務(wù)
- (NSArray<OriginModel*> *)downloadedItems;
//根據(jù)id獲取已下載的item
- (OriginModel *)downloadedItemForId:(id<NSCopying>)itemId;
//是否下載過指定id的item
- (BOOL)didDownloadedItem:(id<NSCopying>)itemId;
//批量下載
- (void)downloadItems:(NSArray<OriginModel*> *)items;
//暫停下載
- (void)pauseDownloadForItem:(id<NSCopying>)itemId;
//恢復(fù)下載
- (void)resumeDownloadForItem:(id<NSCopying>)itemId;
//取消下載
- (void)cancelDownloadForItem:(id<NSCopying>)itemId;
@end
復(fù)制代碼
//下載管理業(yè)務(wù)模塊的主要實(shí)現(xiàn)

@implementation DownloadManager

- (void)downloadItems:(NSArray<OriginModel *> *)items {

//    解析任務(wù)結(jié)構(gòu),將所有任務(wù)push進(jìn)任務(wù)隊(duì)列
    MissionStruct *oneStruct = [self analyzeMission];
    for (MissionItem *item in oneStruct) {
        [self.missionList pushItem:item];
    }
    ...
//    若非空疲吸,從任務(wù)隊(duì)列中取出任務(wù)元素
    if (![self.missionList isEmpty]) {
        MissionItem *oneMission = [self.missionList pop];
        [self handleMission:oneMission];
    }
}

- (void)handleMission:(MissionItem *)mission {

    //    調(diào)用數(shù)據(jù)庫模塊座每,插入一條新紀(jì)錄
    [DatabaseManager insertMission:mission];
    dispatch_group_t downloadGroup;

    //    下載視頻
    for (videoMission in mission.videos) {
        dispatch_group_enter(downloadGroup);
        //        調(diào)用文件管理模塊,獲取該url對應(yīng)的文件路徑
        targetPath = [FileManager pathForURL:videoMission.url];
        //        調(diào)用大文件下載模塊摘悴,下載該視頻
        [FileDownloadManager downloadFile:videoMission.url
                               targetPath:targetPath
                                  success:^(){
                                      dispatch_group_leave(downloadGroup);
                                  }];
    }

    //    下載音頻
    for (audioMission in mission.audios) {
        dispatch_group_enter(downloadGroup);
        //        調(diào)用文件管理模塊峭梳,獲取該url對應(yīng)的文件路徑
        targetPath = [FileManager pathForURL:audioMission.url];
        //        調(diào)用大文件下載模塊,下載該音頻
        [FileDownloadManager downloadFile:audioMission.url
                               targetPath:targetPath
                                  success:^(){
                                      dispatch_group_leave(downloadGroup);
                                  }];
    }

    //    緩存圖片
    for (imageMission in mission.images) {
        dispatch_group_enter(downloadGroup);
        //        調(diào)用圖片緩存模塊蹂喻,緩存該圖片
        [ImageCacheManager cacheImage:imageMission.url
                                  success:^(){
                                      dispatch_group_leave(downloadGroup);
                                  }];
    }

    //    緩存網(wǎng)絡(luò)請求
    for (contentMission in mission.contents) {
        dispatch_group_enter(downloadGroup);
        //        調(diào)用網(wǎng)絡(luò)請求緩存模塊葱椭,緩存該網(wǎng)絡(luò)請求
        [RequestCacheManager cacheRequest:contentMission.url
                              success:^(){
                                  dispatch_group_leave(downloadGroup);
                              }];
    }

    ...

    //    所有子任務(wù)均完成
    dispatch_group_notify(downloadGroup, dispatch_get_global_queue(0, 0), ^{
    //    通過完整性校驗(yàn)
        if ([self verifyAllSubMission:mission]) {
            //    調(diào)用數(shù)據(jù)庫模塊,更新該下載紀(jì)錄
            [DatabaseManager updateMission:mission];
        } else {
            //    未通過完整性校驗(yàn)口四,移除數(shù)據(jù)庫對應(yīng)記錄
            [DatabaseManager removeMission:mission];
        }
    });
}

@end

文章出處https://juejin.im/post/5b3a303be51d4555c07a7ae1

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末孵运,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蔓彩,更是在濱河造成了極大的恐慌治笨,老刑警劉巖踱侣,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異大磺,居然都是意外死亡抡句,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門杠愧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來待榔,“玉大人,你說我怎么就攤上這事流济∪衤啵” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵绳瘟,是天一觀的道長雕憔。 經(jīng)常有香客問我,道長糖声,這世上最難降的妖魔是什么斤彼? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮蘸泻,結(jié)果婚禮上琉苇,老公的妹妹穿的比我還像新娘。我一直安慰自己悦施,他們只是感情好并扇,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著抡诞,像睡著了一般穷蛹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上昼汗,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天肴熏,我揣著相機(jī)與錄音,去河邊找鬼乔遮。 笑死扮超,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蹋肮。 我是一名探鬼主播出刷,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼坯辩!你這毒婦竟也來了馁龟?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤漆魔,失蹤者是張志新(化名)和其女友劉穎坷檩,沒想到半個(gè)月后却音,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡矢炼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年系瓢,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片句灌。...
    茶點(diǎn)故事閱讀 38,137評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡夷陋,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出胰锌,到底是詐尸還是另有隱情骗绕,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布资昧,位于F島的核電站酬土,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏格带。R本人自食惡果不足惜撤缴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望践惑。 院中可真熱鬧腹泌,春花似錦嘶卧、人聲如沸尔觉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽侦铜。三九已至,卻和暖如春钟鸵,著一層夾襖步出監(jiān)牢的瞬間钉稍,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工棺耍, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留贡未,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓蒙袍,卻偏偏與公主長得像俊卤,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子害幅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 完成開發(fā)任務(wù)的同時(shí)以现,我們總希望自己能夠交付高質(zhì)量的代碼狠怨。代碼質(zhì)量的測度有很多方法约啊,可擴(kuò)展性、可復(fù)用性是其中的兩項(xiàng)指...
    halohily閱讀 1,814評論 0 12
  • 第十周 引言 “知識型組織”是圍繞著信息而非層級體制來進(jìn)行構(gòu)建的佣赖。比如恰矩,德魯克認(rèn)為:“典型的公司是基于知識的,...
    4af85c91ffc8閱讀 908評論 0 1
  • 開學(xué)前的最后一個(gè)下午憎蛤,安排好準(zhǔn)備返校的兒子枢里,莫名感覺渾身不舒服,于是鉆進(jìn)被窩里打算瞇會兒蹂午,姐姐的電話到了:“晚上跟...
    夢里飄香閱讀 1,101評論 0 0
  • 雨中荷花鮮栏豺,如在夢里面。 蛙鳴荷塘中豆胸,人醉清風(fēng)前奥洼。
    蠻力閱讀 224評論 2 3
  • 他,怯懦著晚胡,手部微微顫抖灵奖,望著眼前的景色:遍地殘尸,哦估盘!那血紅的一片喧嘩著瓷患、寂鬧著,殘骸似不在是殘骸遣妥,也許是的擅编,他...
    瀟染閱讀 208評論 2 10