[讀書心得]深入淺出物件導向分析與設計-良好程式設計基石 OOAD Code設計範例介紹-3

在上一篇最後提到的,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