接口与单元测试

接口的本质是契约(contract),双方都可见。

求数组中的数字之和和平均数

不同类型的数组,不同方法

using System;
using System.Collections;

namespace March9
{
    internal class Program
    {
        public 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;
        }
        
    }
}

利用接口

using System;
using System.Collections;

namespace March9
{
    internal class Program
    {
        public static void Main(string[] args)
        {
            int[] nums1 = new int[] {1, 2, 3, 4, 5};
            ArrayList nums2 = new ArrayList {1, 2, 3, 4, 5,6};//ArrayList,array继承自IEnumerable接口
            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;
        }
        
    }
}

接口是为松耦合而生的,方便功能的可替换性,

using System;
using System.Collections;

namespace March9
{
    internal class Program
    {
        public static void Main(string[] args)
        {
            var user=new User(new VIVO());//实现用户使用不同的手机打电话,发信息
            user.Userphone();
        }

    }

    class User
    {
        private IPhone _phone;
        public User(IPhone phone)//接收接口类型的变量
        {
            _phone = phone;
        }
        public void Userphone()
        {
            _phone.Dial();
            _phone.Pickup();
            _phone.Send();
            _phone.Receive();
        }
        
    }


    interface IPhone
    {
        void Dial();//拨号
        void Pickup();//接听
        void Send();//发送
        void Receive();//接收
    }

    class NokiaPhone:IPhone
    {
        public void Dial()
        {
            Console.WriteLine("Nokia is calling");
        }
        public void Pickup()
        {
            Console.WriteLine("hello,tim");
        }
        public void Send()
        {
            Console.WriteLine("Nokia is ring");
        }
        public void Receive()
        {
            Console.WriteLine("hello,maray");
        }
    }
    
    class VIVO:IPhone
    {
        public void Dial()
        {
            Console.WriteLine("VIVO is calling");
        }
        public void Pickup()
        {
            Console.WriteLine("hello");
        }
        public void Send()
        {
            Console.WriteLine("Vivo is send");
        }
        public void Receive()
        {
            Console.WriteLine("Vivo is receiving");
        }
    }
    
    
}

语言对面向对象设计的内建支持:依赖反转,接口隔离,开闭原则……

依赖反转

被依赖的再下面,上面是司机下面的是车。Driver里有一个car类型的字段,他们是紧耦合的。这种情况下每一个人只能开对应的车,不能开其他的车

Driver里的字段不再是car等车的类型,而是一个基接口,去调用各种车里的run方法,出现了依赖反转。

当多个服务的提供者和服务的使用者都遵循一个接口时,就可以进行多种配对。通过接口作为桥梁,实现调用不同实例的方法。

接口隔离

契约:甲方(派生类)不会多要,乙方(接口)不会少给。如果一直没有调用,就说明他是多余的,是个胖接口。
单一职责原则,一个类只做一件事。
例子:让一个人开多种交通工具

using System;

namespace March10
{
    internal class Program
    {
        public static void Main(string[] args)
        {
            var a=new Driver(new Car());//此时car类和Truck都能开汽车,因为他继承自IVhicle接口,如果我想开坦克呢?那么就要改变Driver引用实例的变量的类型。
            a.Drive();
        }
    }


    class Driver
    {
        private IVehicle _vehicle;//此时,IVehicle类型的变量能引用继承自IVhicle接口实例的方法;ITank类型的变量能引用继承自ITank接口的实例的方法

        public Driver(IVehicle vehicle)
        {
            _vehicle = vehicle;
        }

        public void Drive()
        {
            _vehicle.Run();
        }


    }

    interface IVehicle
    { 
        void Run();
    }

    class Car:IVehicle
    {
        public void Run()
        {
            Console.WriteLine("car is runnning!");
        }
    }

    class Truck:IVehicle
    {
        public void Run()
        {
            Console.WriteLine("truck is running!");
        }
    }

    interface ITank
    {
        void Fire();
        void Run();
    }

    class LightTank:ITank
    {
        public void Fire()
        {
            Console.WriteLine("开炮");
        }

        public void Run()
        {
            Console.WriteLine("ka……ka……ka");
        }
    }
}

上面这种换来换取的方法很麻烦,问题就是传进去了胖接口,fire()方法用不到,因为功能不同,我只想开车,而不是开火做其他的事。
把fire()方法单独做一个接口,然后让ITank接口继承两个基接口。接口是可以多重继承的,而类不行。

using System;

namespace March10
{
    internal class Program
    {
        public static void Main(string[] args)
        {
            var a=new Driver(new Car());
            a.Drive();
        }
    }


    class Driver
    {
        private IVehicle _vehicle;

        public Driver(IVehicle vehicle)
        {
            _vehicle = vehicle;
        }

        public void Drive()
        {
            _vehicle.Run();
        }


    }

    interface IVehicle
    { 
        void Run();
    }
    interface Single
    { 
        void Fire();
    }

    class Car:IVehicle
    {
        public void Run()
        {
            Console.WriteLine("car is runnning!");
        }
    }

    class Truck:IVehicle
    {
        public void Run()
        {
            Console.WriteLine("truck is running!");
        }
    }

    interface ITank:IVehicle,Single
    {
    }

    class LightTank:ITank
    {
        public void Fire()
        {
            Console.WriteLine("开炮");
        }

        public void Run()
        {
            Console.WriteLine("ka……ka……ka");
        }
    }
}

此时,我们调用ITank接口的时候,只关注了里面的继承自IVhicle的run方法,Driver只调用run方法,服务的调用者不会多要。
问题又来了,如果我们把Driver类里的改为private ITank _vehicle;构造器的变量类型改变了,那么会发现,car和truck类的run方法无法调用,因为接口用的太大了,应该传小接口。

using System;
using System.Collections;

namespace March10
{
    internal class Program
    {
        public static void Main(string[] args)
        {
            int[] nums1 = new int[] {1, 2, 3, 4, 5};
            ArrayList nums2 = new ArrayList {1, 2, 3, 4,5,6};//ArrayList,array继承自IEnumerable接口
            var nums3=new ReadOnlyCollection(nums1);
            Console.WriteLine(Sum(nums1));
            foreach (var n in nums3)
            {
                Console.WriteLine(n);
            }
        }

        static int Sum(ICollection nums)
        {
            int sum = 0;
            foreach (var n in nums)
            {
                sum +=(int) n;
            }
            return sum;
        }

        
    }

    public class ReadOnlyCollection:IEnumerable
    {
        private int[] _array;

        public ReadOnlyCollection(int[] array)
        {
            _array = array;//接受过来的数组只读
        }
        public IEnumerator GetEnumerator()
        {
            return new Enumerator(this);
        }

        
        //成员类
        public class Enumerator:IEnumerator
        {
            private ReadOnlyCollection _collection;
            private int _head;
            public Enumerator(ReadOnlyCollection collection)
            {
                _collection = collection;
                _head = -1;
            }
            public bool MoveNext()
            {
                if (++_head < _collection._array[_head])
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }

            public void Reset()
            {
                _head = -1;
            }

            public object Current
            {
                get
                {
                    object o = _collection._array[_head];
                    return o;
                }
            } //只读属性
        }
    }
    
    
}

sum方法里需要循环然后再加,所以参数只需要迭代就可以了,但是他的参数类型设计成了IColllection,导致符合迭代要求的IEnumerable类型的参数无法传进来,就这是所谓的“使用方要求过多”。
改为static int Sum(IEnumerable nums) { }就可以了
接口的显示与隐式

using System;

namespace March11
{
    internal class Program
    {
        public static void Main(string[] args)
        {
            var kill=new Kill1();
            kill.love();//此时看不到kill方法
            IKiller k = kill;
            k.kill();//此时看不到love方法

        }
    }


    interface IGentleman
    {
        void love();
    }

    interface IKiller
    {
        void kill();
    }


    class Kill1 : IGentleman,IKiller
    {
        public void love()
        {
            Console.WriteLine("I love you");
        }

        void IKiller.kill()
        {
            Console.WriteLine("I will kill you");
        }
    }
    
    
}
posted @ 2020-03-10 15:47  翱翔的猴子  阅读(347)  评论(0编辑  收藏  举报