Vererbung finale Methoden überschreiben

apple-itob

Mitglied
Hallo Forum,

dieses mal melde ich mich mit einer etwas konkreteren Frage als das letzte man und sogar mit einem Beispiel. Dafür gibt’s nur nen eher kryptischen Thread-Titel, weil ich mein Problem nicht auf eine Zeile zusammenstampfen kann. Hier also das Problem in seiner ganzen Breite:

Ich schreibe ein Programm, dass sich mit einem Interface der Frima Phidgets verbinden soll, um Messwerte zu erfassen und der Hersteller liefert eine entsprechende API (zum Download). So weit so gut. Das Interface hat nun eine Methode waitForAttachment(int timeOut), die versucht die Verbindung herzustellen und nach timeOut eine PhidgetException wirft, wenn kein Interface gefunden wurde. Damit mein Program auch ohne Interface (quasi im Demomodus) benutzt werden kann, soll die Exception abgefangen werden und dann ein neues Interface-Objekt erzeugt werden, dass die von mir benötigen Methoden simuliert. Hier das Beispiel dazu:

Java:
import com.phidgets.InterfaceKitPhidget;
import com.phidgets.PhidgetException;

public class IKTest {

   InterfaceKitPhidget ik;

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

   public void start() {
      try {
         ik = new InterfaceKitPhidget();
         ik.waitForAttachment(5000);

      } catch (PhidgetException ex) {
         System.out.println("Konnte Verbindung nicht herstellen");
         System.out.println("Demo-Modus starten ...");
         try {
            ik = new InterfaceKitPhidget() {
               @Override
               Integer getSensorValue(int sensorCount) throws PhidgetException {
                  return (int) (Math.random() * 1001);
               }
               // Weitere Methoden
            };
         } catch (Exception e) {}
      }
   }
}

ich glaube, mein Ansatz ist gar nicht so schlecht, das Problem ist nur, dass in der originalen Klasse alle Methoden final sind und ich sie deshalb nicht überschreiben kann. Meine bisherige Lösung war, eine eigene Klasse InterfaceKitPhidget zu schreiben, die alle Methoden hat und um den Demo-Modus zu bekommen den Import von com.phidgets.InterfaceKitPhidget auszukommentieren, so dass meine Klasse benutzt wird. Das ist aber natürlich herzlich wenig dynamisch und ich muss dann immer zwei Versionen kompilieren und verteilen.

Hat jemand eine Idee, wie man das machen könnte? Ich habe natürlich schon gesucht, aber es scheint keine Möglichkeit zu geben, eine finale Methode zu überschreiben, oder?

Vielen Dank!
Tobi
 
Zuletzt bearbeitet von einem Moderator:
Hat jemand eine Idee, wie man das machen könnte? Ich habe natürlich schon gesucht, aber es scheint keine Möglichkeit zu geben, eine finale Methode zu überschreiben, oder?
Finale Klasse und Methoden kann man nicht erweitern bzw. überschreiben. Wobei ich nicht ausschließen will, dass es da nicht Mittel und Wege gibt.

Allerdings ist mir nicht so ganz klar was Du da genau vorhast und inwiefern Dir das Überschreiben helfen würde, da Du die notwendigen Methoden ohnehin überschreiben müsstest. Evlt. kann man da ja irgendwo geschickt Delegation einsetzen.
 
Also was ich will, ist, dass das Programm benutzbar ist, auch wenn es keine realen Messwerte erfassen kann, weil kein physisches Interface vorhanden ist. In dem Fall soll das Interface (das ja durch das Objekt ik vom Typ InterfaceKitPhidget repräsentiert werden) durch ein Objekt vom gleichen Typ ersetzt werden, nur dass der Ersatztyp (meine anonyme Klasse) die originalen Methoden überschreibt, so dass diese einen Wert liefern können, auch wenn kein Interface angeschlossen ist.

