• 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

     

  • 相关阅读:
    数据库sql常见优化方法
    String字符串创建与存储机制
    ==运算符和equals()方法的区别
    Math类中round、ceil和floor方法的功能
    String、StringBuffer和StringBuilder类的区别
    Flask 系列之 构建 Swagger UI 风格的 WebAPI
    Docker 系列之 常用镜像
    Docker 系列之 基础入门
    在 DotNetCore 3.0 程序中使用通用协议方式启动文件关联应用
    .NET Framework VS .NET Core
  • 原文地址:https://www.cnblogs.com/anjingdian/p/13149984.html
Copyright © 2020-2023  润新知