設(shè)計(jì)模式之行為類模式02

備忘錄模式

又名快照模式,目的是在不破壞封裝性的情況下將一個(gè)實(shí)例的數(shù)據(jù)進(jìn)行捕捉,然后外部化保存起來.在未來的某個(gè)時(shí)刻進(jìn)行數(shù)據(jù)還原.
這里有幾個(gè)關(guān)鍵詞解釋一下:

  1. 不破壞封裝性: 有些變量是私有變量正常情況下是不允許被訪問到的.但是這個(gè)變量對(duì)于這個(gè)類的數(shù)據(jù)又至關(guān)重要,捕捉數(shù)據(jù)的同時(shí)不能更改這個(gè)變量的訪問限制.
  2. 捕捉: 就是把有用的數(shù)據(jù)記錄下來.
  3. 外部化: 保存到別的地方去,并不保存在自己身上.

用處: 文本編輯器的撤銷功能, 瀏覽器的后退功能, 一些游戲的時(shí)間倒退功能.

這個(gè)模式需要用到3個(gè)類: 備忘類(Memento), 發(fā)起者(Originator) 以及 照看者(Caretaker).

我們舉個(gè)時(shí)間倒退的功能的例子:

首先我們創(chuàng)建一個(gè)備忘錄,記錄一個(gè)GameObject的Transform數(shù)據(jù)以備還原:

public class Memento
{
    public GameObject go;
    public float time;
    public Vector3 position;
    public Vector3 rotation;
    public Vector3 scale;
}

然后創(chuàng)建一個(gè)可以被備忘的MonoBehaviour

public class Memoable : MonoBehaviour
{
    public virtual Memento CreateMemo()
    {
        var m = new Memento()
        {
            go = gameObject,
            time = Time.realTimeSinceStartup,
            position = transform.position,
            rotation = transform.rotation,
            scale = transform.localScale
        };
        return m;
    }
    
    public virtual void RestoreFromMemo(Memento m)
    {
        if(m.go != gameObject)
            return;
        transform.position = m.position;
        transform.rotation = m.roation;
        transform.localScale = m.scale;
    }
}

最后創(chuàng)建一個(gè)備忘錄的照看者:

public class Caretaker
{
    private List<Memento> mementos = new List<Memento>();
    
    public void AddMemento(Memento m)
    {
        mementos.Add(m);
    }
    
    public IEnumerable<Memento> GetMementos(GameObject g)
    {
        return mementos.Where(m=>m.go == g);
    }
}

使用的時(shí)候如下:

public class Controller : MonoBehaviour
{
    private Caretaker ct;
    
    private void Start()
    {
        ct = new CareTaker();
    }
    private void Update()
    {
        var m = GetComponent<Memoable>();
        ct.AddMemento(m.CreateMemo());
    }
}

上面寫的是一個(gè)非常簡(jiǎn)單的備忘錄模式基礎(chǔ)結(jié)構(gòu)俗或,如果我們有一個(gè)單位不但包含Transform的基本信息,還包含血量需要記錄。
那我們可以基于上面的基礎(chǔ)結(jié)構(gòu)進(jìn)行擴(kuò)展:

public class MementoHealth : Memento
{
    public int health;
}

public class MemoableHealth : MemoableHealth
{
    private int health;
    public int Health { get{ return health; } }
    public override Memento CreateMemo()
    {
        var m = new MementoHealth()
        {
            go = gameObject,
            time = Time.realTimeSinceStartup,
            position = transform.position,
            rotation = transform.rotation,
            scale = transform.localScale,
            htealth = health
        };
    }
    
    public override void RestoreFromMemo(Memento m)
    {
        var mh = (MementoHealth)h;
        if(mh==null||m.go != gameObject)
            return;
        transform.position = m.position;
        transform.rotation = m.roation;
        transform.localScale = m.localScale;
        health = m.health;
    }
}

Caretaker和實(shí)際應(yīng)用場(chǎng)景都不需要修改锥忿。然而正常情況下給一個(gè)備忘錄模式的功能都可以設(shè)計(jì)出類似的結(jié)構(gòu)考余,那么這個(gè)備忘錄模式比較好的地方在哪呢辅肾?

個(gè)人認(rèn)為:在于它不破壞封裝性上雏掠。上面可以記錄血量的例子當(dāng)中我們可以看出术健,這個(gè)health字段對(duì)外是只讀不可寫的變量络断。如果我們寫一個(gè)第三方類來創(chuàng)建和恢復(fù)這個(gè)類的數(shù)據(jù)的時(shí)候我們就不得不把health字段設(shè)置為即可讀又可寫的字段裁替,如此就破壞了封裝性了。

而如果將數(shù)據(jù)保存在本類當(dāng)中貌笨,那么就缺少了可擴(kuò)展性弱判,上面我們使用了列表的形式存儲(chǔ)這些數(shù)據(jù),然而有些情況下我們的需求可能只需要陰陽兩種狀態(tài)互相切換锥惋,那么使用列表的形式就有點(diǎn)不太好用了昌腰。

觀察者模式

觀察者模式开伏,我們這樣理解:看電影。幾十個(gè)人在看一塊屏幕剥哑,并對(duì)于屏幕上的顯示變化進(jìn)行反映硅则。

觀察者模式就是這種一對(duì)多行為的結(jié)構(gòu)。

C#語言中實(shí)現(xiàn)觀察者模式的一個(gè)東西名叫事件株婴。百度以下C#事件的使用方式我們可以看到以下的代碼:

public class MyClass
{
    public event EventHandler OnValueChanged;
    
    public int Value { get; private set; }
    
    public void Increment()
    {
        Value++;
        if(OnValueChanged!=null)
            OnValueChanged();
    }
    
    public void Decrement()
    {
        Value--;
        if(OnValueChanged!=null)
            OnValueChanged();
    }
}

public class MyTest : MonoBehaviour
{
    private void Start()
    {
        var mc = new MyClass();
        mc.OnValueChanged += OnValueChanged;
        mc.Increment();
        mc.Decrement();
        mc.Increment();
        mc.Increment();
        mc.Increment();
        mc.Decrement();
    }
    
    private void OnValueChanged(Object sender, EventArgs e)
    {
        Debug.Log("Value Changed: "+ ((MyClass)sender).Value);
    }
}

當(dāng)我們調(diào)用Increment或者Decrement的時(shí)候怎虫,我們通知了所有登記OnValueChanged事件的觀察者,我們Value值變化了困介。這時(shí)候所有觀察者注冊(cè)的事件處理函數(shù)會(huì)被調(diào)用大审。

包括UnityEvent也是屬于這種模式,具體的使用方式自行百度座哩,原理也是一樣的徒扶。

這樣的好處:

這樣可以讓代碼變主動(dòng)為被動(dòng)。也就是說根穷,我們?cè)谂袛嘁粋€(gè)值是否變化的時(shí)候姜骡,可能是這樣的:

public class MyTest
{
    private MyClass mc;
    private float oldVal;
    
    private void Start()
    {
        mc = new MyClass();
        oldVal = mc.Value;
    }
    
    private void Update()
    {
        if(mc.Value != oldValue)
        {
            Debug.Log("Value Changed: " + mc.Value);
            oldValue = mc.Value;
        }
    }
}

這樣我們?cè)诿恳粋€(gè)Update的函數(shù)當(dāng)中都在主動(dòng)的詢問mc.Value的值。這樣做不是不行,性能消耗也不會(huì)多到哪里去,但是代碼看起來就稍微丑了點(diǎn).

觀察者模式和中介模式有點(diǎn)類似,他們差別在哪里?觀察模式1對(duì)多通信, 中介模式是N與N之間隔了一個(gè)中介,它們通過中介通信.

狀態(tài)模式

狀態(tài)模式代表了一個(gè)類型對(duì)內(nèi)對(duì)外有可能處于多種狀態(tài),在這種狀態(tài)下每個(gè)行為可能產(chǎn)生的結(jié)果都是不同的,如果這些狀態(tài)多而復(fù)雜顯然使用if else會(huì)把自己弄暈的.

