MVC箫荡,MVP 和 MVVM 模式如何選擇魁亦?

轉(zhuǎn)摘:http://www.linuxidc.com/Linux/2015-10/124622.htm

前言

做客戶端開發(fā)、前端開發(fā)對(duì)MVC菲茬、MVP吉挣、MVVM這些名詞不了解也應(yīng)該大致聽過,都是為了解決圖形界面應(yīng)用程序復(fù)雜性管理問題而產(chǎn)生的應(yīng)用架構(gòu)模式婉弹。

網(wǎng)上很多文章關(guān)于這方面的討論比較雜亂,各種MV*模式之間的區(qū)別分不清终吼,甚至有些描述都是錯(cuò)誤的镀赌。本文追根溯源,從最經(jīng)典的Smalltalk-80 MVC模式開始逐步還原圖形界面之下最真實(shí)的MV*模式际跪。


GUI程序所面臨的問題

圖形界面的應(yīng)用程序提供給用戶可視化的操作界面商佛,這個(gè)界面提供給數(shù)據(jù)和信息。用戶輸入行為(鍵盤姆打,鼠標(biāo)等)會(huì)執(zhí)行一些業(yè)務(wù)邏輯良姆,可能會(huì)導(dǎo)致對(duì)應(yīng)用程序數(shù)據(jù)的變更,數(shù)據(jù)的變更自然需要用戶界面的同步變更以提供最準(zhǔn)確的信息幔戏。例如用戶對(duì)一個(gè)電子表格重新排序的操作玛追,應(yīng)用程序需要響應(yīng)用戶操作,對(duì)數(shù)據(jù)進(jìn)行排序闲延,然后需要同步到界面上痊剖。

在開發(fā)應(yīng)用程序的時(shí)候,以求更好的管理應(yīng)用程序的復(fù)雜性垒玲,基于職責(zé)分離(Speration of Duties)的思想都會(huì)對(duì)應(yīng)用程序進(jìn)行分層陆馁。在開發(fā)圖形界面應(yīng)用程序的時(shí)候,會(huì)把管理用戶界面的層次稱為View合愈,應(yīng)用程序的數(shù)據(jù)為Model(注意這里的Model指的是Domain Model叮贩,這個(gè)應(yīng)用程序?qū)π枰鉀Q的問題的數(shù)據(jù)抽象击狮,不包含應(yīng)用的狀態(tài),可以簡(jiǎn)單理解為對(duì)象)益老。Model層對(duì)應(yīng)用程序的業(yè)務(wù)邏輯無知帘不,只保存數(shù)據(jù)結(jié)構(gòu)和提供數(shù)據(jù)操作的接口

有了View和Model的分層,那么就有了兩個(gè)問題:

1杨箭、響應(yīng)用戶操作的業(yè)務(wù)邏輯(例如排序)的管理寞焙。

2、View如何同步Model的變更互婿。

帶著這兩個(gè)問題開始探索MV*模式秫筏,會(huì)發(fā)現(xiàn)這些模式之間的差異可以歸納為對(duì)這兩個(gè)問題處理的方式的不同肃弟。而幾乎所有的MV*模式都是經(jīng)典的Smalltalk-80 MVC的修改版。

Smalltalk-80 MVC

歷史背景

早在上個(gè)世紀(jì)70年代,美國的施樂公司(Xerox)的工程師研發(fā)了Smalltalk編程語言边琉,并且開始用它編寫圖形界面的應(yīng)用程序。而在Smalltalk-80這個(gè)版本的時(shí)候鬓催,一位叫Trygve Reenskaug的工程師設(shè)計(jì)了MVC圖形應(yīng)用程序的架構(gòu)模式同诫,極大地降低了圖形應(yīng)用程序的管理難度。而在四人?眾(GoF)的設(shè)計(jì)模式當(dāng)中并沒有把MVC當(dāng)做是設(shè)計(jì)模式壮锻,而僅僅是把它看成解決問題的一些類的集合琐旁。Smalltalk-80 MVC和GoF描述的MVC是最經(jīng)典的MVC模式。


MVC的依賴關(guān)系

MVC除了把應(yīng)用程序分成View猜绣、Model層灰殴,還額外的加了一個(gè)Controller層,它的職責(zé)就是專門管理應(yīng)用程序的業(yè)務(wù)邏輯掰邢。Model牺陶、View、Controller三個(gè)層次的依賴關(guān)系如下:

依賴關(guān)系

