六大设计原则二

依赖倒置原则

依赖倒置原则:上层模块不应该依赖于低层模块,二者应该通过抽象依赖,就是说应该依赖于抽象,而不是依赖于细节,面向抽象编程

看下面的代码:

学生类:

 public class Student
    {
       
        public int Id { get; set; }
        public string Name { get; set; } 

        public void PlayiPhone(iPhone phone)
        {
            Console.WriteLine("这里是{0}", this.Name);
            phone.Call();
            phone.Text();
        }

        public void PlayLumia(Lumia phone)
        {
            Console.WriteLine("这里是{0}", this.Name);
            phone.Call();
            phone.Text();
        }
    }

手机抽象类:

public abstract class AbstractPhone
    {
        public int Id { get; set; }
        public string Branch { get; set; }
        public abstract void Call();
        public abstract void Text();
    }

各种手机类:

   /// <summary>
    /// iPhone
    /// </summary>
    public class iPhone : AbstractPhone
    {
        public override void Call()
        {
            Console.WriteLine("User {0} Call", this.GetType().Name);
        }
        public override void Text()
        {
            Console.WriteLine("User {0} Call", this.GetType().Name);
        }
    }

public class Lumia : AbstractPhone
    {
        public override void Call()
        {
            Console.WriteLine("User {0} Call", this.GetType().Name);
        }
        public override void Text()
        {
            Console.WriteLine("User {0} Text", this.GetType().Name);
        }

        public void New()
        { }
    }

调用:

 public class DIPShow
    {
        public static void Show()
        {
            Console.WriteLine("**************DIPShow***************");
            //Student 为上层  手机为低层  因为学生要用手机,所以学生是处于高层的
            Student student = new Student()
            {
                Id = 191,
                Name = "候鸟班长"
            }; 

            {
                iPhone phone = new iPhone();
                student.PlayiPhone(phone);
            }
            {
                Lumia phone = new Lumia();
                student.PlayLumia(phone);
            }
        }
    }

 从上面的代码逻辑中我们可以得知,Student 为上层,手机为低层  因为学生要用手机,所以学生是处于高层的。依赖倒置原则又要求我们说高层不能直接依赖于底层,二者要通过抽象进行依赖,现在的代码也能实现功能,但是后期如果说要增加不同的手机类别的话,在学生类中,就要不断改动学生类,增加学生类中使用手机的方法,此时手机和学生是依赖于细节的,所以我们需要将二者依赖于抽象,这样的话后期不管增加多少中手机品类都不用更改学生类,因为手机的变化本来就不应该和学生有联系,现在的耦合很严重,改动如下:

学生类:

 public class Student
    {
        private AbstractPhone phone = null;
        public Student(AbstractPhone phone)
        {
            this.phone = phone;
        }
        public int Id { get; set; }
        public string Name { get; set; }
        public void Play()
        {
            Console.WriteLine("这里是{0}", this.Name);
            this.phone.Call();
            this.phone.Text();
        }
 
    }

调用:

 public class DIPShow
    {
        public static void Show()
        {
            Console.WriteLine("**************DIPShow***************");
            //Student 为上层  手机为低层  因为学生要用手机,所以学生是处于高层的
            Student student = new Student(new iPhone())
            {
                Id = 191,
                Name = "候鸟班长"
            }; 
            student.Play(); 
        }
    }

这样就实现依赖于抽象,耦合降低了。

 如果你的东西根本不会变化了,那就不需要考虑依赖倒置原则。

好处:扩展性更高了,

接口隔离原则

接口隔离原则:客户端不应该依赖它不需要的接口; 一个类对另一个类的依赖应该建立在最小的接口上;或者说实现一个接口的时候,只需要自己必须的功能。

接口就是作为一个约束,必须实现全部的接口。

看下面的方法,手机继承了一个基类,这个基类是作为手机存在必须要实现存在的功能,是作为这个物品本质上存在的属性和行为,但是手机也有不同的,也存在不同的功能,所以我们可以通过接口来定义并实现它们:

  public abstract class AbstractPhone
    {
        public int Id { get; set; }
        public string Branch { get; set; }
        public abstract void Call();
        public abstract void Text();
    }

    /// <summary>
    /// 一个大而全的接口
    /// </summary>
    public interface IExtend
    {
        void Photo();
        void Online();
        void Game();
        void Record();
        void Movie();
        void Map();
        void Pay();
    }

华为手机:

   public class Honor : AbstractPhone, IExtend
    {
        public override void Call()
        {
            Console.WriteLine("User {0} Call", this.GetType().Name);
        }

        public override void Text()
        {
            Console.WriteLine("User {0} Call", this.GetType().Name);
        }

        public void Photo()
        {
            Console.WriteLine("User {0} Photo", this.GetType().Name);
        }

        public void Online()
        {
            Console.WriteLine("User {0} Online", this.GetType().Name);
        }

        public void Game()
        {
            Console.WriteLine("User {0} Game", this.GetType().Name);
        }

        public void Map()
        {
            Console.WriteLine("User {0} Map", this.GetType().Name);
        }

        public void Pay()
        {
            Console.WriteLine("User {0} Pay", this.GetType().Name);
        }

        public void Record()
        {
            Console.WriteLine("User {0} Record", this.GetType().Name);
        }

        public void Movie()
        {
            Console.WriteLine("User {0} Movie", this.GetType().Name);
        }
    }

IPhone:

 /// <summary>
    /// OldMan 老人机
    /// </summary>
    public class OldMan : AbstractPhone, IExtend
    {
        public override void Call()
        {
            Console.WriteLine("User {0} Call", this.GetType().Name);
        }
        public override void Text()
        {
            Console.WriteLine("User {0} Call", this.GetType().Name);
        }

        public void Photo()
        {
            Console.WriteLine("User {0} Photo", this.GetType().Name);
        }

        public void Online()
        {
            Console.WriteLine("User {0} Online", this.GetType().Name);
        }

        public void Game()
        {
            Console.WriteLine("User {0} Game", this.GetType().Name);
        }

        public void Map()
        {
            Console.WriteLine("User {0} Map", this.GetType().Name);
        }

        public void Pay()
        {
            Console.WriteLine("User {0} Pay", this.GetType().Name);
        }

        public void Record()
        {
            Console.WriteLine("User {0} Record", this.GetType().Name);
        }

        public void Movie()
        {
            Console.WriteLine("User {0} Movie", this.GetType().Name);
        }
    }

但是现在有一个问题,接口作为一个约束,必须要实现它的所有方法,但是老人机没办法全部实现的,因为老人机没有导航功能,也没有支付功能等,IExtend接口功能太全了,所以我们应该拆分一下,保证它的灵活性。接口是能够多实现的,但是继承只能单继承。

    /// <summary>
    ///  
    /// </summary>
    public interface IExtend
    {
        void Photo();
        void Online();
        void Game();
      
    }

    public interface IExtendAdvanced
    {
        void Record();
        void Movie();
        void Map();
        void Pay();
    }

手机类:

 /// <summary>
    /// iPhone
    /// </summary>
    public class iPhone : AbstractPhone, IExtend, IExtendAdvanced
    {
        public override void Call()
        {
            Console.WriteLine("User {0} Call", this.GetType().Name);
        }
        public override void Text()
        {
            Console.WriteLine("User {0} Call", this.GetType().Name);
        }

        public void Photo()
        {
            Console.WriteLine("User {0} Photo", this.GetType().Name);
        }

        public void Online()
        {
            Console.WriteLine("User {0} Online", this.GetType().Name);
        }

        public void Game()
        {
            Console.WriteLine("User {0} Game", this.GetType().Name);
        }

        public void Map()
        {
            Console.WriteLine("User {0} Map", this.GetType().Name);
        }

        public void Pay()
        {
            Console.WriteLine("User {0} Pay", this.GetType().Name);
        }

        public void Record()
        {
            Console.WriteLine("User {0} Record", this.GetType().Name);
        }

        public void Movie()
        {
            Console.WriteLine("User {0} Movie", this.GetType().Name);
        }
    }

    /// <summary>
    /// OldMan 老人机
    /// </summary>
    public class OldMan : AbstractPhone, IExtend 
    {
        public override void Call()
        {
            Console.WriteLine("User {0} Call", this.GetType().Name);
        }
        public override void Text()
        {
            Console.WriteLine("User {0} Call", this.GetType().Name);
        }

        public void Photo()
        {
            Console.WriteLine("User {0} Photo", this.GetType().Name);
        }

        public void Online()
        {
            Console.WriteLine("User {0} Online", this.GetType().Name);
        }

        public void Game()
        {
            Console.WriteLine("User {0} Game", this.GetType().Name);
        } 
    }

比如说,我又新加了一个相机,相机默认只能拍照和上网,我再次进行接口的拆分:

  /// <summary>
    ///  
    /// </summary>
    public interface IExtend
    { 
        void Game(); 
    }

    public interface IExtendAdvanced
    {
        void Record();
        void Movie();
        void Map();
        void Pay();
    }
     /// <summary>
     /// 拆分出来的 相机所具有的方法 IExtend接口方法又少了
     /// </summary>
    public interface IExtendPhontography
    {
        void Photo();
        void Online();
    }

 手机相机类:

 /// <summary>
    /// iPhone
    /// </summary>
    public class iPhone : AbstractPhone, IExtend, IExtendAdvanced, IExtendPhontography
    {
        public override void Call()
        {
            Console.WriteLine("User {0} Call", this.GetType().Name);
        }
        public override void Text()
        {
            Console.WriteLine("User {0} Call", this.GetType().Name);
        }

        public void Photo()
        {
            Console.WriteLine("User {0} Photo", this.GetType().Name);
        }

        public void Online()
        {
            Console.WriteLine("User {0} Online", this.GetType().Name);
        }

        public void Game()
        {
            Console.WriteLine("User {0} Game", this.GetType().Name);
        }

        public void Map()
        {
            Console.WriteLine("User {0} Map", this.GetType().Name);
        }

        public void Pay()
        {
            Console.WriteLine("User {0} Pay", this.GetType().Name);
        }

        public void Record()
        {
            Console.WriteLine("User {0} Record", this.GetType().Name);
        }

        public void Movie()
        {
            Console.WriteLine("User {0} Movie", this.GetType().Name);
        }
    }

    /// <summary>
    /// OldMan 老人机
    /// </summary>
    public class OldMan : AbstractPhone, IExtend
    {
        public override void Call()
        {
            Console.WriteLine("User {0} Call", this.GetType().Name);
        }
        public override void Text()
        {
            Console.WriteLine("User {0} Call", this.GetType().Name);
        }

        public void Photo()
        {
            Console.WriteLine("User {0} Photo", this.GetType().Name);
        }

        public void Online()
        {
            Console.WriteLine("User {0} Online", this.GetType().Name);
        }

        public void Game()
        {
            Console.WriteLine("User {0} Game", this.GetType().Name);
        }
    }
    /// <summary>
    ///相机 假设相机只能拍照和上网
    /// </summary>
    public class Camera : IExtendPhontography
    {
        /// <summary>
        /// 拍照
        /// </summary>
        public void Photo()
        {
            Console.WriteLine("User {0} Photo", this.GetType().Name);
        }
        /// <summary>
        /// 录音
        /// </summary>
        public void Online()
        {
            Console.WriteLine("User {0} Online", this.GetType().Name);
        }

    }

 为什么分要对接口进行拆分呢?比如说新进相机之后,我不再拆分IExtend接口,我新建一个接口IExtendPhontographyTest,IExtend接口不动:

 /// <summary>
    ///  
    /// </summary>
    public interface IExtend
    { 
        void Game(); 
        void Photo();
        void Online();
    }

    public interface IExtendAdvanced
    {
        void Record();
        void Movie();
        void Map();
        void Pay();
    }
     /// <summary>
     /// 拆分出来的 相机所具有的方法 IExtend接口方法又少了
     /// </summary>
    public interface IExtendPhontographyTest
    {
         void Photo();
        void Online();
    }

实现类:

   /// <summary>
    ///相机 假设相机只能拍照和上网
    /// </summary>
    public class Camera : IExtendPhontographyTest
    {
        /// <summary>
        /// 拍照
        /// </summary>
        public void Photo()
        {
            Console.WriteLine("User {0} Photo", this.GetType().Name);
        }
        /// <summary>
        /// 录音
        /// </summary>
        public void Online()
        {
            Console.WriteLine("User {0} Online", this.GetType().Name);
        }

    }

这样功能上是没问题的,但是这样的话就少了复用性,但是如果使用拆分接口的形式的话,就能够实现复用性:

     public class ISPShow
    {
        public static void Show()
        { 
            IExtendPhontography phontography = new iPhone();
            IExtendPhontography camara = new Camera();
            Photography(camara);
            Photography(phontography);
        }
         
        /// <summary>
        /// 只要是实现了这个接口IExtendPhontography的类都可以调用这个方法
        /// </summary>
        /// <param name="extend"></param>
        public static void Photography(IExtendPhontography extend)
        {
            extend.Photo();
            extend.Online();
        }

    }

IPhone和camera类都实现了IExtendPhontography类,所以也都能调用这个方法,但是如果只是另外新建了一个类的话,这个方法就只能相机类能用了。 

所以说,接口隔离原则希望我们能够尽量将接口拆分一下,保证它的灵活性,只依赖于自己本身需要的接口,也是依赖倒置的延续,复用性更强,都是为了未来更好 的扩展开放, c#中很多封装的类都是这种形式,实现多接口比如List,Dictionary。

最后,为什么我们要拆分接口呢?因为不能让类型实现自己没有的功能,也能让方法具备复用性。

究竟如何设计呢?

1 接口不能太大,否则会实现不需要的功能

2 接口尽量的小,但是一致的功能应该在一起,也不能过度设计。

3 接口合并:如果一个业务包含多个步骤,我们不能把步骤都暴露,而是提供一个入口即可。比如手机定位,定位肯定要有很多步骤,不能都开放出来,只留出一个定位的接口就行。

开闭原则

开闭原则:对扩展开发,对修改关闭。如果有功能扩展变化的需求,希望是增加而不是修改 ,尽量不影响之前的功能

IExtendPhontographyTest
原文地址:https://www.cnblogs.com/anjingdian/p/15333353.html