Questa domanda riguarda le migliori pratiche dell'interfaccia Segregation Principle. Uso esempi astratti di seguito, ma la domanda nasce dal codice reale che ho visto che esegue ciò che posso solo chiamare "Accoppiamento implementazione".
Se dovessi rompere la mia classe di MilkMan
nei suoi rispettivi ruoli, vedrei che MilkMan
ha tre ruoli. Lui interpreta il ruolo di Person
(cammina e mastica il bubblegum), interpreta il ruolo di Deliverer
(funzioni di inventario e contabilità?) E colpisce mia moglie quando non sono a casa.
Quindi quando scrivo / rivedo la mia logica di contabilità (o Unit Testing it), vedo che alcuni metodi nella mia classe contabile possono prendere un parametro MilkMan
. Quando vedo questo, ovviamente ho qualcosa di sbagliato. L'unica cosa di cui si preoccupa la mia classe contabile (o Test di unità di questo tipo) è il ruolo di Deliverer.
Quindi quello che faccio è identificare i ruoli del mio MilkMan
e segregare alcune interfacce:
-
IPerson
-
IDeliverer
-
ICovetThyW
Ora, quando la mia classe contabile (oi suoi Test unitari) devono prendere in giro o passare in giro la funzionalità di Deliverer, le relazioni sono chiare. Ora posso avere il mio MailMan
, UpsMan
e PoolBoy
parlare con la mia logica Contabilità passando chiaramente attorno a IDeliverer
.
Quindi ora Unit Testing è molto più facile da prendere in giro e le relazioni diventano più chiare nel mio codice. Tutto è fantastico.
Cosa succede se un metodo in un'implementazione sta effettuando chiamate a un altro metodo nella stessa classe, ma che fa parte di un'altra implementazione? Prendi in considerazione il seguente codice:
namespace HomeAccounting.KeepAnEyeOnPayments.Sample
{
class Program
{
static void Main(string[] args)
{
IDeliverer jose = new MilkMan();
double paymentAmount = 69;
if (jose.AcceptPaymentFromCustomer(paymentAmount))
{
System.Console.WriteLine("He got " + paymentAmount.ToString());
}
}
}
public class MilkMan : IPerson, IDeliverer, ICovetThyW
{
public bool CanWalk { get; set; }
public bool CanChewBubblegum { get; set; }
public bool AcceptPaymentFromCustomer(double amountDue)
{
if (GetPaymentFromCustomer(amountDue) == amountDue)
{
return true;
}
else
{
if (AlternativePaymentMethod(amountDue) == amountDue)
{
return true;
}
}
return false;
}
private double GetPaymentFromCustomer(double amountDue)
{
return 0; //Not willing and/or able to give money.
}
public double AlternativePaymentMethod(double amountDue)
{
return amountDue;
}
}
public interface IPerson
{
bool CanWalk { get; set; }
bool CanChewBubblegum { get; set; }
}
public interface IDeliverer
{
bool AcceptPaymentFromCustomer(double amountDue);
}
public interface ICovetThyW
{
double AlternativePaymentMethod(double amountDue);
}
}
La domanda è : qualcuno vede qualcosa di sbagliato in questo? Ma in tutta serietà: mi piacerebbe sapere se c'è un termine o una discussione o una best practice elencati là fuori che dice che non dovresti accoppiare le tue implementazioni insieme.
Voglio sottolineare che l'implementazione esplicita dell'interfaccia (che spingo per) non rende nemmeno questo possibile. Ma onestamente non l'ho mai visto in un ambiente professionale da 15 anni.