zoukankan      html  css  js  c++  java
  • 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

     

  • 相关阅读:
    再次或多次格式化导致namenode的ClusterID和datanode的ClusterID之间不一致的问题解决办法
    Linux安装aria2
    POJ 3335 Rotating Scoreboard 半平面交
    hdu 1540 Tunnel Warfare 线段树 区间合并
    hdu 3397 Sequence operation 线段树 区间更新 区间合并
    hud 3308 LCIS 线段树 区间合并
    POJ 3667 Hotel 线段树 区间合并
    POJ 2528 Mayor's posters 贴海报 线段树 区间更新
    POJ 2299 Ultra-QuickSort 求逆序数 线段树或树状数组 离散化
    POJ 3468 A Simple Problem with Integers 线段树成段更新
  • 原文地址:https://www.cnblogs.com/anjingdian/p/13149984.html
Copyright © 2011-2022 走看看