简体   繁体   English

静态物料的松散耦合

[英]Loose coupling of static stuff

I have a class, ClassA that uses a client I wrote to send text messages, TextClient , to send some text messages via a call to the static method 我有一个类, ClassA使用我编写的客户端发送短信, TextClient ,通过调用静态方法发送一些短信

TextClient.Send(string text, string destination)
// where destination is a phone number

However, I also have a mail client class, MailClient , which sends emails with the same signature: 但是,我还有一个邮件客户端类MailClient ,它发送具有相同签名的电子邮件:

MailClient.Send(string text, string destination)
// where destination is an email address

I would like to "inject" which of these clients should be used - is this possible? 我想“注入”应该使用哪些客户端 - 这可能吗?

(Note: I'm aware of problems that might arise when there are entirely different rules for what values destination can hold and be considered valid, but the values are fetched from someplace else, so this class doesn't need to bother. That's why I want to abstract this away in the first place.) (注意:我知道当destination可以容纳哪些值并且被认为有效时,可能会出现问题,但是这些值是从其他地方获取的,所以这个类不需要打扰。这就是为什么我想首先把它抽象出来。)

Basically, get rid of the static methods. 基本上,摆脱静态方法。 Create an interface ( IMessageClient ) and then two implementations ( TextClient and MailClient ) with instance methods implementing the interface. 创建一个接口( IMessageClient ),然后使用实现该接口的实例方法实现两个实现( TextClientMailClient )。 Then you can inject the appropriate IMessageClient into the rest of the application with ease. 然后,您可以轻松地将适当的IMessageClient注入到应用程序的其余部分。

You certainly can use delegates to avoid creating the interface here - but I would definitely change to using interfaces instead: 您当然可以使用委托来避免在这里创建接口 - 但我肯定会改为使用接口:

  • The names involved (the interface name, method name and parameter names) convey information when you're using them 涉及的名称(接口名称,方法名称和参数名称)在您使用它们时传达信息
  • It allows for multiple methods in the same interface 它允许在同一界面中使用多种方法
  • It prevents methods which happen to have the same argument types but a completely unrelated meaning from being used accidentally 它可以防止偶然使用具有相同参数类型但完全不相关的意义的方法

Sure, make your clients implement some sending interface. 当然,让您的客户端实现一些发送接口。

public interface IMessageClient
{
    public void Send(string text, string destination);
}

public class TextClient : IMessageClient
{
    public void Send(string text, string destination)
    {
        // send text message
    }
}

public class MailClient : IMessageClient
{
    public void Send(string text, string destination)
    {
        // send email
    }
}

public class ClassA
{
    private IMessageClient client;

    public ClassA(IMessageClient client)
    {
        this.client = client;
    }
}

Same as Jon Skeet's answer, but he beat me by not typing out the code. 和Jon Skeet的答案一样,但他没有输入代码就打败了我。

It's generally better to avoid static classes when designing such services because it makes it more challenging to decouple from other code. 在设计此类服务时,通常最好避免使用静态类,因为它使得与其他代码分离更具挑战性。

If you have control over the design and implementation of the TextClient and MailClient classes, I would suggest considering making them singleton instance classes rather than static classes. 如果您可以控制TextClientMailClient类的设计和实现,我建议考虑将它们作为单例实例类而不是静态类。 You could then implement a common interface IMessageSender (see below) in both and pass that as an instance to the object that needs to make the call. 然后,您可以在两者中实现公共接口IMessageSender (请参见下文),并将其作为实例传递给需要进行调用的对象。

public interface IMessageSender
{
    void Send( string message, string destination );
}

public class TextClient : IMessageSender { ... }
public class MailClient : IMessageSender { ... }

If you don't have control over the implementation of those classes (or can't change them at this point), you could pass a delegate into the object that needs to make the call: 如果您无法控制这些类的实现(或者此时无法更改它们),则可以将委托传递给需要进行调用的对象:

class SomeConsumer
{
    private Action<string,string> m_SendDelegate;
    public SomeConsumer( Action<string,string> sendDelegate ) 
    { 
        m_SendDelegate = sendDelegate;
    } 

    public DoSomething()
    {
        // uses the supplied delegate to send the message
        m_SendDelegate( "Text to be sent", "destination" ); 
    }
}

var consumerA = new SomeConsumer( TextClient.Send ); // sends text messages
var consumerB = new SomeConsumer( MailClient.Send ); // will send emails

Don't make the methods static, create an interface with the Send() method and implement this interface on TextClient and MailClient . 不要使方法静态,使用Send()方法创建接口并在TextClientMailClient上实现此接口。 Now you can just inject an instance using the interface. 现在您可以使用该接口注入实例。

If it is not possible to make the methods non-static you can just write to thin wrappers around the static method call both implementing the said interface. 如果无法使方法成为非静态方法,则可以在静态方法调用周围写入瘦包装器,同时实现所述接口。

I would have thought so. 我会这么想的。 If you have an interface as such 如果您有这样的界面

public interface ISender
{
void Send(string text, string destination);
}

Then just use dependency injection to pick up which sender to use. 然后使用依赖注入来获取要使用的发件人。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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