c#之接口,依赖反转,单元测试

1.接口 

弱类型语言允许将一块内存看做多种类型。比如直接将整型变量与字符变量相加。C and C++ 是静态语言,也是弱类型语言;Perl and PHP 是动态语言,但也是弱类型语言。 
强类型语言在没有强制类型转化前,不允许两种不同类型的变量相互操作。Java、C# 和 Python 等都是强类型语言。

下面代码简单介绍了使用接口的例子,因为c#是强类型语言,所以如果Sum()方法的参数不是IEnumerable的话,就只能是int[]或者ArryList,这种情况下,如果想num与arrayList对象都用sum和avg方法的话就得每个都写一遍,但是因为数组和ArrayList都继承了IEnumerable,所以可以直接用它做参数接收。

 class Program
    {
        static void Main(string[] args)
        {
            int[] num = new int[] { 1, 2, 3, 4, 5 };
            ArrayList arrayList = new ArrayList { 1, 2, 3, 4, 5 };
            Console.WriteLine(Sum(num));
            Console.WriteLine(Avg(num));

            Console.WriteLine(Sum(arrayList));
            Console.WriteLine(Avg(arrayList));

            Console.ReadKey();
        }
        static int Sum(IEnumerable num)
        {
            int sum = 0;
            foreach(var n in num)
            {
                sum += (int)n;
            }
            return sum;
        }
        static double Avg(IEnumerable num)
        {
            int sum = 0;
            double count = 0;
            foreach (var n in num)
            {
                sum += (int)n;
                count++;
            }
            return sum/ count;
        }
    }

 

结果:

15
3
15
3

  

2.依赖反转

https://blog.csdn.net/jerry11112/article/details/79027834 这里写的面向对象编程很不错

面向对象编程就是先抽象出对象,然后用对象执行方法的方式解决问题。

面向对象就是对现实世界的抽象,在现实世界中人与人之间相互协作分工做事,但是在抽象世界中,这种分工合作可以理解成“依赖”,相应的就出现了耦合。

(1)比如说汽车依赖于引擎才能跑,没有引擎,车也就没什么用处了。用面向对象的方式抽象出汽车和引擎2个类,代码如下:

  class Program
    {
        static void Main(string[] args)
        {
            Engine engine = new Engine();
            Car car = new Car(engine);
            car.Run(3);
            Console.WriteLine(car.Speed);

            Console.ReadKey();
        }
    }

    /// <summary>
    /// 引擎
    /// </summary>
    class Engine
    {
        /// <summary>
        /// 转速,这里的属性只能读,不能在其他类进行修改
        /// </summary>
        public int RPM { get; private set; }
        /// <summary>
        /// 引擎转动,
        /// </summary>
        /// <param name="gas">汽油,汽油越多,转速越快,</param>
        public void Work(int gas)
        {
            this.RPM = 1000 * gas;
        }

    }
    /// <summary>
    /// 车类
    /// </summary>
    class Car
    {
        private Engine _engine;
        /// <summary>
        /// 车必须要有一个引擎,否则没法动。
        /// </summary>
        /// <param name="engine"></param>
        public Car(Engine engine)
        {
            _engine = engine;
        }
        public int Speed { get; private set; }
        /// <summary>
        /// 汽车运行
        /// </summary>
        /// <param name="gas"></param>
        public void Run(int  gas)
        {
            _engine.Work(gas);
            this.Speed = _engine.RPM / 100;

        }
    }

从上面代码我们可以看出,car类是完全依赖于engine类的,被依赖的类一旦除了问题,依赖的一方也会出问题,如果代码一多,问题处理起来就会很麻烦。在实际工作中,如果一个程序员负责开发engine类,但是只有这个类问题改完,其他依赖于这个类的其他类才能继续使用。

 (2)接口

如上述情况,我们可以使用接口,来实现降低耦合度。接口就是一组契约,用来约束一组功能。接口的调用者是被约束的,只能调用接口中所包含的功能。还保证了这些接口中的功能一定是实现好了的。

