Квадратное уравнение

В этой статье мы спроектируем и напишем красивый, структурированный класс квадратного уравнения. Это уравнение вида a*x^2 + b*x + c = 0. Класс сможет находить корни уравнения. Он будет соответствовать парадигме ООП.

Поля и конструкторы

Класс назовём QuadraticEquation. Такое содержательное название лучше всего отражает его суть. Что должно хранить в себе квадратное уравнение, выстроенное согласно объектно-ориентированной парадигме? Логично, что это его коэффициенты: a, b и c. Они однозначно характеризуют разные уравнения этого типа. Так что добавим три приватных поля типа double. Впоследствии эти скрытые от глаз внешних наблюдателей данные будут использоваться в методах класса.

Отлично. Чтобы задать значения коэффициентов сразу при создании объекта QuadraticEquation, создадим конструктор. В нём будет три параметра типа double, которые будут соответствовать этим трём коэффициентам квадратного уравнения.

Этого может быть вполне достаточно для управления коэффициентами a, b и c. Однако давайте предоставим возможность также менять коэффициенты после создания объекта. Это можно сделать при помощи сеттеров и второго конструктора без аргументов. Он будет выставлять значения коэффициентам по умолчанию (нули). Помимо прочего, добавим геттеры, чтобы извне можно было получить значения коэффициентов. Если для решаемой задачи это не нужно, их можно не прописывать. Но в качестве примера добавим.

Чудно, подготовительный этап завершён. Приступим к математической части!

Нахождение корней

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

В рамках квадратных уравнений одна из таких маленьких задач – вычисление дискриминанта. И хотя это вычисление происходит в одну строку, хорошим тоном будет выделить его в отдельный метод. По названию метода calcDiscriminant() можно сразу понять, что вычисляется. По формуле Math.pow(b, 2) – 4 * a * c – придётся задуматься. А это затраты времени и сил.

Теперь давайте продумаем все варианты решения. В квадратном уравнении может быть несколько ситуаций:

  • дискриминант < 0, тогда корней нет,
  • дискриминант == 0, тогда корень один,
  • дискриминант > 0, тогда корней два,
  • и два варианта, когда a == 0:
    • b != 0, тогда корень один (x = -c/b),
    • b == 0, тогда корней бесконечное множество.

Для всех этих ситуаций я предлагаю создать отдельные методы. Тогда в общем методе нахождения корней будет легко проследить логику их вызовов. Напишем методы для каждого из этих вариантов. Во всех случаях набор корней будем возвращать как массив double, так как количество корней заранее неизвестно.

Как видите, я решила обработать случай, когда корней бесконечное множество, выбросом исключения. Ну не будем же мы возвращать бесконечность элементов double? А вернуть пустое множество корней будет обманом. Ведь корни-то есть, просто их бесконечно много. Бросать исключение в исключительных ситуациях это хорошая манера. Так мы даём понять тому, кто будет извне вызывать метод нахождения корней, что случилось что-то, выходящее за рамки ожидаемого.

Класс исключения QuadraticEquationException выглядит самым обычным образом, наследуясь от базового класса исключений Exception:

Супер. Осталось объединить логику написанных маленьких частей решения уравнения в один общий публичный метод, который и будет вызываться извне. Назовём его findRoots().

Да, методу findRoots() также пришлось обозначить в его шапке, что он может выбросить QuadraticEquationException. Что ж, поздравляю – наш класс научился математике. Однако есть ещё одна нужная часть. Это – как мы эти данные пользователю отдадим.

Категорически не рекомендуется писать такие методы, где одновременно происходит и расчёт корней, и, скажем, вывод в консоль. Во-первых, вы нарушаете логику приложения, заставляя один метод как рабочую лошадку и рассчитывать, и выводить. Это целых две задачи, и необходимо их разграничивать. Во-вторых, мы с вами не знаем, где могут пригодиться эти данные. Да, они могут быть использованы в консоли. Но что, если нужно будет поменять формат вывода в консоль? Или вообще закинуть корни уравнения в форму с графическим интерфейсом? А может, отправить на сервер? Или записать в файл? Короче говоря, пишите гибкие и разбитые на задачи программы.

