Найти тему
NM.getJavaPractice();

Abstract Classes and Inheritance

Оглавление
Цель: научиться работать с интерфейсами, абстрактными классами и взаимодействием классов.
Цель: научиться работать с интерфейсами, абстрактными классами и взаимодействием классов.

❗️ Задача:

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().

Аргументы и возвращаемое значение метода выберите в соответствии с логикой начисления зарплат. В интерфейсе объявите необходимые методы.

Для демонстрации и тестирования работы ваших классов:

  1. Создайте и наймите в компанию: 180 операторов Operator, 80 менеджеров по продажам Manager, 10 топ-менеджеров TopManager.
  2. Распечатайте список из 10–15 самых высоких зарплат в компании.
  3. Распечатайте список из 30 самых низких зарплат в компании.
  4. Увольте 50% сотрудников.
  5. Распечатайте список из 10–15 самых высоких зарплат в компании.
  6. Распечатайте список из 30 самых низких зарплат в компании.

✒️ Приступим!

-2

Интерфейс 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

Протестируем наши классы, проделав действия из условия задачи

  1. Создайте и наймите в компанию: 180 операторов Operator, 80 менеджеров по продажам Manager, 10 топ-менеджеров TopManager.
  2. Распечатайте список из 10–15 самых высоких зарплат в компании.
  3. Распечатайте список из 30 самых низких зарплат в компании.
  4. Увольте 50% сотрудников.
  5. Распечатайте список из 10–15 самых высоких зарплат в компании.
  6. Распечатайте список из 30 самых низких зарплат в компании.
  7. ↘️
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()'

-3
-4