第三节:接口隔离原则、迪米特法则、组合聚合原则

一. 接口隔离原则

1. 定义

 一个类对另一个类的依赖应该建立在最小的接口上,不应该依赖他不需要的接口。

 通俗的说:要为每个类建立它们需要的专用接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。

与单一职责原则的区别:

(1). 单一职责原则注重的是职责,而接口隔离原则注重的是对接口依赖的隔离。

(2). 单一职责原则主要是约束类,它针对的是程序中的实现和细节;接口隔离原则主要约束接口,主要针对抽象和程序整体框架的构建。

2. 优点

  接口隔离原则是为了约束接口、降低类对接口的依赖性,遵循接口隔离原则有以下 优点:

 (1). 将臃肿庞大的接口分解为多个粒度小的接口,可以预防外来变更的扩散,提高系统的灵活性和可维护性。

 (2). 接口隔离提高了系统的内聚性,减少了对外交互,降低了系统的耦合性。

 (3). 如果接口的粒度大小定义合理,能够保证系统的稳定性;但是,如果定义过小,则会造成接口数量过多,使设计复杂化;如果定义太大,灵活性降低,无法提供定制服务,给整体项目带来无法预料的风险。

 (4). 使用多个专门的接口还能够体现对象的层次,因为可以通过接口的继承,实现对总接口的定义。

 (5). 能减少项目工程中的代码冗余。过大的大接口里面通常放置许多不用的方法,当实现这个接口的时候,被迫设计冗余的代码。

3. 实现方法和案例

(1). 实现方法

 A. 接口尽量小,但是要有限度。一个接口只服务于一个子模块或业务逻辑。

 B. 为依赖接口的类定制服务。只提供调用者需要的方法,屏蔽不需要的方法。

 C. 了解环境,拒绝盲从。每个项目或产品都有选定的环境因素,环境不同,接口拆分的标准就不同深入了解业务逻辑。

 D. 提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情。

(2). 案例

 在成绩管理系统中,会有很多业务,这里拆分出来多个接口,分别是:输入模块接口、统计模块接口、打印模块接口,需要哪个接口就去实现哪个接口。

   /// <summary>
   /// 基础模块接口 
   /// </summary>
    public interface IBasicModel
    {
        void insert();
        void delete();
        void modify();
    }
    /// <summary>
    /// 统计模块接口
    /// </summary>
    public interface ICountModule
    {
        void countTotalScore();
        void countAverage();
    }
    /// <summary>
    /// 打印模块接口
    /// </summary>
    public interface IPrintModule
    {
        void printStuInfo();
        void queryStuInfo();
    }

二. 迪米特法则

1. 定义

 又叫:最小知道原则。

 其含义:如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。 通俗的来说:只与你的直接朋友交谈,不跟“陌生人”说话。

   目的:降低类之间的耦合度,提高模块的相对独立性。

进一步解析:

 (1). 从依赖者的角度来说,只依赖应该依赖的对象。

 (2). 从被依赖者的角度说,只暴露应该暴露的方法。

2. 优点

 (1). 降低了类之间的耦合度,提高了模块的相对独立性。

 (2). 由于亲合度降低,从而提高了类的可复用率和系统的扩展性。

同样也有弊端:

 过度使用迪米特法则会使系统产生大量的中介类,从而增加系统的复杂性,使模块之间的通信效率降低。所以,在釆用迪米特法则时需要反复权衡,确保高内聚和低耦合的同时,保证系统的结构清晰。

3. 实现方法和案例

(1). 实现方法

 A. 在类的划分上,应该创建弱耦合的类。类与类之间的耦合越弱,就越有利于实现可复用的目标。

 B. 在类的结构设计上,尽量降低类成员的访问权限。

 C. 在类的设计上,优先考虑将一个类设置成不变类。

 D. 在对其他类的引用上,将引用其他对象的次数降到最低。

 E. 不暴露类的属性成员,而应该提供相应的访问器(set 和 get 方法)。

 F. 谨慎使用序列化(Serializable)功能。

(2). 案例

 明星 ----- 经纪人 ------ 粉丝、媒体;  明星不直接和粉丝、媒体打交道,都是通过经纪人来打交道。