Controller和View都依賴Model層辣之,Controller和View可以互相依賴掰伸。在一些網(wǎng)上的資料Controller和View之間的依賴關(guān)系可能不一樣,有些是單向依賴怀估,有些是雙向依賴狮鸭,這個(gè)其實(shí)關(guān)系不大,后面會(huì)看到它們的依賴關(guān)系都是為了把處理用戶行為觸發(fā)的業(yè)務(wù)邏輯的處理權(quán)交給Controller奏夫。

MVC的調(diào)用關(guān)系

用戶的對(duì)View操作以后怕篷,View捕獲到這個(gè)操作,會(huì)把處理的權(quán)利交移給Controller(Pass calls)酗昼;Controller接著會(huì)執(zhí)行相關(guān)的業(yè)務(wù)邏輯廊谓,這些業(yè)務(wù)邏輯可能需要對(duì)Model進(jìn)行相應(yīng)的操作;當(dāng)Model變更了以后麻削,會(huì)通過觀察者模式(Observer Pattern)通知View蒸痹;View通過觀察者模式收到Model變更的消息以后春弥,會(huì)向Model請(qǐng)求最新的數(shù)據(jù),然后重新更新界面叠荠。如下圖:

看似沒有什么特別的地方匿沛,但是由幾個(gè)需要特別關(guān)注的關(guān)鍵點(diǎn):

1、View是把控制權(quán)交移給Controller榛鼎,自己不執(zhí)行業(yè)務(wù)邏輯逃呼。

2、Controller執(zhí)行業(yè)務(wù)邏輯并且操作Model者娱,但不會(huì)直接操作View抡笼,可以說它是對(duì)View無知的。

3黄鳍、View和Model的同步消息是通過觀察者模式進(jìn)行推姻,而同步操作是由View自己請(qǐng)求Model的數(shù)據(jù)然后對(duì)視圖進(jìn)行更新。

需要特別注意的是MVC模式的精髓在于第三點(diǎn):Model的更新是通過觀察者模式告知View的框沟,具體表現(xiàn)形式可以是Pub/Sub或者是觸發(fā)Events藏古。而網(wǎng)上很多對(duì)于MVC的描述都沒有強(qiáng)調(diào)這一點(diǎn)。通過觀察者模式的好處就是:不同的MVC三角關(guān)系可能會(huì)有共同的Model忍燥,一個(gè)MVC三角中的Controller操作了Model以后拧晕,兩個(gè)MVC三角的View都會(huì)接受到通知,然后更新自己灾前。保持了依賴同一塊Model的不同View顯示數(shù)據(jù)的實(shí)時(shí)性和準(zhǔn)確性防症。我們每天都在用的觀察者模式,在幾十年前就已經(jīng)被大神們整合到MVC的架構(gòu)當(dāng)中哎甲。

這里有一個(gè)MVC模式的JavaScript Demo,實(shí)現(xiàn)了一個(gè)小的TodoList應(yīng)用程序饲嗽。經(jīng)典的Smalltalk-80 MVC不需要任何框架支持就可以實(shí)現(xiàn)炭玫。目前Web前端框架當(dāng)中只有一個(gè)號(hào)稱是嚴(yán)格遵循Smalltalk-80 MVC模式的:maria.js

MVC的優(yōu)缺點(diǎn)

優(yōu)點(diǎn):

1貌虾、把業(yè)務(wù)邏輯全部分離到Controller中吞加,模塊化程度高。當(dāng)業(yè)務(wù)邏輯變更的時(shí)候尽狠,不需要變更View和Model衔憨,只需要Controller換成另外一個(gè)Controller就行了(Swappable Controller)。

2袄膏、觀察者模式可以做到多視圖同時(shí)更新践图。

缺點(diǎn):

1、Controller測(cè)試?yán)щy沉馆。因?yàn)橐晥D同步操作是由View自己執(zhí)行码党,而View只能在有UI的環(huán)境下運(yùn)行德崭。在沒有UI環(huán)境下對(duì)Controller進(jìn)行單元測(cè)試的時(shí)候,Controller業(yè)務(wù)邏輯的正確性是無法驗(yàn)證的:Controller更新Model的時(shí)候揖盘,無法對(duì)View的更新操作進(jìn)行斷言眉厨。

2、View無法組件化兽狭。View是強(qiáng)依賴特定的Model的,如果需要把這個(gè)View抽出來作為一個(gè)另外一個(gè)應(yīng)用程序可復(fù)用的組件就困難了箕慧。因?yàn)椴煌绦虻牡腄omain Model是不一樣的

MVC Model 2

