Gyakorlati alapok II.

Celsius-Fahrenheit váltó (el)veszetten bonyolult validálással

 

A fejezet előzményei az előző, Celsius-Fahrenheit váltó "kivételes" validálással című fejezetben tanulmányozhatók. Benne megértettük a validálás fogalmát, valamint azt a lehetőséget, hogy kihasználva a rendszer hibadetektáló mechanizmusait, azokat önálló validálásra is felhasználhatjuk.

 

Ebben a fejezetben merő hiúságból és/vagy programozási gyakorlatként arra vállakozunk, hogy megírjunk egy olyan összetett validáló rutinegyüttest, amely teljes egészében képes kiváltani a konvertáló rutin (itt Double.parseDouble(bemenetiAdat)) validáló részeit. Tehát a megalkotott kód validáló részét bővítjük ki egy külön függvényegyüttes formájában, amely valamilyen szempont szerint hiteles vagy érvényes kimeneti adatot fog szolgáltatni a belső, hőmérsékletváltó függvényeknek.

 

Ismételjük meg ezzel kapcsolatos legfontosabb megállapításainkat:

 

www.informatika-programozas.hu - Ezt most meg kell tanulni!

 

Másképpen fogalmazva: a kódot "bolondbiztossá" kell tennünk, hogy ne kerüljön a rendszerbe rossz bemeneti adat, ami veszélyt jelenthet a rendszer futására vagy biztonságára nézve (véletlen vagy szándékosan rossz felhasználói adatmegadás, illetve szándékos hacker-támadások). Ugyanakkor belátjuk, hogy mindez még nem elégséges a rendszerek teljeskörű védelméhez.

 

A validálás elméletileg 2 szintű:

  1. ellenőriznünk kell a felhasználó által megadott, csakis kizárólag String típusú bemeneti adatot; ennek sokféle szempontja lehetséges, így kijelenthetjük, hogy ez mindenképpen az általánosabb megközelítés,

  2. szintén ellenőriznünk kell, hogy a konvertáló, azaz típusváltó függvények megfelelő formátumban kapják meg az adatokat; ez egyértelműen típusspecifikus, így konkrétabb, csakis a konvertáló függvények szempontjából lényeges.

  3. Természetesen elismerjük, hogy a fenti 2 validálás összetartozhat, sőt működhet 1 függvényen belül is (néha azonban nem).

A feladat előzményeinek érdekessége, hogy már adtam fel tanítványnak programozási gyakorlatként, bevallom kissé meggondolatlanul, hiszen az előzetesen nem volt kidolgozva (gondolván, hogy majd publikálom az elkészült implementációt). A lelkes tanítvány el is kezdte nagy lelkesedéssel, ám egyre nagyobb nehézségekkel szembesült. Végül lett valamiféle eredmény, ám annak minősége nem ütötte meg a kellő mértéket, így kénytelen voltam nekiállni saját változat elkészítésének.

 

Nézzük meg pontról pontra a feladat szempontjait!

 

Arra vállalkozunk, hogy kizárjuk a konvertáló rutin (Double.parseDouble(bemenetiAdat)) validáló részeit és hozzá csak valóban az utolsó pillanatban, a tényleges átváltáskor nyúlunk. Így tehát a fenti, 1. lépés szerint járunk el.

 

A követelményekben való motoszkáláskor fokozatosan döbbenhetünk rá azon tényekre, miszerint double, azaz lebegőpontos számok validálásakor:

  1. a vártnál sokkal többféle és sokszor nem is oly nyilvánvaló validálási szabály van,

  2. ráadásul ezen szabályokban egyértelmű hierarchiát is fel kell állítanunk, másként validálási és egyéb hibákba futhatunk. Ilyen módon az általánosból szűkítünk az egyre specifikusabb megközelítésekbe.

  3. Validálás során sokféle adattípussal dolgozhatunk, ám a legfontosabb közülük, a "végső kiértékelő", a boolean.

  4. Az alábbi ismertetésnél és rossz határértékeket is jelzünk, ezek megadása később a futtatás során lesz hasznos.

  5. Egyúttal érdemes az alfüggvényekbe hibaüzeneteket is tenni, másként összeillesztéskor rosszabb esetben halvány fogalmunk sem lesz, hogy melyik alfüggvény jelez hibát.

Az általam felállított validálási hierarchia a következő (ez egyúttal a végső implementáció is):

 

1.

Mivel sok és sokféle validáló függvényünk lesz, érdemes egy fő-, kiértékelő függvényt megalkotnunk (isValidMain(String input)). A többiek csak alfüggvények lesznek, amelyek előre jól meghatározott validáló szempontokat ellenőriznek le és a végeredményeiket ebbe a függvénybe vezetik be:

 

static boolean isValidMain(String input) {
    boolean [] isValidArray = new boolean[3];
    boolean isValid = true;
    boolean isPoint = false;
    boolean isNegSign = false;
    isValid = isValidLength(input);
    if(!isValid) return isValid;
    isValidArray = isValidCharacter(input);
    isValid = isValidArray[0];
    if(!isValid) return isValid;

    isPoint = isValidArray[1];
    if(isPoint) {
        isValid = isValidPoint(input);
        if(!isValid) return isValid;
        isValid = isValidPosNullAndPoint(input);
        if(!isValid) return isValid;
    }

    isNegSign = isValidArray[2];
    if(isNegSign) {
        isValid = isValidNegSignGoodPos(input);
        if(!isValid) return isValid;
        isValid = isValidOnlyOneNegSign(input);
        if(!isValid) return isValid;
        isValid = isValidNegNullAndPoint(input);
        if(!isValid) return isValid;
    }
    isValid = isValidNulls(input);
    if(!isValid) return isValid;
    return isValid;
}
 

2.

Először alsó és felső karakterhatárt kell szabni a bemeneti adatnak (isValidLength(String input)). Ezt én 0 és 5 értékekre állítottam.

static boolean isValidLength(String input) {
    boolean isValid = true;
    if(input.length() > 5 || input.length() == 0){
        isValid = false;
        System.out.println("Ures karaktermegadas vagy max. 5 digit!");
        return isValid;
    }
    return isValid;
}
 

3.

Ellenőriznünk kell, hogy a rendszerbe csakis jó karakterek kerüljenek (isValidCharacter(String input)). Az alfüggvény nemcsak ezt ellenőrzi, hanem azt is, hogy van-e a bemeneti adatban tizedespont (.) vagy negatívjel (-). Ezen célból nemcsak egy boolean változót, hanem egy 3 elemű, szintén boolean típusú tömböt ad vissza.

static boolean [] isValidCharacter(String input) {
    boolean [] isValid = new boolean[3];
    isValid[0] = true;
    isValid[1] = false;
    isValid[2] = false;
    for(int i = 0; i < input.length(); i++){
        char character = input.charAt(i);
        if(character != '0'
            && character != '1'
            && character != '2'
            && character != '3'
            && character != '4'
            && character != '5'
            && character != '6'
            && character != '7'
            && character != '8'
            && character != '9'
            && character != '.'
            && character != '-'
        ){
        isValid[0] = false;
        System.out.println("Ervenytelen karakter!");
        return isValid;
    }
    if(input.contains(".")) {
        isValid[1] = true;
    }
    if(input.contains("-")) {
        isValid[2] = true;
        }
    }
    return isValid;
}
 

4.

Ha a bemeneti adatban van tizedespont, akkor az nem lehet az első és az utolsó karakterpozícióban, illetve nem lehet belőle egynél több (isValidPoint(String input)).

static boolean isValidPoint(String input) {
    boolean isValid = true;
    boolean onlyOnePoint = false;
    char characterFirst = input.charAt(0);
    char characterLast = input.charAt(input.length()-1);
    if(characterFirst == '.' || characterLast == '.') {
        isValid = false;
        System.out.println("Tizedespont rossz pozicioban!");
        return isValid;
    }
    for(int i = 0; i < input.length(); i++){
        char character = input.charAt(i);
        if(character == '.' && onlyOnePoint == true) {
            isValid = false;
            System.out.println("Tobb tizedespont!");
            return isValid;
        }
        if(character == '.'){
            onlyOnePoint = true;
            }
        }
    return isValid;
}
 

5.

Ha a bemeneti adatban van negatívjel, akkor az csakis első karakterpozícióban lehet (isValidNegSignGoodPos(String input)).

static boolean isValidNegSignGoodPos(String input) {
    boolean isValid = true;
    char character = input.charAt(0);
    if(character != '-') {
        isValid = false;
        System.out.println("Negativjel rossz pozicioban!");
        return isValid;
    }
    return isValid;
}
 

6.

Ha a bemeneti adatban van negatívjel, akkor az csakis egyszer fordulhat elő (isValidOnlyOneNegSign(String input)).

static boolean isValidOnlyOneNegSign(String input) {
    boolean isValid = true;
    boolean onlyOneNegSign = false;
    for(int i = 0; i < input.length(); i++){
        char character = input.charAt(i);
        if(character == '-' && onlyOneNegSign == true) {
            isValid = false;
            System.out.println("Tobb negativjel!");
            return isValid;
        }
        if(character == '-'){
            onlyOneNegSign = true;
        }
    }
    return isValid;
}
 

7.

Ha csak "0." a bemeneti adat, a karakterfüzér érvénytelen (isValidPosNullAndPoint(String input)).

static boolean isValidPosNullAndPoint(String input) {
    boolean isValid = true;
    char character0 = input.charAt(0);
    char character1 = input.charAt(1);
    if(character0 == '0' && character1 != '.'){
        isValid = false;
        System.out.println("Rossz adatmegadás: 0.!");
        return isValid;
    }
    return isValid;
}
 

8.

Ha csak "-0." a bemeneti adat, a karakterfüzér érvénytelen (isValidNegNullAndPoint(String input)).

static boolean isValidNegNullAndPoint(String input) {
    boolean isValid = true;
    if(input.equals("-0")){
        isValid = false;
        System.out.println("Rossz adatmegadás: -0!");
        return isValid;
    }
    char character0 = input.charAt(0);
    char character1 = input.charAt(1);
    char character2 = input.charAt(2);
    if(character0 == '-' && character1 == '0' && character2 != '.'){
        isValid = false;
        System.out.println("Rossz adatmegadás: -0.!");
        return isValid;
    }
    return isValid;
}
 

9.

Bár a végső konvertáló függvény (Double.parseDouble(bemenetiAdat)) elfogad olyan bemeneti adatot is, hogy "00" és azt 0 értékké alakítja át, én személy szerint nem szeretem a gonoszkodás vagy éppen az ostobaság ilyen jellegű megnyilvánulásait, ezért az ilyen eseteket külön alfüggvénybe tettem (boolean isValidNulls(String input)).

static boolean isValidNulls(String input) {
    boolean isValid = true;
    if(input.equals("00")
        || input.equals("000")
        || input.equals("0000")
        || input.equals("00000")){
        isValid = false;
        System.out.println("Rossz adatmegadás: tul sok 0!");
        return isValid;
    }
    return isValid;
}

 

A validálási hierarchia elsődleges célja az "érvényesítési elvekben való rendrakás", főként azért, hogy az alapelvek rendszerezésével ne keletkezzen validálási redundancia, azaz olyan többletkód, amely voltaképpen ugyanazon szempontokat vizsgálja meg (értsd: több rutin ugyanazt ellenőrzi). Nem garantálom, hogy a fenti kódmegközelítés tökéletesen kielégíti ezen elvárást, azonban az alapelvek tekintetében mindenképpen hierarchikus és ez logikával felépített, valamint szakmai véleményem szerint a kód inkább legyen (kissé) redundáns, mint valamit egyáltalán nem ellenőrző.

 

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

 

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

 

 

 

 

 

 



import java.util.*;

