One of the primary principles in object-oriented programming is encapsulation. By creating a well-defined interface for our classes, we can ensure data integrity and enhance reusability. Let's take a closer look at how this is achieved in the Employee class.
When dealing with class components in certain frameworks, it's essential to update the state based on the previous state correctly. This avoids issues due to asynchronous nature of some state setting functions.
/// <summary>
/// Initializes a new instance of the Employee class.
/// </summary>
/// <param name="id">The unique identifier of the Employee. This parameter is required.</param>
/// <param name="firstName">The first name of the employee. This parameter is required.</param>
/// <param name="lastName">The last name of the employee. This parameter is required.</param>
/// <param name="department">The department name of the employee. This parameter is required.</param>
/// <param name="jobTitle">The job title of the employee. This parameter is required.</param>
/// <param name="salary">The employee's salary. This parameter is required.</param>
public class Employee
{
public int Id { get; private set; }
public string FirstName { get; private set; }
public string LastName { get; private set; }
public string Department { get; private set; }
public string JobTitle { get; private set; }
public decimal Salary { get; private set; }
public Employee(int id, string firstName, string lastName, string department, string jobTitle, decimal salary)
{
if (string.IsNullOrWhiteSpace(firstName))
throw new ArgumentNullException(nameof(firstName));
if (string.IsNullOrWhiteSpace(lastName))
throw new ArgumentNullException(nameof(lastName));
if (string.IsNullOrWhiteSpace(department))
throw new ArgumentNullException(nameof(department));
if (string.IsNullOrWhiteSpace(jobTitle))
throw new ArgumentNullException(nameof(jobTitle));
if (salary <= 0)
throw new ArgumentOutOfRangeException(nameof(salary), "Salary must be a positive value.");
Id = id;
FirstName = firstName;
LastName = lastName;
Department = department;
JobTitle = jobTitle;
Salary = salary;
}
/// <summary>
/// Updates the department of the employee.
/// </summary>
/// <param name="newDepartment">The new department name.</param>
internal void ChangeDepartment(string newDepartment)
{
if (string.IsNullOrWhiteSpace(newDepartment))
throw new ArgumentNullException(nameof(newDepartment));
Department = newDepartment;
}
}
Beyond just encapsulating data in classes, we often require functions to manipulate, retrieve, and manage that data. In the context of our Employee class, consider the EmployeeFunctions class: a simulation of how we might handle and interact with a collection of employees in a database.
/// <summary>
/// Contains functions to retrieve and modify a collection of employee data.
/// These functions are meant to simulate database operations without actual external dependencies.
/// </summary>
public class EmployeeFunctions {
private readonly List _employees;
/// <summary>
/// Initializes a new instance of the <see cref="EmployeeFunctions"/> class with a predefined set of <see cref="Employee"/> objects.
/// This is a simulation and the collection is meant to replicate data typically queried from a database or other data source.
/// </summary>
public EmployeeFunctions()
{
_employees = new List()
{
new Employee(1, "John", "Doe", "IT", "Programmer", 95000),
new Employee(2, "Jane", "Doe", "IT", "System Analyst", 120000),
new Employee(3, "Robert", "Smith", "IT", "Database Admin", 125000),
new Employee(4, "Emily", "Johnson", "IT", "Network Engineer", 102000),
new Employee(5, "Michael", "Jones", "Finance", "CFO", 175000),
new Employee(6, "Sarah", "Williams", "Finance", "Accountant", 65000),
new Employee(7, "David", "Brown", "Finance", "Treasurer", 90000),
new Employee(8, "Jessica", "Taylor", "Finance", "Audit Manager", 95000),
new Employee(9, "James", "Davis", "Sales", "Domestic Sales Rep", 60000),
new Employee(10, "Linda", "Miller", "Sales", "International Sales Rep", 65000),
new Employee(11, "Paul", "Wilson", "Sales", "Sales Manager", 90000),
new Employee(12, "Angela", "Moore", "Maintenance", "Custodian", 35000),
new Employee(13, "William", "Jackson", "Maintenance", "Technician", 45000),
};
}
/// <summary>
/// Return a collection of <see cref="Employee"/> within an inclusive salary range.
/// </summary>
/// <param name="minSalary">The minimum salary allowed to be returned. This field is required.</param>.
/// <param name="maxSalary">The maximum salary allowed to be returned. This field is required.</param>
/// <returns>A <see cref="List{Employee}"/> of employees that are within the inclusive range of <paramref name="minSalary"/> and <paramref name="maxSalary"/></returns>
public List GetEmployeesBySalaryRange(decimal minSalary, decimal maxSalary)
{
if (minSalary > maxSalary)
{
throw new ArgumentException("Minimum salary should not be greater than maximum salary.");
}
return _employees.Where(e => e.Salary >= minSalary && e.Salary <= maxSalary).ToList();
}
/// <summary>
/// Fetches an employee based on their ID. Throws an exception if the ID is not valid or if no employee is found.
/// </summary>
/// <param name="id">A <see cref="int"/> representing the ID of the employee to get. This field is required.</param>
/// <returns>A <see cref="Employee"/> matching the <paramref name="id"/>. A null result indicates the ID was not found.</returns>
public Employee GetById(int id)
{
if (id <= 0)
throw new ArgumentException($"Id must be a positive number {id}.");
var employee = _employees.FirstOrDefault(e => e.Id == id);
if (employee == null)
throw new ArgumentException($"No employee found with ID {id}.");
return employee;
}
/// <summary>
/// Retrieves a list of unique employee departments from the collection of <see cref="Employee"/> objects.
/// </summary>
/// <returns>A list of type <see cref="List{T}" /> with <c>T</c> as <see cref="string"/> of unique department names.</returns>
public List GetEmployeeDepartments()
{
return _employees.Select(e => e.Department).Distinct().ToList();
}
/// <summary>
/// Creates an employee and adds it to the collection, then returns the assigned ID for the new employee.
/// </summary>
/// <param name="firstName">The first name of the new employee. This field is required.</param>
/// <param name="lastName">The last name of the new employee. This field is required.</param>
/// <param name="department">The name of the department of the new employee. This field is required.</param>
/// <param name="jobTitle">The job title of the new employee. This field is required.</param>
/// <param name="salary">The salary of the new employee. This field is required.</param>
/// <returns>The ID assigned to the newly created employee.</returns>
public int CreateEmployee(string firstName, string lastName, string department, string jobTitle, decimal salary)
{
if (string.IsNullOrWhiteSpace(firstName))
throw new ArgumentException("First name is required.");
if (string.IsNullOrWhiteSpace(lastName))
throw new ArgumentException("Last name is required.");
if (string.IsNullOrWhiteSpace(department))
throw new ArgumentException("Department is required.");
if (string.IsNullOrWhiteSpace(jobTitle))
throw new ArgumentException("Job title is required.");
if (salary <= 0)
throw new ArgumentException("Valid salary is required.");
var employee = new Employee(_employees.Max(e => e.Id) + 1, firstName, lastName, department, jobTitle, salary);
_employees.Add(employee);
return employee.Id;
}
/// <summary>
/// Update the <see cref="Employee.Department"/> property.
/// </summary>
/// <param name="id">The Id of the <see cref="Employee"/> to update. This field is required.</param>
/// <param name="department">The name of the department to update the <see cref="Employee.Department"/> with.</param>
public void UpdateEmployeeDepartment(int id, string department)
{
var employee = _employees.FirstOrDefault(e => e.Id == id);
if (employee == null)
throw new ArgumentException($"No employee found with ID {id}.");
if (string.IsNullOrWhiteSpace(department))
throw new ArgumentException("Department cannot be empty.");
employee.ChangeDepartment(department);
}
/// <summary>
/// Remove an <see cref="Employee"/> based on their id
/// </summary>
/// <param name="id">The id of the <see cref="Employee"/> to remove. This field is required.</param>
public void Delete(int id)
{
var employee = _employees.FirstOrDefault(e => e.Id == id);
if (employee == null)
throw new ArgumentException($"No employee found with ID {id}.");
_employees.Remove(employee);
}
}
As you traverse through the classes, you'll notice checks and validations ensuring data integrity. This level of detail, combined with encapsulation, promotes maintainability and robustness.