Das Überschreiben hilft mir also insofern, dass die überschreibende, anonyme Klasse auch ohne physisches Interface „simulierte“ Messwerte liefert.
Unterm Strich brauche ich (für meinen derzeitigen Ansatz, wobei nicht gesagt ist, das das ein guter ist) zwei Klassen: Eine, die das physische Interface ansprechen und z.B. reale Messwerte auslesen kann (das ist die vorhandene com.phidget.PhidgetInterfaceKit) und eine zweite, die exakt die gleichen Methoden hat, aber eben nicht auf ein Interface zugreift sondern sich Messwerte ausdenkt. Wichtig ist nur, dass Objekte beider Klassen sich fehlerfrei der Variable
Code:
InterfaceKitPhidget ik
zuweisen lassen. Hier kommt ja vielleicht auch sowas wie Polymorphie ins Spiel, es ist nur problematisch, weil ich die Klasse des Herstellers eigentlich nicht verändern will.


Ich glaube, ich habe rausgefunden, was Delegation bedeutet, aber wie wende ich sie hier an? Könntest du ein Beispiel machen?
 
So … ich habe noch einen anderen Ansatz gefunden. Ich lege einfach eine neue Klassa an, die weiß ob es ein reales Interface gibt oder nicht und sich dann entsprechend Verhält. Ich brauche dann zwar in jeder Methode ein [c]if (realInterface)[/c], aber ansonsten scheint es so zu gehen …

Java:
import com.phidgets.InterfaceKitPhidget;
import com.phidgets.PhidgetException;

public class IKTest {

   FieldsInterface ik;

   public static void main(String[] args) {
      IKTest app = new IKTest();
      app.start();
   }

   public void start() {

      ik = new FieldsInterface();

      if (!ik.connectIK()) {
         System.out.println("Simmuliere Interface");
      }
      try {
         System.out.println("Messwert: "+ik.getSensorValue(0));
      } catch (PhidgetException ex) {
         System.out.println("Konnte messwert nicht erfassen");
      }

   }

   public class FieldsInterface {

      InterfaceKitPhidget ik;
      boolean realInterface = false;

      public boolean connectIK() {
         try {
            ik = new InterfaceKitPhidget();
            ik.openAny();
            ik.waitForAttachment(5000);
            realInterface = true;
         } catch (PhidgetException ex) {}
         return realInterface;
      }

      public int getSensorValue(int sensorCount) throws PhidgetException {
         if (realInterface) {
            return ik.getSensorValue(sensorCount);
         } else {
            return (int) (Math.random() * 1001);
         }
      }
   }
}

Was meint ihr? Ist das eine brauchbare und robuste Lösung?
 
wie schon angemerkt: Delegation.
z.B.:

Java:
public interface IKitPhidget
{
    public Integer getSensorValue(int sensorCount) throws DeineException
}

public class KitPhidgetReal implements IKitPhidget
{
    private InterfaceKitPhidget kitPhidget;
    public KitPhidgetReal()
    { 
       kitPhidget = new InterfaceKitPhidget();
    }
    public Integer getSensorValue(int sensorCount) throws DeineException 
    {
       try
       {
           return kitPhidget.getSensorValue(sensorCount);
       } 
       catch (PhidgetException ex) 
       {
           throw new DeineException();
       } 
    }
}

public class KitPhidgetDemo implements IKitPhidget
{
    public Integer getSensorValue(int sensorCount) throws DeineException 
    {
           if(deinExceptionAuslöserinDemoversion)
           {
             throw new DeineException();
           }
           return (int) (Math.random() * 1001);               
    }
}

IKitPhidget kitPhidget = null;

if(!real)kitPhidget = new KitPhidgetDemo();
if(real)kitPhidget = new KitPhidgetReal();

kitPhidget.getSensorValue(1);
 
naja die Delegation ist hier keine wirkliche Delegation (parabool).

Bei einer Delegation erweitert man das entsprechende Interface, bekommt ein solches als Instanzvariable und delegiert alle Methoden des interfaces weiter.

aber delegation an sich ist hier schon der richtige ansatz
 
Die Sache wäre einfacher, wenn [c]InterfaceKitPhidget[/c] wirklich ein Interface wäre, und nicht eine finale (!) Klasse. So kannst du dir Wrapper schreiben und auf die leute fluchen, die diese Lib verbockt haben.
 
Der Methodenaufruf wird doch bei IKitPhidget (in der Realimplementierung) an die Fremdbibliothek
delegiert ?

ok, dieser Schritt fehlt noch:
(aus: Delegation pattern - Wikipedia, the free encyclopedia)
Java:
class C implements I {
    // delegation
    I i = new A();
 
