设计模式系列:迪米特法则

问题由来

问题由来:类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。

解决方案:尽量降低类与类之间的耦合。
自从我们接触编程开始,就知道了软件编程的总的原则:低耦合,高内聚。无论是面向过程编程还是面向对象编程,只有使各个模块之间的耦合尽量的低,才能提高代码的复用率。低耦合的优点不言而喻,但是怎么样编程才能做到低耦合呢?那正是迪米特法则要去完成的。

定义

一个软件实体应当尽可能少的与其他实体发生相互作用。一个对象应该对其他对象有最少的了解。通俗的讲,一个类应该对自己需要耦合调用的类知道得最少,你(被耦合或调用的类)的内部是如何复杂都和我没关系,那是你的事情,我就知道你提供的这么多public方法,我就调用这么多,其他的我一概不关心。即:一个对象对其他对象的引用应该降到最低。

迪米特法则对类的低耦合做了明确的要求,其包含以下4层含义:

  • 只和朋友交流
    朋友类的定义是这样的,出现在成员变量、方法的输入输出参数中的类成为成员朋友类,如果当前对象的成员对象是一个集合,那么集合中的元素也都是朋友。而出现在方法内部的类不属于朋友类。

  • 朋友间也是有距离的
    一个类公开的public属性或方法越多,修改时涉及的面也就越大,变更引起的风险扩散也就越大。因此,为了保持朋友类间的距离,在设计时需要反复衡量:是否可以再减少public方法和属性,是否可以修改为private、protected等访问权限,是否可以加上final关键字等。

  • 是自己的就是自己的
    在实际应用中经常会出现这样的一个方法:放在本类中也可以,放在其他类中也没有错,那怎么去衡量呢?你可以坚持这样一个原则:如果一个方法放在本类中,既不增加类间关系,也不对本类产生负面影响,就放置在本类中。

  • 谨慎使用serializable
    RMI中注意

最佳实践

迪米特法则的核心观念就是:类间解耦、弱耦合,只有弱耦合了,类的复用性才可以提高,其要求的结果就是产生了大量的中转或跳转类,导致系统的复杂性提高,同时也为维护带来了难度。我们在使用时要反复权衡,既做到结构清晰,又做到高内聚低耦合。另外解耦是有限度的,需要适当的考虑这个原则。防止过犹不及。

例子

举一个例子:有一个集团公司,下属单位有分公司和直属部门,现在要求打印出所有下属单位的员工ID。先来看一下违反迪米特法则的设计。

//总公司员工 
    class Employee{  
        private String id;  
        public void setId(String id){  
            this.id = id;  
        }  
        public String getId(){  
            return id;  
        }  
    }  

    //分公司员工 
    class SubEmployee{  
        private String id;  
        public void setId(String id){  
            this.id = id;  
        }  
        public String getId(){  
            return id;  
        }  
    }  

    class SubCompanyManager{  
        public List<SubEmployee> getAllEmployee(){  
            List<SubEmployee> list = new ArrayList<SubEmployee>();  
            for(int i=0; i<100; i++){  
                SubEmployee emp = new SubEmployee();  
                //为分公司人员按顺序分配一个ID 
                emp.setId("分公司"+i);  
                list.add(emp);  
            }  
            return list;  
        }  
    }  

    class CompanyManager{  

        public List<Employee> getAllEmployee(){  
            List<Employee> list = new ArrayList<Employee>();  
            for(int i=0; i<30; i++){  
                Employee emp = new Employee();  
                //为总公司人员按顺序分配一个ID 
                emp.setId("总公司"+i);  
                list.add(emp);  
            }  
            return list;  
        }  

        public void printAllEmployee(SubCompanyManager sub){  
            List<SubEmployee> list1 = sub.getAllEmployee();  
            for(SubEmployee e:list1){  
                System.out.println(e.getId());  
            }  

            List<Employee> list2 = this.getAllEmployee();  
            for(Employee e:list2){  
                System.out.println(e.getId());  
        }  
        }  
    }  

    public class Client{  
        public static void main(String[] args){  
            CompanyManager e = new CompanyManager();  
            e.printAllEmployee(new SubCompanyManager());  
        }  
    }  

现在这个设计的主要问题出在CompanyManager中,根据迪米特法则,只与直接的朋友发生通信,而SubEmployee类并不是CompanyManager类的直接朋友(以局部变量出现的耦合不属于直接朋友),从逻辑上讲总公司只与他的分公司耦合就行了,与分公司的员工并没有任何联系,这样设计显然是增加了不必要的耦合。按照迪米特法则,应该避免类中出现这样非直接朋友关系的耦合。修改后的代码如下:

class SubCompanyManager{  
        public List<SubEmployee> getAllEmployee(){  
            List<SubEmployee> list = new ArrayList<SubEmployee>();  
            for(int i=0; i<100; i++){  
                SubEmployee emp = new SubEmployee();  
                //为分公司人员按顺序分配一个ID 
            emp.setId("分公司"+i);  
                list.add(emp);  
            }  
            return list;  
        }  
        public void printEmployee(){  
            List<SubEmployee> list = this.getAllEmployee();  
            for(SubEmployee e:list){  
                System.out.println(e.getId());  
            }  
        }  
    }  

    class CompanyManager{  
        public List<Employee> getAllEmployee(){  
            List<Employee> list = new ArrayList<Employee>();  
            for(int i=0; i<30; i++){  
                Employee emp = new Employee();  
                //为总公司人员按顺序分配一个ID 
                emp.setId("总公司"+i);  
                list.add(emp);  
            }  
            return list;  
        }  

        public void printAllEmployee(SubCompanyManager sub){  
            sub.printEmployee();  
            List<Employee> list2 = this.getAllEmployee();  
            for(Employee e:list2){  
                System.out.println(e.getId());  
            }  
        }  
    }  

修改后,为分公司增加了打印人员ID的方法,总公司直接调用来打印,从而避免了与分公司的员工发生耦合。
迪米特法则的初衷是降低类之间的耦合,由于每个类都减少了不必要的依赖,因此的确可以降低耦合关系。但是凡事都有度,虽然可以避免与非直接的类通信,但是要通信,必然会通过一个“中介”来发生联系,例如本例中,总公司就是通过分公司这个“中介”来与分公司的员工发生联系的。过分的使用迪米特原则,会产生大量这样的中介和传递类,导致系统复杂度变大。所以在采用迪米特法则时要反复权衡,既做到结构清晰,又要高内聚低耦合

你可能感兴趣的:(设计模式系列:迪米特法则)