java設(shè)計模式-建造者模式(Builder)

定義

建造模式是對象的創(chuàng)建模式。建造模式可以將一個產(chǎn)品的內(nèi)部表象(internal representation)與產(chǎn)品的生產(chǎn)過程分割開來,從而可以使一個建造過程生成具有不同的內(nèi)部表象的產(chǎn)品對象。

產(chǎn)品的內(nèi)部表象

一個產(chǎn)品常有不同的組成成分作為產(chǎn)品的零件,這些零件有可能是對象,也有可能不是對象,他們通常又稱為產(chǎn)品的內(nèi)部表象(internal representation)。

不同的產(chǎn)品可以有不同的內(nèi)部表象,也就是不同的零件。使用建造模式可以使客戶端不需要知道所生產(chǎn)的產(chǎn)品有哪些零件,每個產(chǎn)品的對應(yīng)零件有何不同,是如何建造出來的,以及如何組成產(chǎn)品。

對象性質(zhì)的建造

有些情況下,一個對象會有一些重要的性質(zhì),在它們沒有恰當(dāng)?shù)闹抵埃瑢ο蟛荒茏鳛橐粋€完整的產(chǎn)品使用。比如:一個電子郵件有發(fā)件人地址、收件人地址、主題、內(nèi)容、附件等部分,而在最基本的發(fā)件人地址得到賦值之前,這個電子郵件是不可以發(fā)送的。

有些情況下,一個對象的有些性質(zhì)必須按照某個順序賦值才有意義。在某個性質(zhì)沒有賦值之前,另一個性質(zhì)則無法賦值。這些情況使得性質(zhì)本身的建造設(shè)計到復(fù)雜的商業(yè)邏輯。設(shè)置后,此對象相當(dāng)于一個有待建造的產(chǎn)品,而對象的這些性質(zhì)相當(dāng)于產(chǎn)品的零件,建造產(chǎn)品的過程是建造零件的過程。由于建造零件的過程很復(fù)雜,因此,這些零件的建造過程往往被“外部化”到另一個成為建造者的對象中,建造者對象返還給客戶端的是一個全部零件都建造完畢的產(chǎn)品對象。

建造模式利用一個導(dǎo)演者對象和具體建造者對象一個個的建造出所有的零件,從而建造出完整的產(chǎn)品對象。建造者模式將產(chǎn)品的結(jié)構(gòu)和產(chǎn)品的零件的建造過程對客戶端隱藏起來,把對建造過程進(jìn)行指揮的責(zé)任和具體建造者零件的責(zé)任分割開來,達(dá)到責(zé)任劃分和封裝的目的。

建造模式的結(jié)構(gòu)

建造模式的結(jié)構(gòu)

在這個示意性的系統(tǒng)里,最終產(chǎn)品Product只有兩個零件,即part1part2。相應(yīng)的構(gòu)造方法也有兩個:buildPart1()buildPart2()。同時可以看出本模式涉及到四個角色,他們分別為:

  • 抽象建造者(Builder):給出一個抽象接口,以規(guī)范產(chǎn)品對象的各個組成成分的建造。一般而言,此接口獨(dú)立于應(yīng)用程序的商業(yè)邏輯。模式中直接創(chuàng)建產(chǎn)品對象的是具體建造者ConcreteBuilder角色。具體建造者類必須實(shí)現(xiàn)這個接口要求的兩種方法:一種是建造方法,buildPart1()buildPart2();另一種是返回結(jié)構(gòu)方法retrieveResult()。一般來說,產(chǎn)品所包含的零件數(shù)目與建造方法的數(shù)目相符。換言之,有多少零件需要建造,就會有多少相應(yīng)的建造方法。
  • 具體建造者(ContreteBuilder):擔(dān)任這個角色的是與應(yīng)用程序緊密相關(guān)的一些類,他們在應(yīng)用程序調(diào)用下創(chuàng)建產(chǎn)品的實(shí)例。這個角色要完成的任務(wù)包括:
    1. 實(shí)現(xiàn)抽象建造者Builder所聲明的接口,給出一步步完成創(chuàng)建產(chǎn)品實(shí)例的操作。
    2. 在建造過程完成后,提供產(chǎn)品的實(shí)例。
  • 導(dǎo)演者(Director):擔(dān)任這個角色的類調(diào)用具體建造者角色以創(chuàng)建產(chǎn)品對象。應(yīng)當(dāng)指出的是,導(dǎo)演者角色并沒有產(chǎn)品類的具體知識,真正擁有產(chǎn)品類的具體知識的是具體建造者角色。
  • 產(chǎn)品(Product):產(chǎn)品便是建造中的復(fù)雜對象,一半來說,一個系統(tǒng)中會有多于一個的產(chǎn)品類,而且這些產(chǎn)品類并不一定有共同的借口,而完全可以是不相關(guān)聯(lián)的。

導(dǎo)演者角色是與客戶端打交道的角色,導(dǎo)演者將客戶端創(chuàng)建產(chǎn)品的請求劃分為對各個零件的建造請求,再將這些請求委派給具體建造者角色。
具體建造者角色是做具體建造工作的,但是卻對客戶端透明。
一般來說,每有一個產(chǎn)品類,就有一個相應(yīng)的具體建造者類。這些產(chǎn)品應(yīng)當(dāng)有一樣數(shù)目的零件,而每有一個零件就相應(yīng)的在所有的建造者角色中有一個建造方法。

示例代碼

產(chǎn)品類Product

public class Product {
    /**
     * 產(chǎn)品零件
     */
    private String part1;
    private String part2;
    
    public String getPart1() {
        return part1;
    }
    public void setPart1(String part1) {
        this.part1 = part1;
    }
    public String getPart2() {
        return part2;
    }
    public void setPart2(String part2) {
        this.part2 = part2;
    }
    
    @Override
    public String toString() {
        return "Product [part1=" + part1 + ", part2=" + part2 + "]";
    }
}

抽象建造者接口Builder

/**
 * 抽象建造者角色
 * 
 * 提供零件建造方法及返回結(jié)果方法
 */
public interface Builder {
    void buildPart1();
    void buildPart2();
    
    Product retrieveResult();
}

具體建造者角色類ConcreteBuilder

/**
 * 具體建造者角色
 */
public class ConcreteBuilder implements Builder {
    
    private Product product = new Product();
    
    /**
     * 建造零件1
     */
    @Override
    public void buildPart1() {
        product.setPart1("編號:9999");
    }

    /**
     * 建造零件2
     */
    @Override
    public void buildPart2() {
        product.setPart2("名稱:建造攻城獅");
    }

    /**
     * 返回建造后成功的產(chǎn)品
     * @return
     */
    @Override
    public Product retrieveResult() {
        return product;
    }

}

導(dǎo)演者角色類

/**
 * 導(dǎo)演者角色
 */
public class Director {
    /**
     * 創(chuàng)建建造者對象
     */
    private Builder builder;
    
    /**
     * 構(gòu)造函數(shù),給定建造者對象
     * @param builder 建造者對象
     */
    public Director(Builder builder) {
        this.builder = builder;
    }
    
    /**
     * 產(chǎn)品構(gòu)造方法,在該方法內(nèi),調(diào)用產(chǎn)品零件建造方法。
     */
    public void construct(){
        builder.buildPart1();
        builder.buildPart2();
    }
}

客戶端類Client

/*
 * 客戶端
 */
public class Client {
    public static void main(String[] args) {
        //創(chuàng)建具體建造者對象
        Builder builder = new ConcreteBuilder();
        //創(chuàng)造導(dǎo)演者角色,給定建造者對象
        Director director = new Director(builder);
        //調(diào)用導(dǎo)演者角色,創(chuàng)建產(chǎn)品零件
        director.construct();
        //接收建造者角色產(chǎn)品建造結(jié)果
        Product product = builder.retrieveResult();
        System.out.println(product.toString());
    }
}

客戶端創(chuàng)建具體建造者對象,然后將具體建造者對象交給導(dǎo)演者角色,導(dǎo)演者操作建造者對象建造產(chǎn)品零件,當(dāng)產(chǎn)品創(chuàng)建完成后,建造者將產(chǎn)品返回給客戶端。

將創(chuàng)建具體建造者對象的任務(wù)交給客戶端,而不是導(dǎo)演者對象的原因,是為了將導(dǎo)演者對象同具體建造者對象的耦合變成動態(tài)的,從而使得導(dǎo)演者對象可以操作多個具體建造者對象中的任何一個。

使用場景

假設(shè)有一個電子雜志系統(tǒng),定期的向用戶的電子郵箱發(fā)送電子雜志,用戶可以通過網(wǎng)頁訂閱電子雜志,也可以通過網(wǎng)頁結(jié)束訂閱。

當(dāng)用戶開始訂閱時,系統(tǒng)發(fā)送一個電子郵件表示歡迎,當(dāng)客戶結(jié)束訂閱時,系統(tǒng)發(fā)送一個電子郵件表示歡送。

本例子就是這個系統(tǒng)中負(fù)責(zé)發(fā)送“歡迎”和“歡送”郵件的模塊。

