面向?qū)ο笤O(shè)計(jì)的五個(gè)原則_第1頁(yè)
面向?qū)ο笤O(shè)計(jì)的五個(gè)原則_第2頁(yè)
面向?qū)ο笤O(shè)計(jì)的五個(gè)原則_第3頁(yè)
面向?qū)ο笤O(shè)計(jì)的五個(gè)原則_第4頁(yè)
面向?qū)ο笤O(shè)計(jì)的五個(gè)原則_第5頁(yè)
已閱讀5頁(yè),還剩14頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(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ì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論