第六章 Caché 設(shè)計模式 原型模式

第六章 Caché 設(shè)計模式 原型模式

定義

  • 用原型實例指定創(chuàng)建對象的種類订歪,并且通過拷貝這些原型創(chuàng)建新的對象串结。
  • 原型模式其實就是從一個對象再創(chuàng)建另外一個可定制的對象,而且不需要知道任何創(chuàng)建的細(xì)節(jié)自点。

使用場景

使用原形模式可以解決復(fù)雜對象構(gòu)建資源消耗的問題生音。也可以用來只讀保護(hù)。注意其java里繼承Cloneable接口缩麸。C#繼承ICloneable接口铸磅。Caché 直接使用%ConstructClone()方法,克隆時不會走構(gòu)造函數(shù)杭朱。

優(yōu)點

  • 一般在初始化的信息不發(fā)生變的情況下阅仔,克隆是最好的辦法,這即隱藏了對象創(chuàng)建的細(xì)節(jié)弧械,又對性能是大大的提高八酒。
  • 不用重新初始化對象,而是動態(tài)地獲得對象運行時的狀態(tài)刃唐。

結(jié)構(gòu)圖

描述

要求有一個簡歷類羞迷,必須要有姓名,可以設(shè)置性別和年齡画饥,可以設(shè)置工作經(jīng)歷衔瓮。最終需要三分簡歷。

示例

簡歷類

Class PHA.YX.Design.Prototype.Resume Extends %RegisteredObject
{

Property name As %String [ Private ];

Property sex As %String [ Private ];

Property age As %String [ Private ];

Property timeArea As %String [ Private ];

Property company As %String [ Private ];


Method %OnNew(name As %String) As %Status [ Private, ServerOnly = 1 ]
{
    s $this.name = name
    Quit $$$OK
}

Method SetPersonalInfo(sex As %String, age As %String)
{
    s $this.sex=sex
    s $this.age=age
}

Method SetWorkExperience(timeArea As %String, company As %String)
{
    s $this.timeArea=timeArea
    s $this.company=company
}

Method Display()
{
    w ..name _ " " _ ..sex _ " " _ ..age,!
    w "工作經(jīng)歷:" _ ..timeArea _ " " _ ..company,!
}

}

初級寫法

/// d ##class(PHA.YX.Design.Program).PrototypeExamplePrimary() 
ClassMethod PrototypeExamplePrimary()
{
    s mResumeA = ##class(PHA.YX.Design.Prototype.Resume).%New("姚鑫")
    d mResumeA.SetPersonalInfo("男","30")
    d mResumeA.SetWorkExperience("2015-2020","XX公司")
    
    s mResumeB = ##class(PHA.YX.Design.Prototype.Resume).%New("姚鑫")
    d mResumeB.SetPersonalInfo("男","30")
    d mResumeB.SetWorkExperience("2015-2020","XX公司")
    
    s mResumeC = ##class(PHA.YX.Design.Prototype.Resume).%New("姚鑫")
    d mResumeC.SetPersonalInfo("男","30")
    d mResumeC.SetWorkExperience("2015-2020","XX公司")
    
    d mResumeA.Display()
    d mResumeB.Display()
    d mResumeC.Display()
}

DHC-APP>d ##class(PHA.YX.Design.Program).PrototypeExamplePrimary()
姚鑫 男 30
工作經(jīng)歷:2015-2020 XX公司
姚鑫 男 30
工作經(jīng)歷:2015-2020 XX公司
姚鑫 男 30
工作經(jīng)歷:2015-2020 XX公司
 

缺點

三分簡歷需要實例化三次抖甘,如果需要一百份热鞍,就需要實例化一百次,徒增內(nèi)存。

中級寫法

/// d ##class(PHA.YX.Design.Program).PrototypeExampleIntermediate() 
ClassMethod PrototypeExampleIntermediate()
{
    s mResumeA = ##class(PHA.YX.Design.Prototype.Resume).%New("姚鑫")
    d mResumeA.SetPersonalInfo("男","30")
    d mResumeA.SetWorkExperience("2015-2020","XX公司")
    
    # dim mResumeB as PHA.YX.Design.Prototype.Resume
    s mResumeB = mResumeA
    
    # dim mResumeC as PHA.YX.Design.Prototype.Resume
    s mResumeC = mResumeA
    
    d mResumeA.Display()
    d mResumeB.Display()
    d mResumeC.Display()
}
DHC-APP>d ##class(PHA.YX.Design.Program).PrototypeExampleIntermediate()
姚鑫 男 30
工作經(jīng)歷:2015-2020 XX公司
姚鑫 男 30
工作經(jīng)歷:2015-2020 XX公司
姚鑫 男 30
工作經(jīng)歷:2015-2020 XX公司

