views:

57

answers:

5

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

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

+2  A: 

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

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
LBushkin
+7  A: 

Basically, get rid of the static methods. Create an interface (IMessageClient) and then two implementations (TextClient and MailClient) with instance methods implementing the interface. Then you can inject the appropriate IMessageClient into the rest of the application with ease.

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
Jon Skeet
Since I don't own the object where I want to do the instantiation, I had hoped I wouldn't have to go this way. But I guess I have to find some workaround to use this pattern instead.
Tomas Lycken
A: 

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.

Dave
This will not work unless the `Send()` method is made non-static.
Daniel Brückner
+1  A: 

Don't make the methods static, create an interface with the Send()method and implement this interface on TextClient and MailClient. 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.

Daniel Brückner
+3  A: 

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.

Tesserex