设计模式很简单—迪米特法则

迪米特法则-类与类之间关系简单化

一、概念

一个类尽量不与其他类联系,只跟特定类联系,这个法则重要的是这些特定类,特定类即我们常说的“朋友”,我们通过这些“朋友”来实现类之间关系的简化。

迪米特法则(Law of Demeter)又叫作最少知识原则(The Least Knowledge
Principle),一个类对于其他类知道的越少越好,就是说一个对象应当对其他对象有尽可能少的了解,只和朋友通信,不和陌生人说话。英文简写为:
LOD。

二、狭义的迪米特法则

类A和类B之间要如果要遵循迪米特法则,那么类A调用类B的方法的时候,需要一个类C(朋友)帮忙做转发。

如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的相互作用。如果其中的一个类需要调用另一个类的某一个方法的话,可以通过第三者(朋友)转发这个调用。

三、哪些是“朋友”

1)类A本身
2)以参数形式传入类A方法的对象
3) 类A内部声明和创建的对象B
以上所说的这三类都可以做为转发器,帮助两个类之间做方法的转发

1)当前对象本身(this)
2)以参量形式传入到当前对象方法中的对象
3)当前对象的实例变量直接引用的对象
4)当前对象所创建的对象
任何一个对象,如果满足上面的条件之一,就是当前对象的“朋友”;否则就是“陌生人”。 假设当前为类A

四、实战演武

概述:有一个聊天室程序,有两个类User和ChatClient;
User类收发信息的方法,发信息调用的是ChatClient类的方法;
ChatClient类保存User类对象集合(登录聊天室的用户们),提供发信息的方法。

// 聊天客户端类
    public class ChatClient
    {
        private List<User> users;

        public ChatClient()
        {
            users = new List<User>();
        }

        public void AddUser(User user)
        {
            users.Add(user);
        }
        //发送信息的方法,User类调用这个方法发送信息
        public void SendMessage(string message, User sender)
        {
            foreach (User user in users)
            {
                if (user != sender)
                {
                    user.ReceiveMessage(message);
                }
            }
        }
    }
// 用户类
    public class User
    {
        private string name;
        public User(string name)
        {
            this.name = name;
        }
        //用户类调用ChatClient类的SendMessage方法进行发送信息
        public void SendMessage(string message, ChatClient chatClient)
        {
            chatClient.SendMessage(message, this);
        }

        public void ReceiveMessage(string message)
        {
            Console.WriteLine("Received message: " + message);
        }
    }
//客户端调用
static void Main(string[] args)
        {
            ChatClient chatClient = new ChatClient();

            User user1 = new User("myx");
            User user2 = new User("pxy");

            chatClient.AddUser(user1);
            chatClient.AddUser(user2);

            user1.SendMessage("Hello", chatClient);
            user2.SendMessage("Hi", chatClient);
            Console.ReadKey();
        }

在上面的设计中,User类和ChatClient类之间的SendMessage方法是不满足“迪米特法则”的,同时如果ChatClient类的SendMessage方法又有可能是变化的,两个类之间,如果其中一个类的方法有可能变化,而我们却不使用迪米特法则”,这样会有什么问题呢?
假如有一天,我们不再使用ChatClient类发送消息,改用消息中心(MessageCenter)来进行发送消息:

public class MessageCenter
    {
        private List<User> users= new List<User>();
        public void AddUser(User user)
        {
            users.Add(user);
        }
        public void SendMessage(string message, User sender)
        {
            //消息中心发送信息
        }
    }

这个时候,因为之前没有满足“迪米特法则”,那么我们修改的地方很多;
1.需要修改 User 类的 SendMessage 方法,修改为

public void SendMessage(string message, MessageCenter messageCenter)
{
    messageCenter.SendMessage(message, this);
}

2.需要修改 ChatClient 类的 SendMessage 方法,用不到了,可以注释或者删掉

public void SendMessage(string message, User sender)
{
    messageCenter.SendMessage(message, sender);
}

从这里看,不用“迪米特法则”也就只需要改两个地方(当然第二点可改可不改),
好像也没什么!
但是在具体项目中,
系统代码中所有出现ChatClient 类的我们都要改成MessageCenter类,
这样想一想是不是就感觉需要改很多东西了,
大项目中好几个程序员分管不同的模块,你可能还要通知他们,
你们代码中用到ChatClient 类的,现在都要改成MessageCenter类,
是不是感觉非常麻烦!
那么,假如我们User类和ChatClient 类一开始就遵循“迪米特法则”的话,
代码改写如下:

// 用户类
    public class User
    {
        private string name;
        
        public User(string name)
        {
            this.name = name;
        }
        //1.由Frend类负责与外界(ChatClient)交流
        public void SendMessage(string message,Frend frend)
        {
            frend.SendMessage(message,this);
        }

        public void ReceiveMessage(string message)
        {
            Console.WriteLine("Received message: " + message);
        }
    }
//1.创建一个User类的朋友类,用于帮助User类SendMessage方法的转发
    public class Frend
    {
        ChatClient chatClient;
        public Frend(ChatClient chatClient)
        {
            this.chatClient = chatClient;
        }
        public void SendMessage(string message,User user)
        {
            //1.Frend内部调用的是ChatClient类的SendMessage方法,即由Frend代替User与ChatClient类交流。
            chatClient.SendMessage(message, user);
        }
        public void AddUser(User user)
        {
            chatClient.AddUser(user);
        }
    }
// 聊天客户端类
    public class ChatClient
    {
        private List<User> users;

        public ChatClient()
        {
            users = new List<User>();
        }

        public void AddUser(User user)
        {
            users.Add(user);
        }
        //发送信息的方法,User类调用这个方法发送信息
        public void SendMessage(string message, User sender)
        {
            foreach (User user in users)
            {
                if (user != sender)
                {
                    user.ReceiveMessage(message);
                }
            }
        }
    }

客户端调用

static void Main(string[] args)
        {
            Frend frend = new Frend(new ChatClient());

            User user1 = new User("myx");
            User user2 = new User("pxy");

            frend.AddUser(user1);
            frend.AddUser(user2);

            user1.SendMessage("Hello", frend);
            user2.SendMessage("Hi", frend);
            Console.ReadKey();
        }

当代码改写完以后,这个时候假如遇到之前的情况,发送消息改用消息中心MessageCenter,此时我们只需要修改Frend类即可,改写如下:

public class Frend
    {
        MessageCenter messageCenter;
        //ChatClient chatClient;
        public Frend(MessageCenter messageCenter)
        //public Frend(ChatClient chatClient)
        {
            this.messageCenter = messageCenter;
        }
        public void SendMessage(string message, User user)
        {
            messageCenter.SendMessage(message, user);
        }
        public void AddUser(User user)
        {
            messageCenter.AddUser(user);
        }
    }

可能有人会说,用了这个法则也要改Frend类,改的还挺多(从这个例子看,改的很多)
而不用这个法则的话直接新建一个类MessageCenter ,然后User类调用ChatClient替换成MessageCenter ,看起来不用法则反而改的少还轻松?
实际上,User类作为给其他各个模块类调用的类,肯定是被成百上千个类引用的,
而User类SendMessage一改动(本例中形参改了),
则系统中调用SendMessage方法的成千上百的都要一起改,
而使用法则以后,你只需要改Frend类,
因为Frend类只是用来做类关系隔离的,所以只有User类会引用Frend类,
几乎没有其他类会调用Frend类,
你是希望修改一个几乎不被引用的类,还是希望修改被成千上百被引用的类?
当前使用这个法则,在编程中一般用接口或者抽象类实现,面向接口编程,
其实这个Frend类就相当于接口或者抽象类,我们用接口或者抽象类完成Frend的作用,
则代码再次改写如下:

//1.这个相当于之前的Frend
    public abstract class ChatBase
    {
        public List<User> users = new List<User>();
        public abstract void AddUser(User user);
        public abstract void SendMessage(string message, User sender);
    }
// 用户类
    public class User
    {
        private string name;
        
        public User(string name)
        {
            this.name = name;
        }
        //1.形参由Frend换成ChatBase抽象类
        public void SendMessage(string message, ChatBase frend)
        {
            frend.SendMessage(message,this);
        }

        public void ReceiveMessage(string message)
        {
            Console.WriteLine("Received message: " + message);
        }
    }

ChatClient类改写

// 聊天客户端类
    public class ChatClient: ChatBase
    {
        public override void AddUser(User user)
        {
            users.Add(user);
        }
        public override void SendMessage(string message, User sender)
        {
            foreach (User user in users)
            {
                if (user != sender)
                {
                    user.ReceiveMessage(message);
                }
            }
        }
    }

当我们要替换成消息中心的时候,只需要MessageCenter实现ChatBase抽象类即可,代码不需要做修改了。

public class MessageCenter: ChatBase
    {
        public override void AddUser(User user)
        {
            users.Add(user);
        }
        public override void SendMessage(string message, User sender)
        {
            foreach (User user in users)
            {
                if (user != sender)
                {
                    user.ReceiveMessage("消息中心:"+message);
                }
            }
        }
    }

以上就是迪米特法则,你看懂了吗?

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值