接口学习——2. 接口隔离原则学习

        接口隔离原则:功能调用者需要多少功能,接口里面就有多少功能,但是不能多,不能有用不到的功能
        单一职责原则:一个类应该只做一件事或者一组相关的事,如果一个类实现了一个有过多功能的接口,这个类也必然要把接口中所有成员都实现,这个类就会做很多件事,就违反了这个原则

        如果出现了上述的问题应该怎么解决呢
        解决方案就是把一个有很多功能的接口分割成有部分功能的小接口,每个小接口都描述一个单一的功能;其实就是把本质不同的功能给他隔离开,再用接口封装

        场景模拟:你女朋友开车撞了人,人没事车坏了,你安慰你女朋友说买辆坦克就不怕撞坏了。你女朋友很高兴的打出了GG,那么问题来了,你有女朋友吗????
按照注释顺序查看代码

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
using System.Runtime.ConstrainedExecution;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;

namespace _14.接口隔离_反射_特性_依赖注入
{

internal class Program
    {
        static void Main(string[] args)
        {
            //4. 实例化
            var drive = new Driver(new HeavyTank());//5. 因为现在Tank也是实现我们IVehicle接口的,所以一样可以开
            drive.Drive();

            //4.1 如果是开坦克呢
            //var drive1 = new Driver(new HeavyTank());
            //drive1.Drive();

            //4.2 当前存在的问题是为什么我不能又开车又开坦克呢
            //问题就是我们传进来的接口中有多的东西,汽车又不会开炮,所以Fire方法就是那个多余的,违反了接口隔离原则
            //此时应该把这个异常的接口分割成两个小接口

            Console.ReadKey();
        }
    }

    //3. 当前的状态是这两个接口都有类去实现,设计一个类
    class Driver
    {
        private IVehicle _vehicle;
        public Driver(IVehicle vehicle)//5.1 调用tank的时候只关注其Run方法,不管其他的,此时就符合了接口隔离原则
        {
            _vehicle = vehicle;
        }
        //3.1 当他调用自己的Drive的时候,实际上调用的是_vehicle字段的Run
        public void Drive()
        {
            _vehicle.Run();
        }

    }


    //1. 创建接口和车辆类
    interface IVehicle
    {
        void Run();
    }
    class Car : IVehicle
    {
        public void Run()
        {
            Console.WriteLine("Car is running...");
        }
    }
    class Truck : IVehicle
    {
        public void Run()
        {
            Console.WriteLine("Truck is running...");
        }
    }

    //4.5 所以我们新建一个武器接口
    interface IWeapon
    {
        void Fire();
    }

    //2. 创建坦克接口和坦克类
    interface ITank : IVehicle, IWeapon { } //4.6我们让ITank将IVehicle和IWeapon这两个接口合并起来,这就是一个接口对其他多个接口的继承
    //4.7 在C#中,类与类继承的时候只能有一个基类
    //4.8 而当你用一个类或者一个接口去继承其他接口的时候,是可以有多个基接口的
    //{
    //    void Fire();//4.3 实际上Fire这个方法应该隶属于武器
    //    void Run();//4.4 Run方法隶属于交通工具类
    //}
    class LightTank : ITank
    {
        public void Fire()
        {
            //throw new NotImplementedException();
            Console.WriteLine("Boom!");
        }

        public void Run()
        {
            //throw new NotImplementedException();
            Console.WriteLine("ka! ka! ka!...");
        }
    }
    class MediumTank : ITank
    {
        public void Fire()
        {
            //throw new NotImplementedException();
            Console.WriteLine("Boom!!");
        }

        public void Run()
        {
            //throw new NotImplementedException();
            Console.WriteLine("ka!! ka!! ka!!...");

        }
    }
    class HeavyTank : ITank
    {
        public void Fire()
        {
            Console.WriteLine("Boom!!!!");
        }

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

结果如下:

由例一了解了接口隔离原则,但是要注意,在使用接口隔离和单一职责原则的时候,如果玩得过火了就会产生很多很细碎,只有一个方法的接口和类

违反接口隔离原则的情况1

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
using System.Runtime.ConstrainedExecution;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;

namespace _14.接口隔离_反射_特性_依赖注入
{

internal class Program
    {
        static void Main(string[] args)
        {
            var drive = new Driver(new Car());//1.2 此时会报错,因为Drive内的IVehicle变成了ITank
            drive.Drive();

            Console.ReadKey();
        }

    }

    class Driver
    {
        //1. 如果我把字段类型改成ITank的话
        private ITank _ivehicle;
        public Driver(ITank vehicle)//1.1 构造器同样更改,这样的话能作为服务提供者的类就只剩轻型,中型,重型三种坦克了,而Car和Truck类型的实例会被挡在门外,尽管实际上我们只想调用Run方法
        {                           //这就是典型的接口用大了(接口中功能太多,因为ITank继承了两个接口,所以有Run和Fire)
            _ivehicle = vehicle;
        }
        public void Drive()
        {
            _ivehicle.Run();
        }
    }

    interface IVehicle
    {
        void Run();
    }
    class Car : IVehicle
    {
        public void Run()
        {
            Console.WriteLine("car is running...");
        }
    }
    class Truck : IVehicle
    {
        public void Run() { Console.WriteLine("Truck is running..."); }
    }

    interface IWeapon
    {
        void Fire();
    }

    interface ITank : IVehicle, IWeapon { }

    class LightTank : ITank
    {
        public void Fire()
        {
            Console.WriteLine("Boom!");
        }
        public void Run()
        {
            Console.WriteLine("ka ka ka...");
        }
    }
    class MediumTank : ITank
    {
        public void Fire()
        {
            Console.WriteLine("Boom!!");
        }
        public void Run()
        {
            Console.WriteLine("ka! ka! ka!...");
        }
    }
    class heavyTank : ITank
    {
        public void Fire()
        {
            Console.WriteLine("Boom!!!");
        }
        public void Run()
        {
            Console.WriteLine("ka!! ka!! ka!!...");
        }
    }
}

如果上面把字段类型改成ITank,因为ITank继承了两个接口,不只有Run方法,还要实现Fire方法,但是汽车是没有Fire功能的,所以new Car的实例就会报错

违反接口隔离原则的情况2

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
using System.Runtime.ConstrainedExecution;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;

namespace _14.接口隔离_反射_特性_依赖注入
{
  

internal class Program
    {
        static void Main(string[] args)
        {
            //1.0 整型数组和ArrayList都是继承IEnumerable和ICollection接口的,都是可以迭代的,而ICollection接口又是继承IEnumerable接口的
            //1.1 ICollection除了继承被迭代的功能以外,还继承类其他得功能,但是本例用不上
            int[] num1 = { 1, 2, 3, 4, 5 };
            ArrayList num2 = new ArrayList { 1, 2, 3, 4, 5 };
            Console.WriteLine(Sum(num1));
            Console.WriteLine(Sum(num2));
            //1.2 那么有没有一种集合,只实现了IEnumerable接口,没有实现ICollection接口

            //4. 测试一下
            //var roc = new ReadOnlyCollection(num1);
            //foreach (var n in roc)
            //{
            //    Console.WriteLine(n);
            //}
            //4.1 由此证明,我们这个只读的集合是可以被foreach迭代的
            //4.2 foreach语句背后就是通过拿到你的Enumerator,把里面的Current元素给你迭代一遍

            //4.3 但是我们发现,这个num3是无法被Sum函数调用的,因为我们传进去的东西太多了
            var num3 = new ReadOnlyCollection(num1);
            Console.WriteLine(Sum(num3));


            Console.ReadKey();
        }

        //4.4 我们只用得上迭代,但是传进去的时候传的是ICollection,这样就会把一些合格的服务提供者挡在外面
        //static int Sum(ICollection num)
        static int Sum(IEnumerable num)//4.5 解决方法:把这里改成IEnumerable就好了,因为在服务使用者里面,只用得着迭代,用不着别的功能,IEnumerable更加符合接口隔离原则
        {
            int sum = 0;
            foreach (var n in num)
            {
                sum += (int)n;
            }
            return sum;
        }

    }

    //1.3 答案是没有,但是我们可以自己实现一个
    class ReadOnlyCollection : IEnumerable//Alt + enter 选择实现接口
    {

        //2. 接下来看看这些方法怎么实现
        private int[] _array;//2.1 创建一个_array用来接收数组
        public ReadOnlyCollection(int[] array)//2.2 给他一个接受数组的构造器,进来之后这个数组就变成了只读的
        {
            _array = array;
        }

        //1.4 这个IEnumerable接口要求外界迭代其实例的时候,要提供一个迭代器IEnumerator
        public IEnumerator GetEnumerator()//继承IEnumerable要实现的接口方法GetEnumerator,返回一个IEnumerator类型的变量
        {
            //2.3 当别人想要迭代我的时候,这里需要传进来一个Enumerator的实例
            return new Enumerator(this);//2.5 返回对应的实例,转到3
        }

        #region 迭代器
        //1.6 创建一个成员类Enumerator,现在这个类的全名是ReadOnlyCollection.Enumerator,转到2
        public class Enumerator : IEnumerator//Alt + Enter 选择实现接口
        {
            //2.4 被我们这个迭代器所迭代的collection
            private ReadOnlyCollection _collection;
            public Enumerator(ReadOnlyCollection collection)
            {
                _collection = collection;

                //3.5 这里也要给_head设一个初始值
                _head = -1;
                //3.6 完成以上步骤我们就有了一个只能被迭代,不能往里添加、删除新元素的集合,转到4

            }

            //3. 再来看看Enumerator里面怎么实现

            private int _head;//3.2 那就给他一个
            public object Current //3.1 需要一个当前的对象     //=> throw new NotImplementedException();这个语法我们不用,是一个只读属性
            {//当前要传出去的就是_head这个index所指的我们数组里的那个元素
                get
                {
                    object o = _collection._array[_head];//将_head所指的整数传给o
                    return o;//因为这个方法是object类型的,所以不能直接返回整数,要返回object类型
                }
                // 现在是只读状态
            }

            //3.3 继续处理下一个方法
            public bool MoveNext()
            {
                if (++_head < _collection._array.Length)//先加加再比较,如果大于就会越界
                {
                    return true;
                }
                else
                {
                    return false;//越界了
                }
            }

            //3.4 再来处理这个
            public void Reset()
            {
                _head = -1;
            }
        }
        #endregion

    }

    //1.5 如果在外面声明一个Enumerator类可能会干扰整个程序,所以我们使用成员类
}

        这个例子比较难懂,所以注释有点多,新手理解不了也没关系,慢慢学习就知道了,其实我也是一知半解,这个例子同样是违反了接口隔离原则,Sum方法中ICollection接口提供的功能过多了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值