設(shè)計(jì)模式系列 — 解釋器模式

點(diǎn)贊再看,養(yǎng)成習(xí)慣是尔,公眾號(hào)搜一搜【一角錢技術(shù)】關(guān)注更多原創(chuàng)技術(shù)文章。本文 GitHub org_hejianhui/JavaStudy 已收錄,有我的系列文章捺萌。

前言

23種設(shè)計(jì)模式快速記憶的請(qǐng)看上面第一篇,本篇和大家一起來學(xué)習(xí)解釋器模式相關(guān)內(nèi)容膘茎。

模式定義

給分析對(duì)象定義一個(gè)語言桃纯,并定義該語言的文法表示,再設(shè)計(jì)一個(gè)解析器來解釋語言中的句子披坏。也就是說态坦,用編譯語言的方式來分析應(yīng)用中的實(shí)例。這種模式實(shí)現(xiàn)了文法表達(dá)式處理的接口棒拂,該接口解釋一個(gè)特定的上下文伞梯。

這里提到的文法和句子的概念同編譯原理中的描述相同,“文法”指語言的語法規(guī)則,而“句子”是語言集中的元素壮锻。例如琐旁,漢語中的句子有很多,“我是中國(guó)人”是其中的一個(gè)句子猜绣,可以用一棵語法樹來直觀地描述語言中的句子灰殴。

模式的結(jié)構(gòu)和實(shí)現(xiàn)

解釋器模式常用于對(duì)簡(jiǎn)單語言的編譯或分析實(shí)例中,為了掌握好它的結(jié)構(gòu)與實(shí)現(xiàn)掰邢,必須先了解編譯原理中的“文法牺陶、句子、語法樹”等相關(guān)概念辣之。

文法

文法是用于描述語言的語法結(jié)構(gòu)的形式規(guī)則掰伸。沒有規(guī)矩不成方圓,例如怀估,有些人認(rèn)為完美愛情的準(zhǔn)則是“相互吸引狮鸭、感情專一、任何一方都沒有戀愛經(jīng)歷”多搀,雖然最后一條準(zhǔn)則較苛刻歧蕉,但任何事情都要有規(guī)則,語言也一樣康铭,不管它是機(jī)器語言還是自然語言惯退,都有它自己的文法規(guī)則。例如从藤,中文中的“句子”的文法如下催跪。

〈句子〉::=〈主語〉〈謂語〉〈賓語〉
〈主語〉::=〈代詞〉|〈名詞〉
〈謂語〉::=〈動(dòng)詞〉
〈賓語〉::=〈代詞〉|〈名詞〉
〈代詞〉你|我|他
〈名詞〉7大學(xué)生I筱霞I英語
〈動(dòng)詞〉::=是|學(xué)習(xí)

注:這里的符號(hào)“::=”表示“定義為”的意思,用“〈”和“〉”括住的是非終結(jié)符夷野,沒有括住的是終結(jié)符懊蒸。

句子

句子是語言的基本單位,是語言集中的一個(gè)元素扫责,它由終結(jié)符構(gòu)成榛鼎,能由“文法”推導(dǎo)出。例如鳖孤,上述文法可以推出“我是大學(xué)生”者娱,所以它是句子。

語法樹

語法樹是句子結(jié)構(gòu)的一種樹型表示苏揣,它代表了句子的推導(dǎo)結(jié)果黄鳍,它有利于理解句子語法結(jié)構(gòu)的層次。下圖所示是“我是大學(xué)生”的語法樹平匈。

解釋器模式的結(jié)構(gòu)與組合模式相似框沟,不過其包含的組成元素比組合模式多藏古,而且組合模式是對(duì)象結(jié)構(gòu)型模式,而解釋器模式是類行為型模式忍燥。

模式的實(shí)現(xiàn)

解釋器模式實(shí)現(xiàn)的關(guān)鍵是定義文法規(guī)則拧晕、設(shè)計(jì)終結(jié)符類與非終結(jié)符類、畫出結(jié)構(gòu)圖梅垄,必要時(shí)構(gòu)建語法樹厂捞,其代碼結(jié)構(gòu)如下:

package com.niuh.designpattern.interpreter.v1;

/**
 * <p>
 * 解釋器模式
 * </p>
 */
public class InterpreterPattern {
}

//抽象表達(dá)式類
interface AbstractExpression {
    public Object interpret(String info);    //解釋方法
}

//終結(jié)符表達(dá)式類
class TerminalExpression implements AbstractExpression {
    public Object interpret(String info) {
        //對(duì)終結(jié)符表達(dá)式的處理
        return null;
    }
}

//非終結(jié)符表達(dá)式類
class NonterminalExpression implements AbstractExpression {
    private AbstractExpression exp1;
    private AbstractExpression exp2;

    public Object interpret(String info) {
        //非對(duì)終結(jié)符表達(dá)式的處理
        return null;
    }
}

//環(huán)境類
class Context {
    private AbstractExpression exp;

    public Context() {
        //數(shù)據(jù)初始化
    }

    public void operation(String info) {
        //調(diào)用相關(guān)表達(dá)式類的解釋方法
    }
}

解決的問題

對(duì)于一些固定文法構(gòu)建一個(gè)解釋句子的解釋器。

模式組成

組成(角色) 作用
抽象表達(dá)式(Abstract Expression)角色 定義解釋器的接口队丝,約定解釋器的解釋操作靡馁,主要包含解釋方法 interpret()。
終結(jié)符表達(dá)式(Terminal Expression)角色 是抽象表達(dá)式的子類机久,用來實(shí)現(xiàn)文法中與終結(jié)符相關(guān)的操作臭墨,文法中的每一個(gè)終結(jié)符都有一個(gè)具體終結(jié)表達(dá)式與之相對(duì)應(yīng)。
非終結(jié)符表達(dá)式(Nonterminal Expression)角色 也是抽象表達(dá)式的子類膘盖,用來實(shí)現(xiàn)文法中與非終結(jié)符相關(guān)的操作胧弛,文法中的每條規(guī)則都對(duì)應(yīng)于一個(gè)非終結(jié)符表達(dá)式。
環(huán)境(Context)角色 通常包含各個(gè)解釋器需要的數(shù)據(jù)或是公共的功能侠畔,一般用來傳遞被所有解釋器共享的數(shù)據(jù)叶圃,后面的解釋器可以從這里獲取這些值。
客戶端(Client) 主要任務(wù)是將需要分析的句子或表達(dá)式轉(zhuǎn)換成使用解釋器對(duì)象描述的抽象語法樹践图,然后調(diào)用解釋器的解釋方法,當(dāng)然也可以通過環(huán)境角色間接訪問解釋器的解釋方法沉馆。

實(shí)例說明

實(shí)例概況

用解釋器模式設(shè)計(jì)一個(gè)北京公交車卡的讀卡器程序码党。

說明:假如北京公交車讀卡器可以判斷乘客的身份,如果是“海淀區(qū)”或者“朝陽區(qū)”的“老人” “婦女”“兒童”就可以免費(fèi)乘車斥黑,其他人員乘車一次扣 2 元揖盘。

分析:本實(shí)例用“解釋器模式”設(shè)計(jì)比較適合,首先設(shè)計(jì)其文法規(guī)則如下锌奴。

<expression> ::= <city>的<person>
<city> ::= 海淀區(qū)|朝陽區(qū)
<person> ::= 老人|婦女|兒童

然后兽狭,根據(jù)文法規(guī)則按以下步驟設(shè)計(jì)公交車卡的讀卡器程序的類圖。

使用步驟

步驟1:定義一個(gè)抽象表達(dá)式(Expression)接口鹿蜀,它包含了解釋方法 interpret(String info)箕慧。

//抽象表達(dá)式類
interface Expression {
    public boolean interpret(String info);
}