Представление данных

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

Например, его строковое представление. Прототип такого метода выглядит так:

Однако при таком подходе при отрицательных b или c строка будет иметь некрасивый вид. Например, при b=-3.5 и c=-17, получится такой результат: “2*x^2+-3.5*x+-17”. Этого хотелось бы избежать и не ставить плюс там, где число отрицательное. Для этого напишем метод, который будет заниматься этой задачей, и вызовем его из метода getEquationString():

Теперь строка уравнения будет выглядеть как надо: “2*x^2-3.5*x-17”. Добавим ещё один, самый широкий по предоставляемой информации, метод getDescription(). Он будет включать в себя строку уравнения из getEquationString(), а также значение дискриминанта и найденные корни. Для формирования описания используем класс StringBuilder. Он позволяет более оптимизированно по затратам ресурсов складывать большое количество строк.

Для включения сообщения о том, что корней нет, когда размер массива roots равен 0, добавим проверку этого условия. Также среда разработки подчёркивает красным метод findRoots(). Это происходит, поскольку этот метод имеет в своей шапке throws QuadraticEquationException. То есть у нас два варианта. Первый: мы методу getDescription() такую же штуку впишем в шапку, и ловить это исключение придётся оттуда, откуда этот метод будет вызван. Второй: мы обработаем исключение QuadraticEquationException прямо внутри метода getDescription() в блоке try-catch.

Здесь я хочу поступить вторым образом. Если исключение возникнет, можно будет просто добавить его сообщение в формируемое описание уравнения. Таким образом, если корней бесконечно много, информация об этом будет записана.

Готово!

Итоговая программа

Вот написание кода класса “Квадратное уравнение” и подошло к концу. Протестируем его! Для тестирования класса QuadraticEquation напишем небольшой код в классе Main, чтобы запустить его.

А теперь будем вписывать много разных вариантов значений коэффициентов. Это позволит увидеть, как наш класс справляется с различными жизненными ситуациями. Имейте в виду, что здесь не предусмотрена обработка ошибок при вводе double, если разделитель целой и дробной части не соответствует языковым настройкам вашего ПК. Так что при русской локализации разделять вводимые значения нужно запятой, а не точкой (например, “3,4” вместо “3.4”). Иначе возникнет исключение.

Скриншот консоли IntelliJ IDEA с Java-программой, где был создан класс "квадратное уравнение" в парадигме ООП, результат вывода - нет корней
Скриншот консоли IntelliJ IDEA с Java-программой, где был создан класс "квадратное уравнение" в парадигме ООП, результат вывода - два корня

Скриншот консоли IntelliJ IDEA с Java-программой, где был создан класс "квадратное уравнение" в парадигме ООП, результат вывода - один корень
Скриншот консоли IntelliJ IDEA с Java-программой, где был создан класс "квадратное уравнение" в парадигме ООП, результат вывода - два корня

Скриншот консоли IntelliJ IDEA с Java-программой, где был создан класс "квадратное уравнение" в парадигме ООП, результат вывода - один корень
Скриншот консоли IntelliJ IDEA с Java-программой, где был создан класс "квадратное уравнение" в парадигме ООП, результат вывода - корней бесконечное множество

Полный код класса QuadraticEquation:

Пишите программы так, чтобы не стыдно было детям показать. А вообще, чем проще – тем лучше. Серьёзно. Громоздкий код никому не хочется разбирать. Со временем он начинает тухнуть.

В итоге, в этой статье мы написали прикольный пример ООП – создали класс QuadraticEquation для квадратного уравнения. Этот класс хранит в себе коэффициенты a, b, c, умеет находить свои корни и предоставлять описание уравнения.

Добавить комментарий