Gyakorlati alapok III.

Objektumorientált számológépváz interfésszel

 

Az alábbi kódegyüttes bár konkrét, futtatható implementáció és jónéhány dolgot képes kiszámolni, ám egy igazi számológép alkalmazásnak csupán működőképes, “zörgő” csontváza. Lényegében a kód erőteljesen lecsupaszított, hogy meglássuk benne az objektumorientált komponensek egymás közti kapcsolatát, működését, főleg az interfész konkrét alkalmazását. Ilyen minőségében azonban bármikor továbbgondolható és bővíthető. Csak 1 példa: adatbekérés és validálás (bemeneti adatok ellenőrzése) helyett bemeneti számként statikusan 2 változó van deklarálva…

double a = 10;
double b = 2;

...amelyet a tapasztaltak elegáns könnyedséggel bővíthetnek ki egy adatbekérő osztályban.

Először gondolkodjunk el azon, hogy elvi szinten mit is csinál egy számológép? Tevékenységét kissé leegyszerűsítve: számokon matematikai műveleteket végez. Ez sokféle lehet, ám ha csak 1 szintet is feljebb lépünk (ez a már említett elvi, “filozófiai” szint), akkor már csupán 1 tevékenységet láthatunk meg és ez maga a matematikai művelet. Első pillantásra ezzel az elvonatkoztatással (absztrakcióval) nem érünk semmit, azonban ez tévedés. Pontosan ezen az absztrakciós szinten lebegnek az interfészek és várnak arra, hogy végre egy, vagy több osztályba “inkarnálódjanak”. Azok számára, akik a fentieket nem értették meg, nézzük meg mindezt egy másfajta megközelítésben!

Mi a közös a következőkben:

Nyilvánvalóan mindegyikük matematikai művelet. Ha magát a műveletet, mint legáltalánosabb tulajdonságot sikerül egy működőképes kódszerkezetben megfogalmaznunk (pontosan ez a kódszerkezet az interfész), akkor utána viszonylag kevés átalakítással, de tonnaszámra gyárthatjuk belőle a (kissé továbbmódosított) műveleteket. Olyan ez, mint a tervrajz: csak egyszer kell okosan és jól megterveznünk, hogy belőle számos további építmény születhessen. Azonban az interfésznek van egy másik, szintén legalább ennyire hasznos tulajdonsága is: mivel csak elvi kidolgozás, a konkrét, futtatható implementációt ráhagyhatjuk másvalakire. Sőt, néha erre egyenesen szükségünk is van. Gondoljunk csak az interfész eredeti jelentésére: csatlakozási vagy határfelület. Mi történik akkor, ha megírt kódunk már készen van és csatlakozna egy másik, de még meg sem írt kódhoz? Az objektumorientált elveket követve ez nem is történhet másképpen, mint interfészek deklarálásával, majd későbbi kidolgozásával.

Az alábbi Java-kódban a gondolati-tervezési hierarchia csúcsán az Operation (művelet) nevű interfész áll:

 

public interface Operation {
    double execute(double a, double b);
    String getName();
}

 

2 db kidolgozandó metódusa van, ez az execute() és a getName(). Az interfész implementálásával (implements Operation) a 2 metódus specializálható, átalakítható, azaz műveletenként testre szabható. Nézzük meg például szorzás esetében:

class Addition implements Operation{

@Override
public double execute(double a, double b) {
    return a + b;
}

@Override
public String getName() {
    return "Osszeadas";
    }
}

Észrevehetjük, hogy ez az elv van implementálva az összes műveleti osztályban, persze az adott műveletre jellemző specifikummal.

Az összes osztályt pedig a Main osztály, mint irányító, kontroller fogja egybe, az osztályok ott vannak példányosítva, illetve az alapszintű számítások és eredménykiírás is ott találhatók. Ezek ugyan lehetnének külön osztályban is, amellyel tovább követtük volna az objektumorientált elveket, ám a modellezéshez ennyi elég.

További érdekesség, hogy a példányosított osztályokat belegyömöszöltük egy Operation típusú tömbbe…

Operation [] tomb = {addition, subtraction, multiplication, division, exponentation, logarithm};

...amellyel nagyon elegánsan tudtuk kilistázni az eredményeket:

for (int i = 0; i < tomb.length; i++) {
    double result = tomb[i].execute(a, b);
    String operationName = tomb[i].getName();
    System.out.println(operationName + " eredmenye: " + result);
}
 

Nézzük meg a futtatható Java-kódot:

 

www.informatika-programozas.hu - Futtatható Java-kód!

 

 

 

 

 

 

 


Main.java

 

public class Main {
public static void main(String[] args) {
    Addition addition = new Addition();
    Subtraction subtraction = new Subtraction();
    Multiplication multiplication = new Multiplication();
    Division division = new Division();
    Exponentation exponentation = new Exponentation();
    Logarithm logarithm = new Logarithm();

    double a = 10;
    double b = 2;

    Operation [] tomb = {addition, subtraction, multiplication, division, exponentation, logarithm};

    for (int i = 0; i < tomb.length; i++) {
        double result = tomb[i].execute(a, b);
        String operationName = tomb[i].getName();
        System.out.println(operationName + " eredmenye: " + result);
        }
    }
}

 

Operation.java


public interface Operation {
    double execute(double a, double b);
    String getName();
}

 

Addition.java

class Addition implements Operation{

@Override
public double execute(double a, double b) {
    return a + b;
}

@Override
public String getName() {
    return "Osszeadas";
    }
}
 

Division.java


public class Division implements Operation{

@Override
public double execute(double a, double b) {
    return a / b;
}

@Override
public String getName() {
    return "Osztas";
    }
}

 

Exponentation.java

public class Exponentation implements Operation{

@Override
public double execute(double a, double b) {
    return Math.pow(a, b);
}

@Override
public String getName() {
    return "Hatvanyozas";
    }
}

 

Logarithm.java

public class Logarithm implements Operation{

@Override
public double execute(double a, double b) {
    return Math.log(a);
}

@Override
public String getName() {
    return "Logaritmus";
    }
}

 

Multiplication.java

public class Multiplication implements Operation{

@Override
public double execute(double a, double b) {
    return a * b;
}

@Override
public String getName() {
    return "Szorzas";
    }
}

 

Subtraction.java

public class Subtraction implements Operation{

@Override
public double execute(double a, double b) {
    return a - b;
}

@Override
public String getName() {
    return "Kivonas";
    }
}

 

Végeredmény:

Összeadás eredménye: 12.0
Kivonás eredmenye: 8.0
Szorzás eredmenye: 20.0
Osztás eredmenye: 5.0
Hatványozás eredmenye: 100.0
Logaritmus eredmenye: 2.302585092994046