C#接口具体解释(与抽象的区别)

本文详细讲解了C#中接口的使用,对比了接口与抽象类的区别,强调接口在解耦和单元测试中的重要作用。通过实例展示了如何通过接口减少重复代码,提高代码复用性,并解释了依赖反转原则。还介绍了如何利用接口进行单元测试,以提高代码的可测试性和可靠性。
摘要由CSDN通过智能技术生成

本次利用代码细致解释一下接口的具体使用方法,以及介绍解耦的重要性和方法



借用刘铁猛老师PPT里的一个图,感觉很清晰的说明了接口,抽象之间的联系

在这里插入图片描述

接口与单元测试



使用接口的好处

接口的本质,决定了接口中的纯虚方法必须是public,而抽象类中的方法,只要不是private就好了。成员访问级别(public)决定了接口的本质。它的本质是服务的消费者与服务提供者的“契约”

抽象类当中,被protected修饰的成员,只能够让自己的子类看到,internal出了自己的程序集,就看不到了。但是接口想要别人看到,就不能加以限制,必须使用public。


在未使用接口的情况下,会发生这种事。定义了一个数组和一个Arraylist 类型的,想对他们进行求和和求取平均数,由于两个的类型不同,于是,需要写出四组函数。

static void Main(string[] args)
        {
            int[] nums1 = new int[] { 1, 2, 3, 4, 5 };
            ArrayList nums2 = new ArrayList { 1, 2, 3, 4, 5 };
            Console.WriteLine(Sum(nums1));
            Console.WriteLine(Avg(nums1));
        }
        static int Sum(int[] nums)
        {
            int sum = 0;
            foreach (var n in nums) sum += n;
            return sum;
        }
        static double Avg(int[] nums)
        {
            int sum = 0;double count = 0;
            foreach (var n in nums) { sum += n;count++;}
            return sum / count;
        }

上述是求出了数组的Sum和Avg,接下来是Arraylist的:

		static int Sum(ArrayList nums)//这里要改成Arraylist类型
        {
            int sum = 0;
            foreach (var n in nums) sum += (int) n;//因为Arraylist里的是object类型的,所以要强制转换一下类型
            return sum;
        }
        static double Avg(ArrayList nums)
        {
            int sum = 0;double count = 0;
            foreach (var n in nums) { sum += (int)n;count++;}
            return sum / count;
        }

这样要写出四个函数,是相当不方便的。


但如果引入了接口以后,就会变的简易很多,因为Array里面,是有IEnumerable这个接口,而Arraylist类型中也支持IEnumerable接口,于是在函数传进去的值类型里面写入IEnumerable,就可以达到通用的效果,如下。比上述定义4个函数要更方便

static void Main(string[] args)//这样只需要两个函数便能达到之前的目的,便捷了许多
        {
            int[] nums1 = new int[] { 1, 2, 3, 4, 5 };
            ArrayList nums2 = new ArrayList { 1, 2, 3, 4, 5 };
            Console.WriteLine(Sum(nums1));
            Console.WriteLine(Avg(nums1));
            Console.WriteLine(Sum(nums2));
            Console.WriteLine(Avg(nums2));
        }
        static int Sum(IEnumerable nums)
        {
            int sum = 0;
            foreach (var n in nums) sum += (int) n;
            return sum;
        }
        static double Avg(IEnumerable nums)
        {
            int sum = 0;double count = 0;
            foreach (var n in nums) { sum += (int)n;count++;}
            return sum / count;
        }
    }


在面向对象中的合作,有个术语,叫依赖。但是依赖的越直接,耦合就会越紧。
下列代码,Engine类和Car类就是紧耦合了,若Engine类里面出了问题,那么Car类就永远不可能正常运行(Engine类是被依赖类),这就是紧耦合出现的弊端。

 class Program
    {
        static void Main(string[] args)
        {
            var engine = new Engine();
            var car = new Car(engine);
            car.Run(3);
            Console.WriteLine(car.Speed);
        }
    }
    class Engine
    {
        public int RPM { get; private set; }
        public void Work(int gas)
        {
            this.RPM = 1000 * gas;
        }
    }
    class Car
    {
        private Engine _engine;//这是个字段,这样他们两个类就耦合在一起了
        public Car(Engine engine)//这个是构造器
        {
            _engine = engine;
        }
        public int Speed { get; private set; }
        public void Run(int gas)
        {
            _engine.Work(gas);
            this.Speed = _engine.RPM / 100;
        }
    }

将耦合变松的方法,就是引用接口方法(这个例子要十分熟悉,最好自己好好的重写几次)

可以看见下面,只需要改变实例对象传入的值即可。


_phone = phone这里的注释很关键