在Web服務(wù)端開發(fā)的時(shí)候也會(huì)接觸到MVC模式服球,而這種MVC模式不能嚴(yán)格稱為MVC模式。經(jīng)典的MVC模式只是解決客戶端圖形界面應(yīng)用程序的問題销钝,而對(duì)服務(wù)端無效有咨。服務(wù)端的MVC模式又自己特定的名字:MVC Model 2,或者叫JSP Model 2蒸健,或者直接就是Model 2 座享。Model 2客戶端服務(wù)端的交互模式如下:

服務(wù)端接收到來自客戶端的請(qǐng)求,服務(wù)端通過路由規(guī)則把這個(gè)請(qǐng)求交由給特定的Controller進(jìn)行處理似忧,Controller執(zhí)行相應(yīng)的業(yè)務(wù)邏輯渣叛,對(duì)數(shù)據(jù)庫數(shù)據(jù)(Model)進(jìn)行操作,然后用數(shù)據(jù)去渲染特定的模版盯捌,返回給客戶端淳衙。

因?yàn)镠TTP協(xié)議是單工協(xié)議并且是無狀態(tài)的,服務(wù)器無法直接給客戶端推送數(shù)據(jù)饺著。除非客戶端再次發(fā)起請(qǐng)求箫攀,否則服務(wù)器端的Model的變更就無法告知客戶端。所以可以看到經(jīng)典的Smalltalk-80 MVC中Model通過觀察者模式告知View更新這一環(huán)被無情地打破幼衰,不能稱為嚴(yán)格的MVC靴跛。

Model 2模式最早在1998年應(yīng)用在JSP應(yīng)用程序當(dāng)中,JSP Model 1應(yīng)用管理的混亂誘發(fā)了JSP參考了客戶端MVC模式渡嚣,催生了Model 2梢睛。

后來這種模式幾乎被應(yīng)用在所有語言的Web開發(fā)框架當(dāng)中。PHP的ThinkPHP识椰,Python的Dijango绝葡、Flask,NodeJS的Express腹鹉,Ruby的RoR藏畅,基本都采納了這種模式。平常所講的MVC基本是這種服務(wù)端的MVC种蘸。

MVP

MVP模式有兩種:

1墓赴、Passive View

2竞膳、Supervising Controller

而大多數(shù)情況下討論的都是Passive View模式。本文會(huì)對(duì)PV模式進(jìn)行較為詳細(xì)的介紹诫硕,而SC模式則簡(jiǎn)單提及坦辟。

歷史背景

MVP模式是MVC模式的改良。在上個(gè)世紀(jì)90年代章办,IBM旗下的子公司Taligent在用C/C++開發(fā)一個(gè)叫CommonPoint的圖形界面應(yīng)用系統(tǒng)的時(shí)候提出來的锉走。

MVP(Passive View)的依賴關(guān)系

MVP模式把MVC模式中的Controller換成了Presenter。MVP層次之間的依賴關(guān)系如下:

MVP打破了View原來對(duì)于Model的依賴藕届,其余的依賴關(guān)系和MVC模式一致挪蹭。

MVP(Passive View)的調(diào)用關(guān)系

既然View對(duì)Model的依賴被打破了,那View如何同步Model的變更休偶?看看MVP的調(diào)用關(guān)系:

和MVC模式一樣梁厉,用戶對(duì)View的操作都會(huì)從View交移給Presenter。Presenter同樣的會(huì)執(zhí)行相應(yīng)的業(yè)務(wù)邏輯踏兜,并且對(duì)Model進(jìn)行相應(yīng)的操作词顾;而這時(shí)候Model也是通過觀察者模式把自己變更的消息傳遞出去,但是是傳給Presenter而不是View碱妆。Presenter獲取到Model變更的消息以后肉盹,通過View提供的接口更新界面

關(guān)鍵點(diǎn):

1疹尾、View不再負(fù)責(zé)同步的邏輯上忍,而是由Presenter負(fù)責(zé)。Presenter中既有業(yè)務(wù)邏輯也有同步邏輯纳本。

2窍蓝、View需要提供操作界面的接口給Presenter進(jìn)行調(diào)用。(關(guān)鍵)

對(duì)比在MVC中繁成,Controller是不能操作View的它抱,View也沒有提供相應(yīng)的接口;而在MVP當(dāng)中朴艰,Presenter可以操作View,View需要提供一組對(duì)界面操作的接口給Presenter進(jìn)行調(diào)用混移;Model仍然通過事件廣播自己的變更祠墅,但由Presenter監(jiān)聽而不是View。

MVP模式毁嗦,這里也提供一個(gè)用JavaScript編寫的例子

MVP(Passive View)的優(yōu)缺點(diǎn)

優(yōu)點(diǎn):

