Gyakorlati alapok III.

Az osztály alapszintű működése

 

Ebben a fejezetben a valójában egyszerű körszámításon keresztül fogjuk megismerni az osztályok alapszintű, egymáshoz hangolt működését.

 

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

 

Ebből a szempontból a jelen fejezet rendkívül tanulságos és kiemelten hasznos lesz, bár jelzem, hogy a megértés kedvéért alapvető, mostanában már kötelezően elvárt objektumorientált módszerek kifejtését még nem tartalmazza, ezek:

Először ismételjük meg a már publikált példakódot (A kör kerülete és területe egyszerű adatbekéréssel című fejezetből):

 

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

 

 

 

 

 

 

 

 

import java.math.*;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
    final double PI = 3.1415926535;
    Scanner in = new Scanner(System.in);
    System.out.println("Kérem, hogy adja meg a sugárhosszt (r)!");
    String sugar = in.nextLine();
    double r = Double.parseDouble(sugar);

    double kerulet = 2 * r * PI;
    double terulet = Math.pow(r, 2) * PI;
    System.out.println(r + " egységnyi sugarú kör kerülete: " + kerulet);
    System.out.println(r + " egységnyi sugarú kör területe: " + terulet);
    }
}

 

Végeredmény:

Kérem, hogy adja meg a sugárhosszt (r)!
5.8
A 5.8 egységnyi sugarú kör kerülete: 36.4424747806
A 5.8 egységnyi sugarú kör területe: 105.68317686374

 

Ismétlésképpen nézzük át a fenti kód legfontosabb tulajdonságait:

Nyilvánvalóan a kód egyszerűsége miatt a fenti tulajdonságok nem okoznak számunkra hátrányokat. Sőt a kódegyszerűség most számunkra határozottan előnyös, hiszen emiatt könnyebben fogjuk átlátni a módosított kódokat. Ám az objektumorientált programozás magasabb szintű szempontok figyelembevételét követeli meg.

 

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

 

Itt tehát már felmerülnek előzetes tervezési szempontok is (Osztály-, és projekttervezési minták című fejezet), amely tervezési irányok különbözők is lehetnek, azaz minden programozó, projektvezető más és más szempontokat emelhet ki. Ha azonban a tervezési irány rossz, akkor komplett projektek futhatnak zátonyra, amely jelentős pénzügyi és egyéb veszteségeket képesek okozni.

 

Kezdjük az osztály immáron klasszikussá vált meghatározásával: az osztály, mint legkisebb önállóan működő egység a valamilyen logikai szempont alapján összetartozó adatokat és a rajta elvégezhető műveleteket jelenti.

 

Legelőször ezt az alapelvet implementáljuk a fenti körszámító algoritmuson. Ennek során a következőket változtatjuk meg:

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

 

 

 

 

 

 

 

 

import java.math.*;
import java.util.Scanner;

public class Korszamitas{
    final static double PI = 3.1415926535;
    static double sugar;

static double sugarBekeres() {
    Scanner in = new Scanner (System.in);
    String szamString = new String();
    boolean rosszAdat = false;
    System.out.println ("Kérem, hogy adja meg a körsugarat (r > 0)!");
    do{
        szamString = in.nextLine();
        rosszAdat = ellenorzesSzamString(szamString);
    }while (rosszAdat == true);
    rosszAdat = false;
    sugar = Double.parseDouble(szamString);
    return sugar;
}

static boolean ellenorzesSzamString(String szamString){
    boolean rosszAdat = false;
    char karakter = szamString.charAt(0);
    if(karakter == '0'){
        rosszAdat = true;
    }
    for(int i = 0; i < szamString.length(); i++){
        karakter = szamString.charAt(i);
        if(karakter != '0'
            && karakter != '1'
            && karakter != '2'
            && karakter != '3'
            && karakter != '4'
            && karakter != '5'
            && karakter != '6'
            && karakter != '7'
            && karakter != '8'
            && karakter != '9'){
            rosszAdat = true;
            break;
        }
    }
    if(rosszAdat == true){
        System.out.println("Kérem, hogy csak pozitív egész számot adjon meg!");
    }
    return rosszAdat;
}

static double korKeruletSzamitas(double sugar, double PI){
    return 2 * sugar * PI;
}

static double korTeruletSzamitas(double sugar, double PI){
    return Math.pow(sugar, 2) * PI;
}

static void megjelenites(){
    sugar = sugarBekeres();
    System.out.println(sugar + " egységnyi sugarú kör kerülete: "

                                         + korKeruletSzamitas(sugar, PI));
    System.out.println(sugar + " egységnyi sugarú kör területe: "

                                         + korTeruletSzamitas(sugar, PI));
    }


public static void main(String[] args) {
    megjelenites();
    }
}

 

Végeredmény:

Kérem, hogy adja meg a körsugarat (r > 0)!
0
Kérem, hogy csak pozitív egész számot adjon meg!
d
Kérem, hogy csak pozitív egész számot adjon meg!
12
12.0 egységnyi sugarú kör kerülete: 75.398223684
12.0 egységnyi sugarú kör területe: 452.389342104

 