本例子中,產(chǎn)品類就是發(fā)送給某個客戶的郵件,如下圖所示

郵件組成圖

雖然在這個例子里面每個產(chǎn)品類均有一個共同的接口,但這僅為本例子特有的,并不代表建造者模式的特點(diǎn)。
建造者模式可以應(yīng)用到具有完全不同接口的產(chǎn)品類上。大多數(shù)情況下是不知道最終構(gòu)建出來的產(chǎn)品是什么樣的,所以在標(biāo)準(zhǔn)的建造者模式里面,一般對產(chǎn)品是不需要定義抽象接口的,因?yàn)樽罱K建造的產(chǎn)品完全不一樣,給這些產(chǎn)品定義公共抽象接口幾乎是沒有任何意義的。

下圖所示就是這個系統(tǒng)的類圖:

系統(tǒng)類圖

這個系統(tǒng)包含有客戶端(Client)導(dǎo)演者(Director)抽象建造者(Builder)具體建造者(WelcomeBuilder和GoodbyeBuilder)產(chǎn)品(WelcomeMessage和GoodbyeMessage)等角色。

示例代碼

抽象類AutoMessage源代碼,send()方法在此處為測試方法,不進(jìn)行正式發(fā)郵件操作。

import java.util.Date;

public abstract class AutoMessage {
    /**
     * 收件人地址
     */
    private String to;
    /**
     * 發(fā)件人地址
     */
    private String from;
    /**
     * 標(biāo)題
     */
    private String subject;
    /**
     * 內(nèi)容
     */
    private String body;
    /**
     * 發(fā)送日期
     */
    private Date sendDate;
    
    public void send() {
        System.out.println("收件人地址:" + to);
        System.out.println("發(fā)件人地址:" + from);
        System.out.println("標(biāo)題:" + subject);
        System.out.println("內(nèi)容:" + body);
        System.out.println("發(fā)送日期:" + sendDate);
    }

    public String getTo() {
        return to;
    }

    public void setTo(String to) {
        this.to = to;
    }

    public String getFrom() {
        return from;
    }

    public void setFrom(String from) {
        this.from = from;
    }

    public String getSubject() {
        return subject;
    }

    public void setSubject(String subject) {
        this.subject = subject;
    }

    public String getBody() {
        return body;
    }

    public void setBody(String body) {
        this.body = body;
    }

    public Date getSendDate() {
        return sendDate;
    }

    public void setSendDate(Date sendDate) {
        this.sendDate = sendDate;
    }
}

具體歡迎產(chǎn)品類WelcomeMessage

public class WelcomeMessage extends AutoMessage {
    /**
     * 構(gòu)造函數(shù)
     */
    public WelcomeMessage() {
        System.out.println("發(fā)送歡迎信息");
    }
}

具體歡送產(chǎn)品類GoodbyeMessage

public class GoodbyeMessage extends AutoMessage {
    /**
     * 構(gòu)造函數(shù)
     */
    public GoodbyeMessage() {
        System.out.println("發(fā)送歡送信息");
    }
}

抽象建造者類Builder

import java.util.Date;

public abstract class Builder {
    protected AutoMessage message;
    /**
     * 建造標(biāo)題零件的方法
     */
    public abstract void buildSubject();
    /**
     * 建造內(nèi)容零件的方法
     */
    public abstract void buildBody();
    /**
     * 建造收件人零件
     */
    public void buildTo(String to) {
        message.setTo(to);
    }
    /**
     * 建造發(fā)件人零件
     */
    public void buildFrom(String from) {
        message.setFrom(from);
    }
    /**
     * 建造發(fā)送時間零件
     */
    public void buildSendDate() {
        message.setSendDate(new Date());
    }
    
    /**
     * 郵件構(gòu)建完成后,調(diào)用方法發(fā)送郵件
     * 相當(dāng)于建造者角色產(chǎn)品返回方法。
     */
    public void sendMessge() {
        message.send();
    }
}

具體歡迎建造者類WelcomeBuilder

public class WelcomeBuilder extends Builder {
    public WelcomeBuilder() {
        message = new WelcomeMessage();
    }
    
    @Override
    public void buildSubject() {
        message.setSubject("歡迎標(biāo)題");
    }

    @Override
    public void buildBody() {
        message.setBody("歡迎內(nèi)容");
    }

}

具體歡送建造者類GoodbyeBuilder

public class GoodbyeBuilder extends Builder {
    public GoodbyeBuilder() {
        message = new GoodbyeMessage();
    }

    @Override
    public void buildSubject() {
        message.setSubject("歡送標(biāo)題");
    }

    @Override
    public void buildBody() {
        message.setBody("歡送正文");
    }
}

導(dǎo)演者Director,這個類提供一個construct()方法,此方法調(diào)用建造者的建造方法,包括各個零件的建造方法,從而逐個零件的建設(shè)對象。

public class Director {
    Builder builder;
    public Director(Builder builder) {
        this.builder = builder;
    }
    
    public void construct(String toAddress, String fromAdress) {
        this.builder.buildTo(toAddress);
        this.builder.buildFrom(fromAdress);
        this.builder.buildSubject();
        this.builder.buildBody();
        this.builder.buildSendDate();
    }
}

客戶端Client

public class Client {
    public static void main(String[] args) {
        Builder builder = new GoodbyeBuilder();
        Director director = new Director(builder);
        director.construct("to@126.com", "from@126.com");
        builder.sendMessge();
    }
}

執(zhí)行結(jié)果為:

發(fā)送歡送信息
收件人地址:to@126.com
發(fā)件人地址:from@126.com
標(biāo)題:歡送標(biāo)題
內(nèi)容:歡送正文
發(fā)送日期:Thu Jun 22 18:14:55 CST 2017

建造者模式中有兩個非常重要的部分:

  1. 一個部分是Builder接口,也就是建造者結(jié)構(gòu)。這里定義了如何構(gòu)建各個部件,也就是知道每個部件功能如何實(shí)現(xiàn),以及如何裝配這些部件到產(chǎn)品中去。
  2. 另外一個部分是Director導(dǎo)演者角色。導(dǎo)演者知道如何組合來建造產(chǎn)品,也就是Director負(fù)責(zé)產(chǎn)品的整體構(gòu)建,通常是分步驟的來執(zhí)行。

不管如何變化,建造模式都存在這么兩個部分,一個部分是部件構(gòu)造方法和產(chǎn)品裝配,另一個部分是整體構(gòu)建的算法。認(rèn)識這點(diǎn)是很重要的,因?yàn)樵诮ㄔ煺吣J街校瑥?qiáng)調(diào)的是固定整體構(gòu)建的算法,而靈活擴(kuò)展和切換部件的是具體建造者角色和產(chǎn)品裝配的方式。

也就是說,建造者模式的中心在于分離構(gòu)造算法和具體的構(gòu)造實(shí)現(xiàn),從而使得建造算法可以重用,具體的構(gòu)造實(shí)現(xiàn)可以很方便的進(jìn)行拓展和切換,從而可以靈活的組合建造不同的產(chǎn)品對象。

使用建造者模式構(gòu)建復(fù)雜對象

考慮這樣一個世紀(jì)應(yīng)用,要創(chuàng)建一個保險合同的對象,里面很多屬性的值都有約束,要求創(chuàng)建出來的對象是滿足這些約束規(guī)則的。

約束規(guī)則例如:保險合同通常情況下可以和個人簽訂,也可以和某個公司簽訂個,但是一份保險合同不能同時和個人和公司簽訂。這個對象里有很多類似于這樣的約束,采用建造者模式來構(gòu)建復(fù)雜的對象,通常會對建造者模式進(jìn)行一定的簡化,因?yàn)槟繕?biāo)明確,就是創(chuàng)建某個復(fù)雜對象,因此做適當(dāng)?shù)暮喕瘯沟贸绦蚋喗椤?/p>

大致簡化如下:

  • 由于是用Builder建造者模式來創(chuàng)建某個對象,因此就沒有必要再定義一個Builder接口,直接提供一個具體的建造類就可以了。
  • 對于創(chuàng)建一個復(fù)雜的對象,可能會有很多種不同的選擇和步驟,干脆去掉導(dǎo)演者Director,把導(dǎo)演者的功能和Client客戶端的功能合并起來,也就是說Client客戶端的功能就相當(dāng)于導(dǎo)演者,它來指導(dǎo)建造者去構(gòu)建需要的復(fù)雜對象。

保險合同及建造者角色類

/**
 * 保險合同編號
 */
public class InstranceContract {
    /**
     * 保險合同編號
     */
    private String contractId;
    /**
     * 受保人名稱,此處因?yàn)橛邢拗茥l件:要么同個人簽訂,要么同公司簽訂
     * 也就是說,受保人名稱屬性同受保公司名稱屬性不能同時有值。
     */
    private String personName;
    /**
     * 受保公司名稱
     */
    private String companyName;
    /**
     * 開始時間
     */
    private long beginDate;
    /**
     * 結(jié)束時間,需要大于開始時間
     */
    private long endDate;
    /**
     * 其他數(shù)據(jù)
     */
    private String otherData;

