在上一篇最後提到的,3.達成可維護、可重利用的設計,這篇文章就介紹關於第三個範例,
如果要增加一個全新的屬性進來時,如何透過封裝的方式,讓程式碼的影響範圍減少。
在上一篇最後提到的,3.達成可維護、可重利用的設計,這篇文章就介紹關於第三個範例,
接下來我們面對一個新的問題,如果要增加一個全新的屬性進來時,我們的程式會面臨到多少的修改呢?
首先增加一個新的屬性 吉他弦數 那我們會影響到要修改的程式有多少呢? 程式詳細連結
1.GuitarSpec要增加一個屬性
2.Guutar 建構式增加這個屬性
3.Inventory 下 search() 的比對要增加
當要新增一個屬性進來的時候,就必須修改 GuitarSpec.cs Guutar.cs Inventory.cs程式碼,
而現在將修正這個牽連太多物件問題,
將透過封裝的方式,來讓程式的擴充性更高,
Step1.我們增加吉他弦數的特性到GuitarSpec.cs
{
private Builder builder;
private String model;
private Type type;
//增加弦數
private int numStrings;
private Wood backWood;
private Wood topWood;
//建構式這邊也增加對應
public GuitarSpec(Builder builder, String model, Type type,
int numStrings, Wood backWood, Wood topWood)
{
this.builder = builder;
this.model = model;
this.type = type;
this.numStrings = numStrings;
this.backWood = backWood;
this.topWood = topWood;
}
public Builder getBuilder()
{
return builder;
}
public String getModel()
{
return model;
}
public Type getType()
{
return type;
}
public int getNumStrings()
{
return numStrings;
}
public Wood getBackWood()
{
return backWood;
}
public Wood getTopWood()
{
return topWood;
}
//原本Inventory的search的程式碼被抽到這裡
public bool matches(GuitarSpec otherSpec)
{
if (builder != otherSpec.builder)
return false;
if ((model != null) && (!model.Equals("")) &&
(!model.ToLower().Equals(otherSpec.model.ToLower())))
return false;
if (type != otherSpec.type)
return false;
if (numStrings != otherSpec.numStrings)
return false;
if (backWood != otherSpec.backWood)
return false;
if (topWood != otherSpec.topWood)
return false;
return true;
}
}
Step2.將Guitar.cs 建構式改為傳入GuitarSpec
public class Guitar
{
private String serialNumber;
private double price;
GuitarSpec spec;
//改為直接接受一個GuitarSpec
public Guitar(String serialNumber, double price, GuitarSpec spec)
{
this.serialNumber = serialNumber;
this.price = price;
this.spec = spec;
}
public String getSerialNumber()
{
return serialNumber;
}
public double getPrice()
{
return price;
}
public void setPrice(float newPrice)
{
this.price = newPrice;
}
public GuitarSpec getSpec()
{
return spec;
}
}
Step3.改變Invetory.cs 的search 將比較的程式抽至 GuitarSpec
public class Inventory
{
private List<Guitar> guitars;
public Inventory()
{
guitars = new List<Guitar>();
}
public void addGuitar(String serialNumber, double price,
GuitarSpec spec)
{
Guitar guitar = new Guitar(serialNumber, price, spec);
guitars.Add(guitar);
}
public Guitar getGuitar(String serialNumber)
{
foreach(Guitar guitar in guitars)
{
if (guitar.getSerialNumber().Equals(serialNumber))
{
return guitar;
}
}
return null;
}
//讓search變的比較單純
public List<Guitar> search(GuitarSpec searchSpec)
{
List<Guitar> matchingGuitars = new List<Guitar>();
foreach (Guitar guitar in guitars)
{
GuitarSpec guitarSpec = guitar.getSpec();
//將兩個GuitarSpec委派給GuitarSpec類別來做比較而不是把比較邏輯寫在Inventory的search裡面
if (guitar.getSpec().matches(searchSpec))
matchingGuitars.Add(guitar);
}
//既使比對條件改變但回傳還是回傳Guitar清單
return matchingGuitars;
}
}
Step4.測試程式,將本來的建構式做修改default.aspx.cs
Inventory inventory = new Inventory();
//給予Inventory數據
initializeInventory(inventory);
//新增了弦數的建構式
GuitarSpec whatErinLikes = new GuitarSpec(Builder.FENDER, "Stratocastor",
Type.ELECTRIC, 6, Wood.ALDER, Wood.ALDER);
//這一次是傳入GuitarSpec給Search
List<Guitar> matchingGuitars = inventory.search(whatErinLikes);
if (matchingGuitars.Count > 0)
{
Response.Write("Erin, you might like these guitars:</BR> ");
foreach (Guitar guitar in matchingGuitars)
{
//取的該Guitar下的GuitarSpec
GuitarSpec spec = guitar.getSpec();
Response.Write(" We have a " +
spec.getBuilder() + " " + spec.getModel() + " " +
spec.getType() + " guitar:</BR> " +
spec.getBackWood() + " back and sides,</BR> " +
spec.getTopWood() + " top.</BR> You can have it for only $" +
guitar.getPrice() + "!</BR> " + guitar.getSerialNumber() + "----");
}
}
else
{
Response.Write("Sorry, Erin, we have nothing for you.");
}
// 上面這段是 Page_Load
// 下面是新的 initializeInventory 傳入GuitarSpec
private static void initializeInventory(Inventory inventory)
{
inventory.addGuitar("11277", 3999.95,
new GuitarSpec(Builder.COLLINGS, "CJ", Type.ACOUSTIC, 6,
Wood.INDIAN_ROSEWOOD, Wood.SITKA));
inventory.addGuitar("V95693", 1499.95,
new GuitarSpec(Builder.FENDER, "Stratocastor", Type.ELECTRIC, 6,
Wood.ALDER, Wood.ALDER));
inventory.addGuitar("V9512", 1549.95,
new GuitarSpec(Builder.FENDER, "Stratocastor", Type.ELECTRIC, 6,
Wood.ALDER, Wood.ALDER));
inventory.addGuitar("122784", 5495.95,
new GuitarSpec(Builder.MARTIN, "D-18", Type.ACOUSTIC, 6,
Wood.MAHOGANY, Wood.ADIRONDACK));
inventory.addGuitar("76531", 6295.95,
new GuitarSpec(Builder.MARTIN, "OM-28", Type.ACOUSTIC, 6,
Wood.BRAZILIAN_ROSEWOOD, Wood.ADIRONDACK));
inventory.addGuitar("70108276", 2295.95,
new GuitarSpec(Builder.GIBSON, "Les Paul", Type.ELECTRIC, 6,
Wood.MAHOGANY, Wood.MAHOGANY));
inventory.addGuitar("82765501", 1890.95,
new GuitarSpec(Builder.GIBSON, "SG '61 Reissue", Type.ELECTRIC, 6,
Wood.MAHOGANY, Wood.MAHOGANY));
inventory.addGuitar("77023", 6275.95,
new GuitarSpec(Builder.MARTIN, "D-28", Type.ACOUSTIC, 6,
Wood.BRAZILIAN_ROSEWOOD, Wood.ADIRONDACK));
inventory.addGuitar("1092", 12995.95,
new GuitarSpec(Builder.OLSON, "SJ", Type.ACOUSTIC, 12,
Wood.INDIAN_ROSEWOOD, Wood.CEDAR));
inventory.addGuitar("566-62", 8999.95,
new GuitarSpec(Builder.RYAN, "Cathedral", Type.ACOUSTIC, 12,
Wood.COCOBOLO, Wood.CEDAR));
inventory.addGuitar("6 29584", 2100.95,
new GuitarSpec(Builder.PRS, "Dave Navarro Signature", Type.ELECTRIC,
6, Wood.MAHOGANY, Wood.MAPLE));
}
四個步驟完成後,首先你會發現,我們讓Invetory.cs 下的search 變的簡單,讓實際比較的邏輯移到了
GuitarSpec 裡面的 matches 裡。
而當我們如果又遇到要再增加一個顏色屬性的時候,我們只需更動 GuitarSpec.cs 的建構式及matches
而以不在像是之前的程式需同時牽動到 Guitar.cs , Inventory.cs
{
private Builder builder;
private String model;
private Type type;
//增加弦數
private int numStrings;
private Wood backWood;
private Wood topWood;
//增加顏色
private string color;
//建構式這邊也增加對應
public GuitarSpec(Builder builder, String model, Type type,
int numStrings, Wood backWood, Wood topWood,string color)
{
this.builder = builder;
this.model = model;
this.type = type;
this.numStrings = numStrings;
this.backWood = backWood;
this.topWood = topWood;
this.color = color;
}
public string getColir()
{
return color;
}
public Builder getBuilder()
{
return builder;
}
public String getModel()
{
return model;
}
public Type getType()
{
return type;
}
public int getNumStrings()
{
return numStrings;
}
public Wood getBackWood()
{
return backWood;
}
public Wood getTopWood()
{
return topWood;
}
//原本Inventory的search的程式碼被抽到這裡
public bool matches(GuitarSpec otherSpec)
{
if (builder != otherSpec.builder)
return false;
if ((model != null) && (!model.Equals("")) &&
(!model.ToLower().Equals(otherSpec.model.ToLower())))
return false;
if (type != otherSpec.type)
return false;
if (numStrings != otherSpec.numStrings)
return false;
if (backWood != otherSpec.backWood)
return false;
if (topWood != otherSpec.topWood)
return false;
//增加Color的比對
if ((color != null) && (!color.Equals("")) &&
(!color.ToLower().Equals(otherSpec.color.ToLower())))
return false;
return true;
}
}
而測試頁面在建構GuitarSpec時多增加一個屬性,其他本來的程式都沒有任何的修改,
則還是能繼續的運作。
//新增了顏色
GuitarSpec whatErinLikes = new GuitarSpec(Builder.FENDER, "Stratocastor",
Type.ELECTRIC, 6, Wood.ALDER, Wood.ALDER,"Red");
//這一次是傳入GuitarSpec給Search
List<Guitar> matchingGuitars = inventory.search(whatErinLikes);
透過這三個程式的演進,慢慢的可以感覺出來透過「物件」方式的設計的感覺是什麼,
如同第一篇文章提到書中所寫到的
好的軟體的三個步驟
1.確認客戶要做的事情。
2.應用基本的OO原則,增加軟體彈性。
3.努力達成可維護、重利用的設計。
透過三個範例程式,一步一步的演進,讓Dotjum第一次有悟道要如何做一個物件化的程式,
書中還提到許多觀念及說明,Dotjum無法一個一個的打上來,但針對程式的演進的部分,
就做了一個詳細的說明,這真的是一本不錯的書,若之後章節Dotjum也能夠悟道,
在Po上來跟大家分享。若你覺得文章有什麼你的想法或建議,在麻煩你回覆,我們一起來討論。
本範例的.NET Code HeadFirstOOAD.rar
本篇文章程式碼及概念取用 深入淺出物件導向分析與設計
(若觀念等相關錯誤有錯誤,請務必告訴Dotjum)
相關文章:
[讀書心得]深入淺出物件導向分析與設計-良好程式設計基石 OOAD Code設計範例介紹-1
[讀書心得]深入淺出物件導向分析與設計-良好程式設計基石 OOAD Code設計範例介紹-2