我何时使用抽象类与接口相关的依赖注入?

lok*_*oki 9 .net c# oop design-patterns

我一直在阅读一些关于SOLID原则和依赖Inversion的文章.从我的角度来看,我必须使用一个界面来与任何一个班级交谈.我的课程是通过使用接口聊天.

第一个问题:

我正在使用抽象类,但对于我的代码的第二部分,我使用了一个接口.

Usage1


namespace DependencyInjection
{

    public interface IMessage
    {

    }
    public abstract class Message
    {
        public abstract void Get();
        public abstract void Send();
    }

    public class Sms : Message, IMessage
    {
        public override void Get()
        {
            Console.WriteLine("Message Get!");
        }
        public override void Send()
        {
            Console.WriteLine("Message Send!");
        }
    }

    public class MessageManager
    {
        private IMessage _message;

        public Sms Sms
        {
            get { return _message as Sms; }
            set { _message = value; }
        }

        public MessageManager(IMessage message)
        {
            _message = message;
        }

    }
}
Run Code Online (Sandbox Code Playgroud)

用法:



    class Program
    {
        static void Main(string[] args)
        {
            MessageManager msg = new MessageManager(new Sms());
            msg.Sms.Get();
            msg.Sms.Send();
            Console.Read();
        }
    }
Run Code Online (Sandbox Code Playgroud)

Usage2


namespace DependencyInjection
{

    public interface IMessage
    {
        public  void Get();
        public  void Send();
    }


    public class Sms :  IMessage
    {
        public  void IMessage.Get()
        {
            Console.WriteLine("Message Get!");
        }
        public  void IMessage.Send()
        {
            Console.WriteLine("Message Send!");
        }
    }

    public class MessageManager
    {
        private IMessage _message;

        public Sms Sms
        {
            get { return _message as Sms; }
            set { _message = value; }
        }

        public MessageManager(IMessage message)
        {
            _message = message;
        }

    }
}
Run Code Online (Sandbox Code Playgroud)

Usage1和usage2有什么区别?我什么时候选择usage1或Usage2?

Ser*_*kiy 22

这里的抽象类用重复的代码来对抗.接口 - 定义合同(API).

依赖于接口 - 它们只是描述依赖的契约(API),并且可以轻松地模拟它们.所以,从界面开始:

public interface IMessage
{
    void Get(); // modifiers like public are not allowed here
    void Send();
}
Run Code Online (Sandbox Code Playgroud)

这是你的依赖类,它应该只依赖于抽象(即接口):

public class MessageManager
{
    private IMessage _message;

    // depend only on abstraction 
    // no references to interface implementations should be here
    public IMessage Message
    {
        get { return _message; }
        set { _message = value; }
    }

    public MessageManager(IMessage message)
    {
        _message = message;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后创建类,它将实现您的接口:

public class Sms : IMessage
{
    // do not use explicit implementation 
    // unless you need to have methods with same signature
    // or you want to hide interface implementation
    public void Get()
    {
        Console.WriteLine("Message Get!");
    }

    public void Send()
    {
        Console.WriteLine("Message Send!");
    }
}
Run Code Online (Sandbox Code Playgroud)

现在你已经反转了依赖 - MessageManager并且Sms只依赖于IMessage.您可以将任何IMessage实现注入MessageManager(MessageManager现在适合OCP - 打开以进行扩展,但关闭以进行修改).

当您在多个IMessage实现者中有重复的代码时才创建基本抽象消息类.当您创建抽象类(位置,移动重复代码的位置)时,您不应更改接口,因为合同保持不变.只需从原始IMessage界面继承您的基类.

  • @NSjonas我通常从界面开始.实际上,当你进行外向开发时,在开始编写它们之前总是有更深层对象的界面.接口完全可以模拟,这也是有益的.当我有几个类,实现接口并且有重复的代码(数据或方法)时,我从这些类中提取基类,并使基类实现原始接口.如果基类没有单独使用,那么我将它作为抽象.所以,是的,抽象类通常应该实现接口,其他类继承它. (3认同)