简体   繁体   中英

Dependency Injection with Constructor

I have two Classes. Student and SMSSenderMessage. where Student depend to SMSSenderMessage and SMSSenderMessage is depended to Student. Student and SMSSenderMessage has just one Constructor. I want to use constructor injection for this two classes.

    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
        }
    }

now. how create instance from Student and SMSSenderMessage?

You have a cyclic dependency, so these classes cannot be created.

Let's say you have class A , which depends on class B , which depends on class A . Then, to create class A , you should firstly create class B , but to create class B , you should firstly create class A . So it's just impossible to create neither class A neither class B .

When you have a cyclic dependency, it means that you have some problem with your design. You should try to redesign your solution.

In your case, a design mistake is pretty obvious. You have a SMSSender which gets IPerson as a dependency to use a person's name when sending a message. But try to imagine how you could use this SMS sender in the future. If you need to send information with the order number, what would you do? Pass Order as a parameter into the SMSSender constructor? You shouldn't do that.

The answer is that your SMSSender shouldn't know anything about the kind of information it's sending. It should just get a message text and send it. So, you should remove IPerson dependency from SMSSender .

As a couple of other people have suggested in comments, you have created a cyclic dependency - where each class has dependency on the other. Think of it like the infamous "Chicken and Egg" scenario. You can't instantiate one object without first instantiating the other.

However, the simplest way to separate concerns is to pass the IPerson object to the Send method.

You do not really want to pass the person into the SMSMessageConstructor because then you would have to create a new instance of SMSMessageConstructor for every different target person, Instead. pass IPerson within the Send method.

Also, I have suggested changing the GetName() method to a property, and advise that you pass the Name and PhoneNumber parameters into the Person constructor with readonly getter methods for immutability.

See updated code below:

    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.
        }
    }

Finally, a slight alternative using the newer Expression-bodied member + Tuple syntax for methods/constructors, see below (it's slightly shorter and rather neat:):

    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.
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM