設(shè)計(jì)模式

Design Pattern:是一套被反復(fù)使用、多數(shù)人知曉的、經(jīng)過(guò)分類編目的粟焊、代碼設(shè)計(jì)經(jīng)驗(yàn)的總結(jié),使用設(shè)計(jì)模式是為了可重用代碼孙蒙、讓代碼更容易被他人理解并且保證代碼可靠性项棠。

一.為什么要學(xué)習(xí)設(shè)計(jì)模式:

  • 1.設(shè)計(jì)模式來(lái)源眾多專家的經(jīng)驗(yàn)和智慧,它們是從許多優(yōu)秀的軟件系統(tǒng)中總結(jié)出的成功的挎峦、能夠?qū)崿F(xiàn)可維護(hù)性沾乘、復(fù)用的設(shè)計(jì)方案,使用這些方案將可以讓我們避免做一些重復(fù)性的工作浑测。

  • 2.設(shè)計(jì)模式提供了一套通用的設(shè)計(jì)詞匯和一種通用的形式來(lái)方便開(kāi)發(fā)人員之間溝通和交流,使得設(shè)計(jì)方案更加通俗易懂歪玲。

  • 3.大部分設(shè)計(jì)模式都兼顧了系統(tǒng)的可重用性和可擴(kuò)展性迁央,這使得我們可以更好地重用一些已有的設(shè)計(jì)方案、功能模塊甚至一個(gè)完整的軟件系統(tǒng)滥崩,避免我們經(jīng)常做一些重復(fù)的設(shè)計(jì)岖圈、編寫(xiě)一些重復(fù)的代碼。

  • 4.合理使用設(shè)計(jì)模式并對(duì)設(shè)計(jì)模式的使用情況進(jìn)行文檔化钙皮,將有助于別人更快地理解系統(tǒng)蜂科。

  • 5.學(xué)習(xí)設(shè)計(jì)模式將有助于初學(xué)者更加深入地理解面向?qū)ο笏枷搿?/p>

二.六大原則

  • 1.開(kāi)閉原則:一個(gè)軟件實(shí)體應(yīng)當(dāng)對(duì)擴(kuò)展開(kāi)放,對(duì)修改關(guān)閉短条。即軟件實(shí)體應(yīng)盡量在不修改原有代碼的情況下進(jìn)行擴(kuò)展导匣。為了滿足開(kāi)閉原則,需要對(duì)系統(tǒng)進(jìn)行抽象化設(shè)計(jì)茸时,抽象化是開(kāi)閉原則的關(guān)鍵

  • 2.里氏替換原則:所有引用基類對(duì)象的地方能夠透明地使用其子類的對(duì)象

  • 3.依賴倒置原則:抽象不應(yīng)該依賴于具體類贡定,具體類應(yīng)當(dāng)依賴于抽象。換言之可都,要針對(duì)接口編程缓待,而不是針對(duì)實(shí)現(xiàn)編程蚓耽。

  • 4.單一職責(zé)原則:一個(gè)類只負(fù)責(zé)一個(gè)功能領(lǐng)域中的相應(yīng)職責(zé),或者可以定義為:就一個(gè)類而言旋炒,應(yīng)該只有一個(gè)引起它變化的原因步悠。是實(shí)現(xiàn)高內(nèi)聚、低耦合的指導(dǎo)方針瘫镇。

  • 5.迪米特法則(最少知道原則):一個(gè)軟件實(shí)體應(yīng)當(dāng)盡可能少地與其他實(shí)體發(fā)生相互作用鼎兽,在類的劃分上,應(yīng)當(dāng)盡量創(chuàng)建松耦合的類汇四,類之間的耦合度越低接奈,就越有利于復(fù)用。在類的結(jié)構(gòu)設(shè)計(jì)上通孽,每一個(gè)類都應(yīng)當(dāng)盡量降低其成員變量和成員函數(shù)的訪問(wèn)權(quán)限序宦。在類的設(shè)計(jì)上,只要有可能背苦,一個(gè)類型應(yīng)當(dāng)設(shè)計(jì)成不變類互捌。在對(duì)其他類的引用上,一個(gè)對(duì)象對(duì)其他對(duì)象的引用應(yīng)當(dāng)降到最低行剂。

  • 6.接口分離原則:使用多個(gè)專門的接口秕噪,而不使用單一的總接口,即客戶端不應(yīng)該依賴那些它不需要的接口厚宰。

