È possibile iniettare un metodo con una chiamata di oggetto?

4

Considera questo esempio di iniezione di metodi:

static void SaveEmail(Func<string> getEmailFunction)
{
    dbcontext.SaveEmail(getEmailFunction());
}

static string GetEmail()
{
    var frmUser = GetUserForm();
    return frmUser.GetEmail();
}

static void ExampleCall()
{
    SaveEmail(GetEmail);
}

In questo esempio, stiamo iniettando un Func<string> che restituisce l'indirizzo email dell'utente. Il metodo SetEmail usa questo come una sorta di callback per ottenere l'indirizzo dell'utente. In questo modo evitiamo che SetEmail abbia una dipendenza hardcoded sul codice che ottiene il modulo dell'utente e legge i suoi campi.

È possibile farlo senza la versione statica di GetEmail ? Qualcosa del genere:

static void SaveEmail(Func<string> getEmailFunction)
{
    dbcontext.SaveEmail(getEmailFunction());
}

static void ExampleCall()
{
    var frmUser = GetUserForm();
    SaveEmail(frmUser.GetEmail);
}
    
posta John Wu 14.03.2017 - 22:27
fonte

1 risposta

1

Certo che puoi ma perché? Questa non è una questione del perché lo farebbe mai, ma perché dovresti farlo invece di iniettare una classe che lo fa per te tramite l'interfaccia (dipendenza da iniezione). Puoi passare la classe alla chiamata al metodo o al costruttore della classe che ha l'e-mail di salvataggio (i contenitori IOC preferiscono il secondo approccio).

Quindi nel caso che hai presentato, questo funzionerà bene se hai metodi brevi che non impazziscono, perché allora potresti anche fare una lezione per te. In realtà, non c'è niente contro l'avere una classe che ha solo 10 righe di codice. Le nuove idee di architettura "microservice" potrebbero avere 10 linee di codice in un intero programma.

Quindi potresti avere un'interfaccia simile a questa:

public interface IEmailProvider
{
    string GetEmail();
}

E una classe che fa qualcosa di semplice:

public class SettingsEmailProvider : IEmailProvider
{
    public string GetEmail()
    {
        // sudo code read from app settings
        return ReadFromSettingFile("useremail");
    }
}

Ma ora devi testare quel codice così da creare una classe moq

public class MoqEmailProvider : IEmailProvider
{
    public string GetEmail()
    {
        return "[email protected]";
    }
}

Ok finora avremmo potuto farlo in una chiamata di funzione passata, ma diciamo che hai bisogno di qualcosa di più complicato in futuro, come andare su un'API di Facebook per ottenere l'indirizzo email dell'utente da lì.

public class FacebookEmailProvider : IEmailProvider
{
    private readonly IFacebookCredentialProvider _creds;
    private readonly IFacebookClient _client;
    private readonly IEmailProvider _fallbackProvider;

    public ILog Logger {get;set;}

    public FacebookEmailProvider(IFacebookCredentialProvider creds, IFacebookClient client, IEmailProvider fallbackProvider)
    {
        _creds = creds;
        _client = client;
        _fallbackProvider = fallbackProvider;
    }

    public string GetEmail()
    {
        //Try to get the email from facebook
        string email;
        try
        {
            var credentials = _creds.GetCredentials();
            var task = Task.Factory.StartNew(()=>{
                _client.Login(credentials);
                email = _client.GetUserEmail();
                }
            task.wait();
            if(string.IsNullOrEmpty(email))
                email = _fallbackProvider.GetEmail();
        }
        catch(Exception ex)
        {
            Logger.Error(ex);
            email = _fallbackProvider.GetEmail();
        }
    }
}

Quindi ora siamo un po 'più complicati, e quindi la tua classe chiamante dovrà fornire tutto questo se fosse solo un Func. Invece sarebbe più facile da gestire se fosse nella sua stessa classe (la mia opinione).

Questo naturalmente crea più classi da mantenere ma cerca di renderlo più testabile.

In breve, sì, ma puoi scegliere un approccio che corrisponda alla tua situazione e alle esigenze del tuo progetto. Mi piace l'approccio basato sull'input della dipendenza perché è testabile, leggibile e facile da espandere in qualcosa di più complesso senza modificare il codice principale, ma sempre con quello che ti senti a tuo agio e quello che è il migliore per il tuo progetto / team.

    
risposta data 19.06.2017 - 16:03
fonte

Leggi altre domande sui tag