理解Javascript中的策略模式

作者:涂根華
原文地址:http://www.cnblogs.com/tugenhua0707/p/4722696.html

策略模式的定義是:定義一系列的算法奕纫,把它們一個(gè)個(gè)封裝起來(lái)叁扫,并且使它們可以相互替換躬贡。

使用策略模式的優(yōu)點(diǎn)如下:

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

  1. 策略模式利用組合劲藐,委托等技術(shù)和思想镶柱,有效的避免很多if條件語(yǔ)句摔癣。
  2. 策略模式提供了開(kāi)放-封閉原則姑曙,使代碼更容易理解和擴(kuò)展襟交。
  3. 策略模式中的代碼可以復(fù)用。

一伤靠、使用策略模式計(jì)算獎(jiǎng)金

下面的demo是我在書(shū)上看到的捣域,但是沒(méi)有關(guān)系,我們只是來(lái)理解下策略模式的使用而已醋界,我們可以使用策略模式來(lái)計(jì)算獎(jiǎng)金問(wèn)題竟宋;

比如公司的年終獎(jiǎng)是根據(jù)員工的工資和績(jī)效來(lái)考核的,績(jī)效為A的人形纺,年終獎(jiǎng)為工資的4倍丘侠;績(jī)效為B的人,年終獎(jiǎng)為工資的3倍逐样;績(jī)效為C的人蜗字,年終獎(jiǎng)為工資的2倍;現(xiàn)在我們使用一般的編碼方式會(huì)如下這樣編寫(xiě)代碼:

var calculateBouns = function(salary,level) {
    if(level === 'A') {
        return salary * 4;
    }
    if(level === 'B') {
        return salary * 3;
    }
    if(level === 'C') {
        return salary * 2;
    }
};
// 調(diào)用如下:
console.log(calculateBouns(4000,'A')); // 16000
console.log(calculateBouns(2500,'B')); // 7500

第一個(gè)參數(shù)為薪資脂新,第二個(gè)參數(shù)為等級(jí)挪捕;

代碼缺點(diǎn)如下:

  1. calculateBouns函數(shù)包含了很多if-else語(yǔ)句。
  2. calculateBouns函數(shù)缺乏彈性争便,假如還有D等級(jí)的話级零,那么我們需要在calculateBouns函數(shù)內(nèi)添加判斷等級(jí)D的if語(yǔ)句。
  3. 算法復(fù)用性差滞乙,如果在其他的地方也有類(lèi)似這樣的算法的話奏纪,但是規(guī)則不一樣,我們這些代碼不能通用斩启。

二序调、使用組合函數(shù)重構(gòu)代碼

組合函數(shù)是把各種算法封裝到一個(gè)個(gè)的小函數(shù)里面,比如等級(jí)A的話兔簇,封裝一個(gè)小函數(shù)发绢,等級(jí)為B的話硬耍,也封裝一個(gè)小函數(shù),以此類(lèi)推边酒;

如下代碼:

var performanceA = function(salary) {
    return salary * 4;
};
var performanceB = function(salary) {
    return salary * 3;
};
        
var performanceC = function(salary) {
    return salary * 2
};
var calculateBouns = function(level,salary) {
    if(level === 'A') {
        return performanceA(salary);
    }
    if(level === 'B') {
        return performanceB(salary);
    }
    if(level === 'C') {
        return performanceC(salary);
    }
};
// 調(diào)用如下
console.log(calculateBouns('A',4500)); // 18000

代碼看起來(lái)有點(diǎn)改善经柴,但是還是有如下缺點(diǎn):

calculateBouns函數(shù)有可能會(huì)越來(lái)越大,比如增加D等級(jí)的時(shí)候甚纲,而且缺乏彈性口锭。

三、使用策略模式重構(gòu)代碼

策略模式指的是定義一系列的算法介杆,把它們一個(gè)個(gè)封裝起來(lái),將不變的部分和變化的部分隔開(kāi)韭寸,實(shí)際就是將算法的使用和實(shí)現(xiàn)分離出來(lái)春哨。算法的使用方式是不變的,都是根據(jù)某個(gè)算法取得計(jì)算后的獎(jiǎng)金數(shù)恩伺,而算法的實(shí)現(xiàn)是根據(jù)績(jī)效對(duì)應(yīng)不同的績(jī)效規(guī)則赴背。