Ha az adattagokat és metódusokat a static jelzővel illetjük, minden azonnal futtatható lesz, másként viszont fordítási hibaüzenet kapunk. Az alábbi kódban a megjelenites() metódustól vettük el a static módosítást:

 

 

A megoldás: a tanultak szerint példányosítanunk kell az egész Korszamitas osztályt.

 

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

 

Ezután a keletkezett Korszamitas objektum referenciájával (itt: korszamitas), egy ponttal (.) és a metódusnévvel hivatkozhatunk az elérendő metódusra: korszamitas.megjelenites().

 

public static void main(String[] args) {
    Korszamitas korszamitas = new Korszamitas();
    korszamitas.megjelenites();
    }
}

 

Vegyünk egy másik osztályrendezési lehetőséget!

A funkcionális kódszegmentálás érdekében külön-külön osztályokba rendezzük az adatokat, a műveleteket, a megjelenítést és a végső, futtató Main osztályt. Azaz voltaképpen logikai ízekre szedjük a kódot. Ekkor a projekten belül következő különálló osztályokat fogjuk létrehozni:

Az AdatOsztaly{} 2 db bemeneti adatot tartalmaz:

public class AdatOsztaly {
    final double PI = 3.1415926535;
    double sugar;
}

 

Emlékezzünk vissza: a PI értéke konstans, nem változtatható, ezért final módosítót használunk deklarálásakor (Ami a változót (és a többieket) változatlanná teszi: a final című fejezet).

 

Ebben a pillanatban azonban felmerül az a probléma, hogy a körsugár deklarációja önmagában nem életképes, hanem mint egy különálló metódus bemeneti adatát a felhasználótól várjuk. A körsugár tehát egyrészt bemeneti adat, ám egy további, ellenőrző metódussal kell elkérnünk a felhasználótól. Most akkor hová tegyük a sugarBekeres() metódust? A rendezési elvek figyelembevételével természetesen a MuveletOsztaly{} osztályba kell tennünk.

 

A MuveletOsztaly{} így 4 db metódust tartalmaz:

Ahhoz viszont, hogy elérjük az Adatosztaly{} adattagjait, ebben az osztályban példányosítani kell: AdatOsztaly adat = new AdatOsztaly();.

 

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

 

Ezután a keletkezett Adatosztaly objektum referenciájával (itt: adat), egy ponttal (.) és a változónévvel hivatkozhatunk az elérendő változóra: adat.sugar. Példányosítás nélkül a MuveletOsztaly{} nem látja a változót, ezért fordítási hibát kapunk.

 

Mind a 4 metódus függvény, azaz van visszatérési értékük (return). Ilyen módon működésük, funkcionalitásuk jobban behatárolható, specifikálható.

 

import java.math.*;
import java.util.Scanner;

public class MuveletOsztaly {
    AdatOsztaly adat = new AdatOsztaly();

public double sugarBekeres() {
    Scanner in = new Scanner (System.in);
    String szamString = new String();
    boolean rosszAdat = false;
    System.out.println ("Kérem, hogy adja meg a körsugarat (r > 0)!");
    do{
        szamString = in.nextLine();
        rosszAdat = ellenorzesSzamString(szamString);
    }while (rosszAdat == true);
    rosszAdat = false;
    adat.sugar = Double.parseDouble(szamString);
    return adat.sugar;
}

static boolean ellenorzesSzamString(String szamString){
    boolean rosszAdat = false;
    char karakter = szamString.charAt(0);
    if(karakter == '0'){
        rosszAdat = true;
    }
    for(int i = 0; i < szamString.length(); i++){
        karakter = szamString.charAt(i);
            if(karakter != '0'
                && karakter != '1'
                && karakter != '2'
                && karakter != '3'
                && karakter != '4'
                && karakter != '5'
                && karakter != '6'
                && karakter != '7'
                && karakter != '8'
                && karakter != '9'){
                rosszAdat = true;
                break;
            }
        }
if(rosszAdat == true){
    System.out.println("Kérem, hogy csak pozitív egész számot adjon meg!");
}
    return rosszAdat;
}

public double korKeruletSzamitas(double sugar, double PI){
    return 2 * sugar * PI;
}

public double korTeruletSzamitas(double sugar, double PI){
    return Math.pow(sugar, 2) * PI;
    }
}
 

A MegjelenitesOsztaly{} 1 db metódust tartalmaz:

Egyetlen feladata a kimeneti adatok megjelenítése. Az osztályban példányosítani kell az összes kapcsolódó osztályt...

 

MuveletOsztaly muvelet = new MuveletOsztaly();
AdatOsztaly adat = new AdatOsztaly();

 

...mert el kell érnünk adattagjait és/vagy metódusait. A korKeruletSzamitas() és korTeruletSzamitas() metódusokhoz szükségünk van a sugarBekeres() függvényre, ezért hívását szintén itt tesszük meg (adat.sugar = muvelet.sugarBekeres();).

 

