




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
模式:
模式,即Pattern。其實就是解決某一類問題的方法論。把解決某類問題的方法總結(jié)歸納到理論高度,那就是模式。Alexander給出的經(jīng)典定義是:每個模式都描述了一個在我們的環(huán)境中不斷出現(xiàn)的問題,然后描述了該問題的解決方案的核心。通過這種方式,你可以無數(shù)次地使用那些已有的解決方案,無需再重復(fù)相同的工作。模式有不同的領(lǐng)域,建筑領(lǐng)域有建筑模式,軟件設(shè)計領(lǐng)域也有設(shè)計模式。當一個領(lǐng)域逐漸成熟的時候,自然會出現(xiàn)很多模式。設(shè)計模式和面向?qū)ο蟮脑O(shè)計模式:
設(shè)計模式(Designpattern)是一套被反復(fù)使用、多數(shù)人知曉的、經(jīng)過分類編目的、代碼設(shè)計經(jīng)驗的總結(jié)。使用設(shè)計模式是為了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。設(shè)計模式最初來源于建筑學(xué)。GOF(“四人幫”,指Gamma,Helm,Johnson&Vlissides,Addison-Wesley四人)的《設(shè)計模式》(1995年出版)是第一次將設(shè)計模式提升到理論高度,并將之規(guī)范化,本系列文章主要就是講解這23種經(jīng)典的設(shè)計模式。
面向?qū)ο笤O(shè)計的模式,顧名思義,就是在面向?qū)ο蠓治雠c設(shè)計中使用的設(shè)計模式,GOF23種設(shè)計模式同時也是面向?qū)ο蟮脑O(shè)計模式,本文不做區(qū)分。良好的設(shè)計模式運用可以實現(xiàn)軟件設(shè)計的“高內(nèi)聚、低耦合”,提高軟件的復(fù)用性和可擴展性。
框架:
框架,即Framework。其實就是某種應(yīng)用的半成品,就是一組組件,供你選用完成你自己的系統(tǒng)。簡單說就是使用別人搭好的舞臺,你來做表演。而且,框架一般是成熟的,不斷升級的軟件??蚣芤话闾幵诘蛯討?yīng)用平臺(如J2EE)和高層業(yè)務(wù)邏輯之間的中間層。
架構(gòu):
架構(gòu)(Architecture)是一系列相關(guān)的抽象模式,用于指導(dǎo)大型軟件系統(tǒng)各個方面的設(shè)計。架構(gòu)是一個系統(tǒng)的草圖。架構(gòu)描述的對象是直接構(gòu)成系統(tǒng)的抽象組件。各個組件之間的連接則明確和相對細致地描述組件之間的通訊。在實現(xiàn)階段,這些抽象組件被細化為實際的組件,比如具體某個類或者對象。在面向?qū)ο箢I(lǐng)域中,組件之間的連接通常用接口來實現(xiàn)。
一些剛?cè)腴T的程序員經(jīng)常會混淆“框架”和“架構(gòu)”這兩個名詞,這里做了一下解釋。
我們?yōu)槭裁匆褂迷O(shè)計模式呢?有人可能會說為了設(shè)計出"高內(nèi)聚低耦合"的軟件。"高內(nèi)聚低耦合"的軟件實際上也就是本文所說的具有可維護性和可復(fù)用性的軟件。
這篇文章主要講解兩方面內(nèi)容,這兩方面是軟件設(shè)計中很重要,也是很關(guān)鍵的內(nèi)容,希望大家認真思考并深刻理解。第一部分就是關(guān)于軟件的可維護性和可復(fù)用性的相關(guān)內(nèi)容,第二部分就是在第一部分的基礎(chǔ)上逐條講解面向?qū)ο筌浖O(shè)計的基本原則,本文內(nèi)容都是些很理論性的東西,這些理論是軟件設(shè)計的基礎(chǔ)。凡是有理論的地方,就有如何恰當?shù)膶⒗碚搼?yīng)用到實踐中去的問題,設(shè)計模式是對于學(xué)習OO設(shè)計原則的具體指導(dǎo),也就是說設(shè)計模式就是將這些理論應(yīng)用到實踐的一種成熟的方式。
軟件的可維護性和可復(fù)用性
首先來上一段大師所說的話,很經(jīng)典?!巴ǔUJ為,一個易于維護的系統(tǒng),就是復(fù)用率較高的系統(tǒng);而一個復(fù)用率較高的系統(tǒng),就是一個易于維護的系統(tǒng)。但是實際上,可維護性和可復(fù)用性是兩個獨立的目標,就像兩只奔跑的兔子一樣,并不總是方向一致的。對于面向?qū)ο蟮能浖到y(tǒng)設(shè)計來說,在支持可維護性(Maintainability)的同時,提高系統(tǒng)的可復(fù)用性(Reuseability)是一個核心的問題?!保禞ava與模式》閻宏博士
軟件系統(tǒng)的可維護性:
軟件維護就是軟件的再生。一個好的軟件設(shè)計,必須能夠允許新的設(shè)計要求以比較容易和平穩(wěn)的方式加入到已有的系統(tǒng)中去,從而使這個系統(tǒng)能夠不斷的的煥發(fā)出活力。一個可維護性較好的系統(tǒng),應(yīng)當允許維護工作能夠以容易、準確、安全和經(jīng)濟的形式進行?!緦?dǎo)致可維護性較低的原因】1、過于僵硬:在系統(tǒng)中加入一個新的功能,不管大小都很難,不僅意味著建造一個獨立的新的模塊,而且因為這個新功能會波及很多其他模塊,最后成跨越幾個模塊的改動。
2、過于脆弱:與軟件的過于僵硬同時存在,是軟件系統(tǒng)在修改已有代碼時過于脆弱。對一個地方的修改,往往會導(dǎo)致看上去沒有什么關(guān)系的另外一個地方發(fā)生故障。
3、復(fù)用率低:所謂復(fù)用,就是指一個軟件的組成部分,可以在同一個項目的不同地方甚至另一個項目中重復(fù)使用。復(fù)用率低,指當一段代碼,函數(shù),模塊的功能可以在新的模塊或新的系統(tǒng)使用,但是已有代碼依賴于其他很多東西,很難分開。
4、黏度過高:一個改動可以保存原始設(shè)計意圖和原始設(shè)計框架的方式進行,也可以以破壞原始意圖和框架進行。第一種方法對系統(tǒng)的未來有利,第二種辦法是權(quán)宜之計,可以解決短期的問題,但是會犧牲中長期的利益。如果一個系統(tǒng)中使用第二種方法比使用第一種方法容易,那么就是黏度過高?!驹O(shè)計的目標】1、可擴展性:新的性能可以很容易地加入到系統(tǒng)中去,就是可擴展性。這就是系統(tǒng)“過于僵硬”的屬性的方面。
2、靈活性:可以允許代碼修改平穩(wěn)地發(fā)生,而不會波及到很多其他的模塊,這就是靈活性。靈活性其實就是“過于脆弱”的屬性的方面。
3、可插入性:可以很容易地將一個類抽出去,同時將另外一個有同樣接口的類加入進來,這就是可插入性。其實,這就是“黏度過高”的方面。
軟件系統(tǒng)的可復(fù)用性:【軟件復(fù)用的好處】1、較高的生產(chǎn)效率;
2、較高的軟件質(zhì)量;
3、恰當使用復(fù)用可以改善系統(tǒng)的可維護性?!緜鹘y(tǒng)的復(fù)用形式】1、代碼的剪貼復(fù)用;
2、算法的復(fù)用;
3、數(shù)據(jù)結(jié)構(gòu)的復(fù)用?!久嫦?qū)ο笤O(shè)計的復(fù)用】在面向?qū)ο笳Z言中,數(shù)據(jù)的抽象化,繼承,封裝和多態(tài)性使得一個系統(tǒng)可以在更高層次上提供可復(fù)用性。數(shù)據(jù)的抽象化和繼承關(guān)系使得概念和定義可以復(fù)用;多態(tài)性使得實現(xiàn)和應(yīng)用得到復(fù)用;而抽象化和封裝可以保持和促進系統(tǒng)的可維護性,復(fù)用的重點轉(zhuǎn)移到含有宏觀商業(yè)邏輯的抽象層次上。在面向?qū)ο蟮脑O(shè)計里面,可維護性復(fù)用是以設(shè)計原則和設(shè)計模式為基礎(chǔ)的。
提高系統(tǒng)可維護性和可復(fù)用性的設(shè)計原則1、“開-閉”原則(Open-ClosedPrinciple,或者OCP);一個軟件實體應(yīng)該對擴展開放,對修改關(guān)閉;在設(shè)計一個模塊的時候,應(yīng)當使這個模塊可以在不被修改的前提下被擴展。換言之,應(yīng)當可以在不必修改源代碼的情況下改變這個模塊的行為。這個原則實際上是對“對可變性的封閉原則“:找到一個系統(tǒng)的可變因素,將之封裝起來。這個原則意昧著兩點:
1)一個可變性不應(yīng)當散落在代碼的很多角落里,而應(yīng)當被封裝到一個對象里面。同一種可變性的不同表象意昧著同一個繼承等級結(jié)構(gòu)中的具體子類。
繼承就當被看作是封裝變化的方法,而不應(yīng)當被認為是從一般的對象生成特殊對象的方法。
2)一種可變性不應(yīng)當與另一種可變性混合在一起。(所有類圖的繼承結(jié)構(gòu)一般不會超過兩層,不然就意昧著將兩種不同的可變性混合在了一起。)這個原則是總的原則,其它幾條是這個原則的手段和工具。
2、里氏替代原則(LiskovSubstitutionPrinciple,或者LSP);如果對于每一個類型為T1的對象o1,都有類型為T2的對象o2,使得以T1定義的所有程序P在所有的對象o1都代換成o2時,程序P的行為沒有變化,那么類型T2是類型T1的子類型。換言之,一個軟件實體如果使用的是一個基類的話,那么一定適用于其子類,而且它根本不能察覺出基類對象和子類對象的區(qū)別。反過來代換不成立。
3、依賴倒轉(zhuǎn)原則(DependencyInversionPrinciple,或者DIP);要依賴于抽象,不要依賴于具體。開閉原則是目標,而達到這一目標的手段是依賴倒轉(zhuǎn)原則。抽象層次包含的是應(yīng)用系統(tǒng)的商務(wù)邏輯和宏觀的、對整個系統(tǒng)來說重要的戰(zhàn)略性決定,是必然性的體現(xiàn),那么抽象層次就應(yīng)當是較為穩(wěn)定的,應(yīng)當是復(fù)用的重點;也應(yīng)當是維護的重點;而具體層次則含有一些次要的與實現(xiàn)有關(guān)的算法和邏輯,以及戰(zhàn)術(shù)性的決定,帶有相當大的偶然性選擇。具體層次的代碼是會經(jīng)常有變動的,不能避免出現(xiàn)錯誤。4、接口隔離原則(InterfaceSegregationPrinciple,或者ISP);使用多個專門的接口比使用單一的總接口要好。換言之,從一個客戶類的角度講:一個類對另一個類的依賴性應(yīng)當是建立在最小的接口上的。接口隔離原則與迪米特法則(下面講到)都是對一個軟件實體與其他的軟件實體的通信限制。迪米特原則要求盡可能地限制通信的寬度和深度,接品隔離原則要求通信的寬度盡可能地窄。這樣做的結(jié)果使一個軟件系統(tǒng)在功能擴展過程當中,不會將修改的壓力傳遞到其他對象。一個接口相當于劇本中的一種角色,而此角色在一個舞臺上由哪一個演員來演則相當于接口的實現(xiàn)。因此,一個接口應(yīng)當簡單地代表一個角色,而不是多個角色。如果系統(tǒng)涉及到多個角色的話,那么每一個角色都應(yīng)當由一個特定的接口代表。5、組合/聚合復(fù)用原則(Composition/AggregationPrinciple,或者CARP);組合/聚合原則就是在一個新的對象里面使用一些已有的對象,使之成為新對象的一部分;新的對象通過向這些對象的委派達到得復(fù)用已有功能的目的。要盡量使用組合/聚合,盡量不要使用繼承。6、迪米特法則(LawofDemeter,或者LoD);一個軟件實體應(yīng)當盡可能少的與其他實體發(fā)生相互作用。模塊之間的交互要少。這樣做的結(jié)果是當系統(tǒng)的功能需要擴展時,會相對更容易地做到對修改的關(guān)閉。一個對象應(yīng)當對其他對象有盡可能少的了解。7、單一職責原則(SingleResponsibilityPrinciple,或者SRP)在設(shè)計中為每種職責設(shè)計一個類,彼此保持正交,互不干涉。這個原則比較容易理解,這里不在多說。
小結(jié)
當我們掌握了C#的語法,當我們了解了面向?qū)ο蟮姆庋b、繼承、多態(tài)等特性,當我們可以用各種框架與技術(shù)構(gòu)建桌面以及Web應(yīng)用時,這并不意味著我們可以寫出面向?qū)ο蟮某绦?,不意味著我們可以很好的實現(xiàn)代碼復(fù)用,彈性維護,不意味著我們可以實現(xiàn)在維護、擴展基礎(chǔ)上的代碼復(fù)用。使用面向?qū)ο笳Z言開發(fā)的程序不一定是面向?qū)ο蟮?,使用面向過程的語言開發(fā)的程序也不一定不是面向?qū)ο蟮?。要想開發(fā)出一個具有可維護性和可復(fù)用性的軟件系統(tǒng),那是需要優(yōu)秀的設(shè)計和長時間的運行才能完成的,其實我們可以觀察一下,任何一個優(yōu)秀的軟件產(chǎn)品都是經(jīng)過長時間的設(shè)計,運行,維護,修改等最后才成為成功的產(chǎn)品,版本上也在不斷的更新。衡量一個軟件開發(fā)者是不是一個好的軟件開發(fā)者,不是看他是否實現(xiàn)了軟件的必要功能,而是要看你的軟件在滿足功能需求的情況下是否做到了復(fù)用性和可擴展性,這對于一個大型系統(tǒng)尤其重要。我們不要靜止的看待一個軟件,而一定要把軟件過程放在時間軸上來觀察與設(shè)計它,只有放在時間軸上經(jīng)得住考驗的軟件系統(tǒng)才是成功的。軟件的復(fù)用性和可擴展性對于大型系統(tǒng)是必要的,我們在設(shè)計自己的軟件系統(tǒng)時,甚至在編寫代碼時更需要考慮一下這樣做是否遵循了系統(tǒng)設(shè)計的原則,是否有利于系統(tǒng)的可維護性和可復(fù)用性,是否達到了常說的“高內(nèi)聚低耦合”呢?設(shè)計模式正是解決這一問題的王道。
從下文開始我們將結(jié)合實例對于GoF23種設(shè)計模式進行一一講解?,F(xiàn)在我們正式進入GoF23種設(shè)計模式中的創(chuàng)建型模式的講解中來,創(chuàng)建型模式主要解決對象如何創(chuàng)建的問題,提倡創(chuàng)建對象的責任和使用對象的責任分離,以達到更好對創(chuàng)建對象的控制的目的,創(chuàng)建型模式主要包括抽象工廠(AbstractFactory),建造者(Builder),工廠方法(FactoryMethod),原型(Prototype),單子(Singleton)。這篇文章主要分為兩大部分內(nèi)容,在第一部分中我將介紹抽象工廠模式的原型,包括抽象工廠的意圖,可以解決的問題,原型代碼和UML等,再結(jié)合一個生活中的小例子進行原型的說明。第二部分我會結(jié)合實際項目來講述一下抽象工廠模式是如何應(yīng)用的。最后我會對抽象工廠模式進行一個小結(jié)。
工廠模式的幾種形態(tài)
工廠模式專門負責將大量有共同接口的類實例化。工廠模式可以動態(tài)決定將哪一個類實例化,不必事先知道每次要實例化哪一個類。工廠模式有以下幾種形態(tài):簡單工廠(SimpleFactory)模式:又稱靜態(tài)工廠方法模式(StaticFactoryMethodPattern)。主要是工廠中提供一個靜態(tài)的方法用來根據(jù)不同的參數(shù)創(chuàng)建不同的抽象產(chǎn)品的具體實例,一般在IoC中應(yīng)用比較多,例如通過反射機制和簡單工廠模式可以解決依賴注入的問題。簡單工廠模式不屬于GoF23種設(shè)計模式,這里也就不再作過多的分析。感興趣的園友可以找找相關(guān)資料。工廠方法(FactoryMethod)模式:又稱多態(tài)性工廠(PolymorphicFactory)模式或虛擬構(gòu)造子(VirtualConstructor)模式。這個模式屬于GoF23種設(shè)計模式之一,在后面的文章中會做詳細的介紹。抽象工廠(AbstractFactory)模式:又稱工具箱(Kit或Toolkit)模式。這是本文的重點。
抽象工廠模式的原型
描述:
假設(shè)一個子系統(tǒng)需要一些產(chǎn)品對象,而這些產(chǎn)品對象又屬于一個以上的產(chǎn)品等級結(jié)構(gòu)。那么為了將消費這些產(chǎn)品的責任和創(chuàng)建這些產(chǎn)品對象的責任分割開來,可以引進抽象工廠模式。這樣的話,消費產(chǎn)品的一方不需要直接參與產(chǎn)品的創(chuàng)建工作,而只需要向一個公用的工廠接口請求所需要的產(chǎn)品。
意圖:
抽象工廠模式可以向客戶端(Client指代碼模式的使用者,后文類同)提供一個接口,使得客戶端在不必指定產(chǎn)品的具體類型的情況下,創(chuàng)建多個產(chǎn)品族(ProductFamily指位于不同產(chǎn)品等級中,功能相關(guān)聯(lián)的產(chǎn)品的集合)中的產(chǎn)品對象。
模式原型UML:
抽象工廠涉及到以下角色:
抽象工廠(AbstractFactory)角色:聲明一個操作集合的接口以創(chuàng)建抽象產(chǎn)品族。
具體工廠(ConcreteFactory)角色:實現(xiàn)創(chuàng)建具體產(chǎn)品族的抽象工廠的實現(xiàn)。
抽象產(chǎn)品(AbstractProduct)角色:聲明一個產(chǎn)品的接口。
具體產(chǎn)品(Product)角色:定義了一個被具體工廠創(chuàng)建的產(chǎn)品對象,實現(xiàn)了抽象工廠接口。
客戶端(Client)角色:使用抽象工廠和抽象產(chǎn)品的類。
模式原型代碼:usingSystem;
namespaceDesignPatterns.Creational
{
//測試程序
classMainApp
{
publicstaticvoidMain()
{
//抽象工廠1
AbstractFactoryfactory1=newConcreteFactory1();
Clientc1=newClient(factory1);
c1.Run();
//抽象工廠2
AbstractFactoryfactory2=newConcreteFactory2();
Clientc2=newClient(factory2);
c2.Run();
//等候用戶輸入
Console.Read();
}
}
//抽象工廠
abstractclassAbstractFactory
{
publicabstractAbstractProductACreateProductA();
publicabstractAbstractProductBCreateProductB();
}
//具體工廠1
classConcreteFactory1:AbstractFactory
{
publicoverrideAbstractProductACreateProductA()
{
returnnewProductA1();
}
publicoverrideAbstractProductBCreateProductB()
{
returnnewProductB1();
}
}
//具體工廠2
classConcreteFactory2:AbstractFactory
{
publicoverrideAbstractProductACreateProductA()
{
returnnewProductA2();
}
publicoverrideAbstractProductBCreateProductB()
{
returnnewProductB2();
}
}
//抽象產(chǎn)品A,產(chǎn)品族中一個成員
abstractclassAbstractProductA
{
}
//抽象產(chǎn)品B,產(chǎn)品族中一個成員
abstractclassAbstractProductB
{
publicabstractvoidInteract(AbstractProductAa);
}
//具體產(chǎn)品A1
classProductA1:AbstractProductA
{
}
//具體產(chǎn)品B1
classProductB1:AbstractProductB
{
publicoverridevoidInteract(AbstractProductAa)
{
Console.WriteLine(this.GetType().Name+
"interactswith"+a.GetType().Name);
}
}
//具體產(chǎn)品A2
classProductA2:AbstractProductA
{
}
//具體產(chǎn)品B2
classProductB2:AbstractProductB
{
publicoverridevoidInteract(AbstractProductAa)
{
Console.WriteLine(this.GetType().Name+
"interactswith"+a.GetType().Name);
}
}
//客戶端,使用環(huán)境
classClient
{
privateAbstractProductAAbstractProductA;
privateAbstractProductBAbstractProductB;
//構(gòu)造,注意通過構(gòu)造傳入抽象工廠
publicClient(AbstractFactoryfactory)
{
AbstractProductB=factory.CreateProductB();
AbstractProductA=factory.CreateProductA();
}
publicvoidRun()
{
AbstractProductB.Interact(AbstractProductA);
}
}
}輸出結(jié)果為:ProductB1interactswithProductA1
ProductB2interactswithProductA2
生活中的實例:
這個生活中的實例代碼演示了一個電腦游戲,在游戲中建立不同的動物世界會使用不同的工廠。雖然創(chuàng)建動物的大陸工廠是不同的,但是動物之間的相互關(guān)系保持不變。
RealworldcodeusingAbstractFactoryinC#
usingSystem;
namespaceDesignPatterns.Creational.AbstractFactory.RealWorld
{classMainApp
{
publicstaticvoidMain()
{
//創(chuàng)建非洲大陸
ContinentFactoryafrica=newAfricaFactory();
AnimalWorldworld=newAnimalWorld(africa);
world.RunFoodChain();
//創(chuàng)建美洲大陸
ContinentFactoryamerica=newAmericaFactory();
world=newAnimalWorld(america);
world.RunFoodChain();
//等待用戶輸入
Console.ReadKey();
}
}
//抽象工廠
abstractclassContinentFactory
{
//創(chuàng)建食草動物
publicabstractHerbivoreCreateHerbivore();
//創(chuàng)建食肉動物
publicabstractCarnivoreCreateCarnivore();
}
//具體工廠1
classAfricaFactory:ContinentFactory
{
publicoverrideHerbivoreCreateHerbivore()
{
//返回牛羚
returnnewWildebeest();
}
publicoverrideCarnivoreCreateCarnivore()
{
//返回獅子
returnnewLion();
}
}
//具體工廠2
classAmericaFactory:ContinentFactory
{
publicoverrideHerbivoreCreateHerbivore()
{
//返回野牛
returnnewBison();
}
publicoverrideCarnivoreCreateCarnivore()
{
//返回狼
returnnewWolf();
}
}
//抽象產(chǎn)品A
abstractclassHerbivore
{
}
//抽象產(chǎn)品B
abstractclassCarnivore
{
//交互關(guān)系,食肉動物可以吃掉食草動物
publicabstractvoidEat(Herbivoreh);
}
//具體產(chǎn)品A1
classWildebeest:Herbivore
{
}
//具體產(chǎn)品B1
classLion:Carnivore
{
publicoverridevoidEat(Herbivoreh)
{
//吃掉牛羚
Console.WriteLine(this.GetType().Name+
"eats"+h.GetType().Name);
}
}
//具體產(chǎn)品A2
classBison:Herbivore
{
}
//具體產(chǎn)品B2
classWolf:Carnivore
{
publicoverridevoidEat(Herbivoreh)
{
//吃掉野牛
Console.WriteLine(this.GetType().Name+
"eats"+h.GetType().Name);
}
}
//客戶端
classAnimalWorld
{
privateHerbivore_herbivore;
privateCarnivore_carnivore;
//通過構(gòu)造器傳入具體工廠
publicAnimalWorld(ContinentFactoryfactory)
{
_carnivore=factory.CreateCarnivore();
_herbivore=factory.CreateHerbivore();
}
publicvoidRunFoodChain()
{
_carnivore.Eat(_herbivore);
}
}
}RealworldcodeusingAbstractFactoryinC#輸出的結(jié)果為:LioneatsWildebeest
WolfeatsBison
什么情況下使用抽象工廠:文獻【GOF95】指出,在以下情況下應(yīng)當考慮使用抽象工廠模式:
1.一個系統(tǒng)不應(yīng)當依賴于產(chǎn)品類實例如何被創(chuàng)建、組合和表達的細節(jié),這對于所有形態(tài)的工廠模式都是重要的。
2、這個系統(tǒng)的產(chǎn)品有多于一個產(chǎn)品族,而系統(tǒng)只消費其中某一個族的產(chǎn)品(上面這一條叫做抽象工廠模式的原始用意。)
3、同屬于同一個產(chǎn)品族的產(chǎn)品是在一起使用的,這一約束必須在系統(tǒng)的設(shè)計中體現(xiàn)出來。
4、系統(tǒng)提供一個產(chǎn)品類的庫,所有的產(chǎn)品以同樣的接口實現(xiàn),從而使客戶端不依賴于實現(xiàn)。
實際項目舉例
現(xiàn)在需要創(chuàng)建分屬于不同操作系統(tǒng)的視窗構(gòu)件。比如命令按鈕(Button)與文本框(Text)等都是視窗構(gòu)件,在UNIX系統(tǒng)的視窗環(huán)境和Windows操作系統(tǒng)的視窗環(huán)境中,這兩個構(gòu)件有不同的本地體現(xiàn),它們的細節(jié)也有所不同。在每一個操作系統(tǒng)中,都有一個視窗構(gòu)件組成構(gòu)件家族。在這里就是Button和Text組成的產(chǎn)品族。而每一個視窗構(gòu)件都構(gòu)成自己的等級結(jié)構(gòu),由一個抽象角色給出抽象的功能描述,而由具體子類給出不同操作系統(tǒng)的具體實現(xiàn),如下圖所示。
可以發(fā)現(xiàn)在上面的產(chǎn)品類圖中,有兩個產(chǎn)品的等級結(jié)構(gòu),分別是Button等級結(jié)構(gòu)和Text等級結(jié)構(gòu)、同時有兩個產(chǎn)品族,也就是UNIX產(chǎn)品族和Windows產(chǎn)品族。UNIX產(chǎn)品族由UnixButton和UnixText產(chǎn)品組成;而Windows產(chǎn)品族由WinButton和WinText產(chǎn)品構(gòu)成。
系統(tǒng)對產(chǎn)品對象的創(chuàng)建需求由一個工廠的等級結(jié)構(gòu)滿足,其中有兩個具體工廠角色,即UnixFactory和WinFactory。UnixFactory對象負責創(chuàng)建Unix產(chǎn)品族中的產(chǎn)品,而WinFactory對象負責創(chuàng)建Windows產(chǎn)品族中的產(chǎn)品。這就是抽象工廠模式的應(yīng)用,抽象工廠模式的解決方案如下圖所示。
顯然,一個系統(tǒng)只能夠在某一個操作系統(tǒng)的視窗環(huán)境下運行,而不能夠同時在不同的操作系統(tǒng)上運行。所以,系統(tǒng)實際上只能消費屬于同一個產(chǎn)品族的產(chǎn)品。這個案例實際上也正是抽象工廠模式的起源。實現(xiàn)的代碼如下。
ProjectcodeusingAbstractFactoryinC#
輸出結(jié)果為:ProjectcodeusingAbstractFactoryinC#
//抽象工廠起源案例
usingSystem;
namespaceDesignPatterns.Creational.AbstractFactory.Project
{
classMainApp
{
publicstaticvoidMain()
{
//創(chuàng)建Unix使用環(huán)境
OSFactoryunix=newUnixFactory();
Clientclient=newClient(unix);
client.Run();
//創(chuàng)建Windows使用環(huán)境
OSFactorywindows=newWinFactory();
client=newClient(windows);
client.Run();
//等待用戶輸入
Console.ReadKey();
}
}
//抽象工廠
abstractclassOSFactory
{
//創(chuàng)建按鈕構(gòu)件
publicabstractButtonCreateButton();
//創(chuàng)建文本框構(gòu)件
publicabstractTextCreateText();
}
//具體工廠1
classUnixFactory:OSFactory
{
publicoverrideButtonCreateButton()
{
//返回Unix下的Button
returnnewUnixButton();
}
publicoverrideTextCreateText()
{
//返回Unix下的Text
returnnewUnixText();
}
}
//具體工廠2
classWinFactory:OSFactory
{
publicoverrideButtonCreateButton()
{
//返回Windows下的Button
returnnewWinButton();
}
publicoverrideTextCreateText()
{
//返回Winodws下的Text
returnnewWinText();
}
}
//抽象產(chǎn)品A
abstractclassButton
{
}
//抽象產(chǎn)品B
abstractclassText
{
//交互關(guān)系
publicabstractvoidInteract(Buttonb);
}
//具體產(chǎn)品A1
classUnixButton:Button
{
}
//具體產(chǎn)品B1
classUnixText:Text
{
publicoverridevoidInteract(Buttonb)
{
Console.WriteLine(this.GetType().Name+
"interactwith"+b.GetType().Name);
}
}
//具體產(chǎn)品A2
classWinButton:Button
{
}
//具體產(chǎn)品B2
classWinText:Text
{
publicoverridevoidInteract(Buttonb)
{
Console.WriteLine(this.GetType().Name+
"interactwith"+b.GetType().Name);
}
}
//客戶端
classClient
{
privateButton_button;
privateText_text;
//通過構(gòu)造器傳入具體工廠
publicClient(OSFactoryfactory)
{
_button=factory.CreateButton();
_text=factory.CreateText();
}
publicvoidRun()
{
_text.Interact(_button);
}
}
}UnixTextinteractwithUnixButton
WinTextinteractwithWinButton
小結(jié)
抽象工廠模式是一個在實際項目中應(yīng)用比較多的設(shè)計模式之一,抽象工廠模式面對的問題是多個產(chǎn)品等級結(jié)構(gòu)的系統(tǒng)設(shè)計,運用抽象工廠模式的關(guān)鍵在于如果把創(chuàng)建產(chǎn)品的職責交給工廠去完成,希望大家在把握住工廠模式原型的基礎(chǔ)上盡量的考慮到應(yīng)用,形成一種思維上的定勢。
這一篇我將向大家講解建造者(Builder)模式。在上一篇文章中我們主要學(xué)習了抽象工廠(AbstractFactory)模式,抽象工廠模式主要解決對不同等級結(jié)構(gòu)的產(chǎn)品的創(chuàng)建工作,主要關(guān)注的是創(chuàng)建哪一批產(chǎn)品的問題,而本文所講的建造者模式主要是解決對于一個產(chǎn)品如何分部創(chuàng)建的問題,這是對于建造者模式的最初描述。同樣,這篇文章主要分為兩大部分來講解,第一部分我會對建造者模式的原型進行詳細的說明,第二部分會對建造者模式如何解決具體問題進行探討。
建造者模式的原型
描述:
在軟件系統(tǒng)中,有時候面臨一個"復(fù)雜對象"的創(chuàng)建工作,其通常由各個部分的子對象用一定算法構(gòu)成;由于需求的變化,這個復(fù)雜對象的各個部分經(jīng)常面臨著劇烈的變化,但是將它們組合到一起的算法卻相對穩(wěn)定。
建造者模式是對對象的創(chuàng)建模式。建造者模式可以將一個產(chǎn)品的內(nèi)部表象與產(chǎn)品的生成過程分隔開來,從而可以使一個建造過程生成具有不同的內(nèi)部表象的產(chǎn)品對象。建造者模式利用一個導(dǎo)演者對象和具體建造者對象一個一個地建造出所有的零件,從而建造出完整的產(chǎn)品對象。建造者模式將產(chǎn)品的結(jié)構(gòu)和產(chǎn)品的零件建造過程對客戶端隱藏起來,把對建造過程進行指揮的責任和具體建造零件的責任分隔開來,達到責任劃分和封裝的目的。
意圖:
將一個復(fù)雜對象的構(gòu)建與其表示相分離,使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示。
模式原型UML:
建造者模式涉及到以下角色:
1、抽象建造者(Builder)角色:給出一個抽象接口,以規(guī)范產(chǎn)品對象的各個組成成分的建造。此接口中一般至少規(guī)定兩個方法,一個是創(chuàng)建部分的方法,例如BuilderPart,另一個是返回結(jié)果的方法,例如GetProduct,以約束具體建造者實現(xiàn)。
2、具體建造者(ConcreteBuilder)角色:擔任這個角色的是與應(yīng)用程序緊密相關(guān)的一些類,它們在應(yīng)用程序的調(diào)用下創(chuàng)建產(chǎn)品的實例。這個角色產(chǎn)品實現(xiàn)了抽象建造者接口,主要完成分部創(chuàng)建產(chǎn)品并提供產(chǎn)品對象的實例。
3、導(dǎo)演者(Director)角色:顧名思義,就是具體指揮使用哪個具體創(chuàng)造者來完成產(chǎn)品的創(chuàng)建,是創(chuàng)建工作的調(diào)用者。但是,導(dǎo)演者角色并沒有產(chǎn)品類的具體知識,真正擁有產(chǎn)品類的具體知識的是具體建造者角色。
4、產(chǎn)品(Product)角色:產(chǎn)品角色就是建造中的復(fù)雜對象。一般只有對于復(fù)雜對象的創(chuàng)建才使用建造者模式,一個系統(tǒng)中會出現(xiàn)多于一個的產(chǎn)品類,而這些產(chǎn)品類并不一定有共同的接口,可能完全不關(guān)聯(lián),這時就需要提供多套抽象和具體的建造者來完成不一致的產(chǎn)品的創(chuàng)建或者是采用一個統(tǒng)一接口來標識產(chǎn)品,我個人推薦前者。請大家注意,這里的產(chǎn)品只是一個產(chǎn)品類,不存在繼承關(guān)系,所以也就沒有像抽象工廠中的那種客戶端依賴抽象的說法了。
模式原型代碼:BuilderpatterncodeinC#輸出結(jié)果為:ProductParts
PartA
PartB
ProductParts
PartX
PartY
生活中的實例:
這個小例子向大家展示了一輛車是如何利用建造者模式被創(chuàng)建的,注意商店(這里就是導(dǎo)演者)是如何利用VehicleBuilders按照一定的順序創(chuàng)建一輛車的。RealworldcodeusingAbstractFactoryinC#輸出結(jié)果為:
VehicleType:Scooter
Frame
:ScooterFrame
Engine:none
#Wheels:2
#Doors:0
VehicleType:Car
Frame
:CarFrame
Engine:2500cc
#Wheels:4
#Doors:4
VehicleType:MotorCycle
Frame
:MotorCycleFrame
Engine:500cc
#Wheels:2
#Doors:0
什么情況下使用建造者:1、需要生成的產(chǎn)品對象有復(fù)雜的內(nèi)部結(jié)構(gòu)。每一個內(nèi)部成分本身可以是對象,也可以僅僅是一個對象的組成部分。
2、需要生成的產(chǎn)品對象的屬性相互依賴。建造者模式可以強制實行一種分步驟進行的建造過程,因此,如果對象的一個屬性必須在另一個屬性被賦值之后才可以被賦值,使用建造者模式便是一個很好的設(shè)計思想。
3、在對象創(chuàng)建過程中會使用到系統(tǒng)中的其他一些對象,這些對象在產(chǎn)品對象的創(chuàng)建過程中不易得到。
實際項目舉例
現(xiàn)在我們需要在一個在PC游戲中構(gòu)建房子的程序??梢詷?gòu)建的房子有多種樣式,但是房屋的結(jié)構(gòu)是相對穩(wěn)定的,每個房子都有地基,墻面,房間,門,窗,地板等,同時也存在一定的構(gòu)建順序(必須先構(gòu)建地基,這也就是產(chǎn)品類的組成部分之間的依賴關(guān)系),房子的具體風格可能包括很多種,例如居民住房,存放貨物的倉儲房等。由于房子的結(jié)構(gòu)穩(wěn)定,變化的只是房子的組成部分,這時我們可以考慮采用建造者模式來完成具體房屋的創(chuàng)建工作,即創(chuàng)建房屋和使用房屋責任的分離,房屋的構(gòu)建與其表示相分離。同樣的建造過程可以創(chuàng)建不同的表示(居民住房,存放貨物的倉儲房等)。
本案例中的類與建造者模式中的角色的對應(yīng)關(guān)系:BuildingBuilder(房屋建造者)——Builder(抽象建造者)Architect(建造師)——Director(導(dǎo)演者)HousingBuilder(住房建造者)——ConcreteBuilder1(具體建造者1)StorageBuilder(倉儲房建造者)——ConcreteBuilder2(具體建造者2)Building(房子)——Product(產(chǎn)品)
實現(xiàn)的代碼如下:ProjectcodeusingAbstractFactoryinC#輸出結(jié)果為:
BuildingType:Housing
Base:HousingBase
Wall:HousingWall
#Windows:2
#Doors:2
#Rooms:3
Floor:3*20m2BuildingType:Storage
Base:StorageBase
Wall:StorageWall
#Windows:0
#Doors:1
#Rooms:0
Floor:1000m
小結(jié)1、Builder模式主要用于“分步驟構(gòu)建一個復(fù)雜的對象”。在這其中“分步驟”是穩(wěn)定的,而復(fù)雜對象的各個部分則經(jīng)常變化。
2、Builder模式主要在于應(yīng)對“復(fù)雜對象各個部分”的頻繁需求變動。其缺點在于難以應(yīng)對“分步驟構(gòu)建算法”的需求變動。
3、AbstractFactory模式解決“系列對象”的需求變化,Builder模式解決“對象部分”的需求變化。Builder械通常和Composite模式組合使用
附錄資料:不需要的可以自行刪除HYPERLINK電腦快捷知識大全編輯本段一、常見用法F1顯示當前程序或者windows的幫助內(nèi)容。F2當你選中一個文件的話,這意味著“重命名”F3當你在桌面上的時候是打開“查找:所有文件”對話框F10或ALT激活當前程序的菜單欄windows鍵或CTRL+ESC打開開始菜單CTRL+ALT+DELETE在win9x中打開關(guān)閉程序?qū)υ捒駾ELETE刪除被選擇的選擇項目,如果是文件,將被放入回收站SHIFT+DELETE刪除被選擇的選擇項目,如果是文件,將被直接刪除而不是放入回收站CTRL+N新建一個新的文件CTRL+O打開“打開文件”對話框CTRL+P打開“打印”對話框CTRL+S保存當前操作的文件CTRL+X剪切被選擇的項目到剪貼板CTRL+INSERT或CTRL+C復(fù)制被選擇的項目到剪貼板SHIFT+INSERT或CTRL+V粘貼剪貼板中的內(nèi)容到當前位置ALT+BACKSPACE或CTRL+Z撤銷上一步的操作ALT+SHIFT+BACKSPACE重做上一步被撤銷的操作Windows鍵+L鎖屏鍵Windows鍵+M最小化所有被打開的窗口。Windows鍵+SHIFT+M重新將恢復(fù)上一項操作前窗口的大小和位置Windows鍵+E打開資源管理器Windows鍵+F打開“查找:所有文件”對話框Windows鍵+R打開“運行”對話框Windows鍵+BREAK打開“系統(tǒng)屬性”對話框Windows鍵+CTRL+F打開“查找:計算機”對話框SHIFT+F10或鼠標右擊打開當前活動項目的快捷菜單SHIFT在放入CD的時候按下不放,可以跳過自動播放CD。在打開word的時候按下不放,可以跳過自啟動的宏ALT+F4關(guān)閉當前應(yīng)用程序ALT+SPACEBAR打開程序最左上角的菜單ALT+TAB切換當前程序ALT+ESC切換當前程序ALT+ENTER將windows下運行的MSDOS窗口在窗口和全屏幕狀態(tài)間切換PRINTSCREEN將當前屏幕以圖象方式拷貝到剪貼板ALT+PRINTSCREEN將當前活動程序窗口以圖象方式拷貝到剪貼板CTRL+F4關(guān)閉當前應(yīng)用程序中的當前文本(如word中)CTRL+F6切換到當前應(yīng)用程序中的下一個文本(加shift可以跳到前一個窗口)在IE中:ALT+RIGHTARROW顯示前一頁(前進鍵)ALT+LEFTARROW顯示后一頁(后退鍵)CTRL+TAB在頁面上的各框架中切換(加shift反向)F5刷新CTRL+F5強行刷新目的快捷鍵激活程序中的菜單欄F10執(zhí)行菜單上相應(yīng)的命令A(yù)LT+菜單上帶下劃線的字母關(guān)閉多文檔界面程序中的當前窗口CTRL+F4關(guān)閉當前窗口或退出程序ALT+F4復(fù)制CTRL+C剪切CTRL+X刪除DELETE顯示所選對話框項目的幫助F1顯示當前窗口的系統(tǒng)菜單ALT+空格鍵顯示所選項目的快捷菜單SHIFT+F10顯示“開始”菜單CTRL+ESC顯示多文檔界面程序的系統(tǒng)菜單ALT+連字號(-)粘貼CTRL+V切換到上次使用的窗口或者按住ALT然后重復(fù)按TAB,切換到另一個窗口ALT+TAB撤消CTRL+Z編輯本段二、使用“Windows資源管理器”的快捷鍵目的快捷鍵如果當前選擇展開了,要折疊或者選擇父文件夾左箭頭折疊所選的文件夾NUMLOCK+負號(-)如果當前選擇折疊了,要展開或者選擇第一個子文件夾右箭頭展開當前選擇下的所有文件夾NUMLOCK+*展開所選的文件夾NUMLOCK+加號(+)在左右窗格間切換F6編輯本段三、使用WINDOWS鍵可以使用Microsoft自然鍵盤或含有Windows徽標鍵的其他任何兼容鍵盤的以下快捷鍵。目的快捷鍵在任務(wù)欄上的按鈕間循環(huán)WINDOWS+TAB顯示“查找:所有文件”WINDOWS+F顯示“查找:計算機”CTRL+WINDOWS+F顯示“幫助”WINDOWS+F1顯示“運行”命令WINDOWS+R顯示“開始”菜單WINDOWS顯示“系統(tǒng)屬性”對話框WINDOWS+BREAK顯示“Windows資源管理器”WINDOWS+E最小化或還原所有窗口WINDOWS+D撤消最小化所有窗口SHIFT+WINDOWS+M編輯本段四、“我的電腦”和“資源管理器”的快捷鍵目的快捷鍵關(guān)閉所選文件夾及其所有父文件夾按住SHIFT鍵再單擊“關(guān)閉按鈕(僅適用于“我的電腦”)向后移動到上一個視圖ALT+左箭頭向前移動到上一個視圖ALT+右箭頭查看上一級文件夾BACKSPACE編輯本段五、使用對話框中的快捷鍵目的快捷鍵取消當前任務(wù)ESC如果當前控件是個按鈕,要單擊該按鈕或者如果當前控件是個復(fù)選框,要選擇或清除該復(fù)選框或者如果當前控件是個選項按鈕,要單擊該選項空格鍵單擊相應(yīng)的命令A(yù)LT+帶下劃線的字母單擊所選按鈕ENTER在選項上向后移動SHIFT+TAB在選項卡上向后移動CTRL+SHIFT+TAB在選項上向前移動TAB在選項卡上向前移動CTRL+TAB如果在“另存為”或“打開”對話框中選擇了某文件夾,要打開上一級文件夾BACKSPACE在“另存為”或“打開”對話框中打開“保存到”或“查閱”F4刷新“另存為”或“打開”對話框F5編輯本段六、桌面、我的電腦和“資源管理器”快捷鍵選擇項目時,可以使用以下快捷鍵。目的快捷鍵插入光盤時不用“自動播放”功能按住SHIFT插入CD-ROM復(fù)制文件按住CTRL拖動文件創(chuàng)建快捷方式按住CTRL+SHIFT拖動文件立即刪除某項目而不將其放入SHIFT+DELETE“回收站”顯示“查找:所有文件”F3顯示項目的快捷菜單APPLICATION鍵刷新窗口的內(nèi)容F5重命名項目F2選擇所有項目CTRL+A查看項目的屬性ALT+ENTER或ALT+雙擊可將APPLICATION鍵用于Microsoft自然鍵盤或含有APPLICATION鍵的其他兼容鍵編輯本段七、Microsoft放大程序的快捷鍵這里運用Windows徽標鍵和其他鍵的組合??旖萱I目的Windows徽標+PRINTSCREEN將屏幕復(fù)制到剪貼板(包括鼠標光標)Windows徽標+SCROLLLOCK將屏幕復(fù)制到剪貼板(不包括鼠標光標)Windows徽標+PAGEUP切換反色。Windows徽標+PAGEDOWN切換跟隨鼠標光標Windows徽標+向上箭頭增加放大率Windows徽標+向下箭頭減小放大率編輯本段八、使用輔助選項快捷鍵目的快捷鍵切換篩選鍵開關(guān)右SHIFT八秒切換高對比度開關(guān)左ALT+左SHIFT+PRINTSCREEN切換鼠標鍵開關(guān)左ALT+左SHIFT+NUMLOCK切換粘滯鍵開關(guān)SHIFT鍵五次切換切換鍵開關(guān)NUMLOCK五秒QQ快捷鍵,玩QQ更方便Alt+S快速回復(fù)Alt+C關(guān)閉當前窗口Alt+H打開聊天記錄Alt+T更改消息模式Ait+J打開聊天紀錄Ctrl+A全選當前對話框里的內(nèi)容Ctrl+FQQ里直接顯示字體設(shè)置工具條Ctrl+J輸入框里回車(跟回車一個效果)Ctrl+M輸入框里回車(跟回車一個效果)Ctrl+L對輸入框里當前行的文字左對齊Ctrl+R對輸入框里當前行的文字右對齊Ctrl+E對輸入框里當前行的文字居中Ctrl+V在qq對話框里實行粘貼Ctrl+Z清空/恢復(fù)輸入框里的文字Ctrl+回車快速回復(fù)這個可能是聊QQ時最常用到的了Ctrl+Alt+Z快速提取消息Ctrl+Alt+A捕捉屏幕最常用的快捷鍵F5刷新DELETE刪除TAB改變焦點CTRL+C復(fù)制CTRL+X剪切CTRL+V粘貼CTRL+A全選CTRL+Z撤銷CTRL+S保存ALT+F4關(guān)閉CTRL+Y恢復(fù)ALT+TAB切換CTRL+F5強制刷新CTRL+W關(guān)閉CTRL+F查找SHIFT+DELETE永久刪除CTRL+ALT+DEL任務(wù)管理SHIFT+TAB-反向切換CTRL+空格--中英文輸入切換CTRL+Shift輸入法切換CTRL+ESC--開始菜單CTRL+ALT+ZQQ快速提取消息CTRL+ALT+AQQ截圖工具CTRL+ENTERQQ發(fā)消息Alt+1保存當前表單Alt+2保存為通用表單Alt+A展開收藏夾列表資源管理器END顯示當前窗口的底端HOME顯示當前窗口的頂端NUMLOCK+數(shù)字鍵盤的減號(-)折疊所選的文件夾NUMLOCK+數(shù)字鍵盤的加號(+)顯示所選文件夾的內(nèi)容NUMLOCK+數(shù)字鍵盤的星號(*)顯示所選文件夾的所有子文件夾向左鍵當前所選項處于展開狀態(tài)時折疊該項,或選定其父文件夾向右鍵當前所選項處于折疊狀態(tài)時展開該項,或選定第一個子文件夾自然鍵盤【窗口】顯示或隱藏“開始”菜單【窗口】+F1幫助【窗口】+D顯示桌面【窗口】+R打開“運行”【窗口】+E打開“我的電腦”【窗口】+F搜索文件或文件夾【窗口】+U打開“工具管理器”【窗口】+BREAK顯示“系統(tǒng)屬性”【窗口】+TAB在打開的項目之間切換輔助功
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 福建省龍巖市2024-2025學(xué)年高一上學(xué)期1月期末教學(xué)質(zhì)量檢測數(shù)學(xué)試題
- 箱涵混凝土施工方案
- 液壓升降壩施工方案
- 2025年證券培訓(xùn)員面試題及答案
- 2025年三違人員考試試題及答案
- 5月份黃果樹瀑布旅游美篇
- 5年級下冊第5課朗讀
- 5個英語新年祝福語
- c8h10o同分異構(gòu)體找法
- 地暖漏水維修方法
- GB/T 33365-2016鋼筋混凝土用鋼筋焊接網(wǎng)試驗方法
- GB/T 16799-2018家具用皮革
- GB/T 14541-2017電廠用礦物渦輪機油維護管理導(dǎo)則
- GB 10133-2014食品安全國家標準水產(chǎn)調(diào)味品
- 講題比賽游戲中的必勝策略問題-(取棋子游戲)課件
- 旅游學(xué)概論李天元版復(fù)習總結(jié)
- 人教版八年級上歷史思維導(dǎo)圖課件
- 重慶大學(xué)介紹課件
- 江蘇省南京市2020年中考英語試題
- 《電氣裝配車間生產(chǎn)工序流程卡》中英文對譯版
- 四年級下冊英語課件:Unit 4 There are seven days in a week-Lesson 19人教精通版
評論
0/150
提交評論