一個(gè)基于策略模式的程序至少由2部分組成,第一個(gè)部分是一組策略類(lèi)晶渠,策略類(lèi)封裝了具體的算法凰荚,并負(fù)責(zé)具體的計(jì)算過(guò)程。第二個(gè)部分是環(huán)境類(lèi)Context褒脯,該Context接收客戶端的請(qǐng)求便瑟,隨后把請(qǐng)求委托給某一個(gè)策略類(lèi)。我們先使用傳統(tǒng)面向?qū)ο髞?lái)實(shí)現(xiàn)番川。

如下代碼:

var performanceA = function(){};
performanceA.prototype.calculate = function(salary) {
    return salary * 4;
};
        
var performanceB = function(){};
performanceB.prototype.calculate = function(salary) {
    return salary * 3;
};

var performanceC = function(){};
performanceC.prototype.calculate = function(salary) {
    return salary * 2;
};
// 獎(jiǎng)金類(lèi)
var Bouns = function(){
    this.salary = null;    // 原始工資
    this.levelObj = null;  // 績(jī)效等級(jí)對(duì)應(yīng)的策略對(duì)象
};
Bouns.prototype.setSalary = function(salary) {
    this.salary = salary;  // 保存員工的原始工資
};
Bouns.prototype.setlevelObj = function(levelObj){
    this.levelObj = levelObj;  // 設(shè)置員工績(jī)效等級(jí)對(duì)應(yīng)的策略對(duì)象
};
// 取得獎(jiǎng)金數(shù)
Bouns.prototype.getBouns = function(){
    // 把計(jì)算獎(jiǎng)金的操作委托給對(duì)應(yīng)的策略對(duì)象
    return this.levelObj.calculate(this.salary);
};
var bouns = new Bouns();
bouns.setSalary(10000);
bouns.setlevelObj(new performanceA()); // 設(shè)置策略對(duì)象
console.log(bouns.getBouns());  // 40000
        
bouns.setlevelObj(new performanceB()); // 設(shè)置策略對(duì)象
console.log(bouns.getBouns());  // 30000

如上代碼使用策略模式重構(gòu)代碼到涂,可以看到代碼職責(zé)更新分明,代碼變得更加清晰颁督。

四践啄、Javascript版本的策略模式

代碼如下:

var obj = {
        "A": function(salary) {
            return salary * 4;
        },
        "B" : function(salary) {
            return salary * 3;
        },
        "C" : function(salary) {
            return salary * 2;
        } 
};
var calculateBouns =function(level,salary) {
    return obj[level](salary);
};
console.log(calculateBouns('A',10000)); // 40000

可以看到代碼更加簡(jiǎn)單明了。

策略模式指的是定義一系列的算法沉御,并且把它們封裝起來(lái)屿讽,但是策略模式不僅僅只封裝算法,我們還可以對(duì)用來(lái)封裝一系列的業(yè)務(wù)規(guī)則吠裆,只要這些業(yè)務(wù)規(guī)則目標(biāo)一致伐谈,我們就可以使用策略模式來(lái)封裝它們。

表單效驗(yàn)

比如我們經(jīng)常來(lái)進(jìn)行表單驗(yàn)證硫痰,比如注冊(cè)登錄對(duì)話框衩婚,我們登錄之前要進(jìn)行驗(yàn)證操作,比如有以下幾條邏輯:

  1. 用戶名不能為空
  2. 密碼長(zhǎng)度不能小于6位效斑。
  3. 手機(jī)號(hào)碼必須符合格式非春。

比如HTML代碼如下:

<form action = "http://www.baidu.com" id="registerForm" method = "post">
        <p>
            <label>請(qǐng)輸入用戶名:</label>
            <input type="text" name="userName"/>
        </p>
        <p>
            <label>請(qǐng)輸入密碼:</label>
            <input type="text" name="password"/>
        </p>
        <p>
            <label>請(qǐng)輸入手機(jī)號(hào)碼:</label>
            <input type="text" name="phoneNumber"/>
        </p>
</form>

我們正常的編寫(xiě)表單驗(yàn)證代碼如下:

var registerForm = document.getElementById("registerForm");
registerForm.onsubmit = function(){
    if(registerForm.userName.value === '') {
        alert('用戶名不能為空');
        return;
    }
    if(registerForm.password.value.length < 6) {
        alert("密碼的長(zhǎng)度不能小于6位");
        return;
    }
    if(!/(^1[3|5|8][0-9]{9}$)/.test(registerForm.phoneNumber.value)) {
        alert("手機(jī)號(hào)碼格式不正確");
        return;
    }
}

