If you want to modell your classes by the single responsibility principle (SRP), you have to make up your mind what these responsibilities are.
When you want to use your Employee
class in a salary payment application, then the CalculatePayment
method is perfectly right in that place.
The Employee
represents an employed person which for sure has a name. Also the Employee
class has the responsibility to do (trigger) the calculation of its payment, but it's not neccesary that it knows how. In fact you are limiting your design when you give Employee
details on how to calculate the payment.
A better approach could be the following:
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();
}
}
With this approach every Employee
can have a very special payment strategy and you also get rid of the ugly if
statement in your calculatePay
method.
The base for AbstractPayment
could look as following:
public abstract AbstractPayment
{
public abstract decimal Calculate();
}
Now you have to implement a class for any concrete payment, eg.:
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;
}
}
Instantiate your Employees
something like:
var fixedPayment = new FixedPayment(100m);
var regularEmployee = new Employee("Peter", fixedPayment);
var topEmployee = new Employee("Paul", new TopPerformerPayment(fixedPayment, 500m));
For the volunteer you have two posibilities of implementation, depending on the aggreed behaviour of CalculatePayment
for an volunteer is an error or not.
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;
}
}
Now you can instantiate your volunteers like so:
var forbiddenPaymentVolunteer = new Employee("Bob", new VolunteerPaymentIsError());
var zerroSalaryVolunteer = new Employee("Bill", new VolunteerPaymentIsZero());