接口的产生:自底向上(重构),自顶向下(设计)

在接到一个项目的时候,如果你是一个很厉害的并且理解这个项目的业务逻辑,在一开始的时候就想到了在什么地方用接口,此时就是自顶向下的方式产生接口。但是更多的时候是我们在写代码的过程中不断的重构代码,觉得哪些地方需要接口

 

例子:比如以前的旧手机,一定会有的功能是打电话,接电话,发短信,收短信。我们用接口来实现这些功能。我用的手机是一定会有这些功能的,而且这些功能都是好用的。请注意,这个接口对于手机厂商来说,这四个功能是必须要实现的。使用接口实现的好处在于你更换手机之后还是可以直接用,不存在换了手机之后这4个功能就有不能用的了。

代码如下:

namespace TestClass
{
    class Program
    {
        static void Main(string[] args)
        {
            var user = new PhoneUser(new HUANWEIPhone());
            user.UsePhone();
            

            Console.ReadKey();
        }
    }

    public interface IPhone
    {

        void Dail();
        void PickUp();
        void Send();
        void Receive();
    }
    /// <summary>
    /// 诺基亚手机
    /// </summary>
    public class NokiaPhone : IPhone
    {
        public void Dail()
        {
            Console.WriteLine("NokiaPhone  is Dailing");
        }

        public void PickUp()
        {
            Console.WriteLine("NokiaPhone  is PickUping");
        }

        public void Receive()
        {
            Console.WriteLine("NokiaPhone  isReceiveing");
        }

        public void Send()
        {
            Console.WriteLine("NokiaPhone  is Sending");
        }
    }
    public class HUANWEIPhone : IPhone
    {
        public void Dail()
        {
            Console.WriteLine("HUANWEIPhone  is Dailing");
        }

        public void PickUp()
        {
            Console.WriteLine("HUANWEIPhone  is PickUping");
        }

        public void Receive()
        {
            Console.WriteLine("HUANWEIPhone  isReceiveing");
        }

        public void Send()
        {
            Console.WriteLine("HUANWEIPhone  is Sending");
        }
    }
    /// <summary>
    /// 手机使用者:它的行为就是能够使用不同的手机
    /// </summary>
    public class PhoneUser
    {
        private IPhone _phone;
        public PhoneUser(IPhone phone)
        {
            _phone = phone;
        }
        /// <summary>
        /// 使用手机以及其能力
        /// </summary>
        public void UsePhone()
        {
            _phone.Dail();
            _phone.PickUp();
            _phone.Send();
            _phone.Receive();
        } 
    } 
}

 结果:

HUANWEIPhone  is Dailing
HUANWEIPhone  is PickUping
HUANWEIPhone  is Sending
HUANWEIPhone  isReceiveing

(3)所以的依赖倒置,其中的依赖其实就是耦合性,如果A类依赖B类,那么当B类消失或修改时,对A类会有很大的影响,可以说是A类的存在完全就是为B类服务,就是说A类依赖B类。

看上图:1处是汽车司机只能开汽车,卡车司机只能开卡车,不能还换着开。Driver依赖Car,Trucker依赖Truck。

 2:改完设计之后,创建了一个IVehicle接口,让Car和Truck都实现这个接口(此时这2个类和接口也是紧耦合),Driver依赖IVehicle。此时Driver既可以开Car,也可以开Trunk。注意看图,此时的箭头和1时方向变反了,所以依赖反转中的反转就是这样来的。

 3:当有多种使用者,多个选择的时候,就可以实现多种配对,如图3处。此时再进一步,那就是设计模式了。

 使用此时的设计模式来更改上面代码的话,可以加一个年轻人使用者类,如下:

public class YoungUser : PhoneUser
    {
        public YoungUser(IPhone phone) :base(phone)
        {
          
        }
    }

在调用的时候如下:

            var userYong = new YoungUser(new HUANWEIPhone()); 
            userYong.UsePhone();

 

(4)单元测试

写一个例子,表示接口,解耦,依赖倒置原则是怎么被单元测试应用的。

