設(shè)計模式在倍牛工程中的應(yīng)用

前言

脫離了代碼談設(shè)計模式,就像是脫離了業(yè)務(wù)談架構(gòu)一樣苟耻。很多時候,覺得別人代碼寫的好扶檐,是離不開合理的運(yùn)用設(shè)計模式的凶杖,本篇就重點(diǎn)用代碼講述,倍牛工程中用到的設(shè)計模式款筑,文中關(guān)于設(shè)計模式的定義來自《大話設(shè)計模式》一書智蝠。
從最常用的工廠模式說起:

工廠模式

工廠模式是我們最常用的實(shí)例化對象模式,是用工廠方法代替new操作的一種模式奈梳。

在倍牛工程中杈湾,用到的UPHKEmpty類,在該類的.h中如下:

/*
 * 默認(rèn)沒有數(shù)據(jù) 占位view
 * */
+ (instancetype)defaultEmptyView;

/*
 * 轉(zhuǎn)菊花樣式 占位view
 * */
+ (instancetype)progressEmptyView;

/*
 * 網(wǎng)絡(luò)異常 占位view
 * */
+ (instancetype)netErrorEmptyViewWithHandler:(UPHKEmptyViewHandler)handler;

/**
 *  通用的無網(wǎng)絡(luò)的"雷達(dá)"空白頁 點(diǎn)擊可從新加載
 */
+ (instancetype)reconnectEmptyViewWithHandler:(UPHKEmptyViewHandler)handler;

/*
 * 自選空白 占位view
 * */
+ (instancetype)optionalEmptyViewWithHandler:(UPHKEmptyViewHandler)handler;

工廠方法也可以是如下這個初始化的方法:

/**
 *  工廠方法
 */
+ (instancetype) modelWithStartPoint:(CGPoint)startPoint endPoint:(CGPoint)endPoint;

這樣理解起來也并不困難攘须。

根據(jù)不同的場景毛秘,使用不同的工廠方法,即可阻课。

單一職責(zé)原則

先說個人理解叫挟,單一職責(zé)原則,我一直理解為限煞,在一個類中抹恳,要保證該類的功能是單一的,也就是專一署驻,該類不能既承擔(dān)A功能奋献,又承擔(dān)B功能,不然會造成功能耦合旺上,從而導(dǎo)致設(shè)計脆弱瓶蚂、擴(kuò)展性差,此原則的核心就是解耦和增強(qiáng)內(nèi)聚性宣吱。
單一職責(zé)原則的定義如下:

就一個類而言窃这,應(yīng)該僅有一個引起它發(fā)生變化的原因。

我并不能很好的理解“僅有一個引起它發(fā)生變化的原因”這句話征候。
所以在聯(lián)想到倍牛工程的時候杭攻,我覺得祟敛,數(shù)據(jù)庫的創(chuàng)建和讀取的設(shè)計,類UPHKDBHelperUPHKUserDBManager兆解。UPHKDBHelper是直接操作數(shù)據(jù)庫的類馆铁,負(fù)責(zé)UPHKUserSDK中數(shù)據(jù)的IO操作,將讀取或者需要寫入的數(shù)據(jù)锅睛,回調(diào)或者寫入到數(shù)據(jù)庫中埠巨,但是這里的單一職責(zé)我就認(rèn)為它是功能單一,或者唯一现拒,是唯一直接操作數(shù)據(jù)庫的類乖订。
UPHKDBHelper.h中的代碼如下,

//  數(shù)據(jù)表操作相關(guān)
- (id)initDBWithName:(NSString *)dbName;

- (id)initWithDBWithPath:(NSString *)dbPath;
...

///************************ 數(shù)據(jù)庫存取相關(guān) *****************************************

- (void)putObject:(id)object withId:(NSString *)objectId intoTable:(NSString *)tableName;

- (id)getObjectById:(NSString *)objectId fromTable:(NSString *)tableName;
....