三.介紹幾種常用的設(shè)計(jì)模式

1.簡(jiǎn)單工廠模式

專門定義一個(gè)類(工廠類)來(lái)負(fù)責(zé)創(chuàng)建其他類的實(shí)例腌巾。可以根據(jù)創(chuàng)建方法的參數(shù)來(lái)返回不同類的實(shí)例铲觉,被創(chuàng)建的實(shí)例通常都具有共同的父類澈蝙。

首先我們看看API提供的Bitmap工廠
public class BitmapFactory {
    private static final int DECODE_BUFFER_SIZE = 16 * 1024;

    public static Bitmap decodeFile(String pathName, Options opts) {
        validate(opts);
        Bitmap bm = null;
        InputStream stream = null;
        try {
            stream = new FileInputStream(pathName);
            bm = decodeStream(stream, null, opts);
        } catch (Exception e) {
            /*  do nothing.
                If the exception happened on open, bm will be null.
            */
            Log.e("BitmapFactory", "Unable to decode stream: " + e);
        } finally {
            if (stream != null) {
                try {
                    stream.close();
                } catch (IOException e) {
                    // do nothing here
                }
            }
        }
        return bm;
    }

    public static Bitmap decodeFile(String pathName) {
        return decodeFile(pathName, null);
    }

    @Nullable
    public static Bitmap decodeResourceStream(@Nullable Resources res, @Nullable TypedValue value,
            @Nullable InputStream is, @Nullable Rect pad, @Nullable Options opts) {
        validate(opts);
        if (opts == null) {
            opts = new Options();
        }

        if (opts.inDensity == 0 && value != null) {
            final int density = value.density;
            if (density == TypedValue.DENSITY_DEFAULT) {
                opts.inDensity = DisplayMetrics.DENSITY_DEFAULT;
            } else if (density != TypedValue.DENSITY_NONE) {
                opts.inDensity = density;
            }
        }
        
        if (opts.inTargetDensity == 0 && res != null) {
            opts.inTargetDensity = res.getDisplayMetrics().densityDpi;
        }
        
        return decodeStream(is, pad, opts);
    }

    public static Bitmap decodeResource(Resources res, int id, Options opts) {
        validate(opts);
        Bitmap bm = null;
        InputStream is = null; 
        
        try {
            final TypedValue value = new TypedValue();
            is = res.openRawResource(id, value);

            bm = decodeResourceStream(res, value, is, null, opts);
        } catch (Exception e) {
            /*  do nothing.
                If the exception happened on open, bm will be null.
                If it happened on close, bm is still valid.
            */
        } finally {
            try {
                if (is != null) is.close();
            } catch (IOException e) {
                // Ignore
            }
        }

        if (bm == null && opts != null && opts.inBitmap != null) {
            throw new IllegalArgumentException("Problem decoding into existing bitmap");
        }

        return bm;
    }


現(xiàn)在我們來(lái)創(chuàng)建一個(gè)簡(jiǎn)單工廠
1.定義一個(gè)父類
public interface Model {
    public void createModel();
}

2.定義兩個(gè)實(shí)例,實(shí)現(xiàn)父類方法
public class AndroidModel implements Model {    
    @Override
    public void createModel() {
        System.out.println("this is androidModel!");
    }
}
public class IosModel implements Model {
    @Override
    public void createModel() {
        System.out.println("this is iosModel!");
    }
}


3.創(chuàng)建簡(jiǎn)單工廠
public class ModelFactory {
    /**
     * 單個(gè)方法
     */
    public Model createModel(String type) {
        if ("ios".equals(type)) {
            return new IosModel();
        } else if ("android".equals(type)) {
            return new AndroidModel();
        } else {
            return null;
        }
    }
}


 /**
   * 多個(gè)方法
*/
public class ModelFactory {
    private Model createAndroidModel() {
         return new AndroidModel();
    }

    private Model createIosModel() {
         return new IosModel();
    }
}



 /**
     * 多個(gè)靜態(tài)方法
     */
public class ModelFactory {
    private static Model createStaticAndroidModel() {
        return new AndroidModel();
    }

