/GloboCalc

Тестовое задание для GloboGames

Primary LanguageC#

GloboCalc

Тестовое задание для GloboGames

Само задание выглядит так

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

Главный (но не единственный) критерий при оценке задания – разработка с использованием принципов SOLID.

Минимальный набор поддерживаемых математических операций: сложение, вычитание, умножение и деление.

Пример:

Ввод: 23 * 2 + 45 - 24 / 5

Ответ: 86,2

Реализация

Реализация заняла примерно полтора дня. Помимо базовых опрераций "( ) + - * / ^ sin" добавлена возможность расширять калькулятор любыми другими. Для добавления новой операции достаточно реализовать 2 интерфейса: IOperationFactory и IOperation и добавить реализацию IOperationFactory в IoC контейнер.

Алгоритм вычисления выражания:

  • разбиваем выражение на токены, указывая их типы (число, оператор или скобки)
  • используя алгоритм Shunting Yard преобразуем токены в операции, стоящие в обратной польской нотации, например: "1 + 2" => "1 2 +".
  • выполняем операции, получаем результат

Сборка

Программа написана в Visual Studio 2017. Но для компиляции она не нужна.

Нужен dotnet sdk 2.0. Откройте в консоли папку с солюшеном. Выполните команды

dotnet restore
dotnet build
dotnet run --project ./src/GloboCalc.ConsoleApp

Для запуска юнит-тестов:

dotnet test ./test/GloboCalc.Core.Tests

Использование

Либо вызвать калькулятор с параметрами, например:

dotnet GloboCalc.ConsoleApp.dll "1 + 2" "2 * (1 + 2)"

Либо просто запустить без параметров, тогда программа откроется в режиме диалога с пользователем.

SOLID

Приложение написано с использованием принципов SOLID. Подробнее:

Single responsibility

Каждый компонент обладает 1й обязанностью.

Например:

  • Tokenizer - разбирает выражение на токены
  • InfixToPostfixNotationConverter - конвертирует токены в обратную польскую нотацию
  • PostfixNotationCalculator - вычисляет значение выражение из обратной польской нотации

Open-closed

Приложение закрыто для изменения и открыто для расширения

Например:

Для добавления новой операции не нужно менять код. Достаточно реализовать IOperationFactory и IOperation.

Пример расширения функциональности без изменения GloboCalc.Core можно увидеть в ветке custom-operation. Для примера добавлена операция логирифма по основанию 10 (log10)

Liskov substitution

Объекты могут быть заменены подтипами без изменения корректности программы.

Например:

Для того чтобы избежать switсh при создания операции на основе токена используется абстрактная фабрика IOperationFactory создающая операции IOperation. Она реализуется классом Constant и 2мя абстрактными классами BinaryOperation - для операций с двумя операндами и UnaryOperation для операций с 1м. Каждый из этих абстракнтых классов реализуется своими конкретными классами.

Несмотря на то, что опрерации IOperation настолько разные (операторы и операнды), приложение спроектировано так, что не нужно разрушать абстракцию чтобы произвести вычисления (класс PostfixNotationCalculator).

Interface segregation

Интерфесы должны быть для конкрентого назначения.

Например:

IOperationPropertiesExtractor - предоставляет только методы для получения параметров операции, а не для всех возможностей класса AllOperationsFactory

Dependency inversion

Зависимости на абстакции.

Например:

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