// Beispiel fuer objekt-orientierten Programmierstil.
//
// Objekte bestehen aus OPERATIONEN PLUS DATEN, die
// eine untrennbare Einheit bilden.


// Statt explizit sichtbarer - d.h. manipulierbarer -
// Arrays a und b deklarieren wir jetzt eine KLASSE,
// deren Instanzen zur indizierten Aufbewahrung von Daten
// (maximal 10 Elemente) dienen.
//
// Die KLASSE ist die Verallgemeinerung einer
// DATENTYPDEKLARATION: Eine Klasse spezifiziert
// die Menge aller Objekte, welche die dort beschriebenen
// ATTRIBUTE (FELDELEMENTE, ZUSTANDSVARIABLEN) besitzen und 
// dem Nutzer den angegebenen 
// Satz von METHODEN (OPERATIONEN) anbieten.
// 
// Methoden dienen dazu, 
//   1. den Objektzustand zu manipulieren
//      (solche Methoden werden oft PUT-METHODEN oder 
//       GENERATOREN genannt)  
//   2. Den Objektzustand abzufragen
//      (GET-METHODEN oder OBSERVER-METHODEN)

class MyContainer {

    // Mit "static" deklarierte Attribute heissen
    // KLASSENATTRIBUTE. Sie werden zur Uebersetzungszeit allokiert
    // und existieren genau EINMAL PRO KLASSE. Wenn eine
    // Objektinstanz ein Klassenattribut aendert, wird diese
    // Aenderung fuer alle Objekte sichtbar.
    //
    // Als Beispiel fuehren wir einen Zaehler cnt ein, der
    // die Anzahl der Objektinstanzen enthält: Bei
    // jedem Konstruktor-Aufruf wird die Objektanzahl inkrementiert.
    //
    // Das Schluesselwort "private" kennzeichnet, dass die
    // zugehoerigen Attribute oder auch Methoden nur von Methoden
    // derselben Klasse aus manipuliert/gelesen/aufgerufen werden koennen. 

    private static int cnt;

    // Das Attribut, welches jedes Objekt
    // vom Klassentyp MyContainer besitzt:

    private int z[];

    // Das Objektattribut z wird erst im Speicher
    // allokiert, sobald das zugehörige Objekt
    // instantiiert und dabei der KONSTRUKTOR aufgerufen wird:

    MyContainer() { z = new int[10]; cnt++; }

    // eine private Methode, die nicht von ausserhalb aufgerufen werden
    // kann:

    private void outputErrorMessage(int atIndex) {
	System.out.println("Falscher Index " + atIndex + ", Du Dummbär!");
	System.out.println("Nur 0 <= atIndex < " + z.length + " ist erlaubt.");
    }

    // Statische Methoden können aufgerufen werden, OHNE dass
    // eine Objektinstanz existiert. Statische Methoden
    // duerfen daher nur auf Klassenattribute zugreifen. Andernfalls 
    // erhaelt man Fehlermeldungen der Art
    // "Can't make a static reference to nonstatic variable 
    //  ... in class MyContainer".
    //
    // Als Beispiel machen wir eine Zugriffsfunktion
    // auf den Objektinstanz-Zaehler cnt:

    static int getNumInstances() { return cnt; }

    void put(int value, int atIndex) { 
	if ( 0 <= atIndex && atIndex < z.length) {
         z[atIndex] = value; 
	}
	else {
	    outputErrorMessage(atIndex);
	}
    }


    int get(int atIndex) {
        // Verwendung eines unzulaessigen Index loest Exception aus
	return z[atIndex];
    }

    int search(int theNumber) {

	for (int i = 0; i < z.length; i++) {
            if ( z[i] == theNumber ) return i;
	}

	return -1;

    }

    // Beim Kopieren wird als Quelle im Gegensatz zum
    // modularen, nicht objekt-orientierten
    // Beispiel nicht der Array, sondern das
    // OBJEKT, welches den Quellarray verwaltet, uebergeben.
    // In Java duerfen Parameter auch vom Typ KLASSE sein.
    // Parameter fromContainer erhält beim Methodenaufruf die
    // Referenz auf das als Aktualparameter angegebene Objekt.

    void copy(MyContainer fromContainer) {
	for (int iCnt = 0; iCnt < z.length; iCnt++) 
               z[iCnt] = fromContainer.get(iCnt);
    }

}

class Oo {

    // Die main()-methode MUSS das Schluesselwort "public"
    // tragen, da sonst die Java Virtuelle Maschine den
    // Einsprungspunkt fuer das Programm nicht findet/nicht
    // verwenden kann.
    //
    // Das Schluesselwort public ist NICHT erforderlich, um 
    // Methoden oder Attribute IN DERSELBEN JAVA-QUELLDATEI
    // oder IM SELBEN PACKAGE anzusprechen/aufzurufen.
    // Ist diese Ansprache nicht erwünscht, muessen die Attribute
    // oder Methoden mit Chluesselwort private geschuetzt werden.

       public static void main(String args[]) {

        // Statische Klassenmethoden werden mit
        // <klassenname>.<methodenname>(<parameter>) aufgerufen.
        // Beispiel hier: MyContainer.getNumInstances().
        // Nur die statischen Methoden der eigenen Klasse (soweit
        // vorhanden) koennen ohne vorangestellten <klassenname>.
        // verwendet werden.

	System.out.println("Anzahl Objektinstanzen VOR Instanzbildung: " 
                             + MyContainer.getNumInstances());

	int index;
        MyContainer o1 = new MyContainer();
        MyContainer o2 = new MyContainer();

	System.out.println("Anzahl Objektinstanzen NACH Instanzbildung: " 
                             + MyContainer.getNumInstances());


	// Werte werden durch Auruf der
        // zugehoerigen Methoden in das Objekt geschrieben.
        // Die im Objekt verwendete Datenstruktur bleibt
        // fuer das verwendende Programm (also hier main())
        // unsichtbar.

        for (int iCnt=0; iCnt < 10; iCnt++) o1.put(10*iCnt,iCnt);

        // Algorithmen sind als METHODEN implementiert, die auf
        // den internen Zustandsdaten des Objektes operieren.
        // Damit muss die Datenstruktur nicht mehr
        // als Funktionsparameter angegeben werden.
        //
        // Die Methode wird auf dem zugehörigen Objekt aufgerufen.
        // Sprechweise: Das Objekt erhält die NACHRICHT (oder 
        // den BEFEHL), eine bestimmte Operation "auf sich selbst"
        // auszufuehren. Mit der Nachricht können Eingabeparameter
        // angegeben werden, welche die Befehlsausführung beeinflussen.

        o2.copy(o1);


	for (int iCnt=0; iCnt < 10; iCnt++)
	    System.out.println("b["+iCnt+"] = "+o2.get(iCnt));

        o2.put(7,5);

        // Der versuchte Zugriff 
        // o2.z[5] = 7;
        // wuerde zu einem Uebsersetzungsfehler 
        // "Variable z in class MyContainer not accessible from class Oo"
        // führen, da z[]
        // private deklariert worden ist.

        // falscher Index 2555 fuehrt zu einer Fehlermeldung, die
        // mit Hilfe einer privaten Methode ausgegeben wird.

        o2.put(7,2555);

        index = o2.search(7);
        if ( 0 < index) 
            System.out.println("b[] enthält 7 am Index " + index); 
        else
            System.out.println("b[] enthält keine 7");

        index = o1.search(7);
        if ( 0 < index) 
            System.out.println("a[] enthält 7 am Index " + index);
        else
            System.out.println("a[] enthält keine 7");

    }

}