    private static Model createStaticIosModel() {
        return new IosModel();
    }
}


2.工廠模式

簡(jiǎn)單工廠模式有一個(gè)問(wèn)題就是撵幽,類的創(chuàng)建依賴工廠類灯荧,也就是說(shuō),如果想要拓展程序盐杂,必須對(duì)工廠類進(jìn)行修改逗载,這違背了閉包原則,如何解決链烈?就用到工廠方法模式厉斟,創(chuàng)建一個(gè)工廠接口和創(chuàng)建多個(gè)工廠實(shí)現(xiàn)類,這樣一旦需要增加新的功能强衡,直接增加新的工廠類就可以了捏膨。

1.創(chuàng)建對(duì)應(yīng)的工廠

public interface ClientFactory {
   public Model produce();
}

public class IosClientFactory implements ClientFactory{
    @Override
    public Model produce() {
        return new IosModel();
    }
}
public class AndroidClientFactory implements ClientFactory{
    @Override
    public Model produce() {
        return new AndroidModel();
    }
}


2.調(diào)用工廠方法

private void factoryTeat(){
        ClientFactory factory=  
                       new AndroidClientFactory();
        Model model=  factory.produce();
        model.createModel();

    }



3.單例模式

在一個(gè)進(jìn)程中保證一個(gè)類僅有一個(gè)實(shí)例,并提供一個(gè)訪問(wèn)它的全局訪問(wèn)點(diǎn)。實(shí)現(xiàn)的方法為先判斷實(shí)例存在與否号涯,如果存在則直接返回目胡,如果不存在就創(chuàng)建了再返回,這就確保了一個(gè)類只有一個(gè)實(shí)例對(duì)象
一般單例我們分為懶漢式和餓漢式链快,懶漢式即為用到時(shí)再去給初始化單例誉己,餓漢式為程序啟動(dòng)時(shí)就初始化單例

  /*懶漢式*/
public class SingleTest {
    /* 持有私有靜態(tài)實(shí)例,防止被引用域蜗,此處賦值為null巨双,目的是實(shí)現(xiàn)延遲加載 */
   private static SingleTest  instance;
   private SingleTest(){ 
   }

   public static SingleTest getInstance(){
       if(instance==null){
           instance=new SingleTest();
       }
       return instance;
   }
}

  /*餓漢式*/
public class SingleTest {
    /* 持有私有靜態(tài)實(shí)例*/
   private static SingleTest  instance=new SingleTest();
   private SingleTest(){ 
   }

