版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
1、面向?qū)ο笤O(shè)計(jì)的5個(gè)原則 Robert Martin在敏捷軟件開(kāi)發(fā):原則,模式和實(shí)踐提到了面向?qū)ο笤O(shè)計(jì)的5個(gè)原則,每個(gè)原則的第一個(gè)英文字母合在一起名為“SOLID”,這五個(gè)原則分別是:?jiǎn)我宦氊?zé)的原則(Single responsibility Principle),開(kāi)閉原則(Open Close Principle),Liskov替換原則(Liskov substitution principle),“接口分離原則(Interface Segregation Principle),依賴倒置(Dependency Inversion principle)。當(dāng)然眾多軟件設(shè)計(jì)大師
2、們其實(shí)提煉過(guò)更多的原則,但是從“SOLID”起步肯定是一個(gè)不賴的選擇。下面我們從一個(gè)簡(jiǎn)單的程序入手,通過(guò)逐步改進(jìn)程序的方法,逐步體會(huì)這些原則在軟件設(shè)計(jì)中作用。1. 起步如果我們要做如圖1這樣一個(gè)簡(jiǎn)化的繪圖程序,程序界面的上邊是一個(gè)工具欄,工具欄中包含了基本圖形(圓形,矩形,三角形,橢圓形)的按鈕,在工具欄下方是一個(gè)畫(huà)布。用戶使用這個(gè)繪圖程序時(shí),只要點(diǎn)擊工具欄中的圖形按鈕,就可以在畫(huà)布上繪制各種對(duì)應(yīng)的圖形。圖1. 一個(gè)極簡(jiǎn)單的繪圖軟件我們可以建一個(gè)名字叫畫(huà)布(Canvas)的類,在類中建立以繪制各種基本圖形的方法,然后由一個(gè)User類模擬用戶使用這個(gè)程序繪制各種圖形,如圖2所示。圖2.
3、 畫(huà)布類繪制各種基本圖形現(xiàn)在把上述設(shè)想變成代碼,因?yàn)樗械某绦蚨紝?xiě)的一個(gè)文件里,其中User類是public類,并且包含main()函數(shù),所以程序的名稱User.java。我們的第一個(gè)程序如下:class Canvas public void drawCircle() /這里只打印了一個(gè)字符串模擬實(shí)現(xiàn)畫(huà)圓的所有步驟 System.out.println(&quo
4、t;Drew a Circle"); public void drawTriangle() System.out.println("Drew a Triangle"); public void
5、0;drawRactangle() System.out.println("Drew a Ractangle"); public void drawEllipse() System.out.println("Drew a E
6、llipse"); public class User public static void main(String args) Canvas canvas=new Canvas(); /在這里模擬用戶使用軟件
7、canvas.drawCircle(); canvas.drawRactangle(); 當(dāng)然這個(gè)程序做了極大的簡(jiǎn)化,通過(guò)打印字符串來(lái)模擬了畫(huà)圖形的所有復(fù)雜步驟。2.基于單一職責(zé)的第一次改進(jìn)可以看出畫(huà)布類Canvas實(shí)現(xiàn)了各種圖形的繪制程序,這就給程序的維護(hù)帶來(lái)了麻煩,因?yàn)楫?huà)布類需要對(duì)所有的程序員開(kāi)放,任何一個(gè)“基本圖形”繪制程序的修改都需要修改畫(huà)布類,即使是擴(kuò)充一個(gè)新的圖形,也需要修改畫(huà)布類,造成這種狀況的原因,是程序的設(shè)計(jì)違反了“單一職責(zé)的原則(Sin
8、gle responsibility Principle)”,單一職責(zé)原則要求每個(gè)類只負(fù)責(zé)一個(gè)職責(zé),這樣對(duì)這個(gè)類的修改,只會(huì)發(fā)生在這個(gè)類所負(fù)責(zé)的職責(zé)需要改變時(shí),例如某個(gè)類負(fù)責(zé)繪制圓的職責(zé),只有當(dāng)圓的繪制方法出現(xiàn)問(wèn)題或需要修改,才需要修改這個(gè)類,一個(gè)設(shè)計(jì)良好的程序應(yīng)該避免在修改某個(gè)類的同時(shí)需要修改其他類。解決的方法是把畫(huà)布類里的各種不同圖形的繪制函數(shù)放到不同的類里,這樣每個(gè)類的職責(zé)就單純了。在這個(gè)例子中,分配職責(zé)后的圖形類里面只有一個(gè)方法draw(),但是這并不表示單一職責(zé)原則要求一個(gè)類里只能有一個(gè)方法。單一職責(zé)原則要求,一個(gè)類里哪怕有10個(gè)方法,它們也是為同一職責(zé)服務(wù)的?,F(xiàn)在畫(huà)布類要做的只是調(diào)
9、用具體的圖形類中的相應(yīng)方法,這樣,如果修改某個(gè)具體的圖形類(例如Rectangle類)就和不會(huì)引起畫(huà)布類和其他類的修改了,如圖3所示。圖3. 將繪制圖形的職責(zé)分配到具體的類修改后的第二個(gè)程序如下:class Cirle public void draw() /這里只打印了一個(gè)字符串模擬實(shí)現(xiàn)畫(huà)圓的所有步驟 System.out.print
10、ln("Drew a Circle"); class Triangle public void draw() System.out.println("Drew a Triangle"); class Ractangle p
11、ublic void draw() System.out.println("Drew a Ractangle"); class Ellipse public void draw() System.out.println("Drew a Ellipse&qu
12、ot;); class Canvas /畫(huà)布類中只要簡(jiǎn)單地調(diào)用圖形對(duì)象的方法就可以,不再需要實(shí)現(xiàn)復(fù)雜的繪制步驟 public void paint(Cirle c) c.draw(); /只需要簡(jiǎn)單的調(diào)用,不需要關(guān)心細(xì)節(jié)
13、160; public void paint(Triangle t) t.draw(); /只需要簡(jiǎn)單的調(diào)用,不需要關(guān)心細(xì)節(jié) public void paint(Ellipse e) e
14、.draw(); public class User public static void main(String args) /在這里模擬用戶使用軟件 Canvas canvas=new Canvas();
15、 Cirle c=new Cirle(); canvas.paint(c); Triangle t=new Triangle(); canvas.paint(t);
16、0; Ellipse e=new Ellipse(); canvas.paint(e); 3.如何做到擴(kuò)充時(shí)不需要修改但是畫(huà)布類Canvas可能會(huì)碰到這樣的麻煩:如果有新的圖形加入系統(tǒng),就需要不斷修改,例如,如果要畫(huà)“星”,那么就要添加一個(gè)Star類,然后Canvas類要修改代碼,從而可以調(diào)用Star類的方法,也就是說(shuō)Canvas需要不斷修改才能適應(yīng)系統(tǒng)的擴(kuò)充,我們來(lái)看看程序修改
17、的過(guò)程。首先,加入Star類:class Star public void draw() System.out.println("Drew a star"); 然后,在畫(huà)布類(Canvas)中加入新的方法: public void drawStar(Star s)
18、; s.draw(); 最后在User類中模擬用戶調(diào)用: Star s=new Star(); canvas.drawStar(s);完整的代碼:class Cirle public void draw() /
19、這里只打印了一個(gè)字符串模擬實(shí)現(xiàn)畫(huà)圓的所有步驟 System.out.println("Drew a Circle"); class Triangle public void draw() System.out.println("Drew a Triangle");
20、; class Ractangle public void draw() System.out.println("Drew a Ractangle"); class Ellipse public void draw()
21、 System.out.println("Drew a Ellipse"); /新加class Star public void draw() System.out.println("Drew a star"); class Canvas
22、 /畫(huà)布類中只要簡(jiǎn)單地調(diào)用圖形對(duì)象的方法就可以,不再需要實(shí)現(xiàn)復(fù)雜的繪制步驟 public voidpaint(Cirle c) c.draw(); public voidpaint(Triangle t) &
23、#160;t.draw(); public voidpaint(Ellipse e) e.draw(); /新加 public voidpaint(Star
24、160;s) s.draw(); public class User public static void main(String args) /在這里模擬用戶使用軟件 Canvas
25、160;canvas=new Canvas(); Cirle c=new Cirle(); canvas.paint(c); Triangle t=new Triangle();
26、60;canvas.paint(t); Ellipse e=new Ellipse(); canvas.paint(e); /新加 Star s=new St
27、ar(); canvas.paint(s); 這樣就不符合“開(kāi)閉原則(Open Close Principle)”,開(kāi)閉原則換句話就是“對(duì)軟件可以擴(kuò)充但是盡量不要修改"。這個(gè)例子中,在擴(kuò)充了新的圖形后,Canvas就需要修改,這是因?yàn)镃anvas依賴的是具體的圖形,而不是抽象的圖形,這就涉及了面向?qū)ο笤O(shè)計(jì)的另一個(gè)原則“依賴倒置(Dependency Inversion principle)”,即不依賴具體而是依賴于抽象,本例中圓形、矩形、三角形、橢圓形、星
28、形都是具體的圖形,當(dāng)畫(huà)布類依賴于具體的圖形時(shí),畫(huà)任何一種圖形,都需要知道是什么類型的圖形,并需要建立一個(gè)專門的方法(在本例中用了重載,雖然方法名一樣,但是參數(shù)的類型是不一樣的),這樣圖形庫(kù)擴(kuò)充時(shí),修改代碼就是不得不做的事情?,F(xiàn)在通過(guò)對(duì)“圓形、矩形、三角形、橢圓形、星形”的抽象獲得一個(gè)新的概念“圖形(Shape)”,所有具體的圖形就成為這個(gè)概念的具體實(shí)現(xiàn),如圖4所示。圖4. 從具體的圖形抽象出概念通過(guò)對(duì)具體圖形的抽象,得到Shape接口,這個(gè)接口有一個(gè)draw()虛方法,所有實(shí)現(xiàn)這個(gè)接口的具體圖形的類都必須實(shí)現(xiàn)這個(gè)方法,代碼如下:/Shape接口是抽象的概念interface
29、;Shape public void draw();/implements Shape表示實(shí)現(xiàn)Shape接口class Cirle implements Shape public void draw() System.out.println("Drew a Circle");
30、class Triangle implements Shape public void draw() System.out.println("Drew a Triangle"); class Ractangle implements Shape public void draw()
31、 System.out.println("Drew a Ractangle"); class Ellipse implements Shape public void draw() System.out.println("Drew a Ellipse");
32、; 然后讓畫(huà)布類不依賴于具體的圖形而依賴于這個(gè)抽象的概念,如圖5所示。圖5. 畫(huà)布類依賴于抽象的圖形這樣就可以大大的簡(jiǎn)化畫(huà)布類的代碼,而且Canvas類依賴于抽象的Shape而不是具體的圖形,不管系統(tǒng)的圖形類(Circle、Rectangle、Triangle等等這些類)如何擴(kuò)充或修改,畫(huà)布類的程序是不需要修改的:class Canvas public void paint(Shape s) &
33、#160;s.draw(); 用戶使用繪圖程序的模擬代碼:public class User public static void main(String args) Canvas canvas=new Canvas(); Circle
34、c=new Circle(); canvas.paint(c); /注意這里的參數(shù)類型的轉(zhuǎn)變,符合Liskov替換原則 Triangle t=new Triangle(); canvas.paint(t); /注意這里的參數(shù)類型的轉(zhuǎn)變,符合Liskov替換原則
35、0; 完整的代碼如下,已經(jīng)變得簡(jiǎn)單而且易于維護(hù):interface Shape public void draw();/implements Shape表示實(shí)現(xiàn)Shape接口class Cirle implements Shape public void draw() System.o
36、ut.println("Drew a Circle"); class Triangle implements Shape public void draw() System.out.println("Drew a Triangle"); class Ractangle implem
37、ents Shape public void draw() System.out.println("Drew a Ractangle"); class Ellipse implements Shape public void draw()
38、160;System.out.println("Drew a Ellipse"); class Canvas public void paint(Shape s) s.draw(); public class User pub
39、lic static void main(String args) Canvas canvas=new Canvas(); Cirle c=new Cirle(); canvas.paint(c); /注意這里的參數(shù)類型的轉(zhuǎn)變,
40、符合Liskov替換原則 Triangle t=new Triangle(); canvas.paint(t); /注意這里的參數(shù)類型的轉(zhuǎn)變,符合Liskov替換原則 4. 什么是Liskov替換注意上面代碼中,不管是什么類型圖形的對(duì)象都可以作為參數(shù)傳遞到畫(huà)布類的paint方法中,在參數(shù)的傳遞過(guò)程當(dāng)中
41、,具體的圖形對(duì)象的類型會(huì)轉(zhuǎn)變成Shape類型,這符合“Liskov替換原則(Liskov substitution principle)”,即子類可以成為基類的替身。在廣泛使用的面向?qū)ο蟪绦蛘Z(yǔ)言中,例如Java和C+,Liskov替換原則其實(shí)是程序設(shè)計(jì)語(yǔ)言本身所帶的一個(gè)特性。在Java和C+程序設(shè)計(jì)語(yǔ)言中,在方法內(nèi)部創(chuàng)建的“對(duì)象的引用”和“對(duì)象”是存在于不同的內(nèi)存空間中的,對(duì)象的引用創(chuàng)建于棧內(nèi)存,而對(duì)象創(chuàng)建于堆內(nèi)存。在棧中創(chuàng)建的變量有時(shí)被稱之為自動(dòng)變量,因?yàn)楫?dāng)這個(gè)方法運(yùn)行完畢,棧中的變量會(huì)被自動(dòng)清除,但是在堆內(nèi)存當(dāng)中創(chuàng)建的對(duì)象,則需要手工刪除(C+語(yǔ)言)或者用垃圾收集機(jī)制回收(Java語(yǔ)言)。
42、例如這句代碼: Circle c=new Circle();其實(shí)表示了,如圖6所示的事實(shí):圖6. 對(duì)象的引用和對(duì)象Liskov替換原則表述的是如果Circle是Shape的子類,那么Shape類型的引用可以指向Circle類型的對(duì)象,如圖7所示。其實(shí)所謂“可以指向”是想表達(dá)這樣一個(gè)特性:通過(guò)引用s可以訪問(wèn)Circle對(duì)象中,通過(guò)覆蓋Shape的方法(有時(shí)是實(shí)現(xiàn)Shape的方法),而產(chǎn)生的方法,不是通過(guò)覆蓋Shape而產(chǎn)生的方法當(dāng)然是不能訪問(wèn)的。在本例中Circle對(duì)象中的draw()方法是覆蓋Shape的draw()方法產(chǎn)生的,所以通過(guò)引
43、用s可以訪問(wèn)。這就是畫(huà)布類中paint(Shape s)方法的由來(lái): public void paint(Shape s) s.draw(); 圖7. Shape類型的引用可以指向Circle類型的對(duì)象其實(shí),上面的main()函數(shù),還可以寫(xiě)成這樣,同樣就是Liskov替換: public static void main(String
44、0;args) Canvas canvas=new Canvas(); Shape s=new Cirle(); /注意這里的轉(zhuǎn)型 canvas.paint(s); /注意這里的參數(shù)類型的轉(zhuǎn)變,符合Liskov替換原則
45、160; s=new Triangle(); canvas.paint(s); /注意這里的參數(shù)類型的轉(zhuǎn)變,符合Liskov替換原則 5.為不同的用戶提供剛好夠用的接口看到這里,你可能會(huì)覺(jué)得這個(gè)程序太過(guò)于簡(jiǎn)陋,以至于連圖形的大小,顏色等等屬性都無(wú)法設(shè)置,其實(shí)要解決這個(gè)問(wèn)題比較容易,就是在每個(gè)圖形類中增加一些屬性,然后,在構(gòu)造函數(shù)中初始化這些屬性,以Circle類為例,如果要設(shè)置圓的尺寸,添加一個(gè)半徑的屬性,然后在構(gòu)
46、造函數(shù)里初始化,代碼如下:class Cirle implements Shape public double radius; Cirle(double r) radius=r; public void draw()
47、 System.out.printf("Drew a Circle radius:%fn",radius); 然后在創(chuàng)建對(duì)象時(shí): Cirle c=new Cirle(3.5); /在構(gòu)造函數(shù)中初始化 canvas.paint(c); 另外這個(gè)簡(jiǎn)陋的程序也不能夠設(shè)置圖形在畫(huà)布上的位置,為了增加設(shè)置位置這個(gè)功能同時(shí)又不修改畫(huà)布類以及圖形類現(xiàn)在已經(jīng)存在的代碼,可以應(yīng)用“接口分離原則(Interface
48、 Segregation Principle)”,因?yàn)楫?huà)布類的paint()方法只關(guān)心圖形的繪制,而圖形繪制的位置,應(yīng)該是圖形對(duì)象的屬性,這些屬性會(huì)被draw()方法在繪制圖像時(shí)調(diào)用,對(duì)畫(huà)布類paint()方法來(lái)說(shuō)現(xiàn)有的Shape接口已經(jīng)剛好夠用了,不需要了解如何設(shè)置位置。所以另外設(shè)置一個(gè)接口Position,在接口中加入setPosition(double x_position, double y_position)方法,但Canvas類只需要依賴于Shape接口而不需要管Position接口,如圖8所示。 圖8. Canvas類只依賴于Shape接口修改后的完整代碼:/
49、畫(huà)圖的接口interface Shape public void draw();/設(shè)置顯示位置的接口interface Position public void setPosition(double x_position, double y_position);/implements Shape,Position表示實(shí)現(xiàn)Shape,Position接口class Cirle implements Shape,Position publi
50、c double radius; public double x; public double y; Cirle(double r) radius=r; public void draw() S
51、ystem.out.printf("Drew a Circle radius:%f at position:%f,%fn",radius,x,y); public void setPosition(double x_position,double y_position) x=x_position;
52、160; y=y_position; class Ellipse implements Shape,Position public double x; public double y; public void draw() System.out.printf("
53、Drew a Ellipse at position:%f,%fn",x,y); public void setPosition(double x_position,double y_position) x=x_position; y=y_position; &
54、#160;class Canvas public void paint(Shape s) s.draw(); public class User public static void main(String args) Canvas canvas=new Canvas(); Cirle c=new Cirle(3.5); /初始化大小 c.setPosition(5, 6); /設(shè)置位置
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 《唯美模板》課件
- 《禮儀插花的應(yīng)用》課件
- 單位管理制度集粹匯編人員管理十篇
- 《離合器檢修》課件
- 單位管理制度匯編大合集人事管理十篇
- 單位管理制度分享匯編【人力資源管理】十篇
- 單位管理制度分享大全職員管理篇
- 單位管理制度范例選集職員管理篇十篇
- 《中級(jí)計(jì)量經(jīng)濟(jì)學(xué)》課程教學(xué)大綱 (二)
- 八下期中測(cè)試卷02【測(cè)試范圍:第1-11課】(原卷版)
- 過(guò)敏性紫癜課件PPT
- 浙江省紹興市諸暨市2023-2024學(xué)年數(shù)學(xué)三上期末達(dá)標(biāo)檢測(cè)試題含答案
- 腳手架質(zhì)量驗(yàn)收標(biāo)準(zhǔn)
- 小學(xué)思政課《愛(ài)國(guó)主義教育》
- 中藥材的性狀及真?zhèn)舞b別培訓(xùn)-課件
- 泵站項(xiàng)目劃分
- 綠化養(yǎng)護(hù)工作檢查及整改記錄表
- 新能源發(fā)電技術(shù)學(xué)習(xí)通課后章節(jié)答案期末考試題庫(kù)2023年
- GB/T 42752-2023區(qū)塊鏈和分布式記賬技術(shù)參考架構(gòu)
- Module 9 (教案)外研版(一起)英語(yǔ)四年級(jí)上冊(cè)
- 初中物理-初三物理模擬試卷講評(píng)課教學(xué)課件設(shè)計(jì)
評(píng)論
0/150
提交評(píng)論