Se vuoi modellare le tue classi con il principio di responsabilità singola (SRP), devi decidere quali sono queste responsabilità.
Quando vuoi utilizzare la tua classe Employee
in un'applicazione di pagamento salariale, il metodo CalculatePayment
ha perfettamente ragione in quel luogo.
Il Employee
rappresenta un impiegato che di sicuro ha un nome. Anche la classe Employee
ha la responsabilità di eseguire (innescare) il calcolo del suo pagamento, ma non è necessario che sappia come. Di fatto, stai limitando il tuo design quando fornisci i dettagli di Employee
su come calcolare il pagamento.
Un approccio migliore potrebbe essere il seguente:
public class Employee
{
private readonly string name;
private readonly AbstractPayment payment;
public Employee(string name, AbstractPayment payment)
{
this.name = name;
this.payment = payment;
}
public decimal CalculatePayment()
{
return payment.Calculate();
}
}
Con questo approccio ogni Employee
può avere una strategia di pagamento molto speciale e puoi anche eliminare la brutta dichiarazione if
nel tuo metodo calculatePay
.
La base per AbstractPayment
potrebbe avere il seguente aspetto:
public abstract AbstractPayment
{
public abstract decimal Calculate();
}
Ora devi implementare una classe per qualsiasi pagamento concreto, ad esempio:
public class FixedPayment : AbstractPayment
{
private readonly decimal salary;
public FixedPayment(decimal salary)
{
this.salary = salary;
}
public override decimal Calculate()
{
return salary;
}
}
public class TopPerformerPayment : AbstractPayment
{
private readonly AbstractPayment basePayment;
private readonly decimal bonus;
public TopPerformerPayment(AbstractPayment basePayment, decimal bonus)
{
this.basePayment= basePayment;
this.bonus = bonus;
}
public override decimal Calculate()
{
return basePayment.Calculate() + bonus;
}
}
Crea un'istanza del tuo Employees
qualcosa come:
var fixedPayment = new FixedPayment(100m);
var regularEmployee = new Employee("Peter", fixedPayment);
var topEmployee = new Employee("Paul", new TopPerformerPayment(fixedPayment, 500m));
Per il volontario hai due possibilità di implementazione, a seconda del comportamento aggrappato di CalculatePayment
per un volontario è un errore o meno.
public class VolunteerPaymentIsError : AbstractPayment
{
public override decimal Calculate()
{
throw new PaymentNotAllowedException("A volunteer can not be paid!");
}
}
public class VolunteerPaymentIsZero : AbstractPayment
{
public override decimal Calculate()
{
return 0m;
}
}
Ora puoi istanziare i tuoi volontari in questo modo:
var forbiddenPaymentVolunteer = new Employee("Bob", new VolunteerPaymentIsError());
var zerroSalaryVolunteer = new Employee("Bill", new VolunteerPaymentIsZero());