比如世良同學(xué)在做會(huì)說話產(chǎn)品的換裝界面,是這樣的:

需求是這樣的:

  1. 場(chǎng)景切換時(shí)有一系列動(dòng)作
  2. 點(diǎn)擊奇奇有動(dòng)作反饋
  3. 點(diǎn)擊小動(dòng)物有動(dòng)作反饋

換裝界面中總共有5套服裝,有些服裝中有一些小的功能變化,這樣就造成以上的每一個(gè)功能可能需要寫的代碼會(huì)很復(fù)雜.

最好的辦法就是5套服裝分開寫自己的邏輯代碼.

首先我們定義一個(gè)抽象的狀態(tài)基類:

public abstract class MonoCostume : MonoBehaviour
{
    public GameObject kiki;
    public GameObject animal;
    public GameObject Kiki { get { return kiki; } }
    public GameObject Animal { get { return animal;} }
    
    public abstract void Init();
    public abstract void OnTapKiki();
    public abstract void OnTapAnimal();
}

然后繼承這個(gè)基類:

public class HappyBirthdayCostume : MonoCostume
{
    public override void Init()
    {
        //播放生日音樂
        //蛋糕和桌子從一側(cè)劃入場(chǎng)景
    }
    
    public override void OnTapKiki()
    {
        //if(點(diǎn)擊)
        //    吃蛋糕
        //else if(正在播放吃蛋糕動(dòng)作)
        //    收起蛋糕
    }
    
    public override void OnTapAnimal()
    {
        //噴射生日煙花
    }
}

public class AngelCostume : MonoCostume
{
    public override void Init()
    {
        //播放小星星音樂
        //播放奇奇從天而降的動(dòng)作
    }
    
    public override void OnTapKiki()
    {
        //播放奇奇撲騰天使翅膀飛起來的動(dòng)作
    }
    
    public override void OnTapAnimal()
    {
        //播放奇奇打瞌睡動(dòng)作
    }
}

最終寫一個(gè)維持狀態(tài)的類

public class CostumeChanger : MonoBehaviour
{
    public MonoCostume CurrentCostume { get; private set; }
    public void SetState(MonoCostume costume)
    {
        if(CurrentCostume!=null)
            Destroy(CurrentCostume);
        CurrentCostume = costume;
        CurrentCostume.Init();
        
        //設(shè)置奇奇點(diǎn)擊監(jiān)聽,設(shè)置小動(dòng)物點(diǎn)擊監(jiān)聽
        //當(dāng)點(diǎn)擊奇奇調(diào)用CurrentCostume.OnTapKiki();
        //當(dāng)點(diǎn)擊小動(dòng)物調(diào)用CurrentCostume.OnTapAnimal();
    }
}

這樣可以將狀態(tài)的各種處理分在不同的類中執(zhí)行,避免巨大而又繁雜的單類存在.在不同的時(shí)候我們執(zhí)行CostumeChanger.SetState(yourState)即可更換狀態(tài).

策略模式

策略模式和狀態(tài)模式有點(diǎn)像,都是因?yàn)橐粋€(gè)行為在不同的情況下有不同的結(jié)果,所以我們要把不同的執(zhí)行代碼分成不同的類物理隔離開來減少維護(hù)的成本.策略模式和狀態(tài)模式不同的是狀態(tài)模式要在一個(gè)類中維護(hù)這個(gè)類的狀態(tài),隨時(shí)切換狀態(tài)已經(jīng)狀態(tài)的查看.而策略模式并不要這么做.

我們使用一個(gè)購物的例子好了. 有一個(gè)商城有多種付款形式,每種付款形式最終所付的錢都不同這時(shí)候我們需要分開不同的類來計(jì)算每種付款方式最終需要收多少錢:

public interface IPayMethod
{
    float GetFinalPrice(float originPrice);
    void Charge(float money);
}

public class CreditCardPayMethod : IPayMethod
{
    public CreditCardPayMethod(string cardNumber)
    {
        this.cardNumber = cardNumber;
    }
    
    private string cardNumber;
    
