標籤:style blog color 使用 os io for ar
依賴注入
這個原則的要點是什麼。為什麼你不能對類的執行個體進行再次寫入程式碼?當我們編碼,測試的時候,讓我們關注一件很重要的事情。希望你知道單元測試並知道它的重要性。也許在你做任何編碼之前你都應該首先設計你的測試,因此你應該很熟悉測試驅動開發。為了定義新功能你應該去寫測試,你應該嘗試去實現並開始編碼直到測試通過。讓我們先看看之前的文章的代碼。
public class DateBasedTaxFactory:ITaxFactory { Customer _customer; ITaxFactory _taxFactory; public DateBasedTaxFactory(Customer c, ITaxFactory cb) { _customer = c; _taxFactory = cb; } public ITax GetTaxObject() { if (_customer.StateCode == "TX" && DateTime.Now.Month == 4 && DateTime.Now.Day == 4) return new NoTax(); else return _taxFactory.GetTaxObject(); } }
我們有DateBasedTaxFactory,當我們應該測試一下這個工廠是否能夠正常的工作。 是否每年的4月4日這個Tax類型的傳回值都應該是0.我們也許會建立一個如下的測試。
Customer cust = new Customer(){StateCode = "TX", County = "Travis", ZipCode = "78745"}; DateBasedTaxFactory db = new DateBasedTaxFactory(cust, new CustomerBasedTaxFactory(cust)); ITax tax = db.GetTaxObject(); //test for no tax for a certain date if(tax.CalculateTax(3m) != 0) { throw new Exception("the value is supposed to be zero"); }
這裡有什麼問題嗎?我們不能真正的測試這個!正如你可以在DateBasedTaxFactory中看到的,為了測試當前的日期,它直接使用DateTime對象的Now屬性。除非你改變你系統的時間不然我們不能使得NoTax的條件滿足。更改系統時間是不理想的。我們還能做其他的事情嗎?有時這個工廠類有個引用一個隱藏的屬性,它是寫入程式碼實現的,它依賴一些需要變化的東西。工廠類需要一個DateTime對象。它不需要DateTime是當前的日期。它不關注給它的日期是什麼。為了告訴外面的世界這個類需要什麼來工作我們需要使用依賴注入。這將允許我們的測試給他任何需要測試的日期。就如下面所示:
public class DateBasedTaxFactory : ITaxFactory{ Customer _customer; ITaxFactory _taxFactory; DateTime _dt; public DateBasedTaxFactory(Customer c, ITaxFactory cb,DateTime dt) { _customer = c; _taxFactory = cb; _dt = dt; } public ITax GetTaxObject() { if (_customer.StateCode == "TX" && _dt.Month == 4 && _dt.Day == 4) { return new NoTax(); } else return _taxFactory.GetTaxObject(); }}
現在我們可以調整我們的測試來發送任何我們想要測試的日期。
Customer cust = new Customer(){StateCode = "TX",County ="Travis",ZipCode = "78745"};DateBasedTaxFactory db = new DateBasedTaxFactory(cust, new CustomerBasedTaxFactory(cust), new DateTime(2001,4,4));ITax tax = GetTaxObject();//test for no tax for a certain dateif (tax.CalculateTax(3m) != 0){ throw new Exception("the value is supposed to be zero");}
單一原則/開閉原則
為什麼你的對象應該只做一件事情?為什麼你應該從不改變他們?顯示生活變化了那麼為什麼代表生活變化的代碼不能變化?讓我們看看之前的穩重中的第一個版本的Order類。好的,假如說你的公司有一個堅定的政策,在您的系統中的主要程式集BusinessLogic.dll每兩個月只有一個發布版本。假如有個bug或者在這之前需要做些變更,這將是一項繁瑣艱巨的任務。但是我們可以用較少的麻煩來定義一個可補充的發布程式集。如果我們使用如下原始碼:
public class Order{ List<OrderItem> _orderItems = new List<OrderItem>(); public decimal CalculateTotal(Customer customer) { decimal total = _orderItems.Sum((item)=>{ return item.Cost * item.Quantity; }); decimal tax; if (customer.StateCode == "TX") tax = total * .08m; else if (customer.StateCode == "FL") tax = total * .09m; else tax = .03m; total = total + tax; return total; }}
如果在TX的稅費邏輯發生了變化或者需要一個新的State的稅費,我們將必須要去修改Order對象。這將會造成 很大的臭味,因為我們需要去測試並發布BusinessLogic.dll.由於它有稅費有關,如果事情發生了很大的變化並且他將要投入生產ASAP中是,法律和金錢是一個不錯的選擇。
從其他的文章中我們已經做了我們需要做的事情,例如:
public interface ITax { decimal CalculateTax(decimal total); } public class TXTax:ITax { public decimal CalculateTax(decimal total) { return total * 0.08m; } } public class CustomerBasedTaxFactory:ITaxFactory { Customer _customer; static Dictionary<string, ITax> stateTaxObjects = new Dictionary<string, ITax>(); static Dictionary<string, ITax> countyTaxObjects = new Dictionary<string, ITax>(); public CustomerBasedTaxFactory(Customer customer) { _customer = customer; } public ITax GetTaxObject() { ITax tax; if(!string.IsNullOrEmpty(_customer.County)) { if (!countyTaxObjects.Keys.Contains(_customer.StateCode)) { tax = (ITax)Activator.CreateInstance("Tax", "Solid.taxes." + _customer.County + "CountyTax"); countyTaxObjects.Add(_customer.StateCode, tax); } else tax = countyTaxObjects[_customer.StateCode]; } else { if (!stateTaxObjects.Keys.Contains(_customer.StateCode)) { tax = (ITax)Activator.CreateInstance("Tax", "Solid.taxes." + _customer.StateCode + "Tax"); stateTaxObjects.Add(_customer.StateCode, tax); } else tax = stateTaxObjects[_customer.StateCode]; } return tax; } }
我們有我們的TaxFactory來建立Tax對象並且所有的Tax邏輯都是在它的單獨類中來完成的。因此到現在為止ITax類可以被引入到其他的程式集當中來做一些Tax相關的任務。Tax.dll。如果發生了變化,那麼只需要在當前的程式集當中測試,並且它的附加程式集也不會有太多繁文縟節的事情。
好了,就這樣吧,下次再見。