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


    一. 接口隔离原则

    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 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。
     
  • 相关阅读:
    找正环的最快方法!!
    树重量的神仙问题
    2019 ICPC南昌网络赛 B题
    洛谷p-1522又是Floyd
    poj3471
    洛谷p1119--灾难后重建(Floyd不仅仅是板子)
    并查集的超市问题---溜TM的
    利用主席树 搞区间不同值的和
    acwing 102 -利用二分枚举区间平均值
    Week__8
  • 原文地址:https://www.cnblogs.com/yaopengfei/p/13539035.html
Copyright © 2020-2023  润新知