其實(shí)UPHKDBHepler理解為單一職責(zé)原則具练,我并不認(rèn)為合理,還需探討甜无。
我認(rèn)為在倍牛工程中比較符合單一職責(zé)設(shè)計模式的是UPHKCommDataClient以及與它相似的類扛点。
UPHKCommDataClient.h

//  下載
- (UPHKResponse *)downloadFile:(NSString *)filePath;

/**
 * 上傳圖片到服務(wù)器
 */
- (UPHKResponse *)uploadImageFile:(UIImage *)image;

UPHKCommDataClient負(fù)責(zé)了整個工程的文件下載和圖片的上傳,職責(zé)單一岂丘,也不依賴上層邏輯陵究,做到“僅有一個引起它發(fā)生變化的原因”,在倍牛工程共還有許多與之相似的類奥帘,如UserSDK中負(fù)責(zé)線程管理的UserServer铜邮、負(fù)責(zé)組包的UserService、負(fù)責(zé)底層網(wǎng)絡(luò)請求的UserClient等等都有單一職責(zé)的應(yīng)用寨蹋。

開放封閉原則

是說軟件實(shí)體(類松蒜、模塊、函數(shù)等)已旧,應(yīng)該可以擴(kuò)展秸苗,但是不可修改。
開放封閉原則主要體現(xiàn)在兩個方面:對于擴(kuò)展是開放的运褪,對于更改是封閉的惊楼。

在倍牛及股票通工程中使用的UPHybridSDK,是嚴(yán)格開放封閉原則進(jìn)行設(shè)計的秸讹。


image.png

在此之前檀咙,我從未想過一個WebView要做這么深層次的封裝,也才知道璃诀,WebView的功能弧可,可以以插件的形式設(shè)計,對HybridSDK結(jié)構(gòu)理解還不夠深刻劣欢,只是拿來UPHybridPlugin這個類侣诺,對開放封閉原則這一設(shè)計模式做深入理解用殖演。
UPHybridPlugin(插件基類),在實(shí)現(xiàn)上只是將事件分發(fā)給UPHybridPluginManager年鸳。在native需要與web頁面交互的時候趴久,execute方法就是web調(diào)用終端的onJsRequest時觸發(fā)的方法,actionargs分別是需要終端處理的事件和終端需要的參數(shù)搔确。

-(BOOL)execute:(NSString *)callbackId action:(NSString *)action args:(NSDictionary *)args;

在創(chuàng)建功能插件時彼棍,必須繼承UPHybridPlugin,并重載execute方法膳算。在UPHybirdSDK的結(jié)構(gòu)設(shè)計完成后座硕,父類UPHybridPluginexecute方法就不再更改,但是無論模塊多么的封閉涕蜂,都會存在一些無法對之封閉的變化华匾,我理解的變化,針對UPHybridPlugin來說机隙,就是不斷增加的功能蜘拉,就需要各式各樣的功能插件,功能是隨需求不斷變化的有鹿,既然不可能完全封閉旭旭,就必須對設(shè)計的模塊應(yīng)該對那種變化封閉做出選擇,然后構(gòu)造抽象來隔離這些變化葱跋,execute其實(shí)就是這樣抽象出來的方法持寄。
對UPHybridSDK的可以逐步的理解如下:最初寫UPHybird時,假設(shè)只有分享功能娱俺,就不存在當(dāng)前插件形式的結(jié)構(gòu)稍味,但是隨著拍照功能、頁面定制功能的增加荠卷,這時候仲闽,應(yīng)該對程序中呈現(xiàn)頻繁變化的那部分做出抽象,變化時什么僵朗?可以理解為具體的功能代碼的實(shí)現(xiàn)赖欣,但是不變的是什么?是底層Web調(diào)用native的結(jié)構(gòu)验庙,這時候需要抽象的部分就形成了顶吮,面對新功能,對程序的改動是通過增加新插件(新代碼)的形式進(jìn)行的(可擴(kuò)展)粪薛,而不是更改現(xiàn)有的代碼(封閉)悴了。

里氏代換原則

子類型必須能夠替換掉他們的父類型。

一個軟件實(shí)體如果使用的是一個父類的話,那么一定適用于其子類湃交,而且它察覺不出父類對象和子類對象的區(qū)別熟空。也就是說,在軟件里面搞莺,把父類都替換成它的子類息罗,程序的行為沒有變化。
我理解的里氏代換原則才沧,是開放封閉原則的根本迈喉,也就是說,正是由于子類型的可替換性才使得使用父類類型的模塊在無需修改的情況下就可以擴(kuò)展温圆。

依賴倒轉(zhuǎn)原則

高層模塊不應(yīng)該依賴于低層模塊挨摸。兩個都應(yīng)該依賴抽象。抽象不應(yīng)該依賴細(xì)節(jié)岁歉,細(xì)節(jié)應(yīng)該依賴抽象得运。

按照UserSDK和User模塊的關(guān)系,可以理解User是業(yè)務(wù)邏輯相關(guān)的锅移,屬于高層模塊熔掺,UserSDK是低層模塊,這樣的依賴關(guān)系是符合工程模塊化設(shè)計原理的帆啃。


image.png

但是我認(rèn)為把這種設(shè)計理解成依賴倒轉(zhuǎn)的原則,有一點(diǎn)不妥窍帝。按照依賴倒轉(zhuǎn)原則努潘,高層模塊不應(yīng)該依賴低層模塊,兩個都應(yīng)該依賴抽象(有些拗口)坤学。
在高層模塊中疯坤,多處調(diào)用了低層模塊提供的接口,在要做新項目時深浮,如果需求是不更改App的UI布局压怠,換另一個UserSDK直接提供數(shù)據(jù),也就意味著飞苇,高層模塊的業(yè)務(wù)邏輯完全可以復(fù)用菌瘫,但高層模塊多處調(diào)用的低層接口是不是要多處修改?

所以按照依賴倒轉(zhuǎn)的原則應(yīng)該是:


image.png

但實(shí)際開發(fā)考慮到實(shí)際情況布卡,很少這樣去設(shè)計雨让,也是為了便捷開發(fā),適合自己的才是最好的忿等。

代理模式

######為其他對象提供一種代理以控制對這個對象的訪問栖忠。
在iOS代理模式最常見的實(shí)現(xiàn)方式是協(xié)議(Protocol),協(xié)議定義了接口,無需關(guān)心代理是誰庵寞,只要遵循并實(shí)現(xiàn)協(xié)議狸相,就可以訪問到這個對象,在倍牛和股票通中的Router是按照代理模式設(shè)計的捐川。
Router跳轉(zhuǎn)個股詳情為例
RouterProtocol中定義協(xié)議代碼如下:

@protocol UPHKMarketRouterDelegate <NSObject>
/*
 進(jìn)入股票行情頁面
 
 @param code 股票代碼
 @param setCode 市場代碼
 @param category 市場分類
 */
- (void)goMarketHQWithCode:(NSString *)code setCode:(NSInteger)setCode category:(UPMarketStockCategory)category;
@end

在個股詳情頁遵循并實(shí)現(xiàn)該協(xié)議

- (void)goMarketHQWithCode:(NSString *)code setCode:(NSInteger)setCode category:(UPMarketStockCategory)category
{
  ...
}

那么遵循協(xié)議者就是對代理者開放了一種對當(dāng)前對象的訪問權(quán)限脓鹃。

迪米特法則

如果兩個類不需要彼此直接通信,那么這兩個類就不應(yīng)當(dāng)發(fā)生直接的相互作用属拾。如果其中一個類需要調(diào)用另一個類的某一個方法的話将谊,可以通過第三者轉(zhuǎn)發(fā)這個調(diào)用。