    public float GetFinalPrice(float originPrice)
    {
        return originPrice * 1.01f;
    }
    
    public void Charge(float money)
    {
        credCardSvc.Charge(this.cardNumber, money);
    }
}

public class AliPayPayMethod : IPayMethod
{
    public AliPayPayMethod(string userName, string password)
    {
        Login(userName, password);
    }
    
    public float GetFinalPrice(float originPrice)
    {
        var discount = AlipaySerice.GetDiscount("XX商城", originPrice);
        return originPrice - discount;
    }
    
    public void Charge(float money)
    {
        balance -= money;
    }
}

public class ExampleBankPayMethod : IPayMethod
{
    public float GetFinalPrice(float originPrice)
    {
        var time = DateTime.Now.Hour;
        if(time>=20 && time<=23)
            return originPrice * .88f;
        return originPrice;
    }
    
    public void Charge(float money)
    {
        balance -= money;
    }
}

最終在算錢的時(shí)候我們是這樣做的:

public class OnlineStore
{
    public void Checkout(IPayMethod payMethod, IEnumerable<IProduct> products)
    {
        var price = products.Sum(p=>p.Price);
        price = payMethod.GetFinalPrice(originPrice);
        payMethod.Charge(price);
    }
}

public class Program
{
    public void Main()
    {
        var store = new OnlineStore();
        
        var products = new List<IProduct>();
        products.Add(new Apple("紅富士", 1.5));//5
        products.Add(new PaperRoll("清風(fēng)a", 1));//12
        products.Add(new AABattery("南孚", 2));//6
        
        ccpm = new CreditCardPayMethod("6222xxxxxxxxxxxx");
        alipm = new CreditCardPayMethod("Jack Ma", "Alibaba");
        ebpm = new ExampleBankPayMethod("9999xxxxxxxxxxxx");
        
        store.Checkout(ccpm, products); // 23.23
        store.Checkout(alipm, products);// 22.5, 支付寶隨機(jī)減免了5毛錢
        store.Checkout(ebpm, products); // 20~24點(diǎn)之間為20.24, 其他時(shí)段為 23
    }
}

上面我們用策略模式將算錢和扣錢的部分分開來寫,因?yàn)槊糠N支付形式所優(yōu)惠的程度,還有支付的方法都不同.使用策略模式可以很好的將購買物品最終結(jié)算的時(shí)候的邏輯區(qū)分開來.

模版方法模式

模版方法在平常開發(fā)中我們一般會(huì)經(jīng)常用到屿良,比如一個(gè)行為它需要N步驟執(zhí)行圈澈,每個(gè)步驟都執(zhí)行一個(gè)方法,但是步驟的內(nèi)容可能會(huì)變化尘惧。但是總體流程是不變的康栈。

比如一個(gè)自動(dòng)的回合制游戲,在一個(gè)角色回合的時(shí)候他需要做以下幾件事:

  1. 回合開始
  2. 尋找目標(biāo)
  3. 移動(dòng)開始
  4. 執(zhí)行移動(dòng)
  5. 移動(dòng)結(jié)束
  6. 攻擊開始
  7. 執(zhí)行攻擊
  8. 攻擊結(jié)束

這個(gè)流程是完全不會(huì)變的喷橙。但是每個(gè)角色在每個(gè)流程之間的具體行為可能有點(diǎn)不同啥么。我們看代碼:

public class Character
{
    public void MakeAction()
    {
        RoundStart();//1
        target = FindTarget();//2
        if(target != null)
        {
            if(Distance(target)>attackRange)
            {
                bool doMove = BeforeMove();//3
                if(doMove)
                {
                    MoveToTarget();//4
                    AfterMove();//5
                }
            }
            
            if(Distance(target)<=attackRange)
            {
                bool doAttack = BeforeAttack();//6
                if(doAttack)
                {  
                    AttackTarget();//7
                    AfterAttack();//8
                }
            }
        }
    }
    
