Към съдържанието

Тема 8 — Приложение Javanacci

Javanacci е синтетично приложение, което изгражда изчислителна машина за числата на Фибоначи, използвайки всеки значим елемент от курса: пакети, абстрактни класове, параметризирани интерфейси, изключения, глобален кеш чрез колекции, итератори и try-with-resources. Целта не е оптималност на алгоритъма — целта е да се провери разбирането на ООП концепциите чрез реална, свързана имплементация.

1. Архитектура

Йерархията се гради отдолу нагоре:

FibonacciRunnable<R> ← параметризиран интерфейс (return type)
AbstractFibonacciMachine ← абстрактен клас, имплементира Comparable<>
└── FibonacciMachine ← конкретна машина + AutoCloseable
Javanacci ← оркестратор, имплементира Iterable<Result>
└── Result ← вграден статичен клас за резултати
InvalidFibonacciPositionException ← собствено изключение

Позициите са от тип longLong.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) = 0
fib(1) = 1
fib(5) = 5
fib(10) = 55
fib(20) = 6765
fib(50) = 12586269025

fib(100):

fib(100) = 354224848179261915075

Числото fib(100) не се побира в long (максимум ~9.2×10¹⁸), но BigInteger го представя точно.

9. Елементи от курса, използвани в Javanacci

ЕлементКъдето е приложен
package / import / import staticbg.tu_varna.javanacci; BigInteger.ONE, BigInteger.ZERO
Абстрактен класAbstractFibonacciMachine с protected конструктор
Параметризиран интерфейсFibonacciRunnable<R>
GenericsFibonacciRunnable<BigInteger>, NavigableMap<Long, BigInteger>
Множество интерфейсиFibonacciMachine implements FibonacciRunnable<BigInteger>, AutoCloseable
ComparableAbstractFibonacciMachine.compareTo()
IterableJavanacci implements Iterable<Result>
Статичен вграден класJavanacci.Result
Глобален кешstatic NavigableMap<Long, BigInteger> CACHE + static { } инициализация
Собствено изключениеInvalidFibonacciPositionException extends Exception
try-catch с множествен catchcatch (InvalidFibonacciPositionException | IllegalArgumentException e)
try-with-resourcesrunSingle() — затваря машината автоматично
IteratorrunAll() — явно обхождане
for-eachDemo клас — чрез Iterable<Result>
Collections.sort()Сортиране на резултатите по позиция
@Overriderun(), 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].
  • Приложението демонстрира всеки ключов ООП елемент от курса в един свързан контекст.