class Program
    {
        static void Main(string[] args)
        {
            var user = new PhoneUser(new NokiaPhone());
            //这里就引用的Nokia的,想改变只用改这里就能调用不同的手机了
            user.UsePhone();
        }
    }
    interface Iphone
    {
        void Dail();
        void PickUp();
        void Send();
        void Receive();
    }
    class PhoneUser
    {
        private Iphone _phone;//这是接口类型的phone
        public PhoneUser(Iphone phone)
        {
            _phone = phone;
 //这里实际上是把NokiaPhone的实例,交给了接口类型的字段来引用,这样的话_phone访问的就是Iphone里的方法了。然后根据多态原则,所以访问的是NokiaPhone里的具体方法。
        }
        public void UsePhone()
        {
            _phone.Dail();
            _phone.PickUp();
            _phone.Receive();
            _phone.Send();
        }
    }
    class NokiaPhone : Iphone
    {
        public void Dail()
        {
            Console.WriteLine("NokiaPhone calling......");
        }

        public void PickUp()
        {
            Console.WriteLine("Hello,this is tt");
        }

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

        public void Send()
        {
            Console.WriteLine("NokiaPhone sending");
        }
    }
    class HuaWeiPhone : Iphone
    {
        public void Dail()
        {
            Console.WriteLine("HuaWeiPhone calling......");
        }

        public void PickUp()
        {
            Console.WriteLine("Hello,this is tt");
        }

        public void Receive()
        {
            Console.WriteLine("HuaWeiPhone ring......");
        }

        public void Send()
        {
            Console.WriteLine("HuaWeiPhone sending");
        }
    }

这样就达到了松耦合的目的,有更高的可靠性。可以让功能的提供方可以被替换


依赖反转

用来平衡我们常规思路——自顶向下,这种单一的思路。而是将问题从下向上思考:不是使用紧耦合的方法,改用接口,实现使用者和提供者不是紧密一对一的关系,而是通过接口这样一个交换机一样的存在,让使用者自主选择使用的内容,提供者只需提供自己的方法即可。这样可以极大减小出错的检查的成本。

接口与单元测试

下述代码实现了一个,关于桌面电扇的电源供电问题,在给予电量方面不一样的值会有不一样的反应。
但是我们不知道,是否其中哪里有问题,或者是其中哪一个单元板块会异常,这个时候就需要用到测试。用了接口的好处也可体现在,现在可以用单元测试。测试每一个模块,更容易定位问题。

 class Program
    {
        static void Main(string[] args)
        {
            var fan = new DeskFan(new PowerSupply());
            Console.WriteLine(fan.Work());
        }
    }
    public interface IPowerSupply
    {
        int GetPower();
    }
    public class PowerSupply:IPowerSupply
    {
        public int GetPower()
        {
            return 100;
        }
    }
    public class DeskFan
    {
        private IPowerSupply _powersupply;
        public DeskFan(IPowerSupply powersupply)
        {
            _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";
            }
            else return "Fire";
        }
    }


单元测试
需要新建一个项目:
在这里插入图片描述
然后选择:
在这里插入图片描述

接着在这个项目里添加对要测试项目的依赖,就可以开始编写代码了

 public class DeskFanTests
    {
        [Fact]//特征或特性,暂时先不管他
        public void PowerLowerThanZero_Ok()
        //根据名字很好判断要测试的是什么单元
        {
            var fan = new DeskFan(new PowerSupplyLowerThanZero()
            var expected = "Won't work";
            var actual = fan.Work();
            Assert.Equal(expected, actual);
            //测试两个是否相等,正确测试就会通过
        }
        [Fact]
        public void PowerSupplyHigherThan200_Warning()
        {
            var fan = new DeskFan(new PowerSupplyHigherThan200());
            var expected = "Fire";
            var actual = fan.Work();
            Assert.Equal(expected, actual);
        }
    }
    class PowerSupplyLowerThanZero : IPowerSupply
    {
        public int GetPower()
        {
            return 0;
        }
    }
    class PowerSupplyHigherThan200 : IPowerSupply
    {
        public int GetPower()
        {
            return 230;
        }
    }


但是在测试里,每次定义类,会特别麻烦,如图:
    在这里插入图片描述
所以,这里可以引用一下Moq,在Nuget程序包里搜索Moq,在安装Moq后,使用这个名称空间。就可以不用定义类了

using Moq;

namespace Interface.Test
{
    public class DeskFanTests
    {
        [Fact]//特征或特性,暂时先不管他
        public void PowerLowerThanZero_Ok()
        {
            var mock = new Mock<IPowerSupply>();
            mock.Setup(ps => ps.GetPower()).Returns(() => 0); 
            var fan = new DeskFan(mock.Object);
            var expected = "Won't work";
            var actual = fan.Work();
            Assert.Equal(expected, actual);
        }
        [Fact]
        public void PowerSupplyHigherThan200_Warning()
        {
            var mock = new Mock<IPowerSupply>();
            mock.Setup(ps => ps.GetPower()).Returns(() => 0);
            var fan = new DeskFan(mock.Object);
            var expected = "Fire";
            var actual = fan.Work();
            Assert.Equal(expected, actual);

但是Moq,其实不知道具体的实现原理,先知道可以这样写,mock的写法也不太懂。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值