设计模式---迪米特法则

11 阅读5分钟

迪米特法则

迪米特法则,又叫最少知识原则,即一个对象应当对其它对象有尽可能少的了解。其它表述为:

 只与你的直接朋友通信;
 不和陌生人说话;
 每一个软件单位对其它的单位都只有最少知识,而且局限于那些与本单位密切相关的软件单位。
 

迪米特法则首先强调的是前提是在类的结构设计上,每个类都应当尽量降低成员的访问权限,即一个类包装好自己的private状态,不要让其它类知道的字段或行为就不要公开。需要公开的字段通常用属性来体现。

面向对象的设计原则和面向对象的三大特性本就不是矛盾的。迪米特法则强调了面向对象的根本思想---类之间的松耦合。

在程序设计中,类之间的耦合越弱,越利用复用,一个处在弱耦合的类被修改,不会对有关系的类造成影响,即信息的隐藏促进了软件代码的复用。

简单的说,迪米特法则是指一个软件实体应当尽可能少地与其它实体发生相互作用,这样,当一个模块修改时,就会尽量少地影响其它模块,扩展相对容易,这是对实体之间通信的限制,它要求限制软件实体之间通信的宽度和深度。

在迪米特法则中,对于一个对象,其朋友包括以下几类:

  • 当前对象本身;
  • 以参数形式传入到当前对象方法中的对象;
  • 当前对象的成员对象;
  • 如果当前对象的成员对象是一个集合,那么集合中的元素也是朋友;
  • 当前对象所创建的对象

任何一个对象,如果满足上述条件,就是当前对象的"朋友",否则就是陌生人。

狭义的迪米特法则:

可以降低类之间的耦合,但是会在系统中增加大量的小方法,并散落在系统的各个角落,它可以使一个系统的局部设计简化,因为每一个局部都不会和远距离的对象有直接的关联,但是也会造成系统不同模块间的通信效率降低,使得系统的不同模块间不容易协调,在狭义的迪米特法则中,如果两个类之间不必互相通信,那么这两个类就不应当发生直接的相互作用,如果其中的一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用。

广义的迪米特法则

指对象间的信息流量、流向以及信息的影响控制,主要是对信息的隐藏。信息的隐藏可以使各个子系统之间脱耦,从而允许他们被独立的开发、优化、使用和修改,同时可以促进软件的复用,由于每一个模块都不依赖于其它模块而存在,因此每一个模块都可以独立地在其它的地方使用。一个系统的规模越大,信息的隐藏就越重要,而信息隐藏的重要性也就越明显。

迪米特法则分析

迪米特法则主要用途是控制信息的过载;

在类的划分上,应当尽量创建松耦合的类,类之间的耦合度越低,就越有利于复用,一个处在松耦合中的类一旦被修改,不会对关联的类造成太大波及;

在类的结构设计上,每一个类都应当尽量降低其成员变量和成员函数的访问权限;

在类的设计上,只要有可能,一个类型应当被设计成不变类;

在对其它类的引用上,一个对象对其它对象的引用应当降到最低。

namespace 迪米特法则
{
    public class Employee
    {
        private string id;

        public void SetID(string id)
        { 
            this.id = id;
        }

        public string GetID() { return id; }
    }

    public class SubEmployee
    {
        private string id;
        public void SetID(string id) {  this.id = id; }

        public string GetID() { return id; }

    }

    public class SubCompanyManager
    {
        public List<SubEmployee> GetAllEmployee()
        {
            List<SubEmployee> list = new List<SubEmployee>();

            for (int i = 0; i < 100; i++)
            { 
                SubEmployee emp = new SubEmployee();

                //为分公司雇员分配ID
                emp.SetID("分公司" + i);
                list.Add(emp);
            }
            return list;
        }       
    }

    public class CompanyManager
    {
        public List<Employee> GetAllEmployee()
        {
            List<Employee> employees = new List<Employee>();
            for (int i = 0; i < 100; i++)
            { 
                Employee employee = new Employee();
                //分配总公司雇员id
                employee.SetID("总公司" + i);
                employees.Add(employee);
            }
            return employees;
        }

        //想要打印雇员的id,如果把打印方法都放在总公司管理类中,违反了迪米特法则
        //子公司员工的打印id放在总公司管理类中,增加了不必要的耦合,应该防止在分公司员工类中。
        public void PrintAllEmployeeID(SubCompanyManager sub)
        { 
            List<SubEmployee> subs= sub.GetAllEmployee();
            foreach (SubEmployee emp in subs) 
            {
                Console.WriteLine(emp.GetID());
            }

            List<Employee> employees=this.GetAllEmployee();
            foreach (Employee emp in employees)
            {
                Console.WriteLine(emp.GetID());
            }
        }
    }

    internal class Program
    {
        static void Main(string[] args)
        {
            CompanyManager companyManager = new CompanyManager();
            companyManager.PrintAllEmployeeID(new SubCompanyManager());
            Console.Read();
        }
    }
}

修改之后的迪米特法则案例


 public class SubCompanyManager
    {
        public List<SubEmployee> GetAllEmployee()
        {
            List<SubEmployee> list = new List<SubEmployee>();

            for (int i = 0; i < 100; i++)
            { 
                SubEmployee emp = new SubEmployee();

                //为分公司雇员分配ID
                emp.SetID("分公司" + i);
                list.Add(emp);
            }
            return list;
        }

        public void PrintSubEmployeeID()
        {
            List<SubEmployee> subs = GetAllEmployee();
            foreach (SubEmployee emp in subs)
            {
                Console.WriteLine(emp.GetID());
            }
        }
    }

    public class CompanyManager
    {
        public List<Employee> GetAllEmployee()
        {
            List<Employee> employees = new List<Employee>();
            for (int i = 0; i < 100; i++)
            { 
                Employee employee = new Employee();
                //分配总公司雇员id
                employee.SetID("总公司" + i);
                employees.Add(employee);
            }
            return employees;
        }

        //想要打印雇员的id,如果把打印方法都放在总公司管理类中,违反了迪米特法则
        //子公司员工的打印id放在总公司管理类中,增加了不必要的耦合。
        public void PrintAllEmployeeID(SubCompanyManager sub)
        { 
            //直接调用打印,避免与总公司发生耦合
            sub.PrintSubEmployeeID();
            List<Employee> employees=GetAllEmployee();
            foreach (Employee emp in employees)
            {
                Console.WriteLine(emp.GetID());
            }
        }
    }