1回铛、便于測(cè)試狗准。Presenter對(duì)View是通過接口進(jìn)行克锣,在對(duì)Presenter進(jìn)行不依賴UI環(huán)境的單元測(cè)試的時(shí)候∏怀ぃ可以通過Mock一個(gè)View對(duì)象袭祟,這個(gè)對(duì)象只需要實(shí)現(xiàn)了View的接口即可。然后依賴注入到Presenter中捞附,單元測(cè)試的時(shí)候就可以完整的測(cè)試Presenter業(yè)務(wù)邏輯的正確性巾乳。這里根據(jù)上面的例子給出了Presenter的單元測(cè)試樣例

2鸟召、View可以進(jìn)行組件化胆绊。在MVP當(dāng)中,View不依賴Model欧募。這樣就可以讓View從特定的業(yè)務(wù)場(chǎng)景中脫離出來压状,可以說View可以做到對(duì)業(yè)務(wù)邏輯完全無知。它只需要提供一系列接口提供給上層操作跟继。這樣就可以做高度可復(fù)用的View組件种冬。

缺點(diǎn):

1、Presenter中除了業(yè)務(wù)邏輯以外还栓,還有大量的View->Model碌廓,Model->View的手動(dòng)同步邏輯,造成Presenter比較笨重剩盒,維護(hù)起來會(huì)比較困難谷婆。

MVP(Supervising Controller)

上面講的是MVP的Passive View模式,該模式下View非常Passive辽聊,它幾乎什么都不知道纪挎,Presenter讓它干什么它就干什么。而Supervising Controller模式中跟匆,Presenter會(huì)把一部分簡(jiǎn)單的同步邏輯交給View自己去做异袄,Presenter只負(fù)責(zé)比較復(fù)雜的、高層次的UI操作玛臂,所以可以把它看成一個(gè)Supervising Controller烤蜕。

Supervising Controller模式下的依賴和調(diào)用關(guān)系:

因?yàn)镾upervising Controller用得比較少,對(duì)它的討論就到這里為止迹冤。

MVVM

MVVM可以看作是一種特殊的MVP(Passive View)模式讽营,或者說是對(duì)MVP模式的一種改良。

歷史背景

MVVM模式最早是微軟公司提出泡徙,并且了大量使用在.NET的WPF和Sliverlight中橱鹏。2005年微軟工程師John Gossman在自己的博客上首次公布了MVVM模式。

ViewModel

MVVM代表的是Model-View-ViewModel,這里需要解釋一下什么是ViewModel莉兰。ViewModel的含義就是 "Model of View"挑围,視圖的模型。它的含義包含了領(lǐng)域模型(Domain Model)和視圖的狀態(tài)(State)糖荒。 在圖形界面應(yīng)用程序當(dāng)中杉辙,界面所提供的信息可能不僅僅包含應(yīng)用程序的領(lǐng)域模型。還可能包含一些領(lǐng)域模型不包含的視圖狀態(tài)寂嘉,例如電子表格程序上需要顯示當(dāng)前排序的狀態(tài)是順序的還是逆序的奏瞬,而這是Domain Model所不包含的,但也是需要顯示的信息泉孩。

可以簡(jiǎn)單把ViewModel理解為頁面上所顯示內(nèi)容的數(shù)據(jù)抽象硼端,和Domain Model不一樣,ViewModel更適合用來描述View寓搬。

MVVM的依賴

MVVM的依賴關(guān)系和MVP依賴珍昨,只不過是把P換成了VM。

MVVM的調(diào)用關(guān)系

MVVM的調(diào)用關(guān)系和MVP一樣句喷。但是镣典,在ViewModel當(dāng)中會(huì)有一個(gè)叫Binder,或者是Data-binding engine的東西唾琼。以前全部由Presenter負(fù)責(zé)的View和Model之間數(shù)據(jù)同步操作交由給Binder處理兄春。你只需要在View的模版語法當(dāng)中,指令式地聲明View上的顯示的內(nèi)容是和Model的哪一塊數(shù)據(jù)綁定的锡溯。當(dāng)ViewModel對(duì)進(jìn)行Model更新的時(shí)候赶舆,Binder會(huì)自動(dòng)把數(shù)據(jù)更新到View上去,當(dāng)用戶對(duì)View進(jìn)行操作(例如表單輸入)祭饭,Binder也會(huì)自動(dòng)把數(shù)據(jù)更新到Model上去芜茵。這種方式稱為:Two-way data-binding,雙向數(shù)據(jù)綁定倡蝙【糯可以簡(jiǎn)單而不恰當(dāng)?shù)乩斫鉃橐粋€(gè)模版引擎,但是會(huì)根據(jù)數(shù)據(jù)變更實(shí)時(shí)渲染寺鸥。