   public static SingleTest getInstance(){
       return instance;
   }
}

雖然這兩種單例模式都能滿足我們對(duì)單例的需求,但是都會(huì)有一些問(wèn)題
餓漢式:在我們不用這個(gè)單例時(shí)霉祸,也進(jìn)行了初始化筑累,占用不必要的內(nèi)存資源
懶漢式:當(dāng)多個(gè)線程同時(shí)調(diào)用時(shí),會(huì)出現(xiàn)初始化多次的情況
基于兩種情況丝蹭,進(jìn)行以下優(yōu)化

1.方法加鎖 
public static synchronized SingleTest getInstance2(){
        if(instance==null){
            instance=new SingleTest();
        }
        return instance;
  }

但是synchronized關(guān)鍵字鎖住的是這個(gè)對(duì)象慢宗,這樣的用法,在性能上會(huì)有所下降奔穿,因?yàn)槊看握{(diào)用getInstance()镜沽,都要對(duì)對(duì)象上鎖。

2.代碼加鎖
 public static  SingleTest getInstance3(){
        if(instance==null){
            synchronized (instance){
                if(instance==null){
                    instance=new SingleTest();
                }
            }
        }
        return instance;
    }

在Java指令中創(chuàng)建對(duì)象和賦值操作是分開(kāi)進(jìn)行的贱田,也就是說(shuō)instance = new Singleton();語(yǔ)句是分兩步執(zhí)行的缅茉。但是JVM并不保證這兩個(gè)操作的先后順序,也就是說(shuō)有可能JVM會(huì)為新的Singleton實(shí)例分配空間男摧,然后直接賦值給instance成員蔬墩,然后再去初始化這個(gè)Singleton實(shí)例


SingleTest instance=new SingleTest()

new SingleTest()              =>            instance

優(yōu)化方式

1.加volatile 字段
Volatile修飾的成員變量在每次被線程訪問(wèn)時(shí),都強(qiáng)迫從共享內(nèi)存中重讀該成員變量的值耗拓。而且拇颅,當(dāng)成員變量發(fā)生變化時(shí),強(qiáng)迫線程將變化值回寫(xiě)到共享內(nèi)存帆离。這樣在任何時(shí)刻,兩個(gè)不同的線程總是看到某個(gè)成員變量的同一個(gè)值结澄。

private volatile static SingleTest  instance;



2.靜態(tài)內(nèi)部類 :JVM內(nèi)部的機(jī)制能夠保證當(dāng)一個(gè)類被加載的時(shí)候哥谷,這個(gè)類的加載過(guò)程是線程互斥的

public class SingleTest {
    private static class SingleTestInstanceHolder{
        private static final SingleTest singleInstance=new SingleTest();
    }

    public static  SingleTest getInstance(){
        return SingleTestInstanceHolder.singleInstance;
    }
}

當(dāng)我們第一次調(diào)用getInstance的時(shí)候,JVM能夠幫我們保證instance只被創(chuàng)建一次麻献,并且會(huì)保證把賦值給instance的內(nèi)存初始化完畢

4.建造者模式

將一個(gè)復(fù)雜對(duì)象的構(gòu)建與它的表示分離们妥,使得同樣的構(gòu)建過(guò)程可以創(chuàng)建不同的表示,適用于初始化的對(duì)象比較復(fù)雜且參數(shù)較多的情況勉吻。

private void alertBuilder(Context context){
        AlertDialog.Builder builder = new AlertDialog.Builder(context)
                .setTitle("Title")
                .setCancelable(false)
                .setIcon(R.mipmap.ic_launcher)
                .setOnDismissListener(new DialogInterface.OnDismissListener() {
                    @Override
                    public void onDismiss(DialogInterface dialog) {

                    }
                }).setOnCancelListener(new DialogInterface.OnCancelListener() {
                    @Override
                    public void onCancel(DialogInterface dialog) {

                    }
                })
                .setMessage("Message");

        AlertDialog dialog = builder.create();
        dialog.show();
    }

 public class AlertDialog extends Dialog implements DialogInterface {

    public static class Builder {
        private final AlertController.AlertParams P;
        
        public Builder(Context context) {
            this(context, resolveDialogTheme(context, ResourceId.ID_NULL));
        }

        public Builder(Context context, int themeResId) {
            P = new AlertController.AlertParams(new ContextThemeWrapper(
                    context, resolveDialogTheme(context, themeResId)));
        }
        public Builder setTitle(CharSequence title) {
            P.mTitle = title;
            return this;
        }

        public Builder setMessage(CharSequence message) {
            P.mMessage = message;
            return this;
        }

        public AlertDialog create() {
           / Context has already been wrapped with the appropriate theme.
            final AlertDialog dialog = new AlertDialog(P.mContext, 0, false);
            P.apply(dialog.mAlert);
            dialog.setCancelable(P.mCancelable);
            if (P.mCancelable) {
                dialog.setCanceledOnTouchOutside(true);
            }
            dialog.setOnCancelListener(P.mOnCancelListener);
            dialog.setOnDismissListener(P.mOnDismissListener);
            if (P.mOnKeyListener != null) {
                dialog.setOnKeyListener(P.mOnKeyListener);
            }
        }
    }
}

舉一個(gè)甜筒的列子package com.yuyin.live.room.entity:
房間公屏聊天內(nèi)容構(gòu)成
以前的寫(xiě)法

public class HnRoomPublicChatBean {
    private int itemType;
    private String userId;
    private String userName;
    private String userAvater;
    public HnRoomPublicChatBean(int itemType, String userId) {
        this.itemType = itemType;
        this.userId = userId;

    }
    public HnRoomPublicChatBean(int itemType, String userId, String userName) {
        this.itemType = itemType;
        this.userId = userId;
        this.userName = userName;
       
    }
    public HnRoomPublicChatBean(int itemType, String userId, String userName, String userAvater) {
        this.itemType = itemType;
        this.userId = userId;
        this.userName = userName;
        this.userAvater = userAvater;
    }
}

優(yōu)化之后

public class HnRoomPublicChatBean {
    /*
基礎(chǔ)參數(shù)
*/
    private int itemType;
    private String userId;
    private String userName;
    private String userAvater;
    ...
 
/**
構(gòu)造函數(shù)
*/
    public HnRoomPublicChatBean(Builder builder){
        this.itemType = builder.itemType;
        this.userId = builder.userId;
        this.userName = builder.userName;
        this.userAvater = builder.userAvater;
        ...
    }
  /**
建造者
*/
    public static class Builder{
        private int itemType;
        private String userId;
        private String userName;
        private String userAvater;
        ...

     /**
建造
*/
       public HnRoomPublicChatBean build() {
            return new HnRoomPublicChatBean(this);
        }

        public Builder setUserId(String userId) {
            this.userId = userId;
            return this;
        }

        public Builder setUserNumber(String userNumber) {
            this.userNumber = userNumber;
            return this;
        }

        public Builder setUserName(String userName) {
            this.userName = userName;
            return this;
        }
        ...
    }
}

//#############使用
new HnRoomPublicChatBean.Builder()                                     
.setItemType(HnRoomPublicChatBean.MSG_EGG_HAMMER)
.setUserId("用戶ID")            
.setUserName("用戶昵稱")
.setUserAvater("用戶頭像")
...                                          
.build()


5.觀察者模式

當(dāng)一個(gè)對(duì)象變化時(shí)监婶,其它依賴該對(duì)象的對(duì)象都會(huì)收到通知,并且隨著變化!
首先我們看看經(jīng)常用到的觀察者RecyclerViewDataObserver

MyAdapter adapter = new MyAdapter();
RecyclerView recyclerView=new RecyclerView(this)
recyclerView.setAdapter(adapter);
adapter.notifyDataSetChanged();

我們先看看Adapter做了什么

public abstract static class Adapter<VH extends ViewHolder> {
    private final AdapterDataObservable mObservable = new AdapterDataObservable();
    
    public void onBindViewHolder(@NonNull VH holder, int position,
                                 @NonNull List<Object> payloads) {
        onBindViewHolder(holder, position);
    }

    @NonNull
    public final VH createViewHolder(@NonNull ViewGroup parent, int viewType) {
        try {
            TraceCompat.beginSection(TRACE_CREATE_VIEW_TAG);
            final VH holder = onCreateViewHolder(parent, viewType);
            if (holder.itemView.getParent() != null) {
                throw new IllegalStateException("ViewHolder views must not be attached when"
                        + " created. Ensure that you are not passing 'true' to the attachToRoot"
                        + " parameter of LayoutInflater.inflate(..., boolean attachToRoot)");
            }
            holder.mItemViewType = viewType;
            return holder;
        } finally {
            TraceCompat.endSection();
        }
    }

    public final void bindViewHolder(@NonNull VH holder, int position) {
        holder.mPosition = position;
        if (hasStableIds()) {
            holder.mItemId = getItemId(position);
        }
        holder.setFlags(ViewHolder.FLAG_BOUND,
                ViewHolder.FLAG_BOUND | ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID
                        | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
        TraceCompat.beginSection(TRACE_BIND_VIEW_TAG);
        onBindViewHolder(holder, position, holder.getUnmodifiedPayloads());
        holder.clearPayload();
        final ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
        if (layoutParams instanceof RecyclerView.LayoutParams) {
            ((LayoutParams) layoutParams).mInsetsDirty = true;
        }
        TraceCompat.endSection();
    }
    
  
    public final boolean hasObservers() {
        return mObservable.hasObservers();
    }

  
    public void registerAdapterDataObserver(@NonNull AdapterDataObserver observer) {
        mObservable.registerObserver(observer);
    }

   
    public void unregisterAdapterDataObserver(@NonNull AdapterDataObserver observer) {
        mObservable.unregisterObserver(observer);
    }

    
    public final void notifyDataSetChanged() {
        mObservable.notifyChanged();
    }

 
    public final void notifyItemChanged(int position) {
        mObservable.notifyItemRangeChanged(position, 1);
    }

   
    public final void notifyItemRangeChanged(int positionStart, int itemCount) {
        mObservable.notifyItemRangeChanged(positionStart, itemCount);
    }
}

然后我們看看RecyclerView以及setAdapter做了什么

···
private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();

 public void setAdapter(@Nullable Adapter adapter) {
        // bail out if layout is frozen
        setLayoutFrozen(false);
        setAdapterInternal(adapter, false, true);
        processDataSetCompletelyChanged(false);
        requestLayout();
    }

private void setAdapterInternal(@Nullable Adapter adapter, boolean compatibleWithPrevious,
            boolean removeAndRecycleViews) {
        if (mAdapter != null) {
            mAdapter.unregisterAdapterDataObserver(mObserver);
            mAdapter.onDetachedFromRecyclerView(this);
        }

       ...

        mAdapter = adapter;
        if (adapter != null) {
            adapter.registerAdapterDataObserver(mObserver);
            adapter.onAttachedToRecyclerView(this);
        }

        ...

    }


static class AdapterDataObservable extends Observable<AdapterDataObserver> {
    ···
    public void notifyChanged() {
        for (int i = mObservers.size() - 1; i >= 0; i--) {
            mObservers.get(i).onChanged();
        }
    }
    ···
}


private class RecyclerViewDataObserver extends AdapterDataObserver {
    ···
    @Override
    public void onChanged() {
        assertNotInLayoutOrScroll(null);
        mState.mStructureChanged = true;

        processDataSetCompletelyChanged(true);
        if (!mAdapterHelper.hasPendingUpdates()) {
            requestLayout();
        }
    }
    ···
}

那如果我們自己寫(xiě)一個(gè)觀察者應(yīng)該怎么做呢

創(chuàng)建一個(gè)觀察者(接口或者抽象類)
public interface Observer {
    void  update();
}
實(shí)現(xiàn)觀察者接口惑惶,處理數(shù)據(jù)變化之后的操作
public class ObserverOne implements Observer {

    @Override
    public void update() {
        Log.e("observer1","update");
    }
}
public class ObserverTwo implements Observer {

    @Override
    public void update() {
        Log.e("observer2","update");
    }
}
定義一個(gè)接口煮盼,用于注冊(cè)和解綁觀察者
public interface Users {
    void addObserver(Observer observer);
    void removeObserver(Observer observer);
    void notifyDataSetChanged();
    void updateData();
}

實(shí)現(xiàn)類
public  class UsersObj implements Users {
    private Vector<Observer> vector=new Vector();

    @Override
    public void addObserver(Observer observer) {
        vector.add(observer);
    }

    @Override
    public void removeObserver(Observer observer) {
        vector.remove(observer);
    }

    @Override
    public void notifyDataSetChanged() {
        Enumeration<Observer> elements = vector.elements();
        while (elements.hasMoreElements()){
            elements.nextElement().update();
        }
    }

    @Override
    public void updateData() {
        Log.e("updateData","user");
        notifyDataSetChanged();

    }

    private void test(){
        Users users=new ChinaUsers();
        users.addObserver(new ObserverOne());
        users.addObserver(new ObserverTwo());
        users.notifyDataSetChanged();
    }
}
?著作權(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)離奇詭異,居然都是意外死亡千绪,警方通過(guò)查閱死者的電腦和手機(jī)充易,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)荸型,“玉大人盹靴,你說(shuō)我怎么就攤上這事》保” “怎么了鹉究?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)踪宠。 經(jīng)常有香客問(wèn)我自赔,道長(zhǎng),這世上最難降的妖魔是什么柳琢? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任绍妨,我火速辦了婚禮,結(jié)果婚禮上柬脸,老公的妹妹穿的比我還像新娘他去。我一直安慰自己,他們只是感情好倒堕,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布灾测。 她就那樣靜靜地躺著,像睡著了一般垦巴。 火紅的嫁衣襯著肌膚如雪媳搪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,007評(píng)論 1 284
  • 那天骤宣,我揣著相機(jī)與錄音秦爆,去河邊找鬼。 笑死憔披,一個(gè)胖子當(dāng)著我的面吹牛等限,可吹牛的內(nèi)容都是我干的爸吮。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼望门,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼形娇!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起怒允,我...
    開(kāi)封第一講書(shū)人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤埂软,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后纫事,有當(dāng)?shù)厝嗽跇?shù)林里發(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
  • 文/蒙蒙 一诅福、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧拖叙,春花似錦氓润、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至挖滤,卻和暖如春崩溪,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背壶辜。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工悯舟, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留担租,地道東北人砸民。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親岭参。 傳聞我的和親對(duì)象是個(gè)殘疾皇子反惕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

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