先說我的理解渐白,在學(xué)習(xí)迪米特法則這個設(shè)計模式之前尊浓,我是經(jīng)常聽到大家這么說的,但是并不知道這其實(shí)是一種設(shè)計模式纯衍。按照之前的理解栋齿,每一個類在結(jié)構(gòu)設(shè)計上,都應(yīng)當(dāng)盡量降低成員的訪問權(quán)限襟诸,也就是說瓦堵,一個類包裝好了自己的private狀態(tài),不需要讓別的類知道的字段或行為就不要公開歌亲,它強(qiáng)調(diào)類之前的松耦合菇用,這就是該設(shè)計模式的根本思想。

迪米特法則在倍牛中有廣泛應(yīng)用陷揪,如UserSDK中的UserServer惋鸥,在.m將用戶數(shù)據(jù)UserData私有,對外只提供對應(yīng)的方法悍缠,滿足條件的情況下會在內(nèi)部對該屬性進(jìn)行管理卦绣,而非將該屬性暴露出去,我也認(rèn)為這一個類飞蚓,同時滿足了幾種設(shè)計模式滤港。

職責(zé)鏈模式

使多個對象都有機(jī)會處理請求,從而避免請求的發(fā)送者和接受者之間的耦合關(guān)系趴拧。將這個對象連成一條鏈溅漾,并沿著這條鏈傳遞該請求,直到有一個對象處理它為止著榴。
image.png
  • 職責(zé)鏈的好處

①當(dāng)提交一個請求時樟凄,請求沿鏈傳遞直至有一個Handler對象負(fù)責(zé)處理它。
②接受者和發(fā)送者都沒有對方的明確信息兄渺,且鏈中的對象自己也不知道鏈的結(jié)構(gòu)缝龄。結(jié)果是職責(zé)鏈可簡化對象的相互連接汰现,他們僅需保持一個指向其后繼者的引用,而不需要保持它所有候選接受者的引用叔壤。降低了耦合度瞎饲。
③隨時隨地增加或修改一個請求的結(jié)構(gòu),增強(qiáng)了給對象指派職責(zé)的靈活性炼绘。

  • 職責(zé)鏈的應(yīng)用

在倍牛工程中嗅战,并沒有非常符合職責(zé)鏈設(shè)計模式的代碼示例,但是我依然覺的這個設(shè)計模式非常有意思俺亮。
在大話設(shè)計模式一書中驮捍,作者是用員工、經(jīng)理脚曾、總監(jiān)东且、總經(jīng)理的角色分工來舉例說明的。我舉一個iOS中的例子本讥,我認(rèn)為iOS中的響應(yīng)鏈?zhǔn)前凑章氊?zé)鏈模式進(jìn)行設(shè)計的珊泳。
當(dāng)用戶觸摸應(yīng)用中的一個Button時,觸摸生成的Event會沿響應(yīng)鏈進(jìn)行傳遞拷沸,并找到最適合處理事件的對象色查。
UIApplication ~> UIWindow ~> UIViewController~~>UIButton
在查找適合處理事件的對象時,系統(tǒng)會在不同的處理者自動調(diào)用pointInside方法撞芍,如下

- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
    [super pointInside:point withEvent:event];
}

當(dāng)pointInside方法返回YES的時候秧了,會將該視圖加入到UIApplication的響應(yīng)者棧。
在iOS中響應(yīng)者都繼承自UIResponder序无,查找當(dāng)前響應(yīng)者的下一個響應(yīng)者可以通過[self nextResponder]獲取验毡,如果當(dāng)前響應(yīng)者不處理事件,可以將事件繼續(xù)傳遞給父類愉镰,父類再進(jìn)行分發(fā)米罚。
在響應(yīng)鏈中钧汹,滿足了一個請求可以被多個對象處理丈探,并且每個對象僅需保持一個指向其后繼者的引用的條件,所以我認(rèn)為響應(yīng)鏈就是按照職責(zé)鏈設(shè)計模式進(jìn)行設(shè)計的拔莱。

適配器模式

將一個類的接口轉(zhuǎn)換成需求需要的另外一個接口碗降。Adapter模式使得原本由于接口不兼容而不能一起工作的那些類可以一起工作。
  • 應(yīng)用場景

