Тема 8 — Приложение Javanacci
Javanacci е синтетично приложение, което изгражда изчислителна машина за числата на Фибоначи, използвайки всеки значим елемент от курса: пакети, абстрактни класове, параметризирани интерфейси, изключения, глобален кеш чрез колекции, итератори и try-with-resources. Целта не е оптималност на алгоритъма — целта е да се провери разбирането на ООП концепциите чрез реална, свързана имплементация.
1. Архитектура
Йерархията се гради отдолу нагоре:
FibonacciRunnable<R> ← параметризиран интерфейс (return type)AbstractFibonacciMachine ← абстрактен клас, имплементира Comparable<> └── FibonacciMachine ← конкретна машина + AutoCloseableJavanacci ← оркестратор, имплементира Iterable<Result> └── Result ← вграден статичен клас за резултатиInvalidFibonacciPositionException ← собствено изключениеПозициите са от тип long — Long.MAX_VALUE = 9 223 372 036 854 775 807 е горната граница. Резултатите са BigInteger, тъй като числата на Фибоначи нарастват експоненциално и бързо надхвърлят диапазона на long.
Глобален кеш — статично поле CACHE от тип NavigableMap<Long, BigInteger> — позволява на всяка нова машина да продължи изчислението от последната известна позиция вместо от нулата.
2. Пакет и импорти
package bg.tu_varna.javanacci;
import java.math.BigInteger;import java.util.*;import static java.math.BigInteger.ZERO;import static java.math.BigInteger.ONE;Статичният import (import static) позволява директно ползване на константите ONE и ZERO без префикс BigInteger., намалявайки шума в кода.
3. Собствено изключение InvalidFibonacciPositionException
Отрицателна позиция е семантична грешка — трябва да се сигнализира с checked exception, принуждаващо извикващия да го обработи:
class InvalidFibonacciPositionException extends Exception { private final long invalidPosition;
public InvalidFibonacciPositionException(long position) { super("Позиция " + position + " е отрицателна; допустими стойности: [0, " + Long.MAX_VALUE + "]"); this.invalidPosition = position; }
public long getInvalidPosition() { return invalidPosition; }}Наследява Exception (не RuntimeException), затова е checked — компилаторът изисква обработка навсякъде, където FibonacciMachine се конструира.
4. Параметризиран интерфейс FibonacciRunnable<R>
Интерфейсът декларира един метод run(), чийто тип на връщаната стойност е шаблонен параметър R:
interface FibonacciRunnable<R> { R run();}Конкретните имплементации определят R. В FibonacciMachine R = BigInteger. Ако в бъдеще се добави машина, връщаща String или Double, тя имплементира FibonacciRunnable<String> без промяна на интерфейса.
5. Абстрактен клас AbstractFibonacciMachine
Базовият клас пази position и декларира абстрактния run(). Имплементира Comparable<AbstractFibonacciMachine>, за да може резултатите да се сортират по позиция:
abstract class AbstractFibonacciMachine implements Comparable<AbstractFibonacciMachine> { protected final long position;
protected AbstractFibonacciMachine(long position) { this.position = position; }
public long getPosition() { return position; }
public abstract BigInteger run();
@Override public int compareTo(AbstractFibonacciMachine other) { return Long.compare(this.position, other.position); }}Конструкторът е protected — предотвратява директно инстанциране на абстрактния клас отвън, но позволява super(position) в наследниците.
6. Конкретна машина FibonacciMachine
FibonacciMachine е конкретната имплементация. Тя разширява AbstractFibonacciMachine и имплементира два интерфейса: FibonacciRunnable<BigInteger> и AutoCloseable.
6.1 Глобален кеш и статична инициализация
Кешът е static — споделен между всички инстанции. NavigableMap (имплементиран от TreeMap) го съхранява сортиран по ключ, осигурявайки ефективен метод floorKey(n) — „намери най-голям ключ ≤ n”:
class FibonacciMachine extends AbstractFibonacciMachine implements FibonacciRunnable<BigInteger>, AutoCloseable {
public static final long MAX_POSITION = Long.MAX_VALUE; private static final NavigableMap<Long, BigInteger> CACHE = new TreeMap<>();
static { CACHE.put(0L, ZERO); CACHE.put(1L, ONE); }Статичният инициализиращ блок (static { }) се изпълнява веднъж при зареждане на класа и зарежда базовите стойности fib(0) = 0, fib(1) = 1.
6.2 Конструктор и валидация
public FibonacciMachine(long position) throws InvalidFibonacciPositionException { super(position); if (position < 0) { throw new InvalidFibonacciPositionException(position); } }Типът long покрива всички неотрицателни цели числа до Long.MAX_VALUE. Единственото невалидно состояние е отрицателна стойност — при нея се хвърля InvalidFibonacciPositionException.
6.3 Метод run() — обхождане напред с кеш
Алгоритъмът работи в нарастваща посока: винаги изгражда отдолу нагоре към поисканата позиция, като използва вече изчисленото за начална точка:
@Override public BigInteger run() { if (CACHE.containsKey(position)) { return CACHE.get(position); }
// Намери най-голямата позиция в кеша, по-малка или равна на целта. Long startPos = CACHE.floorKey(position); BigInteger prev = CACHE.get(startPos - 1); BigInteger curr = CACHE.get(startPos);
for (long i = startPos + 1; i <= position; i++) { BigInteger next = prev.add(curr); CACHE.put(i, next); prev = curr; curr = next; }
return CACHE.get(position); }При поредица от заявки (например fib(5), fib(10), fib(50)) всяка следваща стартира от последното изчислено, а не от нулата. Кешът нараства прогресивно и повторни заявки за вече изчислени позиции се обслужват мигновено.
6.4 AutoCloseable, equals, hashCode, toString
@Override public void close() { System.out.println("FibonacciMachine[" + position + "] приключи изчислението."); }
@Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof FibonacciMachine)) { return false; } FibonacciMachine other = (FibonacciMachine) obj; return this.position == other.position; }
@Override public int hashCode() { return Long.hashCode(position); }
@Override public String toString() { return "FibonacciMachine[" + position + "]"; }}close() реализира AutoCloseable — машините могат да се използват в try-with-resources. equals и hashCode са наредени: две машини с еднаква позиция са равни. toString позволява директно отпечатване на обекта при дебъгване.
7. Оркестраторът Javanacci
Javanacci координира набор от машини. Имплементира Iterable<Result>, позволявайки директен for-each цикъл върху него.
7.1 Вграден статичен клас Result
public class Javanacci implements Iterable<Javanacci.Result> {
public static class Result { private final long position; private final BigInteger value;
Result(long position, BigInteger value) { this.position = position; this.value = value; }
public long getPosition() { return position; }
public BigInteger getValue() { return value; }
@Override public String toString() { return "fib(" + position + ") = " + value; } }Result е статичен вграден клас (static class): не се нуждае от инстанция на Javanacci, за да съществува. Полетата са final — обектите са неизменяеми след създаването.
7.2 Конструктори
private final List<FibonacciMachine> machines;
// Конструктор 1: масив от явни позиции public Javanacci(long[] positions) throws InvalidFibonacciPositionException { machines = new ArrayList<>(); for (long pos : positions) { machines.add(new FibonacciMachine(pos)); } }
// Конструктор 2: брой машини с произволно генерирани позиции public Javanacci(int count) throws InvalidFibonacciPositionException { machines = new ArrayList<>(); Random rng = new Random(); for (int i = 0; i < count; i++) { long pos = (long) (rng.nextDouble() * 100); machines.add(new FibonacciMachine(pos)); } }Двата конструктора са претоварени — компилаторът избира верния по типа на аргумента (long[] срещу int). ArrayList съхранява машините в List, запазвайки реда на добавяне. Random генерира произволна позиция в практичен диапазон 0–99.
7.3 Методи runAll() и runSingle()
public List<Result> runAll() { List<Result> results = new ArrayList<>(); Iterator<FibonacciMachine> it = machines.iterator(); while (it.hasNext()) { FibonacciMachine machine = it.next(); results.add(new Result(machine.getPosition(), machine.run())); } Collections.sort(results, Comparator.comparingLong(Result::getPosition)); return results; }
public static BigInteger runSingle(long position) throws InvalidFibonacciPositionException { try (FibonacciMachine machine = new FibonacciMachine(position)) { return machine.run(); } }
@Override public Iterator<Javanacci.Result> iterator() { return runAll().iterator(); }}runAll() обхожда машините с явен Iterator, накрая сортира резултатите по позиция чрез Collections.sort() с Comparator.comparingLong.
runSingle() е статичен помощен метод, демонстриращ try-with-resources: машината се затваря автоматично след изпълнението на блока, независимо от изключения.
iterator() реализира Iterable<Result> — делегира към списъка с резултати, за да поддържа for-each.
8. Пример за употреба
public class JavanacciDemo { public static void main(String[] args) {
// 1. Явни позиции — масив try { Javanacci jv = new Javanacci(new long[]{0, 1, 5, 10, 20, 50}); for (Javanacci.Result r : jv) { System.out.println(r); } } catch (InvalidFibonacciPositionException | IllegalArgumentException e) { System.err.println("Грешка при инициализация: " + e.getMessage()); }
// 2. Произволни позиции — брой try { Javanacci jv = new Javanacci(5); for (Javanacci.Result r : jv) { System.out.println(r); } } catch (InvalidFibonacciPositionException e) { System.err.println("Грешка: " + e.getMessage()); }
// 3. Единична позиция с try-with-resources try { BigInteger fib100 = Javanacci.runSingle(100); System.out.println("fib(100) = " + fib100); } catch (InvalidFibonacciPositionException e) { System.err.println("Невалидна позиция: " + e.getInvalidPosition()); } }}Примерен изход (вариант 1):
fib(0) = 0fib(1) = 1fib(5) = 5fib(10) = 55fib(20) = 6765fib(50) = 12586269025fib(100):
fib(100) = 354224848179261915075Числото fib(100) не се побира в long (максимум ~9.2×10¹⁸), но BigInteger го представя точно.
9. Елементи от курса, използвани в Javanacci
| Елемент | Където е приложен |
|---|---|
package / import / import static | bg.tu_varna.javanacci; BigInteger.ONE, BigInteger.ZERO |
| Абстрактен клас | AbstractFibonacciMachine с protected конструктор |
| Параметризиран интерфейс | FibonacciRunnable<R> |
| Generics | FibonacciRunnable<BigInteger>, NavigableMap<Long, BigInteger> |
| Множество интерфейси | FibonacciMachine implements FibonacciRunnable<BigInteger>, AutoCloseable |
Comparable | AbstractFibonacciMachine.compareTo() |
Iterable | Javanacci implements Iterable<Result> |
| Статичен вграден клас | Javanacci.Result |
| Глобален кеш | static NavigableMap<Long, BigInteger> CACHE + static { } инициализация |
| Собствено изключение | InvalidFibonacciPositionException extends Exception |
try-catch с множествен catch | catch (InvalidFibonacciPositionException | IllegalArgumentException e) |
try-with-resources | runSingle() — затваря машината автоматично |
Iterator | runAll() — явно обхождане |
for-each | Demo клас — чрез Iterable<Result> |
Collections.sort() | Сортиране на резултатите по позиция |
@Override | run(), compareTo(), close(), equals(), hashCode(), toString(), iterator() |
| Претоварени конструктори | Javanacci(long[]) и Javanacci(int) |
final полета | position, Result.position, Result.value |
equals() / hashCode() | FibonacciMachine — стандартен договор от Object |
Резюме
FibonacciMachineе изчислителна единица, която стартира от последната позиция в кеша и напредва нагоре, запазвайки всеки резултат.- Глобалният
CACHE(static NavigableMap) е споделен между всички инстанции — последователните заявки натрупват кеша и ускоряват изчислението. Javanacciприема масив от явни позиции или брой за произволно генериране; и двата варианта са поддържани чрез претоварване на конструктора.BigIntegerпозволява точно представяне на числа на Фибоначи при всяка позиция в диапазона[0, Long.MAX_VALUE].- Приложението демонстрира всеки ключов ООП елемент от курса в един свързан контекст.