步驟2:定義一個(gè)終結(jié)符表達(dá)式(Terminal Expression)類,它用集合(Set)類來保存滿足條件的城市或人茴恰,并實(shí)現(xiàn)抽象表達(dá)式接口中的解釋方法 interpret(Stringinfo)颠焦,用來判斷被分析的字符串是否是集合中的終結(jié)符。

class TerminalExpression implements Expression {
    private Set<String> set = new HashSet<String>();

    public TerminalExpression(String[] data) {
        for (int i = 0; i < data.length; i++) {
            set.add(data[i]);
        }
    }

    public boolean interpret(String info) {
        if (set.contains(info)) {
            return true;
        }
        return false;
    }
}

步驟3:定義一個(gè)非終結(jié)符表達(dá)式(AndExpressicm)類往枣,它也是抽象表達(dá)式的子類伐庭,它包含滿足條件的城市的終結(jié)符表達(dá)式對(duì)象和滿足條件的人員的終結(jié)符表達(dá)式對(duì)象粉渠,并實(shí)現(xiàn) interpret(String info) 方法,用來判斷被分析的字符串是否是滿足條件的城市中的滿足條件的人員圾另。

class AndExpression implements Expression {
    private Expression city = null;
    private Expression person = null;

    public AndExpression(Expression city, Expression person) {
        this.city = city;
        this.person = person;
    }

    public boolean interpret(String info) {
        String s[] = info.split("的");
        return city.interpret(s[0]) && person.interpret(s[1]);
    }
}

步驟4:定義一個(gè)環(huán)境(Context)類霸株,它包含解釋器需要的數(shù)據(jù),完成對(duì)終結(jié)符表達(dá)式的初始化集乔,并定義一個(gè)方法 freeRide(String info) 調(diào)用表達(dá)式對(duì)象的解釋方法來對(duì)被分析的字符串進(jìn)行解釋去件。

class Context {
    private String[] citys = {"海淀區(qū)", "朝陽區(qū)"};
    private String[] persons = {"老人", "婦女", "兒童"};
    private Expression cityPerson;

    public Context() {
        Expression city = new TerminalExpression(citys);
        Expression person = new TerminalExpression(persons);
        cityPerson = new AndExpression(city, person);
    }

    public void freeRide(String info) {
        boolean ok = cityPerson.interpret(info);
        if (ok) {
            System.out.println("您是" + info + ",您本次乘車免費(fèi)饺著!");
        } else {
            System.out.println(info + "箫攀,您不是免費(fèi)人員,本次乘車扣費(fèi)2元幼衰!");
        }
    }
}

步驟5:客戶端測(cè)試

public class InterpreterPattern {
    public static void main(String[] args) {
        Context bus = new Context();
        bus.freeRide("海淀區(qū)的老人");
        bus.freeRide("海淀區(qū)的年輕人");
        bus.freeRide("朝陽區(qū)的婦女");
        bus.freeRide("朝陽區(qū)的兒童");
        bus.freeRide("南京的年輕人");
    }
}

輸出結(jié)果

您是海淀區(qū)的老人靴跛,您本次乘車免費(fèi)!
海淀區(qū)的年輕人渡嚣,您不是免費(fèi)人員梢睛,本次乘車扣費(fèi)2元!
您是朝陽區(qū)的婦女识椰,您本次乘車免費(fèi)绝葡!
您是朝陽區(qū)的兒童,您本次乘車免費(fèi)腹鹉!
南京的年輕人藏畅,您不是免費(fèi)人員,本次乘車扣費(fèi)2元功咒!

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

解釋器模式是一種類行為型模式愉阎,其主要優(yōu)點(diǎn)如下。

  1. 擴(kuò)展性好力奋。由于在解釋器模式中使用類來表示語言的文法規(guī)則榜旦,因此可以通過繼承等機(jī)制來改變或擴(kuò)展文法。
  2. 容易實(shí)現(xiàn)景殷。在語法樹中的每個(gè)表達(dá)式節(jié)點(diǎn)類都是相似的溅呢,所以實(shí)現(xiàn)其文法較為容易。

缺點(diǎn)

  1. 執(zhí)行效率較低猿挚。解釋器模式中通常使用大量的循環(huán)和遞歸調(diào)用咐旧,當(dāng)要解釋的句子較復(fù)雜時(shí),其運(yùn)行速度很慢亭饵,且代碼的調(diào)試過程也比較麻煩休偶。
  2. 會(huì)引起類膨脹。解釋器模式中的每條規(guī)則至少需要定義一個(gè)類辜羊,當(dāng)包含的文法規(guī)則很多時(shí)踏兜,類的個(gè)數(shù)將急劇增加词顾,導(dǎo)致系統(tǒng)難以管理與維護(hù)。
  3. 可應(yīng)用的場(chǎng)景比較少碱妆。在軟件開發(fā)中肉盹,需要定義語言文法的應(yīng)用實(shí)例非常少,所以這種模式很少被使用到疹尾。

應(yīng)用場(chǎng)景

  1. 當(dāng)語言的文法較為簡(jiǎn)單上忍,且執(zhí)行效率不是關(guān)鍵問題時(shí)。
  2. 當(dāng)問題重復(fù)出現(xiàn)纳本,且可以用一種簡(jiǎn)單的語言來進(jìn)行表達(dá)時(shí)窍蓝。
  3. 當(dāng)一個(gè)語言需要解釋執(zhí)行,并且語言中的句子可以表示為一個(gè)抽象語法樹的時(shí)候繁成,如 XML 文檔解釋吓笙。

模式的擴(kuò)展

在項(xiàng)目開發(fā)中,如果要對(duì)數(shù)據(jù)表達(dá)式進(jìn)行分析與計(jì)算巾腕,無須再用解釋器模式進(jìn)行設(shè)計(jì)了面睛,Java 提供了以下強(qiáng)大的數(shù)學(xué)公式解析器:Expression4J、MESP(Math Expression String Parser) 和 Jep 等尊搬,它們可以解釋一些復(fù)雜的文法叁鉴,功能強(qiáng)大,使用簡(jiǎn)單佛寿。

現(xiàn)在以 Jep 為例來介紹該工具包的使用方法幌墓。Jep 是 Java expression parser 的簡(jiǎn)稱,即 Java 表達(dá)式分析器冀泻,它是一個(gè)用來轉(zhuǎn)換和計(jì)算數(shù)學(xué)表達(dá)式的 Java 庫克锣。通過這個(gè)程序庫,用戶可以以字符串的形式輸入一個(gè)任意的公式腔长,然后快速地計(jì)算出其結(jié)果。而且 Jep 支持用戶自定義變量验残、常量和函數(shù)捞附,它包括許多常用的數(shù)學(xué)函數(shù)和常量。

使用前先配置依賴包:

<!-- https://mvnrepository.com/artifact/jep/jep -->
<dependency>
    <groupId>jep</groupId>
    <artifactId>jep</artifactId>
    <version>2.24</version>
</dependency>

下面來看一個(gè)案例:

package com.niuh.designpattern.interpreter.v3;


import org.nfunk.jep.JEP;

/**
 * <p>
 * JepDemo
 * </p>
 */
public class JepDemo {

    public static void main(String[] args) {
        JEP jep = new JEP(); //一個(gè)數(shù)學(xué)表達(dá)式
        String exp = "((a+b)*(c+b))/(c+a)/b"; //給變量賦值
        jep.addVariable("a", 10);
        jep.addVariable("b", 10);
        jep.addVariable("c", 10);
        try { //執(zhí)行
            jep.parseExpression(exp);
            Object result = jep.getValueAsObject();
            System.out.println("計(jì)算結(jié)果: " + result);
        } catch (Throwable e) {
            System.out.println("An error occured: " + e.getMessage());
        }

    }
}

程序運(yùn)行結(jié)果如下:

計(jì)算結(jié)果: 2.0

源碼中的應(yīng)用

SpelExpressionParser中解釋器模式應(yīng)用分析

類圖分析

在下面的類圖中您没,Expression是一個(gè)接口鸟召,相當(dāng)于我們解釋器模式中的非終結(jié)符表達(dá)式,而ExpressionParser相當(dāng)于終結(jié)符表達(dá)式氨鹏。根據(jù)不同的Parser對(duì)象欧募,返回不同的Expression對(duì)象。


未命名文件 (1).png

部分源碼分析

Expression接口

//抽象的非終結(jié)符表達(dá)式
public interface Expression {
    Object getValue() throws EvaluationException;
    
    Object getValue(Object rootObject) throws EvaluationException;
}

SpelExpression類

//具體的非終結(jié)符表達(dá)式
public class SpelExpression implements Expression {
    @Override
    public Object getValue() throws EvaluationException {
        Object result;
        if (this.compiledAst != null) {
            try {
                TypedValue contextRoot = evaluationContext == null ? null : evaluationContext.getRootObject();
                return this.compiledAst.getValue(contextRoot == null ? null : contextRoot.getValue(), evaluationContext);
            }
            catch (Throwable ex) {
                // If running in mixed mode, revert to interpreted
                if (this.configuration.getCompilerMode() == SpelCompilerMode.MIXED) {
                    this.interpretedCount = 0;
                    this.compiledAst = null;
                }
                else {
                    // Running in SpelCompilerMode.immediate mode - propagate exception to caller
                    throw new SpelEvaluationException(ex, SpelMessage.EXCEPTION_RUNNING_COMPILED_EXPRESSION);
                }
            }
        }
        ExpressionState expressionState = new ExpressionState(getEvaluationContext(), this.configuration);
        result = this.ast.getValue(expressionState);
        checkCompile(expressionState);
        return result;
    }
}

CompositeStringExpression

//具體的非終結(jié)符表達(dá)式
public class CompositeStringExpression implements Expression {
    @Override
    public String getValue() throws EvaluationException {
        StringBuilder sb = new StringBuilder();
        for (Expression expression : this.expressions) {
            String value = expression.getValue(String.class);
            if (value != null) {
                sb.append(value);
            }
        }
        return sb.toString();
    }
}

ExpressionParser接口

public interface ExpressionParser {
    //解析表達(dá)式
    Expression parseExpression(String expressionString) throws ParseException;
    Expression parseExpression(String expressionString, ParserContext context) throws ParseException;
}

TemplateAwareExpressionParser類

public abstract class TemplateAwareExpressionParser implements ExpressionParser {
    @Override
    public Expression parseExpression(String expressionString) throws ParseException {
        return parseExpression(expressionString, NON_TEMPLATE_PARSER_CONTEXT);
    }
    //根據(jù)不同的parser返回不同的Expression對(duì)象
    @Override
    public Expression parseExpression(String expressionString, ParserContext context)
            throws ParseException {
        if (context == null) {
            context = NON_TEMPLATE_PARSER_CONTEXT;
        }
        if (context.isTemplate()) {
            return parseTemplate(expressionString, context);
        }
        else {
            return doParseExpression(expressionString, context);
        }
    }
    private Expression parseTemplate(String expressionString, ParserContext context)
            throws ParseException {
        if (expressionString.length() == 0) {
            return new LiteralExpression("");
        }
        Expression[] expressions = parseExpressions(expressionString, context);
        if (expressions.length == 1) {
            return expressions[0];
        }
        else {
            return new CompositeStringExpression(expressionString, expressions);
        }
    }
    //抽象的仆抵,由子類去實(shí)現(xiàn)
    protected abstract Expression doParseExpression(String expressionString,
            ParserContext context) throws ParseException;
}

SpelExpressionParser類

public class SpelExpressionParser extends TemplateAwareExpressionParser {
    @Override
    protected SpelExpression doParseExpression(String expressionString, ParserContext context) throws ParseException {
        //這里返回了一個(gè)InternalSpelExpressionParser跟继,
        return new InternalSpelExpressionParser(this.configuration).doParseExpression(expressionString, context);
    }
}

InternalSpelExpressionParser類

class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
    @Override
    protected SpelExpression doParseExpression(String expressionString, ParserContext context) throws ParseException {
        try {
            this.expressionString = expressionString;
            Tokenizer tokenizer = new Tokenizer(expressionString);
            tokenizer.process();
            this.tokenStream = tokenizer.getTokens();
            this.tokenStreamLength = this.tokenStream.size();
            this.tokenStreamPointer = 0;
            this.constructedNodes.clear();
            SpelNodeImpl ast = eatExpression();
            if (moreTokens()) {
                throw new SpelParseException(peekToken().startPos, SpelMessage.MORE_INPUT, toString(nextToken()));
            }
            Assert.isTrue(this.constructedNodes.isEmpty());
            return new SpelExpression(expressionString, ast, this.configuration);
        }
        catch (InternalParseException ex) {
            throw ex.getCause();
        }
    }
}

PS:以上代碼提交在 Githubhttps://github.com/Niuh-Study/niuh-designpatterns.git

文章持續(xù)更新种冬,可以公眾號(hào)搜一搜「 一角錢技術(shù) 」第一時(shí)間閱讀, 本文 GitHub org_hejianhui/JavaStudy 已經(jīng)收錄舔糖,歡迎 Star娱两。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市金吗,隨后出現(xiàn)的幾起案子十兢,更是在濱河造成了極大的恐慌,老刑警劉巖摇庙,帶你破解...
    沈念sama閱讀 217,734評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件旱物,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡卫袒,警方通過查閱死者的電腦和手機(jī)宵呛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來玛臂,“玉大人烤蜕,你說我怎么就攤上這事〖T” “怎么了讽营?”我有些...
    開封第一講書人閱讀 164,133評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)泡徙。 經(jīng)常有香客問我橱鹏,道長(zhǎng),這世上最難降的妖魔是什么堪藐? 我笑而不...
    開封第一講書人閱讀 58,532評(píng)論 1 293
  • 正文 為了忘掉前任莉兰,我火速辦了婚禮,結(jié)果婚禮上礁竞,老公的妹妹穿的比我還像新娘糖荒。我一直安慰自己,他們只是感情好模捂,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評(píng)論 6 392
  • 文/花漫 我一把揭開白布捶朵。 她就那樣靜靜地躺著,像睡著了一般狂男。 火紅的嫁衣襯著肌膚如雪综看。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,462評(píng)論 1 302
  • 那天岖食,我揣著相機(jī)與錄音红碑,去河邊找鬼。 笑死泡垃,一個(gè)胖子當(dāng)著我的面吹牛析珊,可吹牛的內(nèi)容都是我干的羡鸥。 我是一名探鬼主播,決...
    沈念sama閱讀 40,262評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼唾琼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼兄春!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起锡溯,我...
    開封第一講書人閱讀 39,153評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤赶舆,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后祭饭,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體芜茵,經(jīng)...
    沈念sama閱讀 45,587評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評(píng)論 3 336
  • 正文 我和宋清朗相戀三年倡蝙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了九串。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,919評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡寺鸥,死狀恐怖猪钮,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情胆建,我是刑警寧澤烤低,帶...
    沈念sama閱讀 35,635評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站笆载,受9級(jí)特大地震影響扑馁,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜凉驻,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評(píng)論 3 329
  • 文/蒙蒙 一腻要、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧涝登,春花似錦雄家、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蛛淋,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間篡腌,已是汗流浹背褐荷。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留嘹悼,地道東北人叛甫。 一個(gè)月前我還...
    沈念sama閱讀 48,048評(píng)論 3 370
  • 正文 我出身青樓层宫,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親其监。 傳聞我的和親對(duì)象是個(gè)殘疾皇子萌腿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評(píng)論 2 354