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

Условие задачи

Создать ООП-модель для следующей предметной области:

  1. Калькулятор простой. Выполняет четыре арифметические операции: сложение, вычитание, умножение, деление.
  2. Калькулятор простой с памятью. Выполняет четыре арифметические операции и имеет одну ячейку памяти. С данной ячейкой памяти калькулятор выполняет пять операций: очистить ячейку памяти, записать в ячейку памяти текущее значение, присвоить текущему значению содержимое ячейки памяти, увеличить или уменьшить значение в ячейке памяти на текущее значение.
  3. Калькулятор инженерный. Выполняет четыре арифметические операции и вычисляет значения трех функций: косинус, экспонента, корень квадратный.
  4. Калькулятор инженерный с памятью. Это комбинация пунктов 2 и 3.

Создать консольное приложение с единственным аргументом командной строки – имя входного файла.
Структура входного файла: строка 1 – тип калькулятора, остальные строки – числа и операции для вычисления выражения, при этом каждое данное находится в одной строке. Все операции для вычислений происходят последовательно. Например, содержимое файла для выражения cos(5*7-2)+1, которое необходимо вычислить инженерным калькулятором, может иметь вид:

3 5 * 7 - 2 cos + 1

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

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

Требования:

  • Использовать объектно-ориентированный подход для описания сущностей предметной области.
  • Приложение должно быть консольным. Не использовать графический интерфейс!

Предпочтения по выбору языка программирования: 1) Java; 2) C++; 3) другой ООП язык.

Решение

Выложу один класс для ознакомления и архив с полным решением + тесты:

/* CalculatorBuilder.java */ package ru.ozi_blog.task.calc; import ru.ozi_blog.task.calc.primitives.CalcPrimitive; import ru.ozi_blog.task.commands.binary.*; import ru.ozi_blog.task.commands.nullary.*; import ru.ozi_blog.task.commands.unary.*; import ru.ozi_blog.task.exceptions.BuildCalculatorException; /** * The class CalculatorBuilder takes over the process of creating an object of * the class Calculator. A correct build sequence: * <ul> * <li>Director makes a new object of the class Calculator or MemoryCalculator ( * {@link #newCalculator(CalcPrimitive)} or * {@link #newMemoryCalculator(CalcPrimitive)});</li> * <li>Director adds operations to the object ({@link #addBaseCommands()}, * {@link #addMemoryCommands()}, {@link #addScientificCommands()});</li> * <li>Director gets the ready object ({@link #getCalculator()}).</li> * </ul> * * @see ru.ozi_blog.task.calc.Calculator * @see ru.ozi_blog.task.calc.MemoryCalculator * * @author Zhukavets Aleh * * @param <T> * the type of a number that can be used by a Calculator or * a MemoryCalculator */ public class CalculatorBuilder<T extends Number> { /** * An object to build */ private Calculator<T> newCalc = null; /** * Makes a new object of the class Calculator * * @param calcPrimitive * will be used in the creation of the object */ public void newCalculator(CalcPrimitive<T> calcPrimitive) { newCalc = new Calculator<T>(calcPrimitive); } /** * Makes a new object of the class MemoryCalculator * * @param calcPrimitive * will be used in the creation of the object */ public void newMemoryCalculator(CalcPrimitive<T> calcPrimitive) { newCalc = new MemoryCalculator<T>(calcPrimitive); } /** * Adds basic arithmetic operations to object * * @throws BuildCalculatorException * if an object is not be made */ public void addBaseCommands() throws BuildCalculatorException { if (newCalc == null) throw new BuildCalculatorException(); newCalc.addCommand("+", new PlusSimpleCommand<T>(newCalc.getCalcPrimitive())); newCalc.addCommand("-", new MinusSimpleCommand<T>(newCalc.getCalcPrimitive())); newCalc.addCommand("*", new MultiplicationSimpleCommand<T>(newCalc.getCalcPrimitive())); newCalc.addCommand("/", new DivisionSimpleCommand<T>(newCalc.getCalcPrimitive())); } /** * Adds scientific operations to object * * @throws BuildCalculatorException * if an object is not be made */ public void addScientificCommands() throws BuildCalculatorException { if (newCalc == null) throw new BuildCalculatorException(); newCalc.addCommand("sqrt", new SqrtUnaryCommand<T>(newCalc.getCalcPrimitive())); newCalc.addCommand("exp", new ExpUnaryCommand<T>(newCalc.getCalcPrimitive())); newCalc.addCommand("cos", new CosUnaryCommand<T>(newCalc.getCalcPrimitive())); } /** * Adds memory operations to object * * @throws BuildCalculatorException * if an object is not be made or the object is not the * MemoryCalculator */ public void addMemoryCommands() throws BuildCalculatorException { if (newCalc == null) throw new BuildCalculatorException(); /*if an object can't be converted to a MemoryCalculator object, thrown exception*/ MemoryCalculator<T> tmp = null; if (newCalc instanceof MemoryCalculator) { tmp = (MemoryCalculator<T>) newCalc; } else { throw new BuildCalculatorException(); } tmp.addCommand("import", new ImportMemoryCommand<T>(tmp.getMemory(), tmp.getCalcPrimitive())); tmp.addCommand("export", new ExportMemoryCommand<T>(tmp.getMemory(), tmp.getCalcPrimitive())); tmp.addCommand("clear", new ClearMemoryCommand<T>(tmp.getMemory())); tmp.addCommand("plus", new PlusMemoryCommand<T>(tmp.getMemory(), tmp.getCalcPrimitive())); tmp.addCommand("minus", new MinusMemoryCommand<T>(tmp.getMemory(), tmp.getCalcPrimitive())); } /** * Returns the ready object * * @return the ready object * @throws BuildCalculatorException * if an object is not be made */ public Calculator<T> getCalculator() throws BuildCalculatorException { if (newCalc == null) throw new BuildCalculatorException(); Calculator<T> tmp = newCalc; newCalc = null; return tmp; } /* * (non-Javadoc) * * @see java.lang.Object#toString() */ @Override public String toString() { return "CalculatorBuilder [newCalc=" + newCalc + "]"; } }

Архив с решение и тестами

В этой статье описан способ преобразование рабочей системы архитектуры i386 в amd64 для Debian. При желании могут быть сохранены все существующие настройки и установленные программы, а может и нет 😀

Начиная с версии Debian GNU/Linux 7.0 Wheezy для пользователей стала доступна интересная возможность, которая называется мультиархитектура (multiarch). С этого момента в операционной системе могут одновременно находиться бинарные файлы (программы, библиотеки), которые относятся к различным архитектурам. На практике это обычно используется для запуска 32-битных программ из-под 64-битной ОС. Однако есть и более интересные применения.

Мы можем разрешить выполнение 64-битных программ из-под 32-х битной операционной системы. Затем заменить все 32-битные программы на их 64-битные аналоги. Что и будет продемонстрировано в этой статье.

Подготовка

Для успешного обновления ОС необходимо:

  1. 2-3 часа свободного времени;
  2. навыки работы с чистой коммандной строкой, без использования графического интерфейса;
  3. умение самостоятельно исправить недостающие зависимости пакетов с помощью dpkg и apt-get.

Многие детали, на подобии, на подобии: “завершите работу X-сервера и перейдите в консоль Linux с помощью комбинации клавиш Ctrl-Alt-F2”, будут опускаться. Подразумевается, что вы это можете сделать самостоятельно при необходимости.

Резервные копии

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

Универсального рецепта тут нет. К примеру, можно скопировать только те файлы, которые были созданы или отредактированы вручную (для моего десктопа было достаточно /etc/X11/xorg.conf и /boot/grub/grub.cfg).

Загрузка с помощью 64-битного ядра в 32-битную систему

Ядро Linux архитектуры i386 не позволяет запускать программы, скомпилированные под amd64. А вот 64-битное ядро может выполнять как 64, так и 32-битное программное обеспечение. И именно это нам и нужно:

  1. Разрешим пакетному менеджеру устанавливать программы с архитектурой amd64:
    # dpkg --add-architecture amd64
  2. Обновим список пакетов:
    # apt-get update
  3. Установим 64-битное ядро:
    # apt-get install linux-image-amd64:amd64

Кроме этого потребуется утилита, которая поможет скачать пакеты для базовой системы:

# apt-get install debootstrap

Теперь необходимо перезагрузить компьютер. В процессе установки ядра должен обновиться файл grub.cfg. Следует обязательно убедиться, что при загрузке ОС в качестве ядра была выбрана amd64-версия в меню GRUB2.

Подготовка файлов

Создадим файлы, содержащие в себе списки установленных пакетов для разных архитектур:

  1. # dpkg --get-selections "*:i386" | grep -v deinstall > packages.i386
  2. # dpkg --get-selections "*:amd64" | grep -v deinstall > packages.amd64
  3. # dpkg --get-selections "*:all" | grep -v deinstall > packages.all

Теперь скачаем пакеты, которые входят в состав базовой системы Debian.

  1. Для архитектуры i386:
    # mkdir bootstrap-i386 # debootstrap --arch=i386 --download-only jessie ./bootstrap-i386
  2. Для архитектуры amd64:
    # mkdir bootstrap-amd64 # debootstrap --arch=amd64 --download-only jessie ./bootstrap-amd64

Создадим файл, содержащий в себе список пакетов, которые входят в базовую поставку дистрибутива Debian:

# ls -l bootstrap-amd64/var/cache/apt/archives/ | egrep "\.deb$" | cut -d "_" -f1 | awk '{print $1 ":i386 install"}' > packages.base

Последующие операции следует выполнять только при наличии достаточного количества времени. Потому как перезагрузки компьютера крайне нежелательны.

Откат до базовой 32-битной системы в Debian

Производим откат дистрибутива до базовых пакетов. При этой операции появится уведомление о том, что будет удалено текущее (amd64) ядро Linux. Следует подтвердить это действие.

# dpkg --clear-selections # cat packages.base | dpkg --set-selections # apt-get dselect-upgrade

Обновление базового дистрибутива с 32 до 64-битной архитектуры

В первую очередь обновим dpkg:

  1. # mkdir archives-dpkg/partial
  2. # apt-get install -d -y -o Dir::Cache::Archives=./archives-dpkg dpkg:amd64
  3. # mv -i archives-dpkg/dpkg_*.deb .
  4. # dpkg -i archives-dpkg/*.deb
  5. # dpkg -i dpkg_*.deb
  6. # mv -i dpkg_*.deb archives-dpkg

Затем apt:

  1. # mkdir archives-apt/partial
  2. # apt-get install -d -y -o Dir::Cache::Archives=./archives-apt apt:amd64
  3. # dpkg -i archives-apt/*.deb

Очистим кэш пакетов:

# dpkg --clear-selections

Теперь несколько раз запускаем dpkg -i с пропуском ошибок, чтобы установить 64-битные пакеты для базовой системы:

# dpkg --abort-after=1000 -i bootstrap-amd64/var/cache/apt/archives/*.deb ... # dpkg --abort-after=1000 -i bootstrap-amd64/var/cache/apt/archives/*.deb

Делать это нужно до тех пор, пока не разрешатся все зависимости. Если же остаются зависимости после 5-6 запуска, которые не могут разрешиться автоматически, то следует сделать это самостоятельно:

# dpkg --force-overwrite -i bootstrap-amd64/var/cache/apt/archives/имя_пакета.deb

На данном этапе параллельно будут сосуществовать две архитектуры базовой системы Debian: i386 и amd64. Необходимо удалить 32-битные пакеты:

  1. # apt-get autoremove
  2. # apt-get dselect-upgrade

Осталось переустановить 64-битное ядро Linux:

# apt-get install locales linux-image-amd64 grub-pc

Только после этого можно безопасно перезагрузить операционною систему.

На всякий случай проверьте, существуют ли еще 32-битные пакеты в системе и, удалите их в этом случае вручную:

# dpkg --get-selections "*:i386" | grep -v deinstall

Установка программ

Cформируем список программного обеспечение, которое было установлено до обновления Linux с 32 до 64-битной архитектуры:

# cat packages.all | grep -v deinstall > packages.final # cat packages.i386 | grep -v ":i386" | grep -v deinstall | cut -f1 | awk '{print $1 " install"}' >> packages.final # cat packages.i386 | grep ":i386" | grep -v deinstall | cut -f1 | awk '{print $1 " install"}' >> packages.final # cat packages.amd64 | grep ^linux-image | grep -v deinstall | cut -d: -f1 | awk '{print $1 " install"}' >> packages.final

Удалим архитектуру i386 из поддерживаемых и установим пакеты:

# dpkg --clear-selections # dpkg --remove-architecture i386 # dselect update # cat packages.final | dpkg --set-selections # apt-get dselect upgrade # apt-get install `dpkg --get-selections "*:i386" | grep -v deinstall | cut -f1 | cut -d: -f1 | sed ':a;N;$!ba;s/\n/ /g'` # apt-get install `dpkg --get-selections "*:all" | grep -v deinstall | cut -f1 | cut -d: -f1 | sed ':a;N;$!ba;s/\n/ /g'` # apt-get install `dpkg --get-selections "*:amd64" | grep -v deinstall | cut -f1 | cut -d: -f1 | sed ':a;N;$!ba;s/\n/ /g'`

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

Решить эту проблему достаточно просто. Необходимо лишь сначала очистить файлы конфигурации пакета для архитектуры i386:

# apt-get purge имя_пакета:i386

После чего повторить комманду установки. Кроме того, можно начать “жизнь с чистого листа” и вручную установить пакеты, не пользуясь сгенерированным списком, к примеру:

# apt-get install firefox ratpoison feh scrot gimp emacs sudo nano eclipse alsa-utils alsa-tools alsa-oss flashplugin-nonfree

Заключение

В результате всех этих операций у меня получился вполне себе рабочий 64-битный дистрибутив Debian. Надеюсь, что вы добьётесь того же результата =) Если же нет – задавайте вопросы в комментария, постараюсь на них как можно скорее ответить.

С первого взгляда может показаться, что в правах доступа к файлам и каталогам в Linux сложно разобраться. Странные последовательности чисел от 0 до 7 пугают. Но, поверьте, достаточно один раз понять принцип организации прав доступа стандарта POSIX, чтобы успешно использовать его практически во всех дистрибутивах GNU/Linux, BSD и т.д.

Пользователи и группы

Стоит отметить, что в Linux присутствует поддержка ACL (Access Control List) для файлов и папок. Этот инструмент позволяет лучше справляться с темой статьи в некоторых ситуациях. Но подобный функционал не используется по умолчанию и необходим лишь в определённых случаях. Здесь же речь будет идти о стандарте POSIX, не затрагивая ACL.

У каждого файла в качестве владельцев есть один пользователь(user) и одна группа(group). С помощью команды ls -l можно узнать кому принадлежат файлы в текущей дериктории. Права доступа стандарта POSIX позволяют контролировать чтение (read), запись (write) и исполнение (execute) для конткретного пользователя, группы и всех остальных (other). Используя эти три параметра для трёх типов пользователей можно просто и понятно разграничить “кто и что может делать” в файловой системе.

Изображение №1 показывает, как может выглядеть вывод файла командой ls -l. Первое поле обычно содержит “d” или ““, оно указывает на папку (directory) или файл. Для файлов биты прав доступа имеют прямой смысл. Если установлен бит чтения (r), то можно получить его содержимое. Если установлен бит записи (w) — записать данные в этот файл. Бит исполнения (x) позволяет выполнить файл в коммандной оболочке. Он обычно устанавливается для скриптов и бинарных файлов программ.

Пример вывода команды ls -l

Изображение №1. Пример вывод команды ls -l

Для папок используется схожий принцип. Чтение (r) даёт возможность посмотреть какие файлы или папки находятся в директории. Запись (w) — создать или удалить файл или папку. Исполнение (x) несёт менее очевидный смысл. Если бит установлен, то можно перейти в эту директорию. То есть комманда cd для перехода внутрь папки будет работать только тогда, когда есть права доступа на исполнения для неё.

Как изменить права доступа в Linux

Хотя все дистрибутивы Linux имеют стандартизированную файловую структуру, в которой уже распределены права доступа, часто приходится вносить некоторые корректировки вручную. Для этого используется утилита chmod. Изменить права доступа в Linux можно следующим образом:

chmod u+rw,g-x,o+rwx example_file

Это достаточно сложный пример, который нуждается в пояснении. Буквы перед + и обозначают:

  • u — пользователя-владельца (user);
  • g — группу-владельца (group);
  • o — остальных пользователей (other).

Затем устанавливаются (+) или снимаются() соответвующие биты прав доступа. Не обязательно указывать все 3 типа пользователя, они объединяются следующим образом:

chmod ugo+x example_file

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

chmod ugo-rx example_file chmod -rx example_file chmod u-rx,g-rx,o-rx example_file

Несмотря на то, что примеры вида “ugo” выглядят достаточно просто и надёжно, иногда достаточно сложно записать строку из плюсов и минусов для получения нетривиального результата. Для сложных случаев лучше использовать числовое представление прав доступа в Linux. При первом знакомстве этот способ может сбить с толку, но он станет гораздо удобнее после освоения.

Числовое представление прав доступа для файлов и папок в Linux

На изображении №2 видно, какое числовое значение соответвует каждому биту прав доступа. С помощью простого сложения можно создать всё многообразие сочетаний этих трёх чисел. На изображении №3 представлено несколько примеров, как это происходит.

Числовое соответствие битов доступа в Linux

Изображение №2. Числовое соответствие битов доступа.

Пример вычисления прав доступа в Linux

Изображение №3. Пример вычисления.

Следовательно, следующие два варианта записи комманды chmod приведут к одному и тому же результату:

chmod ug+rw,ug-x,o+r,o-wx example_file chmod 662 example_file

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