public class Main {

static boolean isValidMain(String input) {
    boolean [] isValidArray = new boolean[3];
    boolean isValid = true;
    boolean isPoint = false;
    boolean isNegSign = false;
    isValid = isValidLength(input);
    if(!isValid) return isValid;
    isValidArray = isValidCharacter(input);
    isValid = isValidArray[0];
    if(!isValid) return isValid;

    isPoint = isValidArray[1];
    if(isPoint) {
        isValid = isValidPoint(input);
        if(!isValid) return isValid;
        isValid = isValidPosNullAndPoint(input);
        if(!isValid) return isValid;
    }

    isNegSign = isValidArray[2];
    if(isNegSign) {
        isValid = isValidNegSignGoodPos(input);
        if(!isValid) return isValid;
        isValid = isValidOnlyOneNegSign(input);
        if(!isValid) return isValid;
        isValid = isValidNegNullAndPoint(input);
        if(!isValid) return isValid;
    }
    isValid = isValidNulls(input);
    if(!isValid) return isValid;
    return isValid;
}

static boolean isValidLength(String input) {
    boolean isValid = true;
    if(input.length() > 5 || input.length() == 0){
        isValid = false;
        System.out.println("Ures karaktermegadas vagy max. 5 digit!");
        return isValid;
    }
    return isValid;
}

static boolean [] isValidCharacter(String input) {
    boolean [] isValid = new boolean[3];
    isValid[0] = true;
    isValid[1] = false;
    isValid[2] = false;
    for(int i = 0; i < input.length(); i++){
        char character = input.charAt(i);
        if(character != '0'
            && character != '1'
            && character != '2'
            && character != '3'
            && character != '4'
            && character != '5'
            && character != '6'
            && character != '7'
            && character != '8'
            && character != '9'
            && character != '.'
            && character != '-'
        ){
        isValid[0] = false;
        System.out.println("Ervenytelen karakter!");
        return isValid;
    }
    if(input.contains(".")) {
        isValid[1] = true;
    }
    if(input.contains("-")) {
        isValid[2] = true;
        }
    }
    return isValid;
}

static boolean isValidPoint(String input) {
    boolean isValid = true;
    boolean onlyOnePoint = false;
    char characterFirst = input.charAt(0);
    char characterLast = input.charAt(input.length()-1);
    if(characterFirst == '.' || characterLast == '.') {
        isValid = false;
        System.out.println("Tizedespont rossz pozicioban!");
        return isValid;
    }
    for(int i = 0; i < input.length(); i++){
        char character = input.charAt(i);
        if(character == '.' && onlyOnePoint == true) {
            isValid = false;
            System.out.println("Tobb tizedespont!");
            return isValid;
        }
        if(character == '.'){
            onlyOnePoint = true;
            }
        }
    return isValid;
}

static boolean isValidNegSignGoodPos(String input) {
    boolean isValid = true;
    char character = input.charAt(0);
    if(character != '-') {
        isValid = false;
        System.out.println("Negativjel rossz pozicioban!");
        return isValid;
    }
    return isValid;
}

static boolean isValidOnlyOneNegSign(String input) {
    boolean isValid = true;
    boolean onlyOneNegSign = false;
    for(int i = 0; i < input.length(); i++){
        char character = input.charAt(i);
        if(character == '-' && onlyOneNegSign == true) {
            isValid = false;
            System.out.println("Tobb negativjel!");
            return isValid;
        }
        if(character == '-'){
            onlyOneNegSign = true;
        }
    }
    return isValid;
}

static boolean isValidPosNullAndPoint(String input) {
    boolean isValid = true;
    char character0 = input.charAt(0);
    char character1 = input.charAt(1);
    if(character0 == '0' && character1 != '.'){
        isValid = false;
        System.out.println("Rossz adatmegadás: 0.!");
        return isValid;
    }
    return isValid;
}

static boolean isValidNegNullAndPoint(String input) {
    boolean isValid = true;
    if(input.equals("-0")){
        isValid = false;
        System.out.println("Rossz adatmegadás: -0!");
        return isValid;
    }
    char character0 = input.charAt(0);
    char character1 = input.charAt(1);
    char character2 = input.charAt(2);
    if(character0 == '-' && character1 == '0' && character2 != '.'){
        isValid = false;
        System.out.println("Rossz adatmegadás: -0.!");
        return isValid;
    }
    return isValid;
}

static boolean isValidNulls(String input) {
    boolean isValid = true;
    if(input.equals("00")
        || input.equals("000")
        || input.equals("0000")
        || input.equals("00000")){
        isValid = false;
        System.out.println("Rossz adatmegadás: tul sok 0!");
        return isValid;
    }
    return isValid;
}

static double szamBekeres(String valasztas){
    Scanner in = new Scanner (System.in);
    String input = new String();
    System.out.println ("\nKérem, hogy adja meg a fokot!");
    boolean isValid = true;
    do {
        input = in.nextLine();
        isValid = isValidMain(input);
        if(isValid == false) {
            System.out.println("\nKérem gépelje be újra!");
        }
    } while(!isValid);
    double fok = Double.parseDouble(input);
    if(valasztas.equals("1")){
        while (!isValidMain(input)){
            System.out.println("\nKérem gépelje be újra!");
            input = in.nextLine();
            fok = Double.parseDouble(input);
            }
        }
    else if(valasztas.equals("2")){
        while (!isValidMain(input)){
            System.out.println("\nKérem gépelje be újra!");
            input = in.nextLine();
            fok = Double.parseDouble(input);
            }
        }
    return fok;
}

static double celsiusFahrenheit(double fok){
    return (fok * 1.8) + 32;
}

static double fahrenheitCelsius(double fok){
    return (fok - 32) * 0.5555;
}

static void kiiras(double fok, double eredmeny, String valasztas){
    if(valasztas.equals("1")){
        System.out.println("\n" + fok + " C = " + eredmeny + " F");
    }
    else if(valasztas.equals("2")){
        System.out.println("\n" + fok + " F = " + eredmeny + " C");
    }
}

public static void main(String[] args) {
    Scanner in = new Scanner(System.in);
    double fok = 0;
    double eredmeny = 0;
    String[] menu = new String[] {"1 - Celsius -> Fahrenheit", "2 - Fahrenheit -> Celsius", "0 - Kilépés"};

    while(true) {
        System.out.println("\nKérem válasszon az alábbi lehetőségekből: ");
        for(int i = 0; i < menu.length; i++) {
        System.out.println(menu[i]);
    }

    String valasztas = in.nextLine();
    if("1".equals(valasztas)) {
        fok = szamBekeres(valasztas);
        eredmeny = celsiusFahrenheit(fok);
        kiiras(fok, eredmeny, valasztas);
    }

    if("2".equals(valasztas)) {
        fok = szamBekeres(valasztas);
        eredmeny = fahrenheitCelsius(fok);
        kiiras(fok, eredmeny, valasztas);
    }

    if("0".equals(valasztas)) {
        System.out.println("\nViszlát!");
        in.close();
        System.exit(0);
            }
        }
    }
}
 