但是這樣編寫(xiě)代碼有如下缺點(diǎn):

  1. registerForm.onsubmit函數(shù)比較大,代碼中包含了很多if語(yǔ)句;
  2. registerForm.onsubmit函數(shù)缺乏彈性奇昙,如果增加了一種新的效驗(yàn)規(guī)則护侮,或者想把密碼的長(zhǎng)度效驗(yàn)從6改成8,我們必須改registerForm.onsubmit函數(shù)內(nèi)部的代碼储耐。違反了開(kāi)放-封閉原則羊初;
  3. 算法的復(fù)用性差,如果在程序中增加了另外一個(gè)表單什湘,這個(gè)表單也需要進(jìn)行一些類(lèi)似的效驗(yàn)长赞,那么我們可能又需要復(fù)制代碼了。

下面我們可以使用策略模式來(lái)重構(gòu)表單效驗(yàn)闽撤。

第一步得哆,我們先來(lái)封裝策略對(duì)象。如下代碼:

var strategy = {
    isNotEmpty: function(value,errorMsg) {
        if(value === '') {
            return errorMsg;
        }
    },
    // 限制最小長(zhǎng)度
    minLength: function(value,length,errorMsg) {
        if(value.length < length) {
            return errorMsg;
        }
    },
    // 手機(jī)號(hào)碼格式
    mobileFormat: function(value,errorMsg) {
        if(!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
            return errorMsg;
        }
    } 
};

接下來(lái)哟旗,我們準(zhǔn)備實(shí)現(xiàn)Validator類(lèi)贩据,Validator類(lèi)在這里作為Context,負(fù)責(zé)接收用戶的請(qǐng)求并委托給strategy對(duì)象闸餐,如下代碼:

var Validator = function(){
        this.cache = [];  // 保存效驗(yàn)規(guī)則
};
Validator.prototype.add = function(dom,rule,errorMsg) {
    var str = rule.split(":");
    this.cache.push(function(){
        // str 返回的是 minLength:6 
        var strategy = str.shift();
        str.unshift(dom.value); // 把input的value添加進(jìn)參數(shù)列表
        str.push(errorMsg);  // 把errorMsg添加進(jìn)參數(shù)列表
        return strategys[strategy].apply(dom,str);
    });
};
Validator.prototype.start = function(){
    for(var i = 0, validatorFunc; validatorFunc = this.cache[i++]; ) {
        var msg = validatorFunc(); // 開(kāi)始效驗(yàn) 并取得效驗(yàn)后的返回信息
        if(msg) {
            return msg;
        }
    }
};

Validator類(lèi)在這里作為Context饱亮,負(fù)責(zé)接收用戶的請(qǐng)求并委托給strategys對(duì)象。上面的代碼中舍沙,我們先創(chuàng)建一個(gè)Validator對(duì)象近上,然后通過(guò)validator.add方法往validator對(duì)象中添加一些效驗(yàn)規(guī)則,validator.add方法接收3個(gè)參數(shù)场勤,如下代碼:

validator.add(registerForm.password,'minLength:6','密碼長(zhǎng)度不能小于6位');
  • registerForm.password為效驗(yàn)的input輸入框dom節(jié)點(diǎn)戈锻;
  • minLength:6是以一個(gè)冒號(hào)隔開(kāi)的字符串,冒號(hào)前面的minLength代表客戶挑選的strategys對(duì)象和媳,冒號(hào)后面的數(shù)字6表示在效驗(yàn)過(guò)程中所必須驗(yàn)證的參數(shù)格遭,minLength:6的意思是效驗(yàn)registerForm.password這個(gè)文本輸入框的value最小長(zhǎng)度為6位;如果字符串中不包含冒號(hào)留瞳,說(shuō)明效驗(yàn)過(guò)程中不需要額外的效驗(yàn)信息拒迅;
  • 第三個(gè)參數(shù)是當(dāng)效驗(yàn)未通過(guò)時(shí)返回的錯(cuò)誤信息。

當(dāng)我們往validator對(duì)象里添加完一系列的效驗(yàn)規(guī)則之后她倘,會(huì)調(diào)用validator.start()方法來(lái)啟動(dòng)效驗(yàn)璧微。如果validator.start()返回了一個(gè)errorMsg字符串作為返回值,說(shuō)明該次效驗(yàn)沒(méi)有通過(guò)硬梁,此時(shí)需要registerForm.onsubmit方法返回false來(lái)阻止表單提交前硫。下面我們來(lái)看看初始化代碼如下:

var validateFunc = function(){
    var validator = new Validator(); // 創(chuàng)建一個(gè)Validator對(duì)象
    /* 添加一些效驗(yàn)規(guī)則 */
    validator.add(registerForm.userName,'isNotEmpty','用戶名不能為空');
    validator.add(registerForm.password,'minLength:6','密碼長(zhǎng)度不能小于6位');
    validator.add(registerForm.userName,'mobileFormat','手機(jī)號(hào)碼格式不正確');

    var errorMsg = validator.start(); // 獲得效驗(yàn)結(jié)果
    return errorMsg; // 返回效驗(yàn)結(jié)果
};
var registerForm = document.getElementById("registerForm");
registerForm.onsubmit = function(){
    var errorMsg = validateFunc();
    if(errorMsg){
        alert(errorMsg);
        return false;
    }
}

下面是所有的代碼如下:

var strategys = {
    isNotEmpty: function(value,errorMsg) {
        if(value === '') {
            return errorMsg;
        }
    },
    // 限制最小長(zhǎng)度
    minLength: function(value,length,errorMsg) {
        if(value.length < length) {
            return errorMsg;
        }
    },
    // 手機(jī)號(hào)碼格式
    mobileFormat: function(value,errorMsg) {
        if(!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
            return errorMsg;
        }
    } 
};
var Validator = function(){
    this.cache = [];  // 保存效驗(yàn)規(guī)則
};
Validator.prototype.add = function(dom,rule,errorMsg) {
    var str = rule.split(":");
    this.cache.push(function(){
        // str 返回的是 minLength:6 
        var strategy = str.shift();
        str.unshift(dom.value); // 把input的value添加進(jìn)參數(shù)列表
        str.push(errorMsg);  // 把errorMsg添加進(jìn)參數(shù)列表
        return strategys[strategy].apply(dom,str);
    });
};
Validator.prototype.start = function(){
    for(var i = 0, validatorFunc; validatorFunc = this.cache[i++]; ) {
        var msg = validatorFunc(); // 開(kāi)始效驗(yàn) 并取得效驗(yàn)后的返回信息
        if(msg) {
            return msg;
        }
    }
};

var validateFunc = function(){
    var validator = new Validator(); // 創(chuàng)建一個(gè)Validator對(duì)象
    /* 添加一些效驗(yàn)規(guī)則 */
    validator.add(registerForm.userName,'isNotEmpty','用戶名不能為空');
    validator.add(registerForm.password,'minLength:6','密碼長(zhǎng)度不能小于6位');
    validator.add(registerForm.userName,'mobileFormat','手機(jī)號(hào)碼格式不正確');

    var errorMsg = validator.start(); // 獲得效驗(yàn)結(jié)果
    return errorMsg; // 返回效驗(yàn)結(jié)果
};
var registerForm = document.getElementById("registerForm");
registerForm.onsubmit = function(){
    var errorMsg = validateFunc();
    if(errorMsg){
        alert(errorMsg);
        return false;
    }
};

如上使用策略模式來(lái)編寫(xiě)表單驗(yàn)證代碼可以看到好處了,我們通過(guò)add配置的方式就完成了一個(gè)表單的效驗(yàn)荧止。這樣的話屹电,那么代碼可以當(dāng)做一個(gè)組件來(lái)使用阶剑,并且可以隨時(shí)調(diào)用,在修改表單驗(yàn)證規(guī)則的時(shí)候危号,也非常方便牧愁,通過(guò)傳遞參數(shù)即可調(diào)用。

給某個(gè)文本輸入框添加多種效驗(yàn)規(guī)則

上面的代碼我們可以看到外莲,我們只是給輸入框只能對(duì)應(yīng)一種效驗(yàn)規(guī)則猪半,比如上面的我們只能效驗(yàn)輸入框是否為空:

validator.add(registerForm.userName,'isNotEmpty','用戶名不能為空');

但是,如果我們既要效驗(yàn)輸入框是否為空偷线,還要效驗(yàn)輸入框的長(zhǎng)度不要小于10位的話磨确,那么我們期望需要像如下傳遞參數(shù):

validator.add(registerForm.userName,[{strategy:’isNotEmpty’,errorMsg:’用戶名不能為空’},{strategy: 'minLength:6',errorMsg:'用戶名長(zhǎng)度不能小于6位'}])

我們可以編寫(xiě)代碼如下:

// 策略對(duì)象
var strategys = {
    isNotEmpty: function(value,errorMsg) {
        if(value === '') {
            return errorMsg;
        }
    },
    // 限制最小長(zhǎng)度
    minLength: function(value,length,errorMsg) {
        if(value.length < length) {
            return errorMsg;
        }
    },
    // 手機(jī)號(hào)碼格式
    mobileFormat: function(value,errorMsg) {
        if(!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
            return errorMsg;
        }
    } 
};
var Validator = function(){
    this.cache = [];  // 保存效驗(yàn)規(guī)則
};
Validator.prototype.add = function(dom,rules) {
    var self = this;
    for(var i = 0, rule; rule = rules[i++]; ){
        (function(rule){
            var strategyAry = rule.strategy.split(":");
            var errorMsg = rule.errorMsg;
            self.cache.push(function(){
                var strategy = strategyAry.shift();
                strategyAry.unshift(dom.value);
                strategyAry.push(errorMsg);
                return strategys[strategy].apply(dom,strategyAry);
            });
        })(rule);
    }
};
Validator.prototype.start = function(){
    for(var i = 0, validatorFunc; validatorFunc = this.cache[i++]; ) {
    var msg = validatorFunc(); // 開(kāi)始效驗(yàn) 并取得效驗(yàn)后的返回信息
    if(msg) {
        return msg;
    }
    }
};
// 代碼調(diào)用
var registerForm = document.getElementById("registerForm");
var validateFunc = function(){
    var validator = new Validator(); // 創(chuàng)建一個(gè)Validator對(duì)象
    /* 添加一些效驗(yàn)規(guī)則 */
    validator.add(registerForm.userName,[
        {strategy: 'isNotEmpty',errorMsg:'用戶名不能為空'},
        {strategy: 'minLength:6',errorMsg:'用戶名長(zhǎng)度不能小于6位'}
    ]);
    validator.add(registerForm.password,[
        {strategy: 'minLength:6',errorMsg:'密碼長(zhǎng)度不能小于6位'},
    ]);
    validator.add(registerForm.phoneNumber,[
        {strategy: 'mobileFormat',errorMsg:'手機(jī)號(hào)格式不正確'},
    ]);
    var errorMsg = validator.start(); // 獲得效驗(yàn)結(jié)果
    return errorMsg; // 返回效驗(yàn)結(jié)果
};
// 點(diǎn)擊確定提交
registerForm.onsubmit = function(){
    var errorMsg = validateFunc();
    if(errorMsg){
        alert(errorMsg);
        return false;
    }
}

注意:如上代碼都是按照書(shū)上來(lái)做的声邦,都是看到書(shū)的代碼俐填,最主要我們理解策略模式實(shí)現(xiàn)。比如上面的表單驗(yàn)證功能是這樣封裝的代碼翔忽,我們平時(shí)使用jquery插件表單驗(yàn)證代碼原來(lái)是這樣封裝的,為此我們以后也可以使用這種方式來(lái)封裝表單等學(xué)習(xí)盏檐。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末歇式,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子胡野,更是在濱河造成了極大的恐慌材失,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件硫豆,死亡現(xiàn)場(chǎng)離奇詭異龙巨,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)熊响,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)旨别,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人汗茄,你說(shuō)我怎么就攤上這事秸弛。” “怎么了洪碳?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵递览,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我瞳腌,道長(zhǎng)绞铃,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任嫂侍,我火速辦了婚禮儿捧,結(jié)果婚禮上荚坞,老公的妹妹穿的比我還像新娘。我一直安慰自己纯命,他們只是感情好西剥,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著亿汞,像睡著了一般瞭空。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上疗我,一...
    開(kāi)封第一講書(shū)人閱讀 49,950評(píng)論 1 291
  • 那天咆畏,我揣著相機(jī)與錄音,去河邊找鬼吴裤。 笑死旧找,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的麦牺。 我是一名探鬼主播钮蛛,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼剖膳!你這毒婦竟也來(lái)了魏颓?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤吱晒,失蹤者是張志新(化名)和其女友劉穎甸饱,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體仑濒,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡叹话,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了墩瞳。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片驼壶。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖矗烛,靈堂內(nèi)的尸體忽然破棺而出辅柴,到底是詐尸還是另有隱情,我是刑警寧澤瞭吃,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布碌嘀,位于F島的核電站,受9級(jí)特大地震影響歪架,放射性物質(zhì)發(fā)生泄漏股冗。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一和蚪、第九天 我趴在偏房一處隱蔽的房頂上張望止状。 院中可真熱鬧烹棉,春花似錦、人聲如沸怯疤。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)集峦。三九已至伏社,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間塔淤,已是汗流浹背摘昌。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留高蜂,地道東北人聪黎。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像备恤,于是被迫代替她去往敵國(guó)和親稿饰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350

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