    protected virtual void Roundstart() {}
    protected virtual GameObject FindTarget() {}
    protected virtual bool BeforeMove() {}
    protected virtual void MoveToTarget() {}
    protected virtual void AfterMove() {}
    protected virtual bool BeforeAttack() {}
    protected virtual void AttackTarget() {}
    protected virtual void AfterAttack() {}
}

這時(shí)候我們有弓箭兵和戰(zhàn)士?jī)煞N職業(yè),弓箭兵在攻擊時(shí)發(fā)射箭矢贰逾,而戰(zhàn)士并不生成悬荣;另外戰(zhàn)士在攻擊之前有10%的概率會(huì)出發(fā)奮勇一擊技能,使之攻擊力臨時(shí)增加50

public class Archer : Character
{
    preotected override AttackTarget()
    {
        var arrow = Instantiate<GameObject>(arrowPf);
        var p = arrow.GetComponent<Projectile>();
        p.shooter = this;
        p.target = target;
    }
}

public class Soldier : Character
{
    private bool buffed;
    protected override BeforeAttack()
    {
        var r = Random.value <= .1f;
        if(r)
        {
            attackPoint +=50;
            buffed = true;
        }
    }
    
    protected override AfterAttack()
    {
        if(buffed)
        {
            attackPoint-=50;
            buffed = false;
        }
    }
}

我們可以看到不論是士兵還是弓箭手疙剑,雖然他們?cè)诠舴绞缴嫌行┎煌扔兀窃谡嬲龍?zhí)行回合的內(nèi)容上是完全相同的,這種模式非常樸實(shí)單核芽,只使用了一個(gè)類的繼承囚戚,一點(diǎn)都不炫技。這就是模版方法模式轧简。

訪問者模式

訪問者模式有點(diǎn)小復(fù)雜,訪問者模式其實(shí)一個(gè)數(shù)據(jù)列表中有多種元素,他們各自有點(diǎn)不同.我們要對(duì)于這個(gè)數(shù)據(jù)列表當(dāng)中所有的元素,然而對(duì)這些不同的元素要有不同的操作.
比如我們公司里面有很多種類型的員工

public abstract class Employee
{
    public string Name { get; set; }
    public string Gender { get; set; }
    public int Age { get; set; }
    
    public abstract void Work();
}

public class Programmer : Employee
{
    public void Work()
    {
        //寫代碼
    }
}

public class ArtDesigner : Employee
{
    public void Work()
    {
        //畫畫
    }
}

public class ProjectManager : Employee
{
    public void Work()
    {
        //保證項(xiàng)目的進(jìn)度和質(zhì)量
    }
}

然后我們公司有各種考評(píng)措施,有年度考核,也有組內(nèi)考核等等.

public interface IExam
{
    void Examine(Employee employee);
}

public class AnnualExam : IExam
{
    //不同類型的員工有不同的審核標(biāo)準(zhǔn)
    public void Examine(Employee e)
    {
        if(e is Programmer)
        {
            
        }
        else if(e is ArtDesigner)
        {
            
        }
        else if(e is ProjectManager)
        {
            
        }
    }
}

可以看出又是一大堆if,這會(huì)非常煩,但是如果員工的類型是可以確定的個(gè)數(shù)的話,我們可以這樣做, 這樣我們至少可以把每個(gè)員工的考核區(qū)分開來:

public interface IExam
{
    void Examine(Programmer p);
    void Examine(ArtDesigner a);
    void Examine(ProjectManager p);
}

public abstract class Employee
{
    //前面的一樣就不重復(fù)了
    public void AcceptExam(Exam e);
}

public class AnnualExam : IExam
{
    public void Examine(Programmer p)
    {
        //程序員專門的考評(píng)
    }
    
    public void Examine(ArtDesginer a)
    {
        //美術(shù)專門的考評(píng)
    }
    
    public void Examine(ProjectManager p)
    {
        //項(xiàng)目經(jīng)理專門的考評(píng)
    }
}

public class ProgrammerExam : IExam
{
    public void Examine(Programmer p)
    {
        //程序員專門的考評(píng)
    }
    
    public void Examine(ArtDesginer a)
    {
        //不管
    }
    
    public void Examine(ProjectManager p)
    {
        //不管
    }
}