缺點

引用傳值薇宠,改動其中一處其他復(fù)制實例一起改動偷办。把mResumeB設(shè)置一下年齡,mResumeC設(shè)置一下公司昼接。如下:

/// d ##class(PHA.YX.Design.Program).PrototypeExampleIntermediate() 
ClassMethod PrototypeExampleIntermediate()
{
    s mResumeA = ##class(PHA.YX.Design.Prototype.Resume).%New("姚鑫")
    d mResumeA.SetPersonalInfo("男","30")
    d mResumeA.SetWorkExperience("2015-2020","XX公司")
    
    # dim mResumeB as PHA.YX.Design.Prototype.Resume
    s mResumeB = mResumeA
    d mResumeB.SetPersonalInfo("男","31")
    
    # dim mResumeC as PHA.YX.Design.Prototype.Resume
    s mResumeC = mResumeA
    d mResumeC.SetWorkExperience("2017-2020","SS公司")
    d mResumeA.Display()
    d mResumeB.Display()
    d mResumeC.Display()
}

運行結(jié)果

DHC-APP>d ##class(PHA.YX.Design.Program).PrototypeExampleIntermediate()
姚鑫 男 31
工作經(jīng)歷:2017-2020 SS公司
姚鑫 男 31
工作經(jīng)歷:2017-2020 SS公司
姚鑫 男 31
工作經(jīng)歷:2017-2020 SS公司

高級寫法 (淺復(fù)制)

使用 .%ConstructClone()方法復(fù)制

/// d ##class(PHA.YX.Design.Program).PrototypeExampleSenior() 
ClassMethod PrototypeExampleSenior()
{
    s mResumeA = ##class(PHA.YX.Design.Prototype.Resume).%New("姚鑫")
    d mResumeA.SetPersonalInfo("男","30")
    d mResumeA.SetWorkExperience("2015-2020","XX公司")
    
    # dim mResumeB as PHA.YX.Design.Prototype.Resume
    s mResumeB = mResumeA.%ConstructClone()
    d mResumeB.SetPersonalInfo("男","31")
    
    # dim mResumeC as PHA.YX.Design.Prototype.Resume
    s mResumeC =mResumeA.%ConstructClone()
    d mResumeC.SetWorkExperience("2017-2020","SS公司")
    
    d mResumeA.Display()
    d mResumeB.Display()
    d mResumeC.Display()
}
DHC-APP> d ##class(PHA.YX.Design.Program).PrototypeExampleSenior()
姚鑫 男 30
工作經(jīng)歷:2015-2020 XX公司
姚鑫 男 31
工作經(jīng)歷:2015-2020 XX公司
姚鑫 男 30
工作經(jīng)歷:2017-2020 SS公司

淺復(fù)制

如果字段是值類型的爽篷,則對該字段執(zhí)行逐位復(fù)制,如果字段是引用類型慢睡,則復(fù)制引用但不復(fù)制引用的對象逐工,因此原始對象及其副本引用統(tǒng)一對象。如下增加一個工作經(jīng)驗類的引用字段:

增加一個WorkExperience類

Class PHA.YX.Design.Prototype.WorkExperience Extends %RegisteredObject
{

Property workDate As %String [ Private ];

Method workDateGet() As %String [ ServerOnly = 1 ]
{
    Quit i%workDate
}

Method workDateSet(Arg As %String) As %Status [ ServerOnly = 1 ]
{
    s i%workDate = Arg
    Quit $$$OK
}

Property company As %String [ Private ];

Method companyGet() As %String [ ServerOnly = 1 ]
{
    Quit i%company
}

Method companySet(Arg As %String) As %Status [ ServerOnly = 1 ]
{
    s i%company = Arg
    Quit $$$OK
}

}

PHA.YX.Design.Prototype.Resume 增加一個work字段引用WorkExperience

Property work As WorkExperience [ Private ];

并且在構(gòu)造方法初始化它

Method %OnNew(name As %String) As %Status [ Private, ServerOnly = 1 ]
{
    s $this.name = name
    s ..work = ##class(WorkExperience).%New()
    Quit $$$OK
}

給對象添加WorkExperience數(shù)據(jù)方法

Method CloneWorkExperience()
{
    s ..work = ..work.%ConstructClone()
}

輸出也修改一下

Method Display()
{
    w ..name _ " " _ ..sex _ " " _ ..age,!
    w "工作經(jīng)歷:" _ ..timeArea _ " " _ ..company,!
    w "工作經(jīng)歷Object:" _ ..work.workDateGet() _ " " _ ..work.companyGet(),!
}

/// d ##class(PHA.YX.Design.Program).PrototypeExampleShallow() 
ClassMethod PrototypeExampleShallow()
{
    s mResumeA = ##class(PHA.YX.Design.Prototype.Resume).%New("姚鑫")
    d mResumeA.SetPersonalInfo("男","30")
    d mResumeA.SetWorkExperience("2015-2020","XX公司")
    d mResumeA.SetWorkExperienceObject("2010-2011","騰訊")
    
    # dim mResumeB as PHA.YX.Design.Prototype.Resume
    s mResumeB = mResumeA.%ConstructClone()
    d mResumeB.SetPersonalInfo("男","31")
    d mResumeB.SetWorkExperienceObject("2011-2012","阿里")
    
    # dim mResumeC as PHA.YX.Design.Prototype.Resume
    s mResumeC =mResumeA.%ConstructClone()
    d mResumeC.SetWorkExperience("2017-2020","SS公司")
    d mResumeC.SetWorkExperienceObject("2012-2013","百度")
    
    d mResumeA.Display()
    d mResumeB.Display()
    d mResumeC.Display()
}
DHC-APP> d ##class(PHA.YX.Design.Program).PrototypeExampleShallow()
姚鑫 男 30
工作經(jīng)歷:2015-2020 XX公司
工作經(jīng)歷Object:2012-2013 百度
姚鑫 男 31
工作經(jīng)歷:2015-2020 XX公司
工作經(jīng)歷Object:2012-2013 百度
姚鑫 男 30
工作經(jīng)歷:2017-2020 SS公司
工作經(jīng)歷Object:2012-2013 百度

結(jié)果發(fā)現(xiàn)漂辐。引用的字段并沒有重新復(fù)制泪喊。

深復(fù)制

我們在PHA.YX.Design.Prototype.Resume里給work屬性,增加一個克隆方法髓涯。

Method CloneWorkExperience()
{
    s ..work = ..work.%ConstructClone()
}
/// d ##class(PHA.YX.Design.Program).PrototypeExampleDeep() 
ClassMethod PrototypeExampleDeep()
{
    s mResumeA = ##class(PHA.YX.Design.Prototype.Resume).%New("姚鑫")
    d mResumeA.SetPersonalInfo("男","30")
    d mResumeA.SetWorkExperience("2015-2020","XX公司")
    d mResumeA.CloneWorkExperience()
    d mResumeA.SetWorkExperienceObject("2010-2011","騰訊")
    
    # dim mResumeB as PHA.YX.Design.Prototype.Resume
    s mResumeB = mResumeA.%ConstructClone()
    d mResumeB.SetPersonalInfo("男","31")
    d mResumeB.CloneWorkExperience()
    d mResumeB.SetWorkExperienceObject("2011-2012","阿里")
    
    # dim mResumeC as PHA.YX.Design.Prototype.Resume
    s mResumeC =mResumeA.%ConstructClone()
    d mResumeC.SetWorkExperience("2017-2020","SS公司")
    d mResumeC.CloneWorkExperience()
    d mResumeC.SetWorkExperienceObject("2012-2013","百度")
    
    d mResumeA.Display()
    d mResumeB.Display()
    d mResumeC.Display()
}

結(jié)果發(fā)現(xiàn)袒啼,把引用字段work的值也修改成功。

DHC-APP>d ##class(PHA.YX.Design.Program).PrototypeExampleDeep()
姚鑫 男 30
工作經(jīng)歷:2015-2020 XX公司
工作經(jīng)歷Object:2010-2011 騰訊
姚鑫 男 31
工作經(jīng)歷:2015-2020 XX公司
工作經(jīng)歷Object:2011-2012 阿里
姚鑫 男 30
工作經(jīng)歷:2017-2020 SS公司
工作經(jīng)歷Object:2012-2013 百度

完整示例

簡歷類(復(fù)制類)

Class PHA.YX.Design.Prototype.Resume Extends %RegisteredObject
{

Property name As %String [ Private ];

Property sex As %String [ Private ];

Property age As %String [ Private ];

Property timeArea As %String [ Private ];

Property company As %String [ Private ];

Property work As WorkExperience [ Private ];

Method %OnNew(name As %String) As %Status [ Private, ServerOnly = 1 ]
{
    s $this.name = name
    s ..work = ##class(WorkExperience).%New()
    Quit $$$OK
}

Method CloneWorkExperience()
{
    s ..work = ..work.%ConstructClone()
}

Method SetPersonalInfo(sex As %String, age As %String)
{
    s $this.sex=sex
    s $this.age=age
}

Method SetWorkExperience(timeArea As %String, company As %String)
{
    s $this.timeArea=timeArea
    s $this.company=company
}

Method SetWorkExperienceObject(workDate As %String, company As %String)
{
    d ..work.workDateSet(workDate)
    d ..work.companySet(company)
}

Method Display()
{
    w ..name _ " " _ ..sex _ " " _ ..age,!
    w "工作經(jīng)歷:" _ ..timeArea _ " " _ ..company,!
    w "工作經(jīng)歷Object:" _ ..work.workDateGet() _ " " _ ..work.companyGet(),!
}

}

對象類(工作經(jīng)驗類)

Class PHA.YX.Design.Prototype.WorkExperience Extends %RegisteredObject
{

Property workDate As %String [ Private ];

Method workDateGet() As %String [ ServerOnly = 1 ]
{
    Quit i%workDate
}

Method workDateSet(Arg As %String) As %Status [ ServerOnly = 1 ]
{
    s i%workDate = Arg
    Quit $$$OK
}

Property company As %String [ Private ];

Method companyGet() As %String [ ServerOnly = 1 ]
{
    Quit i%company
}

Method companySet(Arg As %String) As %Status [ ServerOnly = 1 ]
{
    s i%company = Arg
    Quit $$$OK
}

}

調(diào)用

/// d ##class(PHA.YX.Design.Program).PrototypeExamplePrimary() 
ClassMethod PrototypeExamplePrimary()
{
    s mResumeA = ##class(PHA.YX.Design.Prototype.Resume).%New("姚鑫")
    d mResumeA.SetPersonalInfo("男","30")
    d mResumeA.SetWorkExperience("2015-2020","XX公司")
    
    s mResumeB = ##class(PHA.YX.Design.Prototype.Resume).%New("姚鑫")
    d mResumeB.SetPersonalInfo("男","30")
    d mResumeB.SetWorkExperience("2015-2020","XX公司")
    
    s mResumeC = ##class(PHA.YX.Design.Prototype.Resume).%New("姚鑫")
    d mResumeC.SetPersonalInfo("男","30")
    d mResumeC.SetWorkExperience("2015-2020","XX公司")
    
    d mResumeA.Display()
    d mResumeB.Display()
    d mResumeC.Display()
}

/// d ##class(PHA.YX.Design.Program).PrototypeExampleIntermediate() 
ClassMethod PrototypeExampleIntermediate()
{
    s mResumeA = ##class(PHA.YX.Design.Prototype.Resume).%New("姚鑫")
    d mResumeA.SetPersonalInfo("男","30")
    d mResumeA.SetWorkExperience("2015-2020","XX公司")
    
    # dim mResumeB as PHA.YX.Design.Prototype.Resume
    s mResumeB = mResumeA
    d mResumeB.SetPersonalInfo("男","31")
    
    # dim mResumeC as PHA.YX.Design.Prototype.Resume
    s mResumeC = mResumeA
    d mResumeC.SetWorkExperience("2017-2020","SS公司")
    d mResumeA.Display()
    d mResumeB.Display()
    d mResumeC.Display()
}

/// d ##class(PHA.YX.Design.Program).PrototypeExampleSenior() 
ClassMethod PrototypeExampleSenior()
{
    s mResumeA = ##class(PHA.YX.Design.Prototype.Resume).%New("姚鑫")
    d mResumeA.SetPersonalInfo("男","30")
    d mResumeA.SetWorkExperience("2015-2020","XX公司")
    
    # dim mResumeB as PHA.YX.Design.Prototype.Resume
    s mResumeB = mResumeA.%ConstructClone()
    d mResumeB.SetPersonalInfo("男","31")
    
    # dim mResumeC as PHA.YX.Design.Prototype.Resume
    s mResumeC =mResumeA.%ConstructClone()
    d mResumeC.SetWorkExperience("2017-2020","SS公司")
    
    d mResumeA.Display()
    d mResumeB.Display()
    d mResumeC.Display()
}

/// d ##class(PHA.YX.Design.Program).PrototypeExampleShallow() 
ClassMethod PrototypeExampleShallow()
{
    s mResumeA = ##class(PHA.YX.Design.Prototype.Resume).%New("姚鑫")
    d mResumeA.SetPersonalInfo("男","30")
    d mResumeA.SetWorkExperience("2015-2020","XX公司")
    d mResumeA.SetWorkExperienceObject("2010-2011","騰訊")
    
    # dim mResumeB as PHA.YX.Design.Prototype.Resume
    s mResumeB = mResumeA.%ConstructClone()
    d mResumeB.SetPersonalInfo("男","31")
    d mResumeB.SetWorkExperienceObject("2011-2012","阿里")
    
    # dim mResumeC as PHA.YX.Design.Prototype.Resume
    s mResumeC =mResumeA.%ConstructClone()
    d mResumeC.SetWorkExperience("2017-2020","SS公司")
    d mResumeC.SetWorkExperienceObject("2012-2013","百度")
    
    d mResumeA.Display()
    d mResumeB.Display()
    d mResumeC.Display()
}

/// d ##class(PHA.YX.Design.Program).PrototypeExampleDeep() 
ClassMethod PrototypeExampleDeep()
{
    s mResumeA = ##class(PHA.YX.Design.Prototype.Resume).%New("姚鑫")
    d mResumeA.SetPersonalInfo("男","30")
    d mResumeA.SetWorkExperience("2015-2020","XX公司")
    d mResumeA.CloneWorkExperience()
    d mResumeA.SetWorkExperienceObject("2010-2011","騰訊")
    
    # dim mResumeB as PHA.YX.Design.Prototype.Resume
    s mResumeB = mResumeA.%ConstructClone()
    d mResumeB.SetPersonalInfo("男","31")
    d mResumeB.CloneWorkExperience()
    d mResumeB.SetWorkExperienceObject("2011-2012","阿里")
    
    # dim mResumeC as PHA.YX.Design.Prototype.Resume
    s mResumeC =mResumeA.%ConstructClone()
    d mResumeC.SetWorkExperience("2017-2020","SS公司")
    d mResumeC.CloneWorkExperience()
    d mResumeC.SetWorkExperienceObject("2012-2013","百度")
    
    d mResumeA.Display()
    d mResumeB.Display()
    d mResumeC.Display()
}

思考

用深復(fù)制實現(xiàn)纬纪,Java蚓再,Python,C#包各,連接Caché數(shù)據(jù)庫(有連接屬性摘仅,連接時間),Caché數(shù)據(jù)庫持有SQL數(shù)據(jù)庫連接(有連接屬性问畅,連接時間)娃属。感興趣的同學(xué)實現(xiàn)后可以發(fā)我一起參考下。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末护姆,一起剝皮案震驚了整個濱河市矾端,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌卵皂,老刑警劉巖秩铆,帶你破解...
    沈念sama閱讀 211,348評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異灯变,居然都是意外死亡殴玛,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評論 2 385
  • 文/潘曉璐 我一進(jìn)店門柒凉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人篓跛,你說我怎么就攤上這事膝捞。” “怎么了?”我有些...
    開封第一講書人閱讀 156,936評論 0 347
  • 文/不壞的土叔 我叫張陵蔬咬,是天一觀的道長鲤遥。 經(jīng)常有香客問我,道長林艘,這世上最難降的妖魔是什么盖奈? 我笑而不...
    開封第一講書人閱讀 56,427評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮狐援,結(jié)果婚禮上钢坦,老公的妹妹穿的比我還像新娘。我一直安慰自己啥酱,他們只是感情好爹凹,可當(dāng)我...
    茶點故事閱讀 65,467評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著镶殷,像睡著了一般禾酱。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上绘趋,一...
    開封第一講書人閱讀 49,785評論 1 290
  • 那天颤陶,我揣著相機(jī)與錄音,去河邊找鬼陷遮。 笑死滓走,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的拷呆。 我是一名探鬼主播闲坎,決...
    沈念sama閱讀 38,931評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼茬斧!你這毒婦竟也來了腰懂?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,696評論 0 266
  • 序言:老撾萬榮一對情侶失蹤项秉,失蹤者是張志新(化名)和其女友劉穎绣溜,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體娄蔼,經(jīng)...
    沈念sama閱讀 44,141評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡怖喻,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,483評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了岁诉。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片锚沸。...
    茶點故事閱讀 38,625評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖涕癣,靈堂內(nèi)的尸體忽然破棺而出哗蜈,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 34,291評論 4 329
  • 正文 年R本政府宣布距潘,位于F島的核電站炼列,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏音比。R本人自食惡果不足惜俭尖,卻給世界環(huán)境...
    茶點故事閱讀 39,892評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望洞翩。 院中可真熱鬧稽犁,春花似錦、人聲如沸菱农。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽循未。三九已至陷猫,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間的妖,已是汗流浹背绣檬。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留嫂粟,地道東北人娇未。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像星虹,于是被迫代替她去往敵國和親零抬。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,492評論 2 348

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