接口,依賴反轉,單元測試
接口是協約是規定,所以必須是公開的,只能是public;
static void Main(string[] args){int[] num1 = new int[] { 1, 2, 3, 4, 5 };Console.WriteLine(Sum(num1).ToString());Console.WriteLine("==================");Console.WriteLine(Avg(num1).ToString());}static int Sum(int[] arr){int result = 0;foreach (var item in arr)result += item;return result;}static double Avg(int[] arr){int result = 0;double count = 0;foreach (var item in arr) { result += item; count++; };return (result/count);}}
在上述代碼中,如果我們的參數不是int[]類型,而是ArrayList類型(存放的是object類型)
方案一進行方法的重載,在方法內部進行強制類型轉換:
static void Main(string[] args){int[] num1 = new int[] { 1, 2, 3, 4, 5 };ArrayList array = new ArrayList { 1, 2, 3, 4, 5 };Console.WriteLine(Sum(array).ToString());Console.WriteLine("==================");Console.WriteLine(Avg(array).ToString());}static int Sum(ArrayList arr){int result = 0;foreach (var item in arr)result += (int)item;return result;}static double Avg(ArrayList arr){int result = 0;double count = 0;foreach (var item in arr) { result += (int)item; count++; };return (result/count);}
在上述問題中,我們要調用的求和和求平均數,就是甲方,提供的方法為乙方,仔細觀察不難發現我們的甲方只要求其方法參數為可迭代的集合就行了,查看int[]和ArrayList的基類,它們都是可迭代的,實現了IEnumerable接口
static void Main(string[] args){int[] num1 = new int[] { 1, 2, 3, 4, 5 };ArrayList array = new ArrayList { 1, 2, 3, 4, 5 };Console.WriteLine(Sum(num1).ToString());Console.WriteLine("==================");Console.WriteLine(Avg(array).ToString());}static int Sum(IEnumerable arr){int result = 0;foreach (var item in arr)result += (int)item;return result;}static double Avg(IEnumerable arr){int result = 0;double count = 0;foreach (var item in arr) { result += (int)item; count++; };return (result/count);}
通過這樣的實現解決問題,把具體參數變抽象,用契約管理供需關系。
下一個實例繼續:
public class Program{static void Main(string[] args){Engine engine = new Engine();Car car = new Car(engine);car.Run(3);Console.WriteLine(car.Speed.ToString());}}public class Engine{public int RPM { get;private set; }public void Work(int gas){this.RPM = 1000 * gas;}}public class Car{public Engine engine { get;private set; }public int Speed { get;private set; }public Car(Engine engine){this.engine = engine;}public void Run(int gas){this.engine.Work(gas);this.Speed = this.engine.RPM / 100;}}
上面這段代碼當Engine中的Work出現問題時,會導致Car調用出現問題,這是因為Car已經和Engine類緊耦合了
追求低耦合的原因:降低對提供方的需求,只需滿足協約就滿足要求,可替換。
接口與單元測試
接口的產生:自底而上(重構),自頂向下(設計)
C#中接口的實現(隱式,顯示,多接口)
語言對面向對象設計的內建支持:依賴反轉,接口隔離,開/閉原則
public class Program{static void Main(string[] args){var fan = new DeskFan(new PowerSupply());Console.WriteLine(fan.Work());}}public class PowerSupply{public int GetPower(){return 100;}}public class DeskFan{private PowerSupply PowerSupply;public DeskFan(PowerSupply powerSupply){this.PowerSupply = powerSupply;}public string Work(){int power = PowerSupply.GetPower();if (power <= 0){return "won't work";}else if (power < 100)return "Slow";else if (power < 200)return "Work fine";elsereturn "Waring";}}
當前代碼如果我們要測試的話會直接修改PowerSupply里面的方法內的數值,在當前是不能這樣干的,可能有別的代碼引用這個PowerSupply這個類,所以我們需要引入接口來解耦。
public interface IPowerSulpply{int GetPower();}private IPowerSulpply PowerSupply;public DeskFan(IPowerSulpply powerSupply){this.PowerSupply = powerSupply;}
現在可以進行單元測試了。具體步驟如下:
點擊VS頂級菜單Test->Window->Test Explore
右擊解決方案,添加項目,添加對應的單元測試項目。
命名為要測試的項目名加“.Test”即可。
接著引用被測試項目。
編寫代碼方法等
[Fact]public void PowerSupplyThanZeroTest(){var oldvalue = "won't work";var power_ = new DeskFan(new PowerSupplyThanZero());var newvalue = power_.Work();Assert.Equal(oldvalue, newvalue);}public class PowerSupplyThanZero : IPowerSulpply{public int GetPower(){return 0;}}
打開測試管理器界面,點擊對應的方法運行,全部變綠證明測試通過了。
上面調用是構造類的實例然后更改調用的方法的值,使用Moq可以極大減輕我們的工作量。
var oldvalue = "won't work";//var power_ = new DeskFan(new PowerSupplyThanZero());var mock = new Mock<IPowerSulpply>();mock.Setup(a => a.GetPower()).Returns(() => 0);var power = new DeskFan(mock.Object);var newvalue = power.Work();Assert.Equal(oldvalue, newvalue);
?