电扇都是由电启动的,电力越大,速度越快。但是它也是有电流保护的功能,电流过大会断开或警告。现在的依赖关系是电扇依赖电源(没有电源,电扇无法运行)。

我们先写一个紧耦合:

namespace TestClass
{
    class Program
    {
        static void Main(string[] args)
        {
            var fan = new DeskFan(new PowerSupply());
            fan.Work();
            Console.ReadKey();
        }
    }
    /// <summary>
    /// 电源供应类
    /// </summary>
    public class PowerSupply
    {
        public int GetPower()
        {
            return 100;
        }
    }
    /// <summary>
    /// 电扇
    /// </summary>
    public class DeskFan
    {
        private PowerSupply _powerSupply;
        public DeskFan(PowerSupply powerSupply)
        {
            _powerSupply = powerSupply;
        }
        /// <summary>
        /// 运行,还要验证电流力度
        /// </summary>
        /// <returns></returns>
        public string Work()
        {
            int power = _powerSupply.GetPower();
            //简单判断一下
            if(power<=0)
            {
                return "Wont't work";
            }
            else
            {
                return "Working";
            }

        }
    } 
}

看上面代码,如果我们更改了PowerSupply中的 GetPower方法的返回值,来测试电扇,而且不只是电扇在使用这个电源,也许还有别的电器在使用,更改之后万一引起别的电器产生问题,那就不对了。所以我们可以抽象出一个电源提供接口,写一个测试类实现电源提供接口,专门用来测试电源对电力大小造成的影响,这样也不会影响到其他电器的使用,如下:

namespace TestClass
{
    class Program
    {
        static void Main(string[] args)
        {
            var fan = new DeskFan(new TestPowerSupply());
            fan.Work();
            Console.ReadKey();
        }
    }
    public  interface IPowerSupply
    {
        int GetPower();
    }

    /// <summary>
    /// 电源供应类
    /// </summary>
    public class PowerSupply:IPowerSupply
    {
        public int GetPower()
        {
            return 100;
        }
    }
    /// <summary>
    /// 测试电流
    /// </summary>
    public class TestPowerSupply : IPowerSupply
    {
        public int GetPower()
        {
            return 100000;
        }
    }
    /// <summary>
    /// 电扇
    /// </summary>
    public class DeskFan
    {
        private IPowerSupply _powerSupply;
        public DeskFan(IPowerSupply powerSupply)
        {
            _powerSupply = powerSupply;
        }
        /// <summary>
        /// 运行,还要验证电流力度
        /// </summary>
        /// <returns></returns>
        public string Work()
        {
            int power = _powerSupply.GetPower();
            //简单判断一下
            if(power<=0)
            {
                return "Wont't work";
            }
            else
            {
                return "Working";
            }

        }
    } 
}

要测试的话,我们可以使用单元测试进行调试。新建一个单元测试

 

namespace TestClassTests
{
    public class UnitTest1
    {
        [Fact]
        public void PowerLowerThanZero_OK()
        {
            var mock = new Mock<IPowerSupply>();
            mock.Setup(s=>s.GetPower()).Returns(()=>0); 
            var fan = new DeskFan(new PowerSupplyThanZero(0));
           var test= fan.Work();
            Assert.Equal("",test);

            //第二种写法,引用Moq.dll。此时就不需要再实现接口了,直接填需要的值
            var mock = new Mock<IPowerSupply>();
            mock.Setup(s => s.GetPower()).Returns(() => 0);

            var fan = new DeskFan(mock.Object);
            var test = fan.Work();
            Assert.Equal("", test);


        }
    } 
    public class PowerSupplyThanZero : IPowerSupply
    {
        private int _power;
        public PowerSupplyThanZero(int power)
        {
            _power = power;
        }
        public int GetPower()
        {
            return _power;
        }
    }
}

 

 引用于:https://www.bilibili.com/video/BV13b411b7Ht?p=28

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted @ 2020-06-16 23:56  安静点--  阅读(480)  评论(0编辑  收藏  举报