?
敏捷軟件開發宣言
個體和交互 勝過 過程和工具
可以工作的軟件 勝過 面面俱到的文檔
客戶合作 勝過 合同談判
響應變化 勝過 遵循計劃
敏捷設計原則:
單一職責原則(The Single Responsibility Principle,簡稱SRP);
開發-封裝原則(The Open-Close Principle,簡稱OCP);
Liskov替換原則(The Liskov Substitution Principle,簡稱LSP);
依賴倒置原則(the Dependency Inversion Principle,簡稱DIP);
接口隔離原則(The Interface Segregation Interface,簡稱ISP);
單一職責原則(SRP):
就一個類而言,應該僅有一個引起它變化的原因。
開放-封裝原則(OCP):
軟件實體(類,模塊,函數等)應該是可以擴展的,但是不可修改的。
1.對于擴展是開發的
這意味著模塊的行為是可以擴展的。當應用的需求改變時,我們可以對模塊進行擴展,使其具有滿足那些改變的新行為。換句話說,我們可以改變模塊的功能。
2.對于更改是封閉的
對模塊行為進行擴展時,不必改動模塊的源代碼或者二進制代碼。模塊的二進可執行版本,無論是可鏈接的庫、DLL或者Java的.jar文件,都無需改動。
怎么可能在不改動模塊源代碼的情況下去更改它的行為呢?關鍵是抽象!
?
public class Square(){ public String type="square"; } public class Circle(){ public String type="circle"; } public class DrawShape{ //list為所有要畫的shape的集合 public void DrawAllShapes(ArrayList<Object> list){ for(int i=0;i<list.size();i++){ switch(s.type){ case "square": DrawSquare(list.get(i));break; case "circle": DrawCircle(list.get(i));break; } } } private void DrawSquare(Object o){} private void DrawCircle(Object o){} }?
?
DrawAllShapes函數是要畫出所有圖形,先檢查圖形的類型,在判斷是調用DrawSquare還是DrawCircle。該函數顯然是不符合OCP,因為它對于新的形狀類型的添加不是封裝的。如果希望這個函數能夠繪制包含有三角形的列表,就必須得更改整個函數。而要解決這個問題,就可以使用抽象
?
public interface Shape{ public void Draw(); } public class Square implements shape{ public void Draw(){System.out.println("Draw square");} } public class Circle implements Shape{ public void Draw(){System.out.println("Draw circle");} } public class DrawShape{ public void DrawAllShapes(ArrayList<Shape> list){ for(int i=0;i<list.size();i++){ i.draw(); } } }?
?
這樣想添加新圖形時,只要創建這個圖形的類,并implements Shape接口,并實現Draw()方法即可,無需再改動DrawAllShapes方法。它也就符合OCP了。
Liskov 替換原則(LSP)
子類型必須能夠替換掉它的基類型。
正方形并不是個矩形!!!
?
class Rectangle{ private double width; private double height; void setWidth(double w){width = w;} void setHeight(double h){height = h;} double getWidth(){reutrn width;} double getHeight(){return height;} double Area(){ return width * height; } }?
正方形應該是一個矩形,所以Square應該派生自Rectangel類。不過,Square并不需要成員變量width和height。但仍會繼承它們,顯然這是種浪費,雖然在許多情況下這種浪費是可以忽略的,但setWidth()和setheight()這種個函數對于Square來說是不適合的,但Square仍會繼承它們。當然,為了避免這個問題,我們也可以寫:
?
class Square extends Rectangel{ void setWidth(double w){super.setWidth(w);super.setHeight(w);} void setHeight(double h){super.setHeight(h);super.setWidth(h);} }?
這樣就可以保證這個正方形的長和寬是一樣的了。但對于下面這個問題
?
void g(Rectangle r){ r.setWidth(5); r.setHeight(4); assert(r.Area()==20); }?
這本來是很正常的,但當r是個正方形是,這個assert就會是錯誤的,因為當最后調用r.setHeight(4)時,長寬都為4,Area()得到的結果就是16!所以Square并不能完全替換Rectangel,也就是說,它不符合LSP原則。
IS-A是關于行為的
對于那些不是g測試方法的編寫者而言,正方形當然是個矩形,但從g的角度來看,Square對象絕對不是Rectangel對象。因為Square對象的行為方式和函數g所期望的Rectangle對象的行為方式不相容。不行為方式的角度來看,Square不是Rectangle。
對象的行為方式才是軟件真正所關注的問題。
依賴倒置原則(DIP)
a.高層模塊不應該依賴于低層模塊。二者都應該依賴于抽象。
b.抽象不應該依賴于細節,細節應該依賴于抽象。
這是一個違反DIP原則的例子,高層的PolicyLayer 使用了低層的MechanismLayer,而MechanismLayer又使用了更低層的UtilityLayer。那么UtilityLayer一些很小的改動都將影響高層的PolicyLayer。而低層的類又是容易改變的,為了解決這個問題,我們可以將這些類低速于抽象。每個較高層次都為它所需要的服務聲明一個抽象接口,較低的層次實習了這些抽象接口,每個高層類都通過該抽象接口使用下一層,這樣高層就不依賴于低層。低層反而依賴于在高層中聲明的抽象服務接口。
1.任何變量都不應該持有一個指向具體類的指針或者引用。
2.任何類都不應該從具體類派生
3.任何方法都不應該復寫它的任何基類中的已經實現了的方法。
一個簡單的例子
?
public class Button{ private Lamp lamp; public void poll(){ if(/*some condition*/) lamp.turnOn(); } }
? ? ?這個方案違反了DIP,Button類直接依賴于Lamp類,當Lamp改變時,高層的Button類也會受到影響,此外,想重用Button類來挖掘一個Motor對象是不可能的。在這個設計中,Button控制著Lamp對象,并且也只能控制Lamp對象。
找出潛在的抽象
這樣,Lamp依賴于ButtonServer,但ButtonServer沒有依賴于Button,任何知道如果去操作ButtonServer接口的對象都能夠控制Lamp。
接口隔離原則(ISP)
接口應該是高內聚的,一個接口應該盡量只為一種客戶服務,而不是為了幾個客戶,否則,就會出現接口污染。
不應該強迫客戶依賴于它們不用的方法。
如果強迫客戶程序依賴于那些它們不使用的方法,那么這些客戶程序就面臨著由于這些未使用方法的改變所帶來的變更。這無意中導致了所有客戶程序之間的耦合。換句話說,如果一個客戶程序依賴于一個含有它不使用的方法的類,但是其他客戶程序卻要使用該方法,那么當其他客戶要求這個類改變時,就會影響到這個客戶程序。我們希望盡可能地避免這種耦合,因此我們希望分享接口。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
