設(shè)計(jì)模式 | 用實(shí)際案例詳解工廠模式

我們來看一個具體需求
看一個披薩的項(xiàng)目仅父,要便于披薩種類的擴(kuò)展,要便于維護(hù)榛斯,有以下要求:

  • 披薩的種類很多(比如GreekPizz磺箕、CheesePizz等)
  • 披薩的制作有prepare,bake扮宠,cut西乖,box
  • 完成披薩店訂購功能

首先我們先編寫一個Pizza抽象類:

package com.cxc.factory.simplefactory.pizzastore.pizza;

/**
 * 抽象Pizza類
 */
public abstract class Pizza {
    //Pizza名字
    protected String name;

    //準(zhǔn)備原材料,不同的披薩不一樣,因此我們做成抽象方法
    public abstract void preable();
    //切
    public void cut(){
        System.out.println("cut");
    }
    //烘烤
    public void bake(){
        System.out.println("bake");
    }
    //打包
    public void box(){
        System.out.println("box");
    }

    public void setName(String name) {
        this.name = name;
    }
}

然后編寫不同的種類的披薩類去繼承這個抽象類浴栽,并實(shí)現(xiàn)抽象方法:

package com.cxc.factory.simplefactory.pizzastore.pizza;

public class CheesePizza extends Pizza {
    @Override
    public void preable() {
        System.out.println("給制作奶酪披薩準(zhǔn)備原材料");
    }
}
package com.cxc.factory.simplefactory.pizzastore.pizza;

public class GreekPizza extends Pizza {
    @Override
    public void preable() {
        System.out.println("給希臘披薩準(zhǔn)備原材料");
    }
}

接下來是完成披薩訂單類荒叼,在這里面根據(jù)輸入的披薩種類的不同來新建不同的披薩對象:

package com.cxc.factory.simplefactory.pizzastore.order;

import com.cxc.factory.simplefactory.pizzastore.pizza.CheesePizza;
import com.cxc.factory.simplefactory.pizzastore.pizza.GreekPizza;
import com.cxc.factory.simplefactory.pizzastore.pizza.Pizza;

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class OrderPizza {

    public OrderPizza() {
        Pizza pizza = null;
        String orderType;   //訂購披薩的類型
        do{
            orderType = getType();
            if (orderType.equals("greek")){
                pizza = new GreekPizza();
            }else if (orderType.equals("cheese")){
                pizza = new CheesePizza();
            }else {
                break;
            }
            pizza.preable();
            pizza.bake();
            pizza.cut();
            pizza.box();
        }while (true);
    }

    private String getType(){
        try {
            BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("input pizza type:");
            String str = strin.readLine();
            return str;
        }catch (Exception e){ }

        return "";
    }
}

披薩店訂購:

package com.cxc.factory.simplefactory.pizzastore.order;

/**
 * 相當(dāng)于一個客戶端,發(fā)出訂購
 */
public class PizzaStore {

    public static void main(String[] args) {
        new OrderPizza();
    }
}

上面實(shí)現(xiàn)的這個案例中存在問題典鸡,因?yàn)槲覀冃陆ㄅ_的代碼編寫在披薩訂單類里面被廓,所以如果我們需要擴(kuò)展一個新的披薩種類的時候,除了需要新建擴(kuò)展類之外萝玷,還需要在此處改代碼嫁乘,違反了開閉原則。在這個案例中需要用到工廠模式來更改球碉。

一蜓斧、簡單工廠模式

1、基本介紹

  • 簡單工廠模式是屬于創(chuàng)建型模式睁冬,是工廠模式的一種挎春。簡單工廠模式是由一個工廠對象決定創(chuàng)建出哪一種產(chǎn)品類的實(shí)例。簡單工廠模式是工廠模式家族中最簡單實(shí)用的模式豆拨。
  • 簡單工廠模式:定義了一個創(chuàng)建對象的類直奋,由這個類來封裝實(shí)例化對象的行為
  • 在軟件開發(fā)中施禾,當(dāng)我們會用到大量的創(chuàng)建某種脚线、某類或某批對象時,就會使用到工廠模式弥搞。

那對于上面的案例我們?nèi)绾问褂煤唵喂S方法來更改呢邮绿?
首先我們需要將披薩訂單類OrderPizza中生產(chǎn)披薩的代碼抽取出來,形成一個簡單工廠類:

package com.cxc.factory.simplefactory.pizzastore.order;

import com.cxc.factory.simplefactory.pizzastore.pizza.CheesePizza;
import com.cxc.factory.simplefactory.pizzastore.pizza.GreekPizza;
import com.cxc.factory.simplefactory.pizzastore.pizza.Pizza;

/**
 * 簡單工廠模式
 */
public class SimpleFactory {

    /**
     * 根據(jù)orderType返回對應(yīng)的Pizza對象
     * @param orderType
     * @return
     */
    public Pizza createPizza(String orderType){
        Pizza pizza = null;

        System.out.println("簡單工廠模式");
        if (orderType.equals("greek")){
            pizza = new GreekPizza();
        }else if (orderType.equals("cheese")){
            pizza = new CheesePizza();
        }

        return pizza;
    }
}

披薩訂單類OrderPizza1可以更改為如下:

package com.cxc.factory.simplefactory.pizzastore.order;

import com.cxc.factory.simplefactory.pizzastore.pizza.CheesePizza;
import com.cxc.factory.simplefactory.pizzastore.pizza.GreekPizza;
import com.cxc.factory.simplefactory.pizzastore.pizza.Pizza;

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class OrderPizza1 {

    //定義一個簡單工廠對象
    private SimpleFactory simpleFactory;

    Pizza pizza = null;

    public OrderPizza1(SimpleFactory simpleFactory) {
        setFactory(simpleFactory);
    }

    public void setFactory(SimpleFactory simpleFactory){
        String orderType = "";  //用戶輸入的
        this.simpleFactory = simpleFactory; //設(shè)置簡單工廠對象

        do {
            orderType = getType();
            pizza = this.simpleFactory.createPizza(orderType);

            if (pizza != null){
                //訂購成功
                pizza.preable();
                pizza.bake();
                pizza.cut();
                pizza.box();
            }else{
                System.out.println("訂購披薩失敗");
                break;
            }
        }while (true);
    }

    private String getType(){
        try {
            BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("input pizza type:");
            String str = strin.readLine();
            return str;
        }catch (Exception e){ }

        return "";
    }
}

訂購客戶端:

package com.cxc.factory.simplefactory.pizzastore.order;

/**
 * 相當(dāng)于一個客戶端攀例,發(fā)出訂購
 */
public class PizzaStore {

    public static void main(String[] args) {
        //使用簡單工廠模式
        new OrderPizza1(new SimpleFactory());
    }
}

這樣改動后船逮,如果我們需要擴(kuò)展新的披薩種類,只需要在簡單工廠類中更改代碼肛度,其它地方不需要進(jìn)行更改傻唾。

接下來對于這個案例,還有一個新需求如下:
客戶在點(diǎn)披薩時承耿,可以點(diǎn)不同口味的披薩,比如北京的奶酪Pizza伪煤、北京的胡椒Pizza或者是倫敦是奶酪Pizza加袋、倫敦的胡椒Pizza。

這個時候有兩種思路:

  • 思路1:使用簡單工廠模式抱既,創(chuàng)建不同的簡單工廠類职烧,比如BJPizzaSimpleFactory、LDPizzaSImpleFactory等,但是考慮到項(xiàng)目的規(guī)模蚀之,以及軟件的可維護(hù)性蝗敢、可擴(kuò)展性這個思路并不是很好。
  • 思路2:使用工廠方法模式足删。

二寿谴、工廠方法模式

1、基本介紹

工廠方法模式:定義了一個創(chuàng)建對象的抽象方法失受,由子類決定要實(shí)例化的類讶泰。工廠方法模式將對象的實(shí)例化推遲到子類

對于上述案例的新需求拂到,我們使用工廠方法模式更改痪署,OrderPizza是一個抽象的工廠類,其中的createPizza()方法為抽象方法兄旬,然后定義了不同地區(qū)口味的披薩工廠類:北京口味披薩工廠類(BJOrderPizza)狼犯,倫敦口味披薩工廠類(LDOrderPizze),這兩個披薩工廠類分別去生產(chǎn)各個地區(qū)的披薩领铐,類圖如下所示:



Pizza類:

package com.cxc.factory.factorymethod.pizzastore.pizza;

/**
 * 抽象Pizza類
 */
public abstract class Pizza {
    //Pizza名字
    protected String name;

    //準(zhǔn)備原材料辜王,不同的披薩不一樣,因此我們做成抽象方法
    public abstract void preable();
    //切
    public void cut(){
        System.out.println("cut");
    }
    //烘烤
    public void bake(){
        System.out.println("bake");
    }
    //打包
    public void box(){
        System.out.println("box");
    }

    public void setName(String name) {
        this.name = name;
    }
}

BJCheesePizza類:

package com.cxc.factory.factorymethod.pizzastore.pizza;

public class BJCheesePizza extends Pizza {
    @Override
    public void preable() {
        System.out.println(" 北京的奶酪pizza準(zhǔn)備原材料 ");
    }
}

BJPepperPizza類:

package com.cxc.factory.factorymethod.pizzastore.pizza;

public class BJPepperPizza extends Pizza {
    @Override
    public void preable() {
        System.out.println(" 北京的胡椒pizza準(zhǔn)備原材料");
    }
}

LDCheesePizza類:

package com.cxc.factory.factorymethod.pizzastore.pizza;

public class LDCheesePizza extends Pizza {
    @Override
    public void preable() {
        System.out.println(" 倫敦的奶酪pizza準(zhǔn)備原材料");
    }
}

LDPepperPizza類:

package com.cxc.factory.factorymethod.pizzastore.pizza;

public class LDPepperPizza extends Pizza {
    @Override
    public void preable() {
        System.out.println(" 倫敦的胡椒pizza準(zhǔn)備原材料");
    }
}

抽象的工廠類OrderPizza:

package com.cxc.factory.factorymethod.pizzastore.order;

import com.cxc.factory.factorymethod.pizzastore.pizza.Pizza;

import java.io.BufferedReader;
import java.io.InputStreamReader;

public abstract class OrderPizza {

    /**
     * 定義一個抽象的方法
     * @param orderType
     * @return
     */
    public abstract Pizza createPizze(String orderType);

    public OrderPizza() {
        Pizza pizza = null;
        String orderType;   //訂購披薩的類型
        do{
            orderType = getType();
            //抽象的方法罐孝,由工廠子類完成
            pizza = createPizze(orderType);
            pizza.preable();
            pizza.bake();
            pizza.cut();
            pizza.box();
        }while (true);
    }

    private String getType(){
        try {
            BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("input pizza type:");
            String str = strin.readLine();
            return str;
        }catch (Exception e){ }

        return "";
    }
}

兩個工廠子類分別去實(shí)現(xiàn)OrderPizza類:

package com.cxc.factory.factorymethod.pizzastore.order;

import com.cxc.factory.factorymethod.pizzastore.pizza.*;

public class LDOrderPizza extends OrderPizza {
    @Override
    public Pizza createPizze(String orderType) {
        Pizza pizza = null;
        if (orderType.equals("cheese")){
            pizza = new LDCheesePizza();
        }else if (orderType.equals("pepper")){
            pizza = new LDPepperPizza();
        }
        return pizza;

    }
}
package com.cxc.factory.factorymethod.pizzastore.order;

import com.cxc.factory.factorymethod.pizzastore.pizza.BJCheesePizza;
import com.cxc.factory.factorymethod.pizzastore.pizza.BJPepperPizza;
import com.cxc.factory.factorymethod.pizzastore.pizza.Pizza;

public class BJOrderPizza extends OrderPizza {

    @Override
    public Pizza createPizze(String orderType) {
        Pizza pizza = null;
        if (orderType.equals("cheese")){
            pizza = new BJCheesePizza();
        }else if (orderType.equals("pepper")){
            pizza = new BJPepperPizza();
        }
        return pizza;
    }
}

披薩訂購:

package com.cxc.factory.factorymethod.pizzastore.order;

public class PizzaStore {
    public static void main(String[] args) {

        //在此示例創(chuàng)建北京口味的各種披薩
        new BJOrderPizza();
    }
}

三呐馆、抽象工廠模式

1、基本介紹

  • 抽象工廠模式定義了一個接口(interface)用于創(chuàng)建相關(guān)或有依賴關(guān)系的對象簇莲兢,而無需指明具體的類
  • 抽象工廠模式可以將簡單工廠模式工廠方法模式進(jìn)行整合
  • 從設(shè)計(jì)層面看汹来,抽象工廠模式就是對簡單工廠模式的改進(jìn)(或者稱為進(jìn)一步的抽象)
  • 將工廠抽象成兩層,抽象工廠和具體實(shí)現(xiàn)的工廠子類改艇。程序員可以根據(jù)創(chuàng)建對象類型使用對應(yīng)的工廠子類收班。這樣將單個的簡單工廠類變成了工廠簇,更利于代碼的維護(hù)和擴(kuò)展

上面的披薩案例使用抽象工廠模式后類圖如下所示:


接下來我們再來將其實(shí)現(xiàn)一下谒兄,抽象的工廠接口如下:

package com.cxc.factory.absfactory.pizzastore.order;
import com.cxc.factory.absfactory.pizzastore.pizza.Pizza;
/**
 * 抽象工廠模式的抽象層
 */
public interface AbsFactory {

    /**
     * 由下面的具體子類實(shí)現(xiàn)
     * @param orderType
     * @return
     */
    public Pizza createPizza(String orderType);
}

兩個具體的工廠子類:

package com.cxc.factory.absfactory.pizzastore.order;

import com.cxc.factory.absfactory.pizzastore.pizza.BJCheesePizza;
import com.cxc.factory.absfactory.pizzastore.pizza.BJPepperPizza;
import com.cxc.factory.absfactory.pizzastore.pizza.Pizza;

/**
 * 具體的工廠子類
 */
public class BJFactory implements AbsFactory {
    @Override
    public Pizza createPizza(String orderType) {
        System.out.println("使用的是抽象工廠模式");
        Pizza pizza = null;
        if (orderType.equals("cheese")){
            pizza = new BJCheesePizza();
        }else if (orderType.equals("pepper")){
            pizza = new BJPepperPizza();
        }

        return pizza;
    }
}
package com.cxc.factory.absfactory.pizzastore.order;

import com.cxc.factory.absfactory.pizzastore.pizza.*;

public class LDFactory implements AbsFactory {
    @Override
    public Pizza createPizza(String orderType) {
        System.out.println("使用的是抽象工廠模式");
        Pizza pizza = null;
        if (orderType.equals("cheese")){
            pizza = new LDCheesePizza();
        }else if (orderType.equals("pepper")){
            pizza = new LDPepperPizza();
        }

        return pizza;
    }
}

披薩訂單類:

package com.cxc.factory.absfactory.pizzastore.order;

import com.cxc.factory.absfactory.pizzastore.pizza.Pizza;

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class OrderPizza {

    AbsFactory absFactory;

    public OrderPizza(AbsFactory absFactory){
        this.setAbsFactory(absFactory);
    }

    private void setAbsFactory(AbsFactory factory){
        Pizza pizza = null;
        String orderType = "";//用戶輸入
        this.absFactory = factory;

        do{
            orderType = getType();
            pizza = factory.createPizza(orderType);
            if (pizza != null){
                pizza.preable();
                pizza.bake();
                pizza.cut();
                pizza.box();
            }else{
                System.out.println("訂購失敗");
                break;
            }
        }while (true);
    }

    private String getType(){
        try {
            BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
            System.out.println("input pizza type:");
            String str = strin.readLine();
            return str;
        }catch (Exception e){ }

        return "";
    }
}

披薩商店類:

package com.cxc.factory.absfactory.pizzastore.order;

public class PizzaStore {

    public static void main(String[] args) {
        new OrderPizza(new BJFactory());  //這里演示北京的工廠子類
    }
}

四摔桦、JDK源碼分析之工廠模式

Calendar是一個簡單工廠模式。
例如下面這個創(chuàng)建Calendar實(shí)例的方法:

Calendar calendar = Calendar.getInstance();

getInstance方法的實(shí)現(xiàn):

    /**
     * Gets a calendar using the default time zone and locale. The
     * <code>Calendar</code> returned is based on the current time
     * in the default time zone with the default
     * {@link Locale.Category#FORMAT FORMAT} locale.
     *
     * @return a Calendar.
     */
    public static Calendar getInstance()
    {
        return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT));
    }

這里返回的createCalendar方法點(diǎn)進(jìn)去:

    private static Calendar createCalendar(TimeZone zone,
                                           Locale aLocale)
    {
        CalendarProvider provider =
            LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale)
                                 .getCalendarProvider();
        if (provider != null) {
            try {
                return provider.getInstance(zone, aLocale);
            } catch (IllegalArgumentException iae) {
                // fall back to the default instantiation
            }
        }

        Calendar cal = null;

        if (aLocale.hasExtensions()) {
            String caltype = aLocale.getUnicodeLocaleType("ca");
            if (caltype != null) {
                switch (caltype) {
                case "buddhist":
                cal = new BuddhistCalendar(zone, aLocale);
                    break;
                case "japanese":
                    cal = new JapaneseImperialCalendar(zone, aLocale);
                    break;
                case "gregory":
                    cal = new GregorianCalendar(zone, aLocale);
                    break;
                }
            }
        }
        if (cal == null) {
            // If no known calendar type is explicitly specified,
            // perform the traditional way to create a Calendar:
            // create a BuddhistCalendar for th_TH locale,
            // a JapaneseImperialCalendar for ja_JP_JP locale, or
            // a GregorianCalendar for any other locales.
            // NOTE: The language, country and variant strings are interned.
            if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") {
                cal = new BuddhistCalendar(zone, aLocale);
            } else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja"
                       && aLocale.getCountry() == "JP") {
                cal = new JapaneseImperialCalendar(zone, aLocale);
            } else {
                cal = new GregorianCalendar(zone, aLocale);
            }
        }
        return cal;
    }

可以看到在創(chuàng)建實(shí)例的時候是根據(jù)你傳入的參數(shù)不同而來創(chuàng)建不同的實(shí)例的承疲,這是一個簡單工廠模式邻耕。

五、工廠模式小結(jié)

1燕鸽、工廠模式的意義

將實(shí)例化對象的代碼抽象出來兄世,放到一個類中統(tǒng)一管理和維護(hù),達(dá)到和主項(xiàng)目的依賴關(guān)系的解耦啊研,從而提高項(xiàng)目的擴(kuò)展和維護(hù)性御滩。

2鸥拧、三種工廠模式

  • 簡單工廠模式
  • 工廠方法模式
  • 抽象工廠模式

3、設(shè)計(jì)模式的依賴抽象原則

  • 創(chuàng)建對象實(shí)例時削解,不要直接new類富弦,而是把這個new類的動作放在一個工廠的方法中,并返回氛驮。也可說為:變量不要直接持有具體類的引用腕柜。
  • 不要讓類繼承具體類,而是繼承抽象類或者實(shí)現(xiàn)interface(接口)柳爽。
  • 不要覆蓋基類中已經(jīng)實(shí)現(xiàn)的方法媳握。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市磷脯,隨后出現(xiàn)的幾起案子蛾找,更是在濱河造成了極大的恐慌,老刑警劉巖赵誓,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件打毛,死亡現(xiàn)場離奇詭異,居然都是意外死亡俩功,警方通過查閱死者的電腦和手機(jī)幻枉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來诡蜓,“玉大人熬甫,你說我怎么就攤上這事÷#” “怎么了椿肩?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長豺谈。 經(jīng)常有香客問我郑象,道長,這世上最難降的妖魔是什么茬末? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任厂榛,我火速辦了婚禮,結(jié)果婚禮上丽惭,老公的妹妹穿的比我還像新娘击奶。我一直安慰自己,他們只是感情好吐根,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布正歼。 她就那樣靜靜地躺著,像睡著了一般拷橘。 火紅的嫁衣襯著肌膚如雪局义。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天冗疮,我揣著相機(jī)與錄音萄唇,去河邊找鬼。 笑死术幔,一個胖子當(dāng)著我的面吹牛另萤,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播诅挑,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼四敞,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了拔妥?” 一聲冷哼從身側(cè)響起忿危,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎没龙,沒想到半個月后铺厨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡硬纤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年解滓,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片筝家。...
    茶點(diǎn)故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡洼裤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出溪王,到底是詐尸還是另有隱情腮鞍,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布在扰,位于F島的核電站缕减,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏芒珠。R本人自食惡果不足惜桥狡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望皱卓。 院中可真熱鬧裹芝,春花似錦、人聲如沸娜汁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽掐禁。三九已至怜械,卻和暖如春颅和,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背缕允。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工峡扩, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人障本。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓教届,卻偏偏與公主長得像,于是被迫代替她去往敵國和親驾霜。 傳聞我的和親對象是個殘疾皇子案训,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評論 2 354