也就是說猪钮,MVVM把View和Model的同步邏輯自動(dòng)化了。以前Presenter負(fù)責(zé)的View和Model同步不再手動(dòng)地進(jìn)行操作胆建,而是交由框架所提供的Binder進(jìn)行負(fù)責(zé)躬贡。只需要告訴Binder,View顯示的數(shù)據(jù)對(duì)應(yīng)的是Model哪一部分即可眼坏。

這里有一個(gè)JavaScript MVVM的例子,因?yàn)镸VVM需要Binder引擎。所以例子中使用了一個(gè)MVVM的庫:Vue.js宰译。

MVVM的優(yōu)缺點(diǎn)

優(yōu)點(diǎn):

1檐蚜、提高可維護(hù)性。解決了MVP大量的手動(dòng)View和Model同步的問題沿侈,提供雙向綁定機(jī)制闯第。提高了代碼的可維護(hù)性。

2缀拭、簡(jiǎn)化測(cè)試咳短。因?yàn)橥竭壿嬍墙挥葿inder做的,View跟著Model同時(shí)變更蛛淋,所以只需要保證Model的正確性咙好,View就正確。大大減少了對(duì)View同步更新的測(cè)試褐荷。

缺點(diǎn):

1勾效、過于簡(jiǎn)單的圖形界面不適用,或說牛刀殺雞叛甫。

2层宫、對(duì)于大型的圖形應(yīng)用程序,視圖狀態(tài)較多其监,ViewModel的構(gòu)建和維護(hù)的成本都會(huì)比較高萌腿。

3、數(shù)據(jù)綁定的聲明是指令式地寫在View的模版當(dāng)中的抖苦,這些內(nèi)容是沒辦法去打斷點(diǎn)debug的毁菱。

結(jié)語

可以看到,從MVC->MVP->MVVM睛约,就像一個(gè)打怪升級(jí)的過程鼎俘。后者解決了前者遺留的問題,把前者的缺點(diǎn)優(yōu)化成了優(yōu)點(diǎn)辩涝。同樣的Demo功能贸伐,代碼從最開始的一堆文件,優(yōu)化成了最后只需要20幾行代碼就完成怔揩。MV*模式之間的區(qū)分還是蠻清晰的捉邢,希望可以給對(duì)這些模式理解比較模糊的同學(xué)帶來一些參考和思路。


?參考

Scaling Isomorphic Javascript Code

Smalltalk-80 MVC

Learning JavaScript Design Patterns

Smalltalk-80 MVC in JavaScript

GUI Architectures

The Model-View-Presenter (MVP) Pattern


最后:

感謝:github.com/livoras


? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?適合自己的商膊,才是最好的?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末伏伐,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子晕拆,更是在濱河造成了極大的恐慌藐翎,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異吝镣,居然都是意外死亡堤器,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門末贾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來闸溃,“玉大人,你說我怎么就攤上這事拱撵』源ǎ” “怎么了?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵拴测,是天一觀的道長(zhǎng)乓旗。 經(jīng)常有香客問我,道長(zhǎng)昼扛,這世上最難降的妖魔是什么寸齐? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮抄谐,結(jié)果婚禮上渺鹦,老公的妹妹穿的比我還像新娘。我一直安慰自己蛹含,他們只是感情好毅厚,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著浦箱,像睡著了一般吸耿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上酷窥,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天咽安,我揣著相機(jī)與錄音,去河邊找鬼蓬推。 笑死妆棒,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的沸伏。 我是一名探鬼主播糕珊,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼毅糟!你這毒婦竟也來了红选?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤姆另,失蹤者是張志新(化名)和其女友劉穎喇肋,沒想到半個(gè)月后坟乾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡苟蹈,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年糊渊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片慧脱。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖贺喝,靈堂內(nèi)的尸體忽然破棺而出菱鸥,到底是詐尸還是另有隱情,我是刑警寧澤躏鱼,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布氮采,位于F島的核電站,受9級(jí)特大地震影響染苛,放射性物質(zhì)發(fā)生泄漏鹊漠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一茶行、第九天 我趴在偏房一處隱蔽的房頂上張望躯概。 院中可真熱鬧,春花似錦畔师、人聲如沸娶靡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽姿锭。三九已至,卻和暖如春伯铣,著一層夾襖步出監(jiān)牢的瞬間呻此,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來泰國打工腔寡, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留焚鲜,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓蹬蚁,卻偏偏與公主長(zhǎng)得像恃泪,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子犀斋,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

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