public class MegjelenitesOsztaly {
MuveletOsztaly muvelet = new MuveletOsztaly();
AdatOsztaly adat = new AdatOsztaly();

public void megjelenites (){
    adat.sugar = muvelet.sugarBekeres();
    System.out.println(adat.sugar + " egységnyi sugarú kör kerülete: "

                             + muvelet.korKeruletSzamitas(adat.sugar, adat.PI));
    System.out.println(adat.sugar + " egységnyi sugarú kör területe: "

                             + muvelet.korTeruletSzamitas(adat.sugar, adat.PI));
    }
}

 

A Main{} osztály main() főprogramja csak a MegjelenitesOsztaly{} osztályt példányosítja és hívja meg: 

 

public class Main {
    public static void main(String[] args) {
    MegjelenitesOsztaly megjelenites = new MegjelenitesOsztaly();
        megjelenites.megjelenites();
        }
    }

 

Végeredmény:

Kérem, hogy adja meg a körsugarat (r > 0)!
0
Kérem, hogy csak pozitív egész számot adjon meg!
d
Kérem, hogy csak pozitív egész számot adjon meg!
10
10.0 egységnyi sugarú kör kerülete: 62.83185307
10.0 egységnyi sugarú kör területe: 314.15926535

 

Ilyen módon az önálló Java-projekt neve Korszamitas és 4 db, a projektben szintén külön-külön létrehozandó osztályból fog állni:

 

www.informatika-programozas.hu

www.informatika-programozas.hu

Pillanatképek a Korszamitas projektről

 

Ellenőrizzük le a megoldást teljes egészében:

 

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

 

 

 

 

 

 

 

 

Main.java

 

public class Main {
    public static void main(String[] args) {
    MegjelenitesOsztaly megjelenites = new MegjelenitesOsztaly();
        megjelenites.megjelenites();
        }
    }
 

MegjelenitesOsztaly.java

 

public class MegjelenitesOsztaly {
MuveletOsztaly muvelet = new MuveletOsztaly();
AdatOsztaly adat = new AdatOsztaly();

public void megjelenites (){
    adat.sugar = muvelet.sugarBekeres();
    System.out.println(adat.sugar + " egységnyi sugarú kör kerülete: "

                             + muvelet.korKeruletSzamitas(adat.sugar, adat.PI));
    System.out.println(adat.sugar + " egységnyi sugarú kör területe: "

                             + muvelet.korTeruletSzamitas(adat.sugar, adat.PI));
    }
}

 

MuveletOsztaly.java

 

import java.math.*;
import java.util.Scanner;

public class MuveletOsztaly {
    AdatOsztaly adat = new AdatOsztaly();

public double sugarBekeres() {
    Scanner in = new Scanner (System.in);
    String szamString = new String();
    boolean rosszAdat = false;
    System.out.println ("Kérem, hogy adja meg a körsugarat (r > 0)!");
    do{
        szamString = in.nextLine();
        rosszAdat = ellenorzesSzamString(szamString);
    }while (rosszAdat == true);
    rosszAdat = false;
    adat.sugar = Double.parseDouble(szamString);
    return adat.sugar;
}

static boolean ellenorzesSzamString(String szamString){
    boolean rosszAdat = false;
    char karakter = szamString.charAt(0);
    if(karakter == '0'){
        rosszAdat = true;
    }
    for(int i = 0; i < szamString.length(); i++){
        karakter = szamString.charAt(i);
            if(karakter != '0'
                && karakter != '1'
                && karakter != '2'
                && karakter != '3'
                && karakter != '4'
                && karakter != '5'
                && karakter != '6'
                && karakter != '7'
                && karakter != '8'
                && karakter != '9'){
                rosszAdat = true;
                break;
            }
        }
if(rosszAdat == true){
    System.out.println("Kérem, hogy csak pozitív egész számot adjon meg!");
}
    return rosszAdat;
}

public double korKeruletSzamitas(double sugar, double PI){
    return 2 * sugar * PI;
}

public double korTeruletSzamitas(double sugar, double PI){
    return Math.pow(sugar, 2) * PI;
    }
}

 

AdatOsztaly.java

 

public class AdatOsztaly {
    final double PI = 3.1415926535;
    double sugar;
}

 

Végeredmény:

Kérem, hogy adja meg a körsugarat (r > 0)!
0
Kérem, hogy csak pozitív egész számot adjon meg!
d
Kérem, hogy csak pozitív egész számot adjon meg!
10
10.0 egységnyi sugarú kör kerülete: 62.83185307
10.0 egységnyi sugarú kör területe: 314.15926535

 

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

 

Itt jegyezzük meg, hogy a fentebb konstruált Adatosztaly csak elvi, ebben az esetben oktatási megközelítés.

Már megtanultuk: fontos szempont az objektumorientált elvek alkalmazása során, hogy az osztály logikailag nézve adatok ÉS metódusok zárt egységét alkotja, ezért adatokat külön osztályba rendezni bár megtehetjük, de inkább kövessük a "klasszikusnak" mondható objektumorientált alapelveket, tehát egymástól őket lehetőség szerint ne különítsük el.

Az már egészen más kérdés, ha az adatok az MVC-modell alapján logikailag és fizikailag más helyre kerülnek.