Végeredmény:

Kérem válasszon az alábbi lehetőségekből:
1 - Celsius -> Fahrenheit
2 - Fahrenheit -> Celsius
0 - Kilépés
1

Kérem, hogy adja meg a fokot!
-0.123
Ures karaktermegadas vagy max. 5 digit!

Kérem gépelje be újra!

Ures karaktermegadas vagy max. 5 digit!

Kérem gépelje be újra!
23w
Ervenytelen karakter!

Kérem gépelje be újra!
.1
Tizedespont rossz pozicioban!

Kérem gépelje be újra!
12.
Tizedespont rossz pozicioban!

Kérem gépelje be újra!
12.2.2
Ures karaktermegadas vagy max. 5 digit!

Kérem gépelje be újra!
12..2
Tobb tizedespont!

Kérem gépelje be újra!
12-
Negativjel rossz pozicioban!

Kérem gépelje be újra!
1-2
Negativjel rossz pozicioban!

Kérem gépelje be újra!
-12-
Tobb negativjel!

Kérem gépelje be újra!
0.
Tizedespont rossz pozicioban!

Kérem gépelje be újra!
-0.
Tizedespont rossz pozicioban!

Kérem gépelje be újra!
000
Rossz adatmegadás: tul sok 0!

Kérem gépelje be újra!
00
Rossz adatmegadás: tul sok 0!

Kérem gépelje be újra!
12

12.0 C = 53.6 F

Kérem válasszon az alábbi lehetőségekből:
1 - Celsius -> Fahrenheit
2 - Fahrenheit -> Celsius
0 - Kilépés
0

Viszlát!

 

Ellenőrzési segédlet: