❗️ Задача:
1. Создайте класс компании Company, содержащей сотрудников и реализующей методы:
- найм одного сотрудника — hire(Employee employee),
- найм списка сотрудников – hireAll(Collection<Employee> employes),
- увольнение сотрудника – fire(Employee employee),
- получение значения дохода компании – getIncome().
Каждый метод НЕ должен иметь модификатор static, это позволит каждому объекту класса Company иметь свой набора сотрудников, свой расчет дохода, увольнение и найм. Аргументы и возвращаемое значение методов выберите на основании логики работы вашего приложения.
2. Создайте два метода, возвращающие список указанной длины (count). Они должны содержать сотрудников, отсортированных по убыванию и возрастанию заработной платы:
- List<Employee> getTopSalaryStaff(int count),
- List<Employee> getLowestSalaryStaff(int count).
3. Создайте классы сотрудников с информацией о зарплатах и условиями начисления зарплаты:
- Manager — зарплата складывается из фиксированной части и бонуса в виде 5% от заработанных для компании денег. Количество заработанных денег для компании генерируйте случайным образом от 115 000 до 140 000 рублей.
- TopManager — зарплата складывается из фиксированной части и бонуса в виде 150% от заработной платы, если доход компании более 10 млн рублей.
- Operator — зарплата складывается только из фиксированной части.
Каждый класс сотрудника должен имплементировать интерфейс Employee. В интерфейсе Employee должен быть объявлен метод, возвращающий зарплату сотрудника, — getMonthSalary().
Аргументы и возвращаемое значение метода выберите в соответствии с логикой начисления зарплат. В интерфейсе объявите необходимые методы.
Для демонстрации и тестирования работы ваших классов:
- Создайте и наймите в компанию: 180 операторов Operator, 80 менеджеров по продажам Manager, 10 топ-менеджеров TopManager.
- Распечатайте список из 10–15 самых высоких зарплат в компании.
- Распечатайте список из 30 самых низких зарплат в компании.
- Увольте 50% сотрудников.
- Распечатайте список из 10–15 самых высоких зарплат в компании.
- Распечатайте список из 30 самых низких зарплат в компании.
✒️ Приступим!
Интерфейс Employee
📌 Создадим интерфейс Employee
public interface Employee {
}
✎ Интерфейсы определяют какой либо функционал, без конкретной реализации, реализация этого функционала будет зависеть от конкретного класса, который будет имплементировать интерфейс Employee.
✎ Для нашего случая, этот функционал ни что иное, как вычисление заработной платы
double getMonthSalary();
⭐️ P.S. методы интерфейса не имеют модификаторов доступа, но фактически по умолчанию доступ public
↘️
public interface Employee {
ㅤdouble getMonthSalary();
}
⭐️ P.P.S. Одно из преимуществ использования интерфейсов состоит в том, что мы можем создавать объекты Employee, как экземпляры классов, которые будут имплементировать этот интерфейс, например:
Employee employee = new Manager(); // создаем экземпляр класса Manager, который имплементирует интерфейс Employee
Благодаря интерфейсу Employee мы можем работать с различными типами сотрудников в нашей системе, используя общий интерфейсный метод getMonthSalary(). Это позволяет нам упростить код и сделать его более гибким и расширяемым.
Класс Company
📌 Создадим класс Company с:
❶ Указанием в нем поля:
List<Employee> employees = new ArrayList<>(); - список в котором будем хранить наших сотрудников (имплементирующих интерфейс Employee).
Для этого поля реализуем "геттер", он еще понадобится
❷ Реализацией методов, указанных в условии задачи.
📌 Метод .hire()
✎ На вход метода будем передавать объект нашего интерфейса, как писал ранее, подразумевая любой класс его имплементирующий
✎ И добавлять этот объект в созданный список employees
↘️
public void hire(Employee employee) {
ㅤemployees.add(employee);
}
📌 Метод .hireAll()
✎ На вход метода будем передавать коллекцию объектов интерфейса Employee
✎ И добавлять эту коллекцию в наш список (используя метод .addAll() интерфейса List)
↘️
public void hireAll (Collection<Employee> employes) {
ㅤemployees.addAll(employes);
}
📌 Метод .getIncome(), в котором реализуем возврат рандомной суммы от 5000000 до 15000000, для чего воспользуемся статическим методом random() класса Math.
Метод сделаем статическим, что бы не создавать экземпляр класса Company в классах сотрудников при вычислении заработной платы.
↘️
public static int getIncome () {
ㅤreturn 5_000_000 + (int) (Math.random() * 10_000_001);
}
⭐️ P.S. Для генерации случайных чисел в определенном диапазоне рекомендую сохранить шпаргалку:
[a, b]
a + (int)(( Math.random() * (b-a+1))
📌 Метод getTopSalaryStaff(), который будет возвращать список сотрудников с самыми высокими зарплатами.
✎ На вход метода будем передавать количество сотрудников, которых хотим получить (int count)
public List<Employee> getTopSalaryStaff(int count) {
}
✎ Одним из самых простых способов отсортировать наш ArrayList employees, это воспользоваться статическим методом sort() класса Collections, но применив этот метод к нашему List'у мы столкнемся с ошибкой, потому что метод не будет знать как их сравнивать
⭕️ Для того, чтобы объекты Employee можно было сравнить и сортировать, они должны расширять интерфейс Comparable, поэтому преобразуем наш интерфейс в следующий вид
↘️
public interface Employee extends Comparable {
ㅤdouble getMonthSalary();
}
⭐️ P.S. Интерфейс Comparable содержит один единственный метод
compareTo(), который мы переопределим в классах сотрудников на предмет сравнения их по заработным платам
✎ Внутри метода инициализируем новый List, который заполним отсортированными объектами Employee из нашего ArrayList employees с учетом желаемого количества, которое мы передавали на вход метода (count)
↘️
public List<Employee> getTopSalaryStaff(int count) {
ㅤCollections.sort(employees);
ㅤList<Employee> limitList = new ArrayList<>();
ㅤfor (int i = employees.size() - 1; i >= employees.size() - count; i--) {
ㅤㅤlimitList.add(employees.get(i));
ㅤ}
ㅤreturn limitList;
}
📌 Метод getLowestSalaryStaff() сделаем по аналогии
↘️
public List<Employee> getLowestSalaryStaff(int count) {
ㅤCollections.sort(employees);
ㅤList<Employee> limitList = new ArrayList<>();
ㅤfor (int i = 0; i < count; i++) {
ㅤㅤlimitList.add(employees.get(i));
ㅤ}
ㅤreturn limitList;
}
Класс Manager
📌 Создадим класс Manager:
❶ Применим интерфейс Employee
public class Manager implements Employee {
}
❷ Укажем в нем поля в соответствии с условие:
- private static final int SALARY = 80_000; - фиксированная зарплата (допустим 80000);
- private int earned; - бонус (в виде 5% от заработанных для компании денег. Количество заработанных денег для компании генерируйте случайным образом от 115 000 до 140 000 рублей)
📌 Что бы значение переменной earnded было разным для всех объектов класса Manager - cоздадим конструктор, в котором будем инициализировать переменную earnded
↘️
public Manager() {
ㅤearned = 115_000 + (int) (Math.random() * 25_001);
}
📌 Реализуем метод применяемого интерфейса - getMonthSalary() в соответствии с условием задачи (фиксированная + бонус в виде 5% от заработанного)
↘️
@Override
public double getMonthSalary() {
ㅤreturn SALARY + earned * 0.05;
}
⭐️ P.S. Используя аннотацию @Override, вы подсказываете компилятору обеспечить проверку того, что вы действительно переопределяете метод. Таким образом, если вы совершите ошибку, по типу опечатки в имени метода, вы будете предупреждены о том, что ваш метод фактически не переопределяет в то время, как вы уверены в обратном.
Дополнительно, данная аннотация, улучшает читаемость кода, делая переопределение более очевидным.
📌 Переопределим метод compareTo(), о чем писалось выше
↘️
@Override
public int compareTo(Object o) {
ㅤEmployee manager = (Employee) o;
ㅤreturn Double.compare(getMonthSalary(), ((Employee) o).getMonthSalary());
}
📌 Напишем небольшой toString для тестирование нашей программы в консоле
↘️
public String toString() {
ㅤreturn "Менеджер - " + getMonthSalary();
}
Класс TopManager
❶ Применим интерфейс Employee
public class TopManager implements Employee {
}
❷ Укажем в нем поле в соответствии с условием:
- private static final int SALARY = 100_000; - фиксированная зарплата (допустим 100000);
⭕️ Если придерживаться условий задачи (зарплата складывается из фиксированной части и бонуса в виде 150% от заработной платы, если доход компании более 10 млн рублей.), то все TopManager в компании получат одинаковую сумму и при тестировании проекта визуально это будет не удобно.
Поэтому дополним, 150% не от заработной платы, а от дополнительной получки, которую будем генерировать случайным образом от 115000 до 140000, для неё зададим поле:
- private int earned;
📌 Что бы значение переменной earnded было разным для всех объектов класса TopManager - cоздадим конструктор, в котором будем инициализировать переменную earnded
public TopManager() {
ㅤearned = 115_000 + (int) (Math.random() * 25_001);
}
📌Далее аналогично Manager
↘️
@Override
public double getMonthSalary() {
ㅤreturn SALARY + (Company.getIncome() > 10_000_000 ? 1.5 * earned : 0);
}
@Override
public int compareTo(Object o) {
ㅤEmployee topManager = (Employee) o;
ㅤreturn Double.compare(getMonthSalary(), ((Employee) o).getMonthSalary());
}
@Override
public String toString() {
ㅤreturn "Топ Менеджер - " + getMonthSalary();
}
Класс Operator
❶ Применим интерфейс Employee
public class Operator implements Employee {
}
❷ Укажем в нем поле в соответствии с условием:
- private static final int SALARY = 60_000; - фиксированная зарплата (допустим 60000)
⭕️ Опять же, если придерживаться условий задачи (зарплата складывается только из фиксированной части), то все Operator в компании получат одинаковую сумму и при тестировании проекта визуально это будет не удобно.
Дополним такой же переменной earned, которую будем генерировать от 20000 до 40000 и добавлять 1% от этого числа к фиксированной зарплате.
- private int earned;
📌 Создадим конструктор, в котором будем инициализировать переменную earnded
↘️
public Operator() {
ㅤearned = 20_000 + (int) (Math.random() * 20_001);
}
📌 Далее аналогично предыдущим классам
↘️
@Override
public double getMonthSalary() {
ㅤreturn SALARY + 0.01 * earned;
}
@Override
public int compareTo(Object o) {
ㅤEmployee operator = (Employee) o;
ㅤreturn Double.compare(getMonthSalary(), ((Employee) o).getMonthSalary());
}
@Override
public String toString() {
ㅤreturn "Оператор - " + getMonthSalary();
}
Класс Main
Протестируем наши классы, проделав действия из условия задачи
- Создайте и наймите в компанию: 180 операторов Operator, 80 менеджеров по продажам Manager, 10 топ-менеджеров TopManager.
- Распечатайте список из 10–15 самых высоких зарплат в компании.
- Распечатайте список из 30 самых низких зарплат в компании.
- Увольте 50% сотрудников.
- Распечатайте список из 10–15 самых высоких зарплат в компании.
- Распечатайте список из 30 самых низких зарплат в компании.
- ↘️
Company company1 = new Company();
for (int i = 0; i < 180; i++) {
ㅤEmployee operator = new Operator();
ㅤcompany1.hire(operator);
}
for (int i = 0; i < 80; i++) {
ㅤEmployee manager = new Manager();
ㅤcompany1.hire(manager);
}
for (int i = 0; i < 10; i++) {
ㅤEmployee topManager = new TopManager();
ㅤcompany1.hire(topManager);
}
System.out.println("15 сотрудников с самыми высокими з.п.:");
for (Employee topSalary : company1.getTopSalaryStaff(15)) {
ㅤSystem.out.println(topSalary);
}
System.out.println("30 сотрудников с самыми низкими з.п.:");
for (Employee lowSalary : company1.getLowestSalaryStaff(30)) {
ㅤSystem.out.println(lowSalary);
}
int employeesCount = company1.getEmployees().size();
for (int i = 0; i < employeesCount / 2; i++) {
ㅤint radomIndex = (int) (Math.random() * company1.getEmployees().size());
ㅤcompany1.fire(company1.getEmployees().get(radomIndex));
}
System.out.println("\n15 сотрудников с самыми высокими з.п.:");
for (Employee topSalary : company1.getTopSalaryStaff(15)) {
ㅤSystem.out.println(topSalary);
}
System.out.println("30 сотрудников с самыми низкими з.п.:");
for (Employee lowSalary : company1.getLowestSalaryStaff(30)) {
ㅤSystem.out.println(lowSalary);
}
✅ Можно считать задачу выполненной, проверим вывод в консоль.
▶️ Run 'Main.main()'