當(dāng)數(shù)據(jù)和行為都正確塘秦,但接口不符時讼渊,我們應(yīng)該考慮使用適配器,目的是使控制范圍之外的一個原有對象和某個接口匹配尊剔。適配模式主要應(yīng)用于希望復(fù)用一些現(xiàn)存的類爪幻,但是接口又與復(fù)用環(huán)境要求不一致的情況。

  • 理解

在看安卓代碼的時候,會發(fā)現(xiàn)有各式各樣的Adapter挨稿,如下:

public class MarketAmountListAdapter extends MarketBaseRecyclerAdapter {

    private final Context mContext;
    private final ArrayList<UPMarketData> mDataList;

    public MarketAmountListAdapter(Context context) {
        mContext = context;
        mDataList = new ArrayList<>();
    }

但是在iOS中仇轻,很少看到專門的Adapter,但并不代表沒有奶甘,多數(shù)時候數(shù)據(jù)的適配或處理是在數(shù)據(jù)模型中完成的篷店,所以并不需要創(chuàng)建單獨(dú)的Adapter
網(wǎng)絡(luò)請求回來的數(shù)據(jù)模型如UPHqStockHq和UI控件需要顯示的數(shù)據(jù)往往有些差別臭家,如類型不匹配疲陕、字體顏色未知等,這時候就需要對改模型進(jìn)行適配.h

@interface UPHKMarketHSGTAHStockModel : NSObject
@property (nonatomic, strong) UPMarketAHStockData *ahStock;

@property (nonatomic, strong) UIImage *marketFlagImage;
...
@property (nonatomic, copy) NSString *aChangeRadio;

//  Color
...
@property (nonatomic, strong) UIColor *aNowPriceColor;
@property (nonatomic, strong) UIColor *aChangeRadioColor;
@property (nonatomic, strong) UIColor *premiumRateColor;
@end

.m

@implementation UPHKMarketHSGTAHStockModel

- (UIImage *)marketFlagImage {
    return UPHKImage(@"up_hk_market_hk");
}
...
- (UIColor *)hChangeRadioColor {
    return [UPCompareTool compareWithData:self.ahStock.hItem.changeRatio baseData:0 precise:3];
}
- (UIColor *)premiumRateColor {
    return [UPCompareTool compareWithData:self.ahStock.premiumRate baseData:0 precise:3];
}
@end
使用一個已經(jīng)存在的類钉赁,但如果它的接口蹄殃,也就是它的方法和你的要求不相同時,就應(yīng)該考慮用適配器模式橄霉。
  • 討論

jayma(馬杰) 4-4 上午 9:25
我覺得對一個類或是一個模塊的設(shè)計窃爷,肯定不是一成不變的,隨著功能的增加姓蜂,需求的變化按厘,架構(gòu)和模式是一個持續(xù)修改和優(yōu)化的過程。
就現(xiàn)在來看倍牛里面還是有很多需要優(yōu)化的地方钱慢。比如UserSDK里面的模塊劃分逮京,分層結(jié)構(gòu),各個類的職責(zé)和功能束莫,用戶狀態(tài)機(jī)的管理等等懒棉,現(xiàn)在感覺實(shí)現(xiàn)有點(diǎn)冗余,邏輯有點(diǎn)混亂览绿,有很大的優(yōu)化空間策严。
還有TradeSDK對UserSDK的依賴調(diào)用,是否有更加合理的方式等等

jayma(馬杰) 4-4 上午 9:27
可以從代碼優(yōu)化的方式入手饿敲,對著各種設(shè)計模式妻导,多思考一下,這樣搞過幾輪后怀各,理解應(yīng)該會更加深入 [強(qiáng)]

....(單例模式倔韭、迭代器模式等待補(bǔ)充)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市瓢对,隨后出現(xiàn)的幾起案子寿酌,更是在濱河造成了極大的恐慌,老刑警劉巖硕蛹,帶你破解...
    沈念sama閱讀 222,681評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件醇疼,死亡現(xiàn)場離奇詭異硕并,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)秧荆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評論 3 399
  • 文/潘曉璐 我一進(jìn)店門鲤孵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人辰如,你說我怎么就攤上這事普监。” “怎么了琉兜?”我有些...
    開封第一講書人閱讀 169,421評論 0 362
  • 文/不壞的土叔 我叫張陵凯正,是天一觀的道長。 經(jīng)常有香客問我豌蟋,道長廊散,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,114評論 1 300
  • 正文 為了忘掉前任梧疲,我火速辦了婚禮允睹,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘幌氮。我一直安慰自己缭受,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,116評論 6 398
  • 文/花漫 我一把揭開白布该互。 她就那樣靜靜地躺著米者,像睡著了一般。 火紅的嫁衣襯著肌膚如雪宇智。 梳的紋絲不亂的頭發(fā)上蔓搞,一...
    開封第一講書人閱讀 52,713評論 1 312
  • 那天,我揣著相機(jī)與錄音随橘,去河邊找鬼喂分。 笑死,一個胖子當(dāng)著我的面吹牛机蔗,可吹牛的內(nèi)容都是我干的蒲祈。 我是一名探鬼主播,決...
    沈念sama閱讀 41,170評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼蜒车,長吁一口氣:“原來是場噩夢啊……” “哼讳嘱!你這毒婦竟也來了幔嗦?” 一聲冷哼從身側(cè)響起酿愧,我...
    開封第一講書人閱讀 40,116評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎邀泉,沒想到半個月后嬉挡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體钝鸽,經(jīng)...
    沈念sama閱讀 46,651評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,714評論 3 342
  • 正文 我和宋清朗相戀三年庞钢,在試婚紗的時候發(fā)現(xiàn)自己被綠了拔恰。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,865評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡基括,死狀恐怖颜懊,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情风皿,我是刑警寧澤河爹,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站桐款,受9級特大地震影響咸这,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜魔眨,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,211評論 3 336
  • 文/蒙蒙 一媳维、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧遏暴,春花似錦侄刽、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至侥啤,卻和暖如春当叭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背盖灸。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評論 1 274
  • 我被黑心中介騙來泰國打工蚁鳖, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人赁炎。 一個月前我還...
    沈念sama閱讀 49,299評論 3 379
  • 正文 我出身青樓醉箕,卻偏偏與公主長得像,于是被迫代替她去往敵國和親徙垫。 傳聞我的和親對象是個殘疾皇子讥裤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,870評論 2 361

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

  • 本文首發(fā)于個人博客:Lam's Blog - 談?wù)?3種設(shè)計模式在Android源碼及項目中的應(yīng)用,文章由Mark...
    格子林ll閱讀 4,654評論 1 105
  • 設(shè)計模式基本原則 開放-封閉原則(OCP)姻报,是說軟件實(shí)體(類己英、模塊、函數(shù)等等)應(yīng)該可以拓展吴旋,但是不可修改损肛。開-閉原...
    西山薄涼閱讀 3,808評論 3 14
  • 設(shè)計模式匯總 一厢破、基礎(chǔ)知識 1. 設(shè)計模式概述 定義:設(shè)計模式(Design Pattern)是一套被反復(fù)使用、多...
    MinoyJet閱讀 3,949評論 1 15
  • 大家好治拿,給大家介紹一下: 我的大名:蘋果 我的品種:紅富士 來自陜北紅色革命圣地延安 我的特點(diǎn)及優(yōu)點(diǎn):從小套袋摩泪、不...
    采蘑菇的佳怡07閱讀 433評論 1 1
  • 走著走著, 天就亮了劫谅。 走著走著见坑, 花就開了。 走著走著捏检, 就回到了童年鳄梅。 走著走著, 就遇到了自己未檩。 走著走著戴尸,...
    Bernardxiao閱讀 243評論 1 4