    public void f() { i.f(); }
    public void g() { i.g(); }
 
    // normal attributes
    public void toA() { i = new A(); }
    public void toB() { i = new B(); }
}
Also eine Instanz des Interfaces delegiert die Aufrufe an eine oder mehrere Implementierungen desselben weiter.
 
Zuletzt bearbeitet:
Hm … also das mit den Delegationen checke ich nicht, spricht was dagegen es so zu machen, wie ich in meinem zweiten Ansatz zeige. Das scheint nämlich gut zu funktionierten.

Geht es um Testing?
Wie wäre es mit einem Mockingframework? Mockito oder ähnliches...

Nein es geht darum, das Leute – im speziellen die Prüfer der Bacherlorarbeit, zu der das Programm gehört –, denen die Hardware fehlt das Programm trotzdem testen können, um zu sehen was es kann, nur eben mit fiktiven Werten.
 
Und was spricht dabei gegen ein Mocking-Framework? ???:L

Im wesentlichen, dass ich bisher nicht wusste, was das ist. Habe eben mal ein Bisschen gegoogelt und es scheint tatsächlich genau das zu sein, was ich brauche. Kann/darf/sollte man das den auch problemlos verwenden für öffentliche Programmversionen. Es ist ja nicht so, das ich das nur kurz testen will die Mocking-Klasse wäre dann fester Teil des Programms und würde immer dann benutzt, wenn eben kein physisches Interface gefunden werden kann …
 
Nein es geht darum, das Leute – im speziellen die Prüfer der Bacherlorarbeit, zu der das Programm gehört –, denen die Hardware fehlt das Programm trotzdem testen können, um zu sehen was es kann, nur eben mit fiktiven Werten.

Mocking kann immer eingesetzt werden, wenn fehlende Komponenten durch wohldefiniertes Verhalten ersetzt werden sollen. Automatisiertes Testing kann ein Anwendungsfall sein (sicher der häufigste). Aber auch Regressionstests in nicht produktiven Systemen, oder ein Anwendungsfall.

Ich kenne sogar ein Projekt, bei dem Mocking produktiv eingesetzt wird. Zwar nicht in Form von den typischen Mocking-Frameworks aber durch ein selbt implementiertes Pattern mit Hilfe von Bytecodemanipulation.
 
Ich denke, ich hätte mich mit den Entwicklern der Bibliothek in Verbindung gesetzt, und um ein ordentliches Interface gebeten. Mehr als "nein" sagen können sie nicht. Aber wenn niemand fragt, bleibt es in jedem Fall Kacke...
 
Ja, das könnte ich machen, aber dann müsste ich genau wissen, was ich brauche ;-) Wenn ich ein Interface hätte, müsste ich (und jeder andere) für jedes Projekt dieses neu implementieren. Und die Methoden, die z.B. Sensorwerte abfragen will ich ja gar nicht implementieren müssen, das ist ja der Witz bei dem Teil, dass ich es direkt mit Java benutzen kann, ohne mir über die Kommunikation mit der Hardware Gedanken machen zu müssen. Insofern wäre ein Interface also wenig hilfreich oder?
 
Ich denke, ich hätte mich mit den Entwicklern der Bibliothek in Verbindung gesetzt, und um ein ordentliches Interface gebeten. Mehr als "nein" sagen können sie nicht. Aber wenn niemand fragt, bleibt es in jedem Fall Kacke...

Hast ja Recht, aber wenn die halbwegs professionell organisiert sind, dauert das ein wenig. CR stellen, der wird dan priorisiert und irgendwann eingetütet. Das ist in der Regel kein kleiner Eingriff, muss gut getestet werden und kommt somit erst in die nächste größere Release. Dann hast du wieder das Problem mit der Migration, weil das neue Release auch wieder mit anderen Abhängigkeiten daher kommt usw.
 
Ich denke, ich hätte mich mit den Entwicklern der Bibliothek in Verbindung gesetzt, und um ein ordentliches Interface gebeten. Mehr als "nein" sagen können sie nicht. Aber wenn niemand fragt, bleibt es in jedem Fall Kacke...

Ja oder erst mal schauen ob es nicht eine spezielle Schnittstelle für Erweiterungen gibt. Die werden sich sicher etwas dabei gedacht haben die "Client-Schnittstelle" nicht erweiterbar zu gestalten i.S.v. Vererbung (dafür gibt es gute Gründe). Es würde mich demnach nicht wundern wenn es eine Art-"Service-Provider-Schnittstelle" gibt die man erweitern kann um sein geändertes Verhalten mit vorhandenen Möglichkeiten in die betroffenen Objekte einzubringen.
 
Ja oder erst mal schauen ob es nicht eine spezielle Schnittstelle für Erweiterungen gibt. Die werden sich sicher etwas dabei gedacht haben die "Client-Schnittstelle" nicht erweiterbar zu gestalten i.S.v. Vererbung (dafür gibt es gute Gründe). Es würde mich demnach nicht wundern wenn es eine Art-"Service-Provider-Schnittstelle" gibt die man erweitern kann um sein geändertes Verhalten mit vorhandenen Möglichkeiten in die betroffenen Objekte einzubringen.

Das könnte sein, allerdings ist die Doku eher mäßig, deswegen habe ich sowas bisher nicht gesehen … werde aber nochmal schauen …
 
Was bitte ist Mocking? Beschreibt es etwa das Verfahren, aus einer finalen Klasse die Methoden als Interface zu extrahieren, mit welchem man dann statt mit dem Original in der weiteren Programmentwicklung arbeitet? Irgendwelche Entwickler damit zu nerven, warum sie eine Klasse unveränderbar gemacht haben, ist meiner Ansicht nach fragwürdiger als mich dieser Extrahierungsmethode zu bedienen. Dazu braucht's nicht mal ein Framework, welches evtl. ebensolche fragwürdigen Dinge, wie eine Klasse unveränderbar zu machen tut.
Wäre, als wenn man fragt, warum Integer, Double oder gar String final sein müssen, gibt es da nicht etwas, mit dem man seine Zahlen oder Zeichenketten individuell (z.B. mutable Strings) gestalten kann (...aber sicher doch... Number und CharSequence. 😉)?
Kurz gesagt, wer kein solches Interface hat, weil's das Framework oder die Lib nicht bietet, extrahiert sich eines selber... (das geht unteranderem auch in Eclipse, nachdem man sich eine Delegationclass erstellt hat: Refactor->Extract Interface)
Java:
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.Locale;

public interface StringInterface extends CharSequence, Comparable<CharSequence> {
	boolean isEmpty();
	int codePointAt(int index);
	int codePointBefore(int index);
	int codePointCount(int beginIndex, int endIndex);
	int offsetByCodePoints(int index, int codePointOffset);
	void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin);
	void getBytes(int srcBegin, int srcEnd, byte[] dst, int dstBegin);
	byte[] getBytes(CharSequence charsetName) throws UnsupportedEncodingException;
	byte[] getBytes(Charset charset);
	byte[] getBytes();
	boolean contentEquals(CharSequence cs);
	boolean equalsIgnoreCase(CharSequence anotherCharSequence);
	int compareToIgnoreCase(CharSequence str);
	boolean regionMatches(int toffset, CharSequence other, int ooffset, int len);
	boolean regionMatches(boolean ignoreCase, int toffset, CharSequence other, int ooffset, int len);
	boolean startsWith(CharSequence CharSequence, int toffset);
	boolean startsWith(CharSequence CharSequence);
	boolean endsWith(CharSequence CharSequence);
	int indexOf(int ch);
	int indexOf(int ch, int fromIndex);
	int lastIndexOf(int ch);
	int lastIndexOf(int ch, int fromIndex);
	int indexOf(CharSequence str);
	int indexOf(CharSequence str, int fromIndex);
	int lastIndexOf(CharSequence str);
	int lastIndexOf(CharSequence str, int fromIndex);
	CharSequence substring(int beginIndex);
	CharSequence substring(int beginIndex, int endindex);
	CharSequence concat(CharSequence str);
	CharSequence replace(char oldChar, char newChar);
	boolean matches(CharSequence regex);
	boolean contains(CharSequence s);
	CharSequence replaceFirst(CharSequence regex, CharSequence replacement);
	CharSequence replaceAll(CharSequence regex, CharSequence replacement);
	CharSequence replace(CharSequence target, CharSequence replacement);
	CharSequence[] split(CharSequence regex, int limit);
	CharSequence[] split(CharSequence regex);
	CharSequence toLowerCase(Locale locale);
	CharSequence toLowerCase();
	CharSequence toUpperCase(Locale locale);
	CharSequence toUpperCase();
	CharSequence trim();
	char[] toCharArray();
	CharSequence intern();
}
Dazu benötigt man aber erst mal eine Delegationsklasse
Java:
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.Locale;

public class DelegatedString implements StringInterface {
	private final String text;

	public DelegatedString(CharSequence text) {
		this.text = text.toString();
	}

	@Override
	public int length() {
		return text.length();
	}

	@Override
	public boolean isEmpty() {
		return text.isEmpty();
	}

	@Override
	public char charAt(int index) {
		return text.charAt(index);
	}

	@Override
	public int codePointAt(int index) {
		return text.codePointAt(index);
	}

	@Override
	public int codePointBefore(int index) {
		return text.codePointBefore(index);
	}

	@Override
	public int codePointCount(int beginIndex, int endIndex) {
		return text.codePointCount(beginIndex, endIndex);
	}

	@Override
	public int offsetByCodePoints(int index, int codePointOffset) {
		return text.offsetByCodePoints(index, codePointOffset);
	}


	@Override
	public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) {
		text.getChars(srcBegin, srcEnd, dst, dstBegin);
	}


	@Override
	@SuppressWarnings("deprecation")
	public void getBytes(int srcBegin, int srcEnd, byte[] dst, int dstBegin) {
		text.getBytes(srcBegin, srcEnd, dst, dstBegin);
	}


	@Override
	public byte[] getBytes(CharSequence charsetName)
			throws UnsupportedEncodingException {
		return text.getBytes(charsetName.toString());
	}


	@Override
	public byte[] getBytes(Charset charset) {
		return text.getBytes(charset);
	}


	@Override
	public byte[] getBytes() {
		return text.getBytes();
	}


	@Override
	public boolean equals(Object anObject) {
		return text.equals(anObject);
	}

	@Override
	public boolean contentEquals(CharSequence cs) {
		return text.contentEquals(cs);
	}

	@Override
	public boolean equalsIgnoreCase(CharSequence anotherString) {
		return text.equalsIgnoreCase(anotherString.toString());
	}

	@Override
	public int compareTo(CharSequence anotherString) {
		return text.compareTo(anotherString.toString());
	}

	@Override
	public int compareToIgnoreCase(CharSequence str) {
		return text.compareToIgnoreCase(str.toString());
	}

	@Override
	public boolean regionMatches(int toffset, CharSequence other, int ooffset, int len) {
		return text.regionMatches(toffset, other.toString(), ooffset, len);
	}

	@Override
	public boolean regionMatches(boolean ignoreCase, int toffset, CharSequence other,
			int ooffset, int len) {
		return text.regionMatches(ignoreCase, toffset, other.toString(), ooffset, len);
	}

	@Override
	public boolean startsWith(CharSequence prefix, int toffset) {
		return text.startsWith(prefix.toString(), toffset);
	}

	@Override
	public boolean startsWith(CharSequence prefix) {
		return text.startsWith(prefix.toString());
	}

	@Override
	public boolean endsWith(CharSequence suffix) {
		return text.endsWith(suffix.toString());
	}

	@Override
	public int hashCode() {
		return text.hashCode();
	}

	@Override
	public int indexOf(int ch) {
		return text.indexOf(ch);
	}

	@Override
	public int indexOf(int ch, int fromIndex) {
		return text.indexOf(ch, fromIndex);
	}

	@Override
	public int lastIndexOf(int ch) {
		return text.lastIndexOf(ch);
	}

	@Override
	public int lastIndexOf(int ch, int fromIndex) {
		return text.lastIndexOf(ch, fromIndex);
	}

	@Override
	public int indexOf(CharSequence str) {
		return indexOf(str, 0);
	}

	@Override
	public int indexOf(CharSequence str, int fromIndex) {
		return text.indexOf(str.toString(), fromIndex);
	}

	@Override
	public int lastIndexOf(CharSequence str) {
		return lastIndexOf(str, 0);
	}

	@Override
	public int lastIndexOf(CharSequence str, int fromIndex) {
		return text.lastIndexOf(str.toString(), fromIndex);
	}

	@Override
	public String substring(int beginIndex) {
		return text.substring(beginIndex);
	}

	@Override
	public String substring(int beginIndex, int endIndex) {
		return text.substring(beginIndex, endIndex);
	}

	@Override
	public CharSequence subSequence(int beginIndex, int endIndex) {
		return text.subSequence(beginIndex, endIndex);
	}

	@Override
	public String concat(CharSequence str) {
		return text.concat(str.toString());
	}

	@Override
	public String replace(char oldChar, char newChar) {
		return text.replace(oldChar, newChar);
	}

	@Override
	public boolean matches(CharSequence regex) {
		return text.matches(regex.toString());
	}

	@Override
	public boolean contains(CharSequence s) {
		return text.contains(s);
	}

	@Override
	public String replaceFirst(CharSequence regex, CharSequence replacement) {
		return text.replaceFirst(regex.toString(), replacement.toString());
	}

	@Override
	public String replaceAll(CharSequence regex, CharSequence replacement) {
		return text.replaceAll(regex.toString(), replacement.toString());
	}

	@Override
	public String replace(CharSequence target, CharSequence replacement) {
		return text.replace(target, replacement);
	}

	@Override
	public String[] split(CharSequence regex, int limit) {
		return text.split(regex.toString(), limit);
	}

	@Override
	public String[] split(CharSequence regex) {
		return text.split(regex.toString());
	}

	@Override
	public String toLowerCase(Locale locale) {
		return text.toLowerCase(locale);
	}

	@Override
	public String toLowerCase() {
		return text.toLowerCase();
	}

	@Override
	public String toUpperCase(Locale locale) {
		return text.toUpperCase(locale);
	}

	@Override
	public String toUpperCase() {
		return text.toUpperCase();
	}

	@Override
	public String trim() {
		return text.trim();
	}

	@Override
	public String toString() {
		return text.toString();
	}

	@Override
	public char[] toCharArray() {
		return text.toCharArray();
	}

	@Override
	public String intern() {
		return text.intern();
	}
}
Mit deiner Klasse dürfte es ähnlich gehen.
Das jetzt nur ein Beispiel anhand einer wohl bekannten finalen Klasse. In der Praxis ist das für String (angesichts der Klasse CharSequence) jedoch nicht praktikabel.
 
