![追求代碼質(zhì)量(11)-用AOP進(jìn)行防御性編程-Java開發(fā)Java經(jīng)驗(yàn)技巧_第1頁](http://file2.renrendoc.com/fileroot_temp3/2021-11/30/2b23293f-dcab-4cd1-8445-2fd2df155b6c/2b23293f-dcab-4cd1-8445-2fd2df155b6c1.gif)
![追求代碼質(zhì)量(11)-用AOP進(jìn)行防御性編程-Java開發(fā)Java經(jīng)驗(yàn)技巧_第2頁](http://file2.renrendoc.com/fileroot_temp3/2021-11/30/2b23293f-dcab-4cd1-8445-2fd2df155b6c/2b23293f-dcab-4cd1-8445-2fd2df155b6c2.gif)
![追求代碼質(zhì)量(11)-用AOP進(jìn)行防御性編程-Java開發(fā)Java經(jīng)驗(yàn)技巧_第3頁](http://file2.renrendoc.com/fileroot_temp3/2021-11/30/2b23293f-dcab-4cd1-8445-2fd2df155b6c/2b23293f-dcab-4cd1-8445-2fd2df155b6c3.gif)
![追求代碼質(zhì)量(11)-用AOP進(jìn)行防御性編程-Java開發(fā)Java經(jīng)驗(yàn)技巧_第4頁](http://file2.renrendoc.com/fileroot_temp3/2021-11/30/2b23293f-dcab-4cd1-8445-2fd2df155b6c/2b23293f-dcab-4cd1-8445-2fd2df155b6c4.gif)
![追求代碼質(zhì)量(11)-用AOP進(jìn)行防御性編程-Java開發(fā)Java經(jīng)驗(yàn)技巧_第5頁](http://file2.renrendoc.com/fileroot_temp3/2021-11/30/2b23293f-dcab-4cd1-8445-2fd2df155b6c/2b23293f-dcab-4cd1-8445-2fd2df155b6c5.gif)
版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)
文檔簡介
1、追求代碼質(zhì)量(11):用aop進(jìn)行防御性編程-編程開發(fā)技術(shù)追求代碼質(zhì)量(11):用aop進(jìn)行防御 性編程原文出處:ibm中國開發(fā)人員測試的主要缺點(diǎn)是:絕大部分測試都是在理想的場景屮進(jìn)行的。在這些 情況下并不會出現(xiàn)缺陷一一能導(dǎo)致出現(xiàn)問題的往往是那些邊界情況。什么是邊界情況呢?比方說,把?null?值傳入一個并未編寫如何處理?null?值的 方法中,這就是一種邊界情況。大多數(shù)開發(fā)人員通常都不能成功測試這樣的場景, 因?yàn)檫@沒多大意義。但不管有沒有意義,發(fā)生了這樣的情況,就會拋出一 個?nullpointerexception,然后整個程序就會崩潰。本刀,我將為您推薦一種多層面的方法,來處理代碼中那些
2、不易預(yù)料的缺陷。嘗 試為應(yīng)用程序整合進(jìn)防御性編程、契約式設(shè)計和-種叫做oval的易用的通用驗(yàn) 證框架。將敵人暴露出來清單1中的代碼為給定的?class?對象(省去了?java. lang. object,因?yàn)樗?對象都最終由它擴(kuò)展)構(gòu)建一個類層次。但如果仔細(xì)看的話,您會注意到一個有 待發(fā)現(xiàn)的潛在缺陷,即該方法對對象值所做的假設(shè)。清單1.不檢驗(yàn)null的方法public static hierarchy buildhierarchy(class clzz) hierarchy hi er 二 new hierarchyo ;hier. setbaseclass(clzz);class supe
3、rclass 二 clzz. gctsuperclass ();if (superclass != null && superclass. getname(). equals(/zjava. lang. object") return hier;elsewhile(clzz. getsuperclass() != null) &&(!clzz. getsuperclass (). getname(). equals (/zjava. lang. object") clzz 二 clzz.getsuperclass();hier.addclas
4、s(clzz);return hier;剛編好這個方法,我還沒注意到這個缺陷,但由于我狂熱地崇拜開發(fā)人員測試, 于是我編寫了一個使用testng的常規(guī)測試。而且,我述利用了 testng方便 的?dataprovider?特性,借助該特性,我創(chuàng)建了一個通用的測試用例并通過另一 個方法來改變它的參數(shù)。運(yùn)行清單2中定義的測試用例會產(chǎn)生兩個通過結(jié)果! 一切都運(yùn)轉(zhuǎn)良好,不是嗎?清單2.驗(yàn)證兩個值的testng測試import java, util .vector;import static org.testng. assert. asscrtequals;import org. testng. ann
5、otations. dataprovider;import org.testng. annotations. test;public class bui 1dhierarchytest dataprovider (name = "class-hierarchies") public object datavalues ()return new objectvector, class, new string /zjava. util. abstractlisz/java, ut訂.abstractcollcctioiwstring, class, new string ;tc
6、st(dataprovidcr 二"class-hierarchies")public void verifyhierarchies(class clzz, string names) throws exception!hierarchy hier 二 hierarchybuilder. buildhierarchy (clzz);assertequal s (hier. gethierarchycleissnennes (), names, "values were not equal");至此,我還是沒有發(fā)現(xiàn)缺陷,但一些代碼問題卻困擾著我。如果有人不
7、經(jīng)意地 為?class?參數(shù)傳入一個?null?值會怎么樣呢?清單1?中第4行的 clzz. gctsuperclass ()?調(diào)用會拋出一個?nullpointcrexccption,是這樣嗎?測試我的理論很容易;其至都不用從頭開始。僅僅把?null, null?添加到初 始?bu i 1 dh i er ar chyt e s t ?的?datavalues?方法中的多維?object?數(shù)組中,然后再 次運(yùn)行它。我定會得到如圖1所示的?nullpointerexception:gyrate4 止圖 l 可怕的 nulipointerexceptionag逢 m zsfated testse
8、 a adm(iwo)0. venfytterttdtes3 sw/)ttarmdm(i=e excapoan多1 "ajaromsair<)er£jxf(tt)n上s gnv<»*ard4d«na>tw<hr.g為6umor.ujdcv.6ygvchy錢三 .hcnvet<m4m*.6y抵 krf>tterar<memprorfcir:"me:?m三 « aveaect 曲六hetha如ewcrk<"r&me h«rg三 mrmeaeclmrvefmhodc
9、om<ru<rf.rrzaojrar«>r<i sebree)三 m dr、o4»p<rh2k<*wrlt<i rt2m(urtoon ry)=««r*d 切 fettect me<n)d rwotefthaomr» source)s «t arg心rttngclarndjmhew>1«|p<r.r¥v*ethod(gf<miie|pw .”.953)三 .qrgzftg.femm.lry心 exibzgexmr.eysn)二& cnerf?
10、)參見這里的?全圖。防御性編程一旦岀現(xiàn)這個問題,下一步就是要拿出對抗的策略。問題是我控制不了這個方法 能否接收這種輸入。對于這類問題,開發(fā)人員通常會使用防御性編程技術(shù),該技 術(shù)專門用來在發(fā)生摧毀性后果前捕捉潛在錯誤。對象驗(yàn)證是處理不確定性的一項(xiàng)經(jīng)典的防御性編程策略。相應(yīng)地,我會添加一項(xiàng) 檢驗(yàn)來驗(yàn)證clzz?是否為?null,如清單3所示。如杲其值最終為?null,我就 會拋出一個runtimeexception?來警告他人注意這個潛在問題。清單3.添加驗(yàn)證null值的檢驗(yàn)publ ic static hierarchy buildhierarchy(class clzz) if (clzz =
11、 null)throw new runtimeexception(zzclass parameter can not be null");hierarchy hier 二 new hierarchy();hier.setbascclass(clzz);class superclass 二 clzz.getsuperclass ();if(superclass != null &&superclass. gctnamc () equals (z/java. lang. objcct)return hier;else while(clzz. getsuperclass()
12、 != nul1) &&(!clzz. getsuperclass(). getname(). equals(z,java. lang. object") clzz = clzz. getsuperclass();hier.addclass(clzz);return hier;很自然,我也會編寫一個快速測試用例來鏗證我的檢驗(yàn)是否真能避 免?nu 11 pointerexception,如清單 4 所示:清單4.驗(yàn)證null檢驗(yàn)test(expectedexceptions=runtimeexception. class) public void verifyhiera
13、rch)null () throws exception!class clzz 二 null;hierarchybui1der. bui1dhierarchy(null);在本例屮,防御性編程似乎解決了問題。但僅依靠這項(xiàng)策略會存在一些缺陷。防御的缺陷盡管防御性編程冇效地保證了方法的輸入條件,但如果在一系列方法中使用它, 不免過于重復(fù)。熟悉面向方面編程(或aop)的人們會把它認(rèn)為是橫切關(guān)注點(diǎn), 這意味著防御性編程技術(shù)橫跨了代碼庫。許多不同的對彖都釆用這些語法,盡管 從純面向?qū)ο蟮挠^點(diǎn)來看這些語法跟對象毫不相關(guān)。而且,橫切關(guān)注點(diǎn)開始滲入到契約式設(shè)計5憂)的概念中。dbc是這樣一項(xiàng)技 術(shù),它通過在組
14、件的接口顯式地陳述每個組件應(yīng)有的功能和客戶機(jī)的期望值來確 保系統(tǒng)屮所有的組件完成它們應(yīng)盡的職責(zé)。從dbc的角度講,組件應(yīng)有的功能 被認(rèn)為是后置條作,本質(zhì)上就是組件的責(zé)任,而客戶機(jī)的期槊值則普遍被認(rèn)為是 前置條件。另外,在純dbc術(shù)語中,遵循dbc規(guī)則的類針對其將維護(hù)的內(nèi)部一 致性與外部世界有一個契約,即人所共知的類/變式。契約式設(shè)計我在以前的一篇關(guān)于用nice編程的文章屮介紹過dbc的概念,nice是一門與 jre兼容的面向?qū)﹀杈幊陶Z言,它的特點(diǎn)是側(cè)重于模塊性、可表達(dá)性和安全性。 有趣的是,nice并入了功能性開發(fā)技術(shù),其屮包括了一些在面向方面編程屮的 技術(shù)。功能性開發(fā)使得為方法指定前置條件和
15、后置條件成為可能。盡管nice支持dbc,但它與java?語言完全不同,因而很難將其用于開發(fā)。 幸運(yùn)的是,很多針對java語言的庫也都為dbc提供了方便。每個庫都有其優(yōu) 點(diǎn)和缺點(diǎn),每個庫在dbc內(nèi)針對java語言進(jìn)行構(gòu)建的方法也不同;但最近的 一些新特性大都利用了 aop來更多地將dbc關(guān)注點(diǎn)包括進(jìn)來,這些關(guān)注點(diǎn)基本 上就相當(dāng)于方法的包裝器。前置條件在包裝過的方法執(zhí)行前擊發(fā),后置條件在該方法完成后擊發(fā)。使用aop 構(gòu)建dbc結(jié)構(gòu)的一個好處(請不要同該語言本身相混淆?。┦?可以在不需要dbc 關(guān)注點(diǎn)的環(huán)境屮將這些結(jié)構(gòu)關(guān)掉(就像斷言能被關(guān)掉一樣)。以橫切的方式對待 安全性關(guān)注點(diǎn)的真正妙處是:可以有
16、效地產(chǎn)勸?這些關(guān)注點(diǎn)。眾所周知,重用是 面向?qū)ο缶幊痰囊粋€基本原則。aop如此完美地補(bǔ)充了 oop難道不是一件極好 的事情嗎?結(jié)合了 oval的aopoval是一個通用的驗(yàn)證框架,它通過aop支持簡單的dbc結(jié)構(gòu)并明確地允許:為類字段和方法返冋值指定約束條件為結(jié)構(gòu)參數(shù)指定約束條件為方法參數(shù)指定約束條件此外,oval還帶來大量預(yù)定義的約束條件,這讓創(chuàng)建新條件變得和當(dāng)容易。由于oval使用aspectj的aop實(shí)現(xiàn)來為dbc概念定義建戎,所以必須將 aspectj并入一個使用oval的項(xiàng)廿中。對于不熟悉aop和aspectj的人們來 說,好消息是這不難實(shí)現(xiàn),且使用oval (甚至是創(chuàng)建新的約束條件
17、)并不需要 真正對方而進(jìn)行編碼,只需編寫一個簡單的自引導(dǎo)程序即可,該程序會使oval 所附帶的默認(rèn)方面植入您的代碼中。在創(chuàng)建這個自引導(dǎo)程序方面前,要先下載aspectjo具體地說,您需要將?aspect jtool s?和?aspect jrt?jar文件并入您的構(gòu)建中來編譯所需的自引導(dǎo) 程序方面并將其編入您的代碼中。自引導(dǎo)aop下載了 aspectj后,下一步是創(chuàng)建一個可擴(kuò)展oval?guardaspect?的方面。它 本身不需要做什么,如清單5所示。請確保文件的擴(kuò)展名以亦結(jié)束,但不要 試著用常規(guī)的?javac?對其進(jìn)行編譯。清單5. defaultguardaspect自引導(dǎo)程序方面imp
18、ort net. sf. oval, aspectj. guardaspect;public aspect defaultguardaspect extends guardaspectpublic defaultguardaspect()super ();aspectj引入了一個ant任務(wù),稱為?iajc,充當(dāng)著?javac?的角色;此過程對 方面進(jìn)行編譯并將其編入主體代碼中。在本例中,只要是我指定了 oval約束條 件的地方,在oval代碼屮定義的邏輯就會編入我的代碼,進(jìn)而充當(dāng)起前置條件 和后置條件。請記住?iajc?代替t?javaco例如,清單6是我的ant build. xml文件的一
19、個 代碼片段,其屮對代碼進(jìn)行了編譯并把通過代碼標(biāo)注發(fā)現(xiàn)的所右oval方面編入 進(jìn)來,如下所示:清單6.用aop編譯的ant構(gòu)建文件片段<target name二aspectjc depends二get-deps><taskdcfresource二org/aspectj/tools/ant/taskdefs/aspectjtaskdefs properties" <classpath><path refid二bu訂d. classpath" />/classpath</taskdcf><iajc destdir=/z
20、$ classesdir/z debu薩on" source二 1. 5"><classpath><path refid二buiid. classpath" /></classpath><sourceroots><pathelement location二src/java" /><pathelement location二test/java /></sourceroots></iajc></target>為oval鋪好了路、為aop過程做了引
21、導(dǎo)之后,就可以開始使用java 5標(biāo)注 來為代碼指定簡單的約束條件了。oval的可重用約束條件用oval為方法指定前置條件必須對方法參數(shù)進(jìn)行標(biāo)注。相應(yīng)地,當(dāng)調(diào)用一個用 oval約束條件標(biāo)注過的方法吋,oval會在該方法真正執(zhí)行麗驗(yàn)證該約朿條件。在我的例子中,我想要指定當(dāng)?class?參數(shù)的值為?null?時,buildllierarchy? 方法不能被調(diào)用。oval通過?notnull?標(biāo)注支持此約束條件,該標(biāo)注在方法所 需的所有參數(shù)前指定。也要注意,任何想要使用oval約束條件的類也必須在類 層次上指定?guarded?標(biāo)注,就像我在清單7中所做的那樣:清單7. oval約束條件import
22、 net.sf. oval, annotations. guarded;import net. sf. oval, constraints. notnull;guardedpublic class hierarchybuiider public static hierarchy bui1dhierarchy(notnull class clzz) hierarchy hier 二 new hierarchy();hicr.sctbaseclass(clzz);class superclass 二 clzz.getsuperclass();if(superclass != null &&
23、amp;superclass. gctnamc () equals (z/java. lang. objcct) return hier;else while(clzz. getsuperclass() != null) &&(!clzz. getsuperclass(). getname(). equals(z,java. lang. object") clzz = clzz. getsuperclass (); hier. addclass (clzz);return hier;通過標(biāo)注指定這個約朿條件意味著我的代碼不再會被重復(fù)的條件弄得亂七八耕, 這些條件檢查?
24、null?值,并且一旦找到該值就會拋出異?!,F(xiàn)在這項(xiàng)邏輯由oval 處理,且處理的方法有些相似 事實(shí)上,如果違反了約束條件,oval會拋 出一個?constraintsviolatcdexccption,它是?runtimcexccption?的 了類。當(dāng)然,我卜一步就要編譯?hierarchybuilder?類和?清單5?屮相應(yīng)的?defaultguardaspect?類。我用?清單6?中的?iajc?任務(wù)來實(shí)現(xiàn)這一目的,這 樣我就能把oval的行為編入我的代碼屮了。接下來,我更新?清單4?中的測試用例來驗(yàn)證是否處出了一個?constraintsviolatcdexccption,如清單 8
25、 所示:清單 & 驗(yàn)證是否拋出 了 constraintsviolatedexception©test(expectedexceptions二constraintsviolatedexception.class) public void verifyhierarchynull() throws exceptionclass clzz = null;hierarchybuilder. bui1dhierarchy (clzz);指定后置條件止如您所見,指定而置條件其實(shí)相當(dāng)容易,指定后置條件的過程也是一樣。例如, 如果我想對所冇調(diào)用?buildhierarchy?的程序保證它不會
26、返回?null?值(這樣, 這些調(diào)用程序就不需要再檢查這個了),我可以在方法聲明之上放置一 個?notnull?標(biāo)注,如清單9所示:清單9. oval中的后置條件©notnullpublic static hierarchy buildhierarchy(notnul1 class clzz) /method body當(dāng)然,notnull?絕不是oval提供的惟一約束條件,但我發(fā)現(xiàn)它能非常有效地 限制這些令人討厭的?nullpointcrexccption,或至少能夠快速地暴謬?它們。更多的oval約束條件oval也支持在方法調(diào)用前或后對類成員進(jìn)行預(yù)先驗(yàn)證。這種機(jī)制具有限制針對 特定
27、約束條件的重復(fù)條件測試的好處,如集合大小或之前討論過的非?null?的情 況。例如,在清單10屮,我使用?hierarchybu訂der?定義了一個為類層次構(gòu)建報告 的ant任務(wù)。請注意?execute()?方法是如何調(diào)用?validate的,后者會依次驗(yàn) 證?fileset?類成員是否含值;如果不含,會拋出一個異常,因?yàn)闆]有了要評估 的類,該報告不能運(yùn)行。清單10.帶條件檢驗(yàn)的hierarchybuiidertaskpublic class hierarch)buiidertask extends task private report report;private list fileset
28、;private void validate() throws buildexceptionif (! (this. fileset. size() > 0) throw new buildexception(/zmust supply classes to evaluate");if (this, report = nul 1)this. log(z,no report defined, printing xml to system, out);public void execute() throws buildexccption validate ();string cla
29、sses = this. getqualifiedclassnames(this. fileset); hierarchy hclz 二 new hierarchyclasses, length;try for(int x = 0; x < classes, length; x+) hclzx = hierarch)builder. buildhierarchy (classesx);batchhi erarchyxmlreport xml er = new batchhi erarchyxmlreport(new date(), hclz);this.handlereportcreat
30、ion(xmler);catch (classnotfoundexception e) throw new buiidexception(zzunable to load class check classpath! + e. £etmessage ();/more methods below.因?yàn)槲矣玫氖莖val,所以我可以完成下列任務(wù):對?fileset?類成員指定一個約束條件,確保使用?size?標(biāo)注時其大小總是至少 為1或更大。 確保在使用?prevalidatethis?標(biāo)注調(diào)用?execute ()?方法湖驗(yàn)證這個約束條 件。這兩步讓我能夠有效地去除?validate()
31、?方法屮的條件檢驗(yàn),讓oval為我完成 這些,如清單11所示:清單11.經(jīng)過改進(jìn)、無條件檢驗(yàn)的hierarchybuiidertask©guardedpublic class hierarchybuiidertask extends task private report report;sizc(min = 1)private list fileset;private void validate() throws buildexception if (this. report 二二 nul1) this.1og("no report defined, printing xm
32、l to system.out");©prevaiidatethi spublic void execute() throws buildexception validate();string classes 二 this. getqualifiedclassnames (this.fileset);hierarchy hclz 二 new hierarchyclasses, length;try for(int x = 0; x < classes.length; x+)hclzx二 hierarchybuilder. buildhierarchy(classesx
33、);batchllicrarchyxmlrcport xmlcr = new batchllicrarchyxmlrcport (newdate(), hclz);this.hand1ereportcreation(xmler);catch(classnotfoundexception e) throw new buildexception(unable to load class check classpath! + c. gctmcssagco);/more methods below.清單11中的?execute()?一經(jīng)調(diào)用(由ant完成),oval就會驗(yàn)證?f訂eset? 成員。如果
34、其為空,就意味著沒有指定任何要評估的類,就會拋岀一個?constraintsviolatedexceptiono這個界常會暫停這一過程,就像初始代碼 一樣,只不過初始代碼會拋出一個?bui 1 dexception0彳口口防御性編程結(jié)構(gòu)阻止了一個又一個缺陷,但這些結(jié)構(gòu)本身卻不免為代碼添加了重 復(fù)的邏輯。把防御性編程技術(shù)和而向方而編程(通過契約式設(shè)計)聯(lián)系起來是抵 御所有重復(fù)性代碼的一道堅(jiān)強(qiáng)防線。oval并不是惟一可用的dbc庫,事實(shí)上其dbc結(jié)構(gòu)對比其他框架來說是相當(dāng) 有限的(例如,它未提供指定類不變式的簡易方法)。從另一方面講,oval很 容易使用,對約束條件也有很大的選擇余地,若想要花少量力氣就可向代碼添加 驗(yàn)證約束條件,它無疑是個上佳之選。另外,用oval創(chuàng)建定制約束條件也相當(dāng) 簡單,所以請不要再添加條件檢驗(yàn)了,盡情享用aop吧!參考資料學(xué)習(xí) 您nj以參閱木文在developerworks全球站點(diǎn)上的?英文原文?。 “aop 解決緊密耦合的難題” (andrew glover, developerworks, 2004 年 2 月): 親自體驗(yàn)一下aop的功能設(shè)計概念z (靜態(tài)橫切)如何把可能亂成一團(tuán)的緊密 耦合的
溫馨提示
- 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)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年鉛壓延加工材合作協(xié)議書
- 2025年清理去石設(shè)備合作協(xié)議書
- 八年級英語下冊 Unit 9 單元綜合測試卷(人教陜西版 2025年春)
- 2024-2025學(xué)年四川省南充市高坪區(qū)四年級(上)期末數(shù)學(xué)試卷
- 2025年臨滄市三方合作出資協(xié)議范文(2篇)
- 2025年產(chǎn)品購銷買賣合同(2篇)
- 2025年產(chǎn)權(quán)交易所項(xiàng)目掛牌服務(wù)協(xié)議(6篇)
- 2025年個人門面出租合同標(biāo)準(zhǔn)樣本(2篇)
- 2025年五年級語文教學(xué)鑒定總結(jié)模版(三篇)
- 2025年代理委托處理房地產(chǎn)協(xié)議(2篇)
- 《中電聯(lián)團(tuán)體標(biāo)準(zhǔn)-220kV變電站并聯(lián)直流電源系統(tǒng)技術(shù)規(guī)范》
- 中國主要蜜源植物蜜源花期和分布知識
- 電化學(xué)免疫傳感器的應(yīng)用
- 數(shù)據(jù)中心基礎(chǔ)知識培訓(xùn)-2024鮮版
- 供電企業(yè)輿情的預(yù)防及處置
- 【高中語文】《氓》課件++統(tǒng)編版+高中語文選擇性必修下冊
- T-WAPIA 052.3-2023 無線局域網(wǎng)設(shè)備技術(shù)規(guī)范 第3部分:接入點(diǎn)和控制器
- 第4課+中古時期的亞洲(教學(xué)設(shè)計)-【中職專用】《世界歷史》(高教版2023基礎(chǔ)模塊)
- 金點(diǎn)子活動總結(jié)匯報
- 運(yùn)動技能學(xué)習(xí)與控制完整
- 原料驗(yàn)收標(biāo)準(zhǔn)知識培訓(xùn)課件
評論
0/150
提交評論