    private InstranceContract(ConcreteBuilder builder){
        this.contractId = builder.contractId;
        this.personName = builder.personName;
        this.companyName = builder.companyName;
        this.beginDate = builder.beginDate;
        this.endDate = builder.endDate;
        this.otherData = builder.otherData;
    }
    
    /**
     * 保險合同的一些操作
     */
    public void someOperation(){
        System.out.println("當(dāng)前正在操作的保險合同編號為【"+this.contractId+"】");
        System.out.println(toString());
    }
    
    @Override
    public String toString() {
        return "InstranceContract [contractId=" + contractId + ", personName=" + personName + ", companyName="
                + companyName + ", beginDate=" + beginDate + ", endDate=" + endDate + ", otherData=" + otherData + "]";
    }

    public static class ConcreteBuilder {
        private String contractId;
        private String personName;
        private String companyName;
        private long beginDate;
        private long endDate;
        private String otherData;
        
        /**
         * 構(gòu)造方法
         * @param contractId 保險合同編號
         * @param beginDate 生效時間
         * @param endDate 失效時間
         */
        public ConcreteBuilder(String contractId, long beginDate, long endDate) {
            this.contractId = contractId;
            this.beginDate = beginDate;
            this.endDate = endDate;
        }
        
        public ConcreteBuilder setPersonName(String personName) {
            this.personName = personName;
            return this;
        }
        
        public ConcreteBuilder setCompanyName(String companyName) {
            this.companyName = companyName;
            return this;
        }
        
        public ConcreteBuilder setOtherData(String otherData) {
            this.otherData = otherData;
            return this;
        }
        
        public InstranceContract build() {
            if (contractId == null || contractId.trim().length() == 0) {
                throw new IllegalArgumentException("合同編號不能為空");
            }
            
            boolean signPerson = (personName != null && personName.trim().length() > 0);
            boolean signCompany = (companyName != null && companyName.trim().length() > 0);
            
            if (signPerson && signCompany) {
                throw new IllegalArgumentException("一份保險合同不能同時與個人和公司簽訂");
            }
            
            if (!signPerson && !signCompany) {
                throw new IllegalArgumentException("一份保險合同不能沒有簽訂對象");
            }
            
            if (beginDate <= 0) {
                throw new IllegalArgumentException("一份保險合同必須有生效的日期");
            }
            
            if (endDate <= 0) {
                throw new IllegalArgumentException("一份保險合同必須有失效的日期");
            }
            
            if (endDate <= beginDate) {
                throw new IllegalArgumentException("一份保險合同的失效日期必須要大于生效的日期");
            }
            
            return new InstranceContract(this);
        }
    }
}

客戶端

public class Client {
    public static void main(String[] args) {
        InstranceContract.ConcreteBuilder builder = 
                new InstranceContract.ConcreteBuilder("8888", 1233L, 2253L);
        
        InstranceContract contract = 
                builder.setPersonName("趙四").setOtherData("測試數(shù)據(jù)").build();
        
        contract.someOperation();
    }
}

在本例子中,我們將建造者角色合并到產(chǎn)品對象中,作為產(chǎn)品對象的內(nèi)部類來使用。同時,我們將產(chǎn)品的構(gòu)造函數(shù)私有化,防止用戶直接使用構(gòu)造函數(shù)創(chuàng)造對象。因?yàn)樵谶@個例子中,建造者角色的主要目的就是構(gòu)建合同對象,因此,可以不用創(chuàng)建單獨(dú)的類。


在什么情況下使用建造者模式

  1. 需要生成的產(chǎn)品對象有復(fù)雜的內(nèi)部結(jié)構(gòu),每一個內(nèi)部成分本身也可以是對象,也可以僅僅是一個對象(產(chǎn)品)的一個組成部分。
  2. 需要生成的產(chǎn)品對象的屬性相互依賴。建造模式可以強(qiáng)制實(shí)行一種分步驟進(jìn)行的建造過程。因此,如果產(chǎn)品對象的一個屬性必須在另外一個屬性賦值之后才可以被賦值,那么,使用建造者模式是一個很好的設(shè)計思想。
  3. 在對象創(chuàng)建過程中會使用到系統(tǒng)中的一些其他對象,這些對象在產(chǎn)品對象的創(chuàng)建過程中不易得到。

參考

《JAVA與模式》之建造模式

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,431評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,637評論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,555評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,900評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,629評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,976評論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,976評論 3 448
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,139評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,686評論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,411評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,641評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,129評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,820評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,233評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,567評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,362評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,604評論 2 380

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