Zuletzt bearbeitet von einem Moderator:
Was bitte ist Mocking?

Dein Delegate-Ansatz arbeitet auch mit einem Mock. Mock heißt ja nur, dass zur Laufzeit eine Implementierung durch eine andere ersetzt wird. Dass das mit Interfaces und Delegates in Java super geht, weiß man ja. Dein Ansatz ist simpel, hat aber zwei Nachteile:

1.) Ich muss das Mocken vorsehen und darf in meinem Code nur gegen das Interface arbeiten. Wenn ich das nicht von Anfang an mache, muss ich erst mal refaktorieren.

2.) Ich kann den Mock nicht auf die ursprüngliche Implementierung casten. Das Problem kann ich in eigenen Programmen durch 1.) umgehen, aber nicht, wenn ich eine Schnittstelle (evtl. von der verwendeten API selber) bedienen muss, die nicht von mir stammt.
Bei dir wäre das irgeneine Methode, die String als Parameter erwartet. Da bist du mit StringInterface nunmal aufgeschmissen...

Ein Mocking-Framework bietet dir da ganz andere Möglichkeiten.
 
@FArt: Das ist klar, mir jedenfalls. Hatte ja auch erwähnt, dass man dann nur mit dem Interface statt mit dem Original arbeitet. Aber selbst ein Mocking-Framework ermöglicht es mir wohl kaum, eine Methode einer Klasse die String erwartet mit meinem StringInterface aufzurufen. Diese Klasse müsste vorher ebenso gemokt werden. Ich denke mal, das übernimmt dann ein Mocking-Framework.
 
Nabend,

also ich habe mal versucht diese Mocking-Geschichte umzusetzen. Aber ich blicks einfach nicht und ich finde im Netzt auch kein Beispiel, dass ich verstehen oder gar übertragen könnte. Kann mir jemand mal an einem Beispiel zeigen, wie das geht oder mir eine Seite nennen, die das erklärt, bitt?

Gute Nacht!
Tobi
 

Neue Themen


Zurück
Oben