构造函数的依赖注入

时间:2020-09-21 07:31:04

标签: c# dependency-injection constructor inversion-of-control constructor-injection

我有两个班级。学生和SMSSenderMessage。学生依赖于SMSSenderMessage,而SMSSenderMessage依赖于学生。学生和SMSSenderMessage只有一个构造函数。 我想对这两个类使用构造函数注入。

    public interface IPerson
    {
        string GetName();
    }

    public interface ISenderMessage
    {
        void Send(string text);
    }

    public class Student : IPerson
    {
        private ISenderMessage S;
        public string Name { get; set; }
        public Student(ISenderMessage s)
        {
            this.S = s;
        }
        public void DoSomeThing()
        {
            //...Some Thing
            S.Send("Hello");
        }

        public string GetName()
        {
            return this.Name;
        }
    }
    public class SMSSenderMessage : ISenderMessage
    {
        IPerson P;
        public SMSSenderMessage(IPerson p)
        {
            P = p;
        }
        public void Send(string text)
        {
            string message = text + " " + P.GetName();

            //Send SMS: message
        }
    }

现在。如何从Student和SMSSenderMessage创建实例?

2 个答案:

答案 0 :(得分:6)

您具有循环依赖性,因此无法创建这些类。

比方说,您有 A类,它取决于 B类,而这又取决于 A类。然后,要创建 A类,首先应创建 B类,但要创建 B类,首先应创建 A类 em>。因此,根本无法创建 A类 B类

当您具有循环依赖性时,这意味着您的设计存在一些问题。您应该尝试重新设计解决方案。

在您的情况下,设计错误非常明显。您有一个SMSSender,该IPerson作为发送消息时使用人名的依赖项。但是,请尝试想象一下您将来如何使用此SMS发件人。如果您需要发送带有订单号的信息,该怎么办?将Order作为参数传递给SMSSender构造函数?你不应该那样做。

答案是您的SMSSender对发送的信息一无所知。它应该只是获取一条消息文本并将其发送。因此,您应该从IPerson中删除SMSSender的依存关系。

答案 1 :(得分:1)

正如其他几个人在注释中所建议的那样,您创建了一个循环依赖关系-每个类都相互依赖。可以将其视为臭名昭著的“鸡和蛋”场景!您必须先实例化另一个对象才能实例化一个对象。

但是,分离关注点的最简单方法是将IPerson对象传递给Send方法。

您确实 不是 真的要将该人传递到SMSMessageConstructor中,因为那样的话,您就必须为每个创建一个新的SMSMessageConstructor实例。不同的目标人!而是在Send方法中传递IPerson。

此外,我建议将GetName()方法更改为属性,并建议您将Name和PhoneNumber参数通过 readonly 传递给Person 构造函数不可变性的吸气剂方法。

请参见下面的更新代码:

    public interface IPerson
    {
        string Name { get; }
        string PhoneNumber { get; }
    }

    public interface ISenderMessage
    {
        void Send(IPerson person, string message);
    }

    public class Student : IPerson
    {
        public string Name { get; }
        public string PhoneNumber { get; }

        public Student(string name, string phoneNumber)
        {
            Name = name;
            PhoneNumber = phoneNumber;
        }
    }

    public class SMSSenderMessage : ISenderMessage
    {
        public SMSSenderMessage() { }

        public void Send(IPerson person, string message)
        {
            // Access property of person, person.PhoneNumber
            // Optionally access name property of person, person.Name   
            // Send SMS: implement message logic.
        }
    }

最后,对方法/构造函数使用较新的Expression-bodied成员+ Tuple语法的稍作选择,请参见下文(它稍短且整洁!):

    public interface IPerson
    {
        string Name { get; }
        string PhoneNumber { get; }
    }

    public interface ISenderMessage
    {
        void Send(IPerson person, string message);
    }

    public class Student : IPerson
    {
        public string Name { get; }
        public string PhoneNumber { get; }

        public Student(string name, string phoneNumber) => (Name, PhoneNumber) = (name, phoneNumber);

    public class SMSSenderMessage : ISenderMessage
    {
        public SMSSenderMessage() { }

        public void Send(IPerson person, string message) => // Implement send message logic.
    }