最終的使用代碼是這樣的:

public class Program
{
    public void Main()
    {
        var com = new Company();
        com.Employees.Add(new Programmer("張三"));
        com.Employees.Add(new ArtDesigner("李四"));
        com.Employees.Add(new ProjectManager("王五"));
        
        var exam1 = new AnnualExam();
        var exam2 = new ProgrammerExam();
        
        foreach(var e in com.Employee)
        {
            e.Accept(exam1);
            e.Accept(exam2);
        }
    }
}

它的優(yōu)點(diǎn)是對(duì)于每個(gè)元素的操作是是提供了豐富的拓展性的,但是對(duì)于元素的種類是比較限制的.如果某一天又增加了一個(gè)新的員工類型,我們的每一個(gè)訪問者都要新增加一種方法來執(zhí)行操作.這樣對(duì)代碼的修改來說比較多.

最終總結(jié)

其實(shí)看遍了我們這些設(shè)計(jì)模式中有很多是關(guān)于如何通過一些形式將單個(gè)類的代碼復(fù)雜性降低,或者多個(gè)類型之間的耦合性降低.設(shè)計(jì)模式最終的目標(biāo)是將代碼的可閱讀性和可維護(hù)性增高,當(dāng)然在增高的過程中難免會(huì)提高代碼結(jié)構(gòu)的復(fù)雜度.

這些模式都有他們的經(jīng)典用法,我們?cè)诰幋a的過程中不一定要完全遵循這些模式的規(guī)范來做,我們只要做到上面所說的目標(biāo),是否使用了傳說中的經(jīng)典模式一點(diǎn)都不重要.

當(dāng)然學(xué)習(xí)了這些模式的使用方式能夠讓我們?cè)谖磥淼木幋a過程中多注重一下代碼的設(shè)計(jì),讓我們的代碼不但對(duì)自己友好,也要對(duì)其他人友好.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末驰坊,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子哮独,更是在濱河造成了極大的恐慌拳芙,老刑警劉巖察藐,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異舟扎,居然都是意外死亡分飞,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門睹限,熙熙樓的掌柜王于貴愁眉苦臉地迎上來譬猫,“玉大人,你說我怎么就攤上這事羡疗∪痉” “怎么了?”我有些...
    開封第一講書人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵叨恨,是天一觀的道長(zhǎng)柳刮。 經(jīng)常有香客問我,道長(zhǎng)痒钝,這世上最難降的妖魔是什么秉颗? 我笑而不...
    開封第一講書人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮送矩,結(jié)果婚禮上蚕甥,老公的妹妹穿的比我還像新娘。我一直安慰自己益愈,他們只是感情好梢灭,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開白布夷家。 她就那樣靜靜地躺著蒸其,像睡著了一般。 火紅的嫁衣襯著肌膚如雪库快。 梳的紋絲不亂的頭發(fā)上摸袁,一...
    開封第一講書人閱讀 51,692評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音义屏,去河邊找鬼靠汁。 笑死,一個(gè)胖子當(dāng)著我的面吹牛闽铐,可吹牛的內(nèi)容都是我干的蝶怔。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼兄墅,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼踢星!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起隙咸,我...
    開封第一講書人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤沐悦,失蹤者是張志新(化名)和其女友劉穎成洗,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體藏否,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡瓶殃,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了副签。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片遥椿。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖淆储,靈堂內(nèi)的尸體忽然破棺而出修壕,到底是詐尸還是另有隱情,我是刑警寧澤遏考,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布慈鸠,位于F島的核電站,受9級(jí)特大地震影響灌具,放射性物質(zhì)發(fā)生泄漏青团。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一咖楣、第九天 我趴在偏房一處隱蔽的房頂上張望督笆。 院中可真熱鬧,春花似錦诱贿、人聲如沸娃肿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽料扰。三九已至,卻和暖如春焙蹭,著一層夾襖步出監(jiān)牢的瞬間晒杈,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工孔厉, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留拯钻,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓撰豺,卻偏偏與公主長(zhǎng)得像粪般,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子污桦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

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