Домашнее задание по Java проблема с абстрактным классом и внешним компаратором. Написал код почти работающий

Что нужно сделать Выполните задание в репозитории java_basics в проекте AbstractClasses/practice_1. Директория пустая, вам необходимо в ней создать новый проект и выполнить задание.

  1. Создайте класс компании Company, содержащей сотрудников и реализующей методы:

найм одного сотрудника — hire(Employee employee), найм списка сотрудников – hireAll(Collection employes), увольнение сотрудника – fire(Employee employee), получение значения дохода компании – getIncome(). Каждый метод НЕ должен иметь модификатор static, это позволит каждому объекту класса Company иметь свой набора сотрудников, свой расчет дохода, увольнение и найм. Аргументы и возвращаемое значение методов выберите на основании логики работы вашего приложения.

  1. Создайте два метода, возвращающие список указанной длины (count). Они должны содержать сотрудников, отсортированных по убыванию и возрастанию заработной платы:

List getTopSalaryStaff(int count), List getLowestSalaryStaff(int count).

  1. Создайте классы сотрудников с информацией о зарплатах и условиями начисления зарплаты:

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 самых низких зарплат в компании.

Еще не сказано что нужно написать работающий компаратор. я по честному перелопатил варианты с кодом, но так и не нашел как составить компаратор так чтобы премия начислялась в соответствии с условием в классе TopManager if ( company.getCompanyRevenue() > 10_000_000.0) , а не так как сейчас премия начисляется при Доходе компании : 800000.0 невзирая на условие в 10_000_000.0. Дайте зацепочку.

  1. прошу подсказать как правильно задавать здесь вопросы, это мой первый опыт.
  2. подскажите с решением.
import java.util.*;
public class Company {
    private double inCome = 0;
    List <Employee> employees = new ArrayList<>();
    public void  hire (Employee employee) {
        employees.add(employee);
    }
    public void  hireAll (List <Employee> employees) {
        for ( Employee e : employees) {
            hire(e);
        }
    }
    public void fire (Employee employee) {
        employees.remove(employee);
    }
    public double getInCome () {
        return inCome;
    }
    public List <Employee> getTopSalaryStaff (int count) {
        employees.sort(new SortByTopSalaryStaff());
        if (count < 0 && count > employees.size()) {
            List <Employee> EmptyList = new ArrayList<>();
            System.out.println("неверное количество");
            return EmptyList;
        }
        return employees.subList(0, count);
    }
    public List <Employee> getLowestSalaryStaff (int count) {
        employees.sort(new SortByLowestSalaryStaff());
        if (count < 0 && count > employees.size()) {
            List <Employee> EmptyList = new ArrayList<>();
            System.out.println("неверное количество");
            return EmptyList;
        }
        return employees.subList(0, count);
    }
    public double getCompanyRevenue () {

        for (Employee e : employees) {
            inCome = inCome + e.getRevenue();
        }
        return inCome;
    }

}

public interface Employee {
    double getMonthSalary ();
    double getRevenue();
}

import java.util.ArrayList;
import java.util.List;
public class Main {
    public static void main(String[] args) {
        Company company = new Company();
        List <Employee> workers = new ArrayList<>();
        Operator operator = new Operator();
        //Manager manager = new Manager();
        //TopManager topManager = new TopManager(company);
        company.hire(operator);
        for (int i = 0; i < 180; i++){
            workers.add(new Operator());
        }
        for (int i = 0; i < 80; i++) {
            workers.add(new Manager());
            if ( i%8 == 0 ) {
                workers.add(new TopManager(company));
            }
        }
        company.hireAll(workers);

        System.out.println();
        System.out.println("Количество работников : " + company.employees.size());
        System.out.println();
        System.out.println("Доход компании : " + company.getCompanyRevenue());
        System.out.println();
        System.out.println("topSalaryStaff");
        System.out.println();

        List <Employee> topSalaryStaff = company.getTopSalaryStaff(12);
        for (Employee e: topSalaryStaff) {
            System.out.println(e.getMonthSalary());
        }
        System.out.println("--------------------------------------");
/*
        System.out.println();
        System.out.println("lowSalaryStaff");
        System.out.println();
        List <Employee> lowSalaryStaff = company.getLowestSalaryStaff(12);
        for (Employee e: lowSalaryStaff) {
            System.out.println(e.getMonthSalary());
        }
        System.out.println("--------------------------------------");


 */

    }
}

import java.util.ArrayList;
import java.util.List;
public class Main {
    public static void main(String[] args) {
        Company company = new Company();
        List <Employee> workers = new ArrayList<>();
        Operator operator = new Operator();
        //Manager manager = new Manager();
        //TopManager topManager = new TopManager(company);
        company.hire(operator);
        for (int i = 0; i < 180; i++){
            workers.add(new Operator());
        }
        for (int i = 0; i < 80; i++) {
            workers.add(new Manager());
            if ( i%8 == 0 ) {
                workers.add(new TopManager(company));
            }
        }
        company.hireAll(workers);

        System.out.println();
        System.out.println("Количество работников : " + company.employees.size());
        System.out.println();
        System.out.println("Доход компании : " + company.getCompanyRevenue());
        System.out.println();
        System.out.println("topSalaryStaff");
        System.out.println();

        List <Employee> topSalaryStaff = company.getTopSalaryStaff(12);
        for (Employee e: topSalaryStaff) {
            System.out.println(e.getMonthSalary());
        }
        System.out.println("--------------------------------------");
/*
        System.out.println();
        System.out.println("lowSalaryStaff");
        System.out.println();
        List <Employee> lowSalaryStaff = company.getLowestSalaryStaff(12);
        for (Employee e: lowSalaryStaff) {
            System.out.println(e.getMonthSalary());
        }
        System.out.println("--------------------------------------");


 */

    }
}

public class Operator implements Employee {
    private  double salary = 30_000;
    final double income = 0;
    @Override
    public double getMonthSalary() {
        return salary;
    }
    @Override
    public double getRevenue() {
        return income;
    }
}
import java.util.Comparator;
public class SortByLowestSalaryStaff implements Comparator<Employee> {
    @Override
    public int compare(Employee employee1, Employee employee2) {
        if (employee1.getMonthSalary() > employee2.getMonthSalary()) {
            return 1;
        }
        if (employee1.getMonthSalary() == employee2.getMonthSalary()) {
            return 0;
        }
        return -1;
    }
}

import java.util.Comparator;
public class SortByTopSalaryStaff implements Comparator <Employee> {

    @Override
    public int compare(Employee employee1, Employee employee2) {

        return Double.compare(employee2.getMonthSalary(), employee1.getMonthSalary());
    }





    /*
    @Override
    public int compare(Employee employee1, Employee employee2) {

        if (employee1.getMonthSalary() < employee2.getMonthSalary()) {
            return 1;
        }
        if (employee1.getMonthSalary() == employee2.getMonthSalary()) {
            return 0;
        }
        return -1;
    }

     */


}

import java.util.Random;
public class TopManager implements Employee{
    private  final Company company;
    private final double salary;
    private final double bonus = 1.5;
    private final double earnedLimit = 10_000_000.0;
    final double income = 0;
    public TopManager (Company company) {
        this.company = company;
        this.salary = 900000; // + new Random().nextInt(40000);
    }
    @Override
    public double getRevenue() {
        return income;
    }
    @Override
    public double getMonthSalary() {
       //double companyIncome = company.getCompanyRevenue();

        if ( company.getCompanyRevenue() > earnedLimit) {
            return  salary + salary * bonus;
        }
        return salary;
    }
}

Ответы (1 шт):

Автор решения: Nowhere Man

Основная ошибка в представленном коде -- некорректное вычисление выручки (revenue) компании в методе getCompanyRevenue(), где значение переменной inCome (данное название следует перевести как приБыль) НЕ обнуляется и соответственно "приБыль" будет нарастать при каждом вызове данного метода, то есть к примеру, при сортировке работников этот метод будет вызываться для каждого топ менеджера при сравнении его дохода с доходом любого другого сотрудника и "выручка" компании взлетит до небес.

Вероятно следовало закешировать вычисленное значение дохода, чтобы сократить количество ненужных вычислений:

public class Company {
    private Double income = null;

