zoukankan      html  css  js  c++  java
  • 【学习笔记】设计模式六大原则之依赖倒置原则、接口隔离原则和开放封闭原则

    一、依赖倒置原则(Dependence Inversion Principle)

    下面我们来看个示例:

    /// <summary>
    /// 学生
    /// </summary>
    public class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }
    
        //依赖细节  高层就依赖了底层
        public void PlayHonor(Honor phone)
        {
            Console.WriteLine("这里是{0}", this.Name);
            phone.Call();
            phone.SendSMS();
        }
    
        //依赖细节  高层就依赖了底层
        public void PlayOppo(Oppo phone)
        {
            Console.WriteLine("这里是{0}", this.Name);
            phone.Call();
            phone.SendSMS();
        }
    }
    
    /// <summary>
    /// 荣耀手机
    /// </summary>
    public class Honor
    {
        /// <summary>
        /// 打电话
        /// </summary>
        public void Call()
        {
            Console.WriteLine("Use {0} Call", this.GetType().Name);
        }
    
        /// <summary>
        /// 发短信
        /// </summary>
        public void SendSMS()
        {
            Console.WriteLine("Use {0} SendSMS", this.GetType().Name);
        }
    }
    
    /// <summary>
    /// Oppo手机
    /// </summary>
    public class Oppo
    {
        /// <summary>
        /// 打电话
        /// </summary>
        public void Call()
        {
            Console.WriteLine("Use {0} Call", this.GetType().Name);
        }
    
        /// <summary>
        /// 发短信
        /// </summary>
        public void SendSMS()
        {
            Console.WriteLine("Use {0} SendSMS", this.GetType().Name);
        }
    }

    在示例中Student类内部使用了Honor类和Oppo类,这里的使用者Student类就称为高层模块,被使用者Honor类和Oppo类就称为低层模块,这时候我们的高层模块就依赖于我们的低层模块。

    那么这样子有什么不好呢?

      1、如果此时我们的低层模块Honor类和Oppo类扩展了或者是发生变动了,则就有可能需要修改Student类,这就导致我们的Student类难以维护,Honor类和Oppo类扩展困难。

      2、面向对象语言开发,就是类与类之间进行交互,如果高层直接依赖低层的细节,细节是多变的,那么低层的变化就导致上层的变化;如果层数多了,低层的修改会直接水波效应传递到最上层,一点细微的改动都会导致整个系统从下往上的修改。

    那么我们怎么才能更好的解决这个问题呢?

      竟然我们的低层模块是多变的,那么我们的高层模块就不应该依赖于低层模块,二者都应该依赖于抽象

    下面我们来改造一下:

    /// <summary>
    /// 学生
    /// </summary>
    public class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }
    
        //依赖抽象
        public void PlayHonor(AbstractPhone phone)
        {
            Console.WriteLine("这里是{0}", this.Name);
            phone.Call();
            phone.SendSMS();
        }
    
        //依赖抽象
        public void PlayOppo(AbstractPhone phone)
        {
            Console.WriteLine("这里是{0}", this.Name);
            phone.Call();
            phone.SendSMS();
        }
    }
    
    /// <summary>
    /// 手机抽象类
    /// </summary>
    public abstract class AbstractPhone
    {
        public int Id { get; set; }
        public string Branch { get; set; }
    
        /// <summary>
        /// 打电话
        /// </summary>
        public abstract void Call();
    
        /// <summary>
        /// 发短信
        /// </summary>
        public abstract void SendSMS();
    }
    
    /// <summary>
    /// 荣耀手机
    /// </summary>
    public class Honor : AbstractPhone
    {
        /// <summary>
        /// 打电话
        /// </summary>
        public override void Call()
        {
            Console.WriteLine("Use {0} Call", this.GetType().Name);
        }
    
        /// <summary>
        /// 发短信
        /// </summary>
        public override void SendSMS()
        {
            Console.WriteLine("Use {0} SendSMS", this.GetType().Name);
        }
    }
    
    /// <summary>
    /// Oppo手机
    /// </summary>
    public class Oppo : AbstractPhone
    {
        /// <summary>
        /// 打电话
        /// </summary>
        public override void Call()
        {
            Console.WriteLine("Use {0} Call", this.GetType().Name);
        }
    
        /// <summary>
        /// 发短信
        /// </summary>
        public override void SendSMS()
        {
            Console.WriteLine("Use {0} SendSMS", this.GetType().Name);
        }
    }

    经过这样子改造之后,我们的高层模块Student就不直接依赖于低层模块了,而是通过抽象AbstractPhone类来依赖的。

    这样子做有什么好处呢?

      1、一个方法满足不同类型的参数。

      2、还支持扩展,只要是实现了这个抽象,不用修改Student。

      3、面向抽象,抽象一般是稳定的,那低层细节的变化或者是扩展就不会影响到高层,这样就能支持层内部的横向扩展,不会影响其他地方,这样的程序架构就是稳定的。

    小结:

    1、依赖倒置原则:高层模块不应该依赖于低层模块,二者都应该依赖于抽象。应该依赖于抽象,而不是依赖细节。

    2、抽象:接口/抽象类,可以包含没有实现的元素。细节:普通类,一切都是确定的。

    3、面向抽象编程:尽量的使用抽象,80%的设计模式都是跟抽象有关。

    4、面向抽象不止一个类型,用的就是通用功能;非通用的,那就不应该面向抽象。

    5、面向抽象,只要抽象不变,高层就不变。

    6、面向对象语言开发,就是类与类之间进行交互,如果高层直接依赖低层的细节,细节是多变的,那么低层的变化就导致上层的变化;如果层数多了,低层的修改会直接水波效应传递到最上层,一点细微的改动都会导致整个系统从下往上的修改。

    7、面向抽象,抽象一般是稳定的,那低层细节的变化或者是扩展就不会影响到高层,这样就能支持层内部的横向扩展,不会影响其他地方,这样的程序架构就是稳定的。

    8、依赖倒置原则(理论基础),IOC控制反转(实践封装),DI依赖注入(实现IOC的手段)。

    二、接口隔离原则(Interface Segregation Principle)

     接口隔离原则:客户端不应该依赖于它不需要的接口; 一个类对另一个类的依赖应该建立在最小的接口上。

    怎么理解这个呢?下面我们来看个示例:

    /// <summary>
    /// 手机抽象类
    /// </summary>
    public abstract class AbstractPhone
    {
        public int Id { get; set; }
        public string Branch { get; set; }
    
        /// <summary>
        /// 打电话
        /// </summary>
        public abstract void Call();
    
        /// <summary>
        /// 发短信
        /// </summary>
        public abstract void SendSMS();
    
        /// <summary>
        /// 拍照
        /// </summary>
        public abstract void Photo();
    }

    可以看出我们在上面的基础上加了一个拍照功能,我们都知道现在的智能手机都有拍照功能,但是呢老旧的诺基亚手机就没有拍照功能,所以说这样子写就是不合理的。

    此时我们就需要对其进行拆分了:

    /// <summary>
    /// 手机抽象类
    /// </summary>
    public abstract class AbstractPhone
    {
        public int Id { get; set; }
        public string Branch { get; set; }
    
        /// <summary>
        /// 打电话
        /// </summary>
        public abstract void Call();
    
        /// <summary>
        /// 发短信
        /// </summary>
        public abstract void SendSMS();
    }
    
    /// <summary>
    /// 拍照接口
    /// </summary>
    public interface IPhoto
    {
        /// <summary>
        /// 拍照
        /// </summary>
        void Photo();
    }

    这样子改造后就符合一个类对另一个类的依赖建立在最小的接口上。

    小结:

    1、接口隔离原则:客户端不应该依赖于它不需要的接口; 一个类对另一个类的依赖应该建立在最小的接口上。

    2、接口interface定义 can do   不局限产品。

    3、接口到底该如何定义?

      既不能是大而全,会强迫实现没有的东西,也会依赖自己不需要的东西。

      也不能太细一个方法一个接口,这样面向抽象就没有意义了。

      按照功能的密不可分来定义接口,而且应该是动态的,随着业务发展会有变化的,但是在设计的时候,要留好提前量,避免抽象的变化。

    4、接口合并,业务细节要尽量的内聚,接口不要暴露太多业务细节(即接口不能太细了)。例如:

      1、地图Map--定位/搜索/导航 ,这种属于固定步骤,就可以合并成一个接口。

      2、手机--打电话/发短信,只要是手机这2个功能就都会有,那么我们就应该合并成一个接口。

    三、开放封闭原则(Open Closed Principle)

    小结:

    1、开放封闭原则:对扩展开放,对修改封闭。

      修改:修改现有代码(类)(例如:程序分支。)

      扩展:增加代码(类)(例如:之前提到的单一职责原则。)

    2、面向对象语言是一种静态语言,最害怕变化,会波及很多东西而导致全面测试。最理想就是新增类,对原有代码没有改动,原有的代码才是可信的。

    3、其他5个原则的建议,就是为了更好的做到OCP,开闭原则也是面向对象语言开发一个终极目标。

    4、如果有功能增加/修改的需求:修改现有方法(最不理想的)---增加方法(相对较好的)---增加类(比较理想的)---增加/替换类库(最理想的)。

  • 相关阅读:
    poj 2942 Knights of the Round Table(无向图的双连通分量+二分图判定)
    Java序列化的几种方式
    Android 四大组件学习之Service六
    cursor:pointer的意思
    JSP中<base href="<%=basePath%>">的作用
    一篇让Java程序猿随时可以翻看的Oracle总结
    史上最全的javascript知识点总结,浅显易懂。
    史上最全的HTML、CSS知识点总结,浅显易懂。
    css学习归纳总结
    Web前端开发Chrome插件
  • 原文地址:https://www.cnblogs.com/xyh9039/p/12723520.html
Copyright © 2011-2022 走看看