Menu Close

Programmieren, Algorithmen und Datenstrukturen 2 (Praktikum 3)

Aufgabe 1

a) Kehren Sie zu den Ergebnissen aus der Aufgabe 2 des zweiten Praktikums zurück, und implementieren Sie die Shape und Frame Klassen(-hierarchie) zur Darstellung geometrischer Figuren als Punkteraster auf der Konsole zu Ende. Ihre Implementierung des Bresenham-Algoritmus zur Ermittlung der Rasterpunkte einer geraden Linie wird zur Methode void Line::draw( Frame* ) und die Frame Klassenhierarchie wird weiter verwendet. Erweitern Sie Ihren Testplan und Ihren main() Treiber entsprechend, überprüfen Sie alles systematisch und wiederholbar. Dokumentieren Sie die Testplanung und die Ergebnisse.

b) Erweitern Sie die Klassenstruktur um class Triangle : public shape, damit entsprechend zu den bisherigen Figurtypen nun auch Dreiecke mit draw() und move() behandelt werden können. Ermöglichen Sie Dreiecke durch Übergabe der Koordinaten der drei Eckpunkte. Konzentrieren Sie sich beim Test auf die Methode, die prüft, ob die drei übergebenen Punkte ein gültiges Dreieck darstellen. Testen Sie wieder geplant, systematisch und ausführlich, dokumentieren Sie wieder alle Tests und Ergebnisse.

c) Erweitern Sie den Typ Triangle so, dass man Triangle Objekte auch erzeugen kann, indem man einem weiteren Konstruktor die horizontale und vertikale Koordinate eines Bezugspunktes („links-unten“) sowie drei int Werte übergibt, die die Seitenlängen des Dreiecks darstellen: Triangle( int hor, int ver, int len1, int len2, int len3 ) //… Konzentrieren Sie sich beim Test auf die Methode, die prüft, ob die drei übergebenen Längen ein gültiges Dreieck darstellen. Erweitern Sie den Testplan, dokumentieren Sie die Ergebnisse.

Lösung

/*
 * File:   main.cpp
 * Author: maximilian.krieg
 *
 * Created on 20. April 2012, 12:41
 */

#include "headers.h"
using namespace std;

int main() {

    Frame frame1;
    Frame* pFrame1 = &frame1;

    // Standardkonstruktor Linie
    Line test0();
    dump_all_shapes(pFrame1);

    // Ungültige Linie
    Line test1(5,5,XMAX+1,YMAX+1);
    dump_all_shapes(pFrame1);

    // Linie auf den Frame schieben
    test1.move(-2,-2);
    dump_all_shapes(pFrame1);

    return 0;
}

    //        // Test #0: Start
    //        //<editor-fold defaultstate="collapsed" desc="Testfall #0: Ungültige Linien zeichnen, Std. Konstruktor + Korrektur">

//    //Standard-Konstruktor aufrufen
//    Line test0();
//
//    //Ungültige Linie zeichnen
//    Line test1(-1, 0, 5, -7);
//
//    // Gültige Linie zeichnen und ins Aus schieben
//    Line test2(5,5,XMAX-5,YMAX-5);
//    dump_all_shapes(pFrame1);
//    test2.set(-1,-5,XMAX+1,YMAX+1);
//    dump_all_shapes(pFrame1);
//
//    // Linie neu setzen
//    pFrame1->frame_reset();
//    test2.set(5,5,XMAX-5,5);
//    dump_all_shapes(pFrame1);

    //        //</editor-fold>
    //        // Test #0: Ende

    //        // Test #1: Start
    //        //<editor-fold defaultstate="collapsed" desc="Testfall #1: Stern zeichnen + Fehler: Linien ins Aus verschieben">
    //
    //        // Stern-Linien setzen
    //        Line line1(10,10 , 10, 20); // Norden
    //        Line line2(10,10 , 20, 10); // Osten
    //        Line line3(10,10 , 10, 0); // Süden
    //        Line line4(10,10 , 0,10); // Westen
    //        Line line5(10,10 , 0, 0); // Südwesten
    //        Line line6(10,10 , 20, 0); // Südosten
    //        Line line7(10,10 , 20, 20); // Nordosten
    //        Line line8(10,10 , 0, 20); // Nordwesten
    //        dump_all_shapes(pFrame1);
    //
    //        // Fehler: Stern-Linien ins Aus schieben
    //        line1.move((-XMAX) - 1, 0);
    //        line2.move((XMAX) + 1, 0);
    //        line3.move(0, YMAX + 1);
    //        line4.move(0, (-YMAX) - 1);
    //        dump_all_shapes(pFrame1);
    //
    //
    //        //</editor-fold>
    //        // Test #1: Ende

    //        // Test #2: Start
    //        //<editor-fold defaultstate="collapsed" desc="Testfall #2: Haus zeichnen + Linien erfolgreich verschieben">
    //        // Haus-Koordinaten
    //
    //        // Haus-Linien
    //        Line line9(15, 30, 25, 20);
    //        Line line10(25, 20, 5, 20);
    //        Line line11(5, 20, 15, 30);
    //        Line line12(5, 20, 5, 0);
    //        Line line13(5, 0, 25, 0);
    //        Line line14(25, 0, 25, 20);
    //        Line line15(5, 0, 25, 20);
    //        Line line16(5, 20, 25, 0);
    //        dump_all_shapes(pFrame1);
    //
    //        // Haus #2
    //        pFrame1->frame_reset();
    //        line9.move(25, 0);
    //        line10.move(25, 0);
    //        line11.move(25, 0);
    //        line12.move(25, 0);
    //        line13.move(25, 0);
    //        line14.move(25, 0);
    //        line15.move(25, 0);
    //        line16.move(25, 0);
    //        dump_all_shapes(pFrame1);
    //        //</editor-fold>
    //        // Test #2: Ende

    //        // Test #3: Start
    //        //<editor-fold defaultstate="collapsed" desc="Testfall #3: 2 Dreiecke zeichnen">
    //        //Dreieck #1: Rechtwinklig
    //        Triangle triangle1(5,40, 10, 40, 5,55);
    //
    //        //Dreieck #2: Gleichschenklig
    //        Triangle triangle2(30,40,50,40,40,60);
    //        dump_all_shapes(pFrame1);
    //        //</editor-fold>
    //        // Test #3: Ende

    //        // Test #4: Start
    //        //<editor-fold defaultstate="collapsed" desc="Testfall #4: Dreieck zeichnen + verschieben + Fehler: Ins Aus verschieben">
    //
    //        //Dreieck #3: Beliebig
    //        Triangle triangle3(9,63,42,40,32,65);
    //        dump_all_shapes(pFrame1);
    //
    //        // Dreieck #3: Verschieben
    //        pFrame1->frame_reset();
    //        triangle3.move(-5, -37);
    //        dump_all_shapes(pFrame1);
    //
    //        // Fehler: Dreieck ins Aus schieben
    //        triangle3.move(200, 0);
    //        triangle3.move(-200, 0);
    //        triangle3.move(0, +200);
    //        triangle3.move(0, -200);
    //        dump_all_shapes(pFrame1);
    //
    //        //</editor-fold>
    //        // Test #4: Ende

    //        // Test #5: Start
    //        //<editor-fold defaultstate="collapsed" desc="Testfall #5: Dreieck im Aus zeichnen und auf den Frame schieben">
    //
    //        // Dreieck #4 im Aus zeichnen
    //        Triangle triangle4(15,15,30, YMAX+1,(-5),30);
    //        dump_all_shapes(pFrame1);
    //
    //        // Dreieck 4 auf den Frame schieben
    //        triangle4.move(+10, -10);
    //        dump_all_shapes(pFrame1);
    //        //</editor-fold>
    //        // Test #5: Ende

    //            // Test #6: Start
    //    //<editor-fold defaultstate="collapsed" desc="Testfall #6: Ungültiges Dreieck zeichnen + Korrektur">
    //        // Ungültiges Dreieck #5 zeichnen (2 Punkte identisch)
    //        Triangle triangle5(15,15, 15,15,5,5);
    //        dump_all_shapes(pFrame1);
    //
    //        // Ungültiges Dreieck #5 zeichnen: Punkte neu setzen (3 Punkte bilden eine Gerade)
    //        triangle5.set(15,15,10,10,20,20);
    //        dump_all_shapes(pFrame1);
    //
    //        // Dreieck #5: Die Punkte korrekt setzen
    //        triangle5.set(20,7,41,10,23,21);
    //        dump_all_shapes(pFrame1);
    //
    //    //</editor-fold>
    //        // Test #6: Ende

/*
 * File:   headers.h
 * Author: maximilian.krieg
 *
 * Created on 27. April 2012, 10:34
 */

#ifndef HEADERS_H
#define	HEADERS_H

// Präprozessordirektiven aller Header
#include <iostream>
#include "Frame.h"
#include "Shape.h"
#include "Line.h"
#include "Triangle.h"

// Deklaration aller Klassen
class Coordinate;
class Frame;
class Shape;
class Line;
class Triangle;

#endif	/* HEADERS_H */

/*
 * File:   Frame.h
 * Author: maximilian.krieg
 *
 * Created on 20. April 2012, 12:42
 */

#ifndef FRAME_H
#define	FRAME_H

const int XMAX = 55; // Zeichen pro Frame-Zeile (55)
const int YMAX = 70; // Zeilen pro Frame   (34)

class Frame {
public:

    enum color {
	on = ' ', off = 254
    };

    Frame();
    void put_point(int, int); // Punkt im Frame speichern
    void frame_reset(); // Frame "leeren"
    void frame_dump() const; // Frame auf die Konsole ausgeben
    bool on_frame(int, int) const; // "Clipping"

private:
    char frame[XMAX][YMAX];
};
#endif	/* FRAME_H */
/*
 * File:   Frame.cpp
 * Author: maximilian.krieg
 *
 * Created on 20. April 2012, 12:42
 */

#include "headers.h"
using std::cout;
using std::endl;
using std::string;

// Standardkonstruktor für Frames

Frame::Frame() {
    frame_reset();
}

// Punkt im Frame speichern

void Frame::put_point(int x, int y) {
    if (on_frame(x, y)) {
	frame[x][y] = on;
    }
}

// Frame "leeren"

void Frame::frame_reset() {
    for (int x = 0; x < XMAX; x++) {
	for (int y = 0; y < YMAX; y++) {
	    frame[x][y] = off;
	}
    }
}

// Frame auf die Konsole ausgeben

void Frame::frame_dump() const {
    for (int y = YMAX - 1; y >= 0; --y) {
	for (int x = 0; x < XMAX; x++) {
	    cout << frame[x][y];
	}
	cout << endl;
    }
    cout << endl;
}

// "Clipping"

bool Frame::on_frame(int x, int y) const {
    if (x >= 0 && x < XMAX && y >= 0 && y < YMAX) {
	return true;
    } else {
	return false;
    }
}

/*
 * File:   Shape.h
 * Author: maximilian.krieg
 *
 * Created on 27. April 2012, 10:09
 */
#include "headers.h"

#ifndef SHAPE_H
#define	SHAPE_H

class Shape {
public:

    Shape();
    Shape* next; // Zeiger auf die nächste Figur
    static Shape* list; // Zeiger auf die Liste aller erzeugten Figuren

    virtual void draw(Frame* ptarget) = 0; // rein virtuelle Methode
    virtual void move(int hor, int ver) = 0; // rein virtuelle Methode

};

// gibt die abgeleiteten Shape Objekte auf Frame* aus
void dump_all_shapes(Frame*);

#endif	/* SHAPE_H */

/*
 * File:   Shape.cpp
 * Author: maximilian.krieg
 *
 * Created on 27. April 2012, 10:09
 */

#include "headers.h"

Shape* Shape::list = 0; // initialisiert den static Member mit dem Nullzeiger

Shape::Shape() :
next(list) {
    list = this;
}

void dump_all_shapes(Frame* pFrame) // gibt die abgeleiteten Shape Objekte auf Frame* aus
{
    for (Shape* s = Shape::list; s; s = s->next) {
	s->draw(pFrame);
    }
    pFrame->frame_dump();
}
/*
 * File:   Line.h
 * Author: maximilian.krieg
 *
 * Created on 27. April 2012, 10:19
 */
#include "headers.h"
#ifndef LINE_H
#define	LINE_H

class Line : public Shape {
public:
    // Konstruktoren
    Line(); // Standardkonstruktor
    Line(int, int, int, int); // Konstruktor mit 2 Koordinaten (A[x/y];B[x/y])

    // Modifizieren der Linie ( neu setzen oder verschieben )
    void set(int, int, int, int); // Koordinaten neu setzen (A[x/y];B[x/y])
    void move(int, int); // rein virtuelles shape::move() überschreiben

    // Print-Methode
    void draw(Frame*); // rein virtuelles shape::draw()überschreiben

    // Check-Methoden
    bool checkpos(int, int) const; // Gültigkeit der Koordinaten prüfen
private:
    int AX, AY, BX, BY; // Koordinaten (A[x/y];B[x/y])
};

#endif	/* LINE_H */

/*
 * File:   Line.cpp
 * Author: maximilian.krieg
 *
 * Created on 27. April 2012, 10:19
 */

#include "headers.h"
using std::cerr;
using std::endl;

Line::Line() // Definition des Standardkonstruktors
: Shape(), AX(-1), AY(-1), BX(-1), BY(-1) { // Koordinaten werden ungültig gesetzt
}

Line::Line(int ax, int ay, int bx, int by) // Def. des weiteren Konstuktors
: Shape(), AX(ax), AY(ay), BX(bx), BY(by) {
    if (checkpos(0, 0) == false) {
	AX = -1;
	AY = -1;
	BX = -1;
	BY = -1;
	cerr << "Linie konnte wegen ungültigen Koordinaten nicht gesetzt werden." << "Bitte alle Koordinaten neu setzen oder Linie verschieben." << endl;
    }
}

bool Line::checkpos(int h, int v) const {

    if (AX + h > XMAX || AX + h < 0) {
	cerr << "Koordinate liegt nicht auf dem Frame: ";
	return false;
    }
    if (AY + v > YMAX || AY + v < 0) {
	cerr << "Koordinate liegt nicht auf dem Frame: ";
	return false;
    }
    if (BX + h > XMAX || BX + h < 0) {
	cerr << "Koordinate liegt nicht auf dem Frame: ";
	return false;
    }
    if (BY + v > YMAX || BY + v < 0) {
	cerr << "Koordinate liegt nicht auf dem Frame: ";
	return false;
    }
    return true;
}

void Line::set(int ax, int ay, int bx, int by) {
    int backup_ax=AX;
    int backup_ay=AY;
    int backup_bx=BX;
    int backup_by=BY;

    AX = ax;
    AY = ay;
    BX = bx;
    BY = by;

    if (checkpos(0,0)== false)
    {
	AX=backup_ax;
	AY=backup_ay;
	BX=backup_bx;
	BY=backup_by;
	cerr << "Koordinaten wurden zurückgesetzt." << endl;
    }
}

void Line::move(int h, int v) { // Def. der line::move() Methode
    if (checkpos(h, v) == true) {
	AX = AX + h;
	AY = AY + v;
	BX = BX + h;
	BY = BY + v;
	std::cout << "Linie erfolgreich verschoben." << endl;
    } else {
	cerr << "Linie kann nicht verschoben werden." << endl;
    }
}

// Tauschen von Variablen

void swap(int& a, int& b) {
    int help = 0;
    help = a;
    a = b;
    b = help;
}

// Bresenham Algorithmus

void Line::draw(Frame* pFrame) {

    bool bMerker1 = false;
    bool bMerker2 = false;

    int iX_end = BX;
    int iX_start = AX;
    int iY_start = AY;
    int iY_end = BY;

    if (iX_end < iX_start) {

	// Start- und Endpunkt vertauschen
	swap(iX_start, iX_end);
	swap(iY_start, iY_end);
    }

    if (iY_end < iY_start) {
	// Vorzeichen beider Y-Koordinaten umdrehen
	iY_end = -iY_end;
	iY_start = -iY_start;
	//und dafür einen Merker setzen
	bMerker1 = true;
    }

    if ((iX_end - iX_start) < (iY_end - iY_start)) {

	swap(iX_start, iY_start); // iX_start mit iY_start vertauschen
	swap(iX_end, iY_end); // iX_end mit iY_end vertauschen
	bMerker2 = true; // dafür einen weiteren Merker setzen
    }

    int dx = iX_end - iX_start;
    int dy = iY_end - iY_start;
    int d = (2 * dy) - dx;
    int y = iY_start;

    for (int x = iX_start; x <= iX_end; ++x) {

	// flach & steigend
	if (bMerker1 == false && bMerker2 == false) {
	    pFrame->put_point(x, y); // Punkt im Frame setzen
	}

	// flach & fallend
	if (bMerker1 == true && bMerker2 == false) {
	    pFrame->put_point(x, -y); // Punkt im Frame setzen
	}

	// steil & steigend
	if (bMerker1 == false && bMerker2 == true) {
	    pFrame->put_point(y, x); // Punkt im Frame setzen
	}

	// steil & fallend
	if (bMerker1 == true && bMerker2 == true) {
	    pFrame->put_point(y, -x); // Punkt im Frame setzen
	}

	//error = error + delta; // Fehler aktualisieren
	if (d < 0) {
	    d = d + 2 * dy; // Fehler anpassen
	} else {
	    y++; // Rechts und hoch gehen
	    d = d + 2 * (dy - dx);
	}
    }
}

/*
 * File:   Triangle.h
 * Author: maximilian.krieg
 *
 * Created on 27. April 2012, 11:00
 */

#include "headers.h"

#ifndef TRIANGLE_H
#define	TRIANGLE_H

class Triangle : public Shape {
public:
    //Konstruktoren
    Triangle(); // Standard-Konstruktor
    Triangle(int, int, int, int, int , int); //Konstruktor mit 3 Koordinaten A[x/y], B[x/y], C[x/y]
    Triangle(int ax, int ay, int laenge_a, int laenge_b, int laenge_c);

    //Koordinaten neu setzen A[x/y], B[x/y], C[x/y]
    void set(int , int , int , int , int , int);

    // Dreieck auf dem Frame zeichnen. ( 3 Linien zeichnen )
    void draw(Frame*);

    //Dreick verschieben um h ( x-Achse )und v ( y-Achse )
    void move(int, int);

    // Prüfen, ob das Dreieck auf dem Frame liegt
    bool checkpos(int, int);

private:
    int AX, AY; // Koordinaten von Punkt A
    int BX, BY; // Koordinaten von Punkt B
    int CX, CY; // Koordinaten von Punkt C
    Line ab; // Linie von A zu B
    Line bc; // Linie von B zu C
    Line ac; // Linie von A zu C
};

#endif	/* TRIANGLE_H */
/*
 * File:   Triangle.cpp
 * Author: maximilian.krieg
 *
 * Created on 27. April 2012, 11:00
 */

#include "headers.h"
#include <math.h>
using std::cout;
using std::cerr;
using std::endl;

// Standard-Konstruktor
Triangle::Triangle() :
Shape(), AX(-1), AY(-1), BX(-1), BY(-1), CX(-1), CY(-1) {
}

// Konstruktor für 3 Koordinaten A[x/y], B[x/y], C[x/y]
Triangle::Triangle(int ax, int ay, int bx, int by, int cx, int cy) :
Shape(), AX(ax), AY(ay), BX(bx), BY(by), CX(cx), CY(cy) {
    if (checkpos(0, 0) == true) {
	ac.set(ax, ay, cx, cy);
	ab.set(ax, ay, bx, by);
	bc.set(bx, by, cx, cy);
    } else {
	cerr << "Dreieck kann nicht gezeichnet werden." << endl;
    }
}

// Überschreiben der virtuellen draw-Methode von Shape
void Triangle::draw(Frame* pFrame) {
    ac.draw(pFrame);
    bc.draw(pFrame);
    ab.draw(pFrame);
}

// Dreieck verschieben um h (x-Achse) und v (y-Achse).
// Prüfung erfolgt mittels Vorbedingung.
void Triangle::move(int h, int v) {

    // Prüfen, ob das Dreieck nach dem Verschieben gültig wäre
    if (checkpos(h, v) == true) {
	AX = AX + h;
	AY = AY + v;
	BX = BX + h;
	BY = BY + v;
	CX = CX + h;
	CY = CY + v;

	// Linien neu setzen
	ac.set(AX, AY, CX, CY);
	ab.set(AX, AY, BX, BY);
	bc.set(BX, BY, CX, CY);

	cout << "Dreieck erfolgreich verschoben." << endl;
    } else {
	cerr << "Dreieck kann nicht verschoben werden." << endl;
    }
}

// Länge einer Linie berechnen
double laenge(int ax, int ay, int bx, int by) {
    double dlaenge = ((bx - ax)*(bx - ax)) + ((by - ay)*(by - ay));
    dlaenge = sqrt(dlaenge);
    return dlaenge;
}

// Prüfen, ob ein Dreieck gültig ist.
bool Triangle::checkpos(int h, int v) {

    // Position prüfen, liegt die neue Koordinate auf dem Frame?, liegt die neue Koordinate auf dem Frame?
    if (AX + h > XMAX || AX + h < 0) {
	std::cerr << "Dreieck liegt nicht auf dem Frame: ";
	return false;
    }
    // Position prüfen, liegt die neue Koordinate auf dem Frame?
    if (AY + v > YMAX || AY + v < 0) {
	std::cerr << "Dreieck liegt nicht auf dem Frame: ";
	return false;
    }
    // Position prüfen, liegt die neue Koordinate auf dem Frame?
    if (BX + h > XMAX || BX + h < 0) {
	std::cerr << "Dreieck liegt nicht auf dem Frame: ";
	return false;
    }
    // Position prüfen, liegt die neue Koordinate auf dem Frame?
    if (BY + v > YMAX || BY + v < 0) {
	std::cerr << "Dreieck liegt nicht auf dem Frame: ";
	return false;
    }
    // Position prüfen, liegt die neue Koordinate auf dem Frame?
    if (CX + h > XMAX || CX + h < 0) {
	std::cerr << "Dreieck liegt nicht auf dem Frame: ";
	return false;
    }
    // Position prüfen, liegt die neue Koordinate auf dem Frame?
    if (CY + v > YMAX || CY + v < 0) {
	std::cerr << "Dreieck liegt nicht auf dem Frame: ";
	return false;
    }

    // Punkte auf Gleichheit prüfen.
    if ((CX == BX && BY == CY) || (BX == AX && BY == AY) || (CX == AX && CY == AY)) {
	std::cerr << "Zwei oder mehr Punkte sind identisch. Kein gültiges Dreieck: ";
	return false;
    }

    // Prüfen, ob 1 Punkt auf der Linie der anderen 2 Punkte liegt.
    double laenge_ac = laenge(AX, AY, CX, CY);
    double laenge_ab = laenge(AX, AY, BX, BY);
    double laenge_bc = laenge(BX, BY, CX, CY);

    if (((laenge_ac + laenge_ab) <= laenge_bc) || (laenge_ac + laenge_bc <= laenge_ab) || (laenge_ab + laenge_bc <= laenge_ac)) {
	std::cerr << "Die Laenge einer Seite ist gleich oder größer als die Summe der anderen Zwei Seiten. Kein gültiges Dreieck: ";
	return false;
    }

    return true;
}

// Koordinaten eines Dreiecks neu setzen.
// Prüfung erfolgt mittels Nachbedingung.
void Triangle::set(int ax, int ay, int bx, int by, int cx, int cy) {

    // Sichern der Koordinaten für etwaiges Zurückspielen
    int backup_ax = AX;
    int backup_ay = AY;
    int backup_bx = BX;
    int backup_by = BY;
    int backup_cx = CX;
    int backup_cy = CY;

    // Übertragen der Koordinaten
    AX = ax;
    AY = ay;
    BX = bx;
    BY = by;
    CX = cx;
    CY = cy;

    // Gültigkeit der neuen Koordinaten prüfen
    if (checkpos(0, 0) == true) {

	// Setzen der Linien
	ac.set(ax, ay, cx, cy);
	ab.set(ax, ay, bx, by);
	bc.set(bx, by, cx, cy);
    } else {

	// Zurücksetzen auf alte Koordinaten
	AX = backup_ax;
	AY = backup_ay;
	BX = backup_bx;
	BY = backup_by;
	CX = backup_cx;
	CY = backup_cy;
	std::cerr << "Punkte des Dreiecks können nicht gesetzt werden." << std::endl;
    }

}

Testplan

Notizen:

  • Probleme bei dem Testfall triangle(1,1,7,7,7), da in der ursprünglichen Lösung der berechnete Double-Wert der Längen zwischen Zwei punkten mittels return als Integer-Wert übergeben wurde. Somit wurden die Kommastellen  und es kam zu dem Fehler, dass das Dreieck nicht gezeichnet wurde. Dies betraf jedoch nur Längen mit einer Nachkommastelle größer als .5, dieser Fall wurde jedoch bei den Tests nicht erzeugt.
  • Künftig müssen die Methoden gründlicher getestet werden!

Schreiben Sie einen Kommentar

Ihre E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahren Sie, wie Ihre Kommentardaten verarbeitet werden.

Index