明星和粉丝、媒体类

   /// <summary>
    /// 粉丝类
    /// </summary>
    public class Fans
    {
        private String name;
        public Fans(String name)
        {
            this.name = name;
        }
        public String getName()
        {
            return name;
        }
    }
    /// <summary>
    /// 媒体公司类
    /// </summary>
    public class Company
    {
        private String name;
        public Company(String name)
        {
            this.name = name;
        }
        public String getName()
        {
            return name;
        }
    }
   /// <summary>
    /// 明星类
    /// </summary>
    public class Star
    {
        private String name;
        public Star(String name)
        {
            this.name = name;
        }
        public String getName()
        {
            return name;
        }
    }
View Code

经纪人

/// <summary>
    /// 经纪人类
    /// </summary>
    public class Agent
    {
        private Star myStar;
        private Fans myFans;
        private Company myCompany;
        public void setStar(Star myStar)
        {
            this.myStar = myStar;
        }
        public void setFans(Fans myFans)
        {
            this.myFans = myFans;
        }
        public void setCompany(Company myCompany)
        {
            this.myCompany = myCompany;
        }
        public void meeting()
        {
            Console.WriteLine(myFans.getName() + "与明星" + myStar.getName() + "见面了。");
        }
        public void business()
        {
            Console.WriteLine(myCompany.getName() + "与明星" + myStar.getName() + "洽淡业务。");
        }
    }
View Code

测试

            {
                Console.WriteLine("--------------------------迪米特法则--------------------------------");
                //明星 ----- 经纪人 ------ 粉丝、媒体;  明星不直接和粉丝、媒体打交道,都是通过经纪人来打交道
                Agent agent = new Agent();
                agent.setStar(new Star("吕饺子"));
                agent.setFans(new Fans("粉丝李马茹"));
                agent.setCompany(new Company("中国传媒有限公司"));
                agent.meeting();
                agent.business();
            }

运行结果

三. 组合聚合原则

1. 定义

  也叫合成复用原则,指的是软件在复用的时候,优先使用组合和聚合的关系来实现,其次才是继承关系。

PS:如果要使用继承关系,则必须严格遵循里氏替换原则。合成复用原则同里氏替换原则相辅相成的,两者都是开闭原则的具体实现规范。

扩展:适配器模式中的类适配模式,是通过继承的方式实现的;对象适配模式,是通过组合的方式实现的。

2. 优点

继承复用存在以下缺点:

 (1). 继承复用破坏了类的封装性。因为继承会将父类的实现细节暴露给子类,父类对子类是透明的,所以这种复用又称为“白箱”复用。

 (2). 子类与父类的耦合度高。父类的实现的任何改变都会导致子类的实现发生变化,这不利于类的扩展与维护。

 (3). 它限制了复用的灵活性。从父类继承而来的实现是静态的,在编译时已经定义,所以在运行时不可能发生变化。

组合聚合复用存在以下优点:

 (1). 它维持了类的封装性。因为成分对象的内部细节是新对象看不见的,所以这种复用又称为“黑箱”复用。

 (2). 新旧类之间的耦合度低。这种复用所需的依赖较少,新对象存取成分对象的唯一方法是通过成分对象的接口。

 (3). 复用的灵活性高。这种复用可以在运行时动态进行,新对象可以动态地引用与成分对象类型相同的对象。

3. 实现方法和案例

(1). 实现方法

 组合聚合原则是通过将已有的对象纳入新对象中,作为新对象的成员对象来实现的,新对象可以调用已有对象的功能,从而达到复用。

(2). 案例

相关类

    /// <summary>
    /// 封装最基本的四则运算方法
    /// </summary>
    public class MyBaseHelp
    {

        public int Add(int a,int b)
        {
            return a + b;
        }

        public int Multiply(int a, int b)
        {
            return a * b;
        }

    }
 public class MyChildHelp1
    {
        //复用原则
        public MyBaseHelp _baseHelp;
        public MyChildHelp1()
        {
            _baseHelp = new MyBaseHelp();
        }

        /// <summary>
        /// 特殊计算
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public int SpecialCalculate(int a, int b)
        {
            return _baseHelp.Add(a, b) + _baseHelp.Multiply(a, b);
        }

    }

测试

          Console.WriteLine("--------------------------组合聚合原则--------------------------------");
                MyChildHelp1 help1 = new MyChildHelp1();
                Console.WriteLine($"特殊运算结果为:{help1.SpecialCalculate(10, 4)}");  

运行结果

 

!

  • 作       者 : Yaopengfei(姚鹏飞)
  • 博客地址 : http://www.cnblogs.com/yaopengfei/
  • 声     明1 : 如有错误,欢迎讨论,请勿谩骂^_^。
  • 声     明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
 
原文地址:https://www.cnblogs.com/yaopengfei/p/13539035.html