    // ...

    public double getCompanyRevenue () {
        if (null == income) {
            income = calcIncome();
        }
        return income.doubleValue();
    }

    protected double calcIncome() {
        double result = 0d;

        for (var emp : employees) {
            result += emp.getRevenue();
        }
        return result;
        // или так (Stream API) 
        // return employees.stream().mapToDouble(Employee::getRevenue).sum();
    }
}

Соответственно, при вызове других методов, которые изменяют штат / персонал компании, значение переменной income следует сбрасывать в null (исключительно в рамках данного учебного задания, предполагая, что эта программа однопоточная).


Дополнение
Может быть альтернативное решение без кеширования (дополнительной логики в геттере getCompanyRevenue), но в любом случае потребуется реализовать отдельный метод для вычисления суммарной выручки/прибыли, который следует не забывать вызывать вместо сеттера в нужный момент, например после заполнения коллекции всех сотрудников.

public class Company {
    // ...
    private double income = 0d;

    // простой геттер, без логики кеширования
    public double getCompanyRevenue() {
        return income;
    }

    public void calculateRevenue() {
        var result = 0d;
        
        for (var emp : employees) {
            result += emp.getRevenue();
        }
        this.income = result;
    }
    // ...
}

В классе Main следует вызвать этот метод после вызова hireAll перед сортировкой:

// ...
company.hireAll(workers);
company.calculateRevenue();

Кроме того, в представленном коде для всех сотрудников их "выручка", которая возвращается из метода getRevenue равна нулю, соответственно и выручка компании всегда будет нулевой.
Чтобы проверить возможность начисления бонусов для топ-менеджеров, нужно чтобы хотя бы операторы приносили 56К дохода при прочих входных данных.


Ещё одна типичная ошибка -- использование типа с плавающей точкой double для хранения результатов финансовых вычислений, где требуется фиксированная точность (например, до копейки).



Также в представленном коде можно улучшить реализацию методов getTopSalaryStaff, getLowestSalaryStaff в классе Company.

Во-первых, сортировку следует выполнять после проверки входного параметра count.

Во-вторых, условие if (count < 0 && count > employees.size()) бессмысленно и никогда не будет выполняться из-за неверной логической операции &&, подразумевающей, что count является отрицательным числом, большим нуля. Следовало использовать оператор ИЛИ ||.

Кроме того, для неправильного аргумента имеет смысл выбрасывать соответствующее исключение типа IllegalArgumentException, а не возвращать пустой список. Если же надо вернуть пустой список, лучше использовать стандартный метод Collections.emptyList, а не создавать каждый раз пустой модифицируемый список.

В-третьих, отдельные классы компараторов НЕ нужны со времени появления Java 8 в 2014 году, вместо них следует использовать статические методы Comparator::comparingDouble:

В-четвёртых, из указанных методов следует возвращать немодифицируемые списки при помощи Collections.unmodifiableList.

public List <Employee> getTopSalaryStaff (int count) {
    if (count <= 0 || count > employees.size()) {
        return emptyListOnError(count);
    }
    employees.sort(Comparator
        .comparingDouble(Employee::getMonthSalary)
        .reversed()
    );

    return Collections.unmodifiableList(employees.subList(0, count));
}

private static List<Employee> emptyListOnError(int count) {
    System.out.println("неверное количество: " + count);
    return Collections.emptyList();
}

Аналогично при помощи Stream API и определения двух статических компараторов:

private static final Comparator<Employee> byMonthSalary = Comparator.comparingDouble(Employee::getMonthSalary);

private static final Comparator<Employee> byMonthSalaryDesc = byMonthSalary.reversed();

public List <Employee> getTopSalaryStaff (int count) {
    return getNEmployees(count, byMonthSalaryDesc);
}

public List <Employee> getLowestSalaryStaff(int count) {
    return getNEmployees(count, byMonthSalary);
}

private List<Employee> getNEmployees(int count, Comparator<Employee> comparator) {
    return count <= 0 || count > employees.size() 
        ? emptyListOnError(count) 
        : employees.stream()
            .sorted(comparator)
            .limit(count)
            .toList();    
}
→ Ссылка