ToyFactory

scratchy1

Bekanntes Mitglied
Hi Leute,
Gegeben seien 4 Klassen:
class Toy { }
class RubberDuck extends Toy { }
class TeddyBear extends Toy { }
class PlasticCar extends Toy { }
Ich will eine Klasse ToyFactory schreiben, welche eine Klassenmethode getToy besitzt,
die als Parameter den Namen der Toy-Klasse als String entgegennimmt,
von der die Fabrik eine Instanz erzeugen soll. Ich will in der Methode
die Switch-Anweisung verwenden, um abhängig vom übergebenen Klassennamen das
passende Toy-Objekt zu erzeugen und zurückzuliefern. Wenn ein String übergeben
wird, zu dem es in der Switch-Anweisung keinen Fall gibt, soll eine
IllegalArgumentException ausgelöst werden.

Nun meine Umsetzung:
Java:
public class ToyFactory {

    class Toy {

        private String toyname;

        Toy(String toyname) {

            this.toyname = toyname;
        }

        void printToy() {
            System.out.println("Toy");
        }
    }

    class RubberDuck extends Toy {

        void printRubberDuck() {
            System.out.println("RubberDuck");
        }
    }

    class TeddyBear extends Toy {

        void printTeddyBear() {
            System.out.println("TeddyBear");
        }
    }

    class PlasticCar extends Toy {

        void printPlasticCar() {
            System.out.println("PlasticCar");
        }
    }

    public Toy getToy(String toyname) {
        switch (toyname) {
        case "rubberduck":
            return new ToyFactory.Toy(rubberduck);
            break;
        case "teddybear":
            return new ToyFactory.Toy(teddybear);
            break;
        case "plasticcar":
            return new ToyFactory.Toy(plasticcar);
            break;
        default:
            throw new IllegalArgumentException();
            break;
        }
    }
}
Nun ergeben sich folgende Probleme:
1. Ist mein Konstruktor korrekt?
2. Bei den Zeilen zu den Unterklassen steht die Fehlermeldung
Implicit superconstructor ToyFactory.Toy() is undefined for default constructor. Must define an explicit constructor.
3. Ich weiss nicht genau, mit welchem Befehl ich ein Objekt einer Unterklasse erzeugen und zurückgeben soll (siehe in den jeweiligen cases).
 

mihe7

Top Contributor
1. der von Toy? Ja.
2. Ja, Konstruktoren werden nicht vererbt. Der Standardkonstruktor ruft implizit den Standardkonstruktor der Superklasse auf. Da Deine Toy-Klasse keinen solchen hat (was auch richtig ist), musst Du in den Subklassen den Konstruktor der Superklasse explizit aufrufen. Beispiel
Java:
class RubberDuck extends Toy {
    RubberDuck() {
        super("RubberDuck");
    }
...
3. Naja, indem Du halt nicht Toy sondern z. B. RubberDuck instantiierst :)
 

scratchy1

Bekanntes Mitglied
Hallo mihe7,
ah, vielen Dank. Ich mache mich ans Instantiieren, da steht nun bei den einzelnen cases: unreachable code.
Das muss was mit den breaks nicht in Ordnung sein.
 

scratchy1

Bekanntes Mitglied
oh grüne Neune, der Osterhase hat die Lösung versteckt: ich bekomm folgende Fehlermeldung bei
Java:
public class MainToys {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
      ToyFactory.getToy("RubberDuck");
    }

}
Cannot make a static reference to the non-static method getToy(String) from the type ToyFactory
 
Zuletzt bearbeitet:

scratchy1

Bekanntes Mitglied
Hallo Blender3D,
Ach, danke für den Tipp. (Aber) Bei
Java:
Toy toy=factory.getToy("RubberDuck");
bekomme ich die Fehlermeldung
Cannot convert from ToyFactory.Toy to Toy
 

Blender3D

Top Contributor
Dein Problem hier sind die inneren Klassen. Das Design das Du gewählt hast ist nicht wirklich brauchbar.
Da
1) Jedes weiter neue Toy in der Toy Factory eingearbeitet wird . Bei 1000 Spielzeugen wird das sehr viel
2) Jedes Deiner Toys hat eine eigene print Methode --> bringt hier die Vererbung nichts.

Versuche diese Klassenstruktur einmal.
Java:
public abstract class Toy {
    protected String name = null;

    public Toy(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return name;
    }
}


public class RubberDuck extends Toy {

    public RubberDuck() {
        super("RubberDuck");
    }
}

public class PlasticCar extends Toy {

    public PlasticCar() {
        super("PlasticCar");
    }
}

public class TeddyBear extends Toy {
    public TeddyBear() {
        super("TeddyBear");
    }
}

public class ToyFactory {
    public enum TYPE {
        RUBBERDUCK, TEDDYBEAR, PLASTICCAR
    };

    public static Toy getToy(TYPE type) {
        switch (type) {
        case PLASTICCAR:
            return new PlasticCar();
        case RUBBERDUCK:
            return new RubberDuck();
        case TEDDYBEAR:
            return new TeddyBear();
        }
        return null;
    }
}


..
System.out.println(ToyFactory.getToy(ToyFactory.TYPE.PLASTICCAR));
::
 

scratchy1

Bekanntes Mitglied
Diese Version ist leider etwas zu fortgeschritten für mich. Von den Schlüsselwörtern abstract,enum, protected, @Override habe ich z.B. noch nie gehört (hab ein bisschen im Internet geschaut, aber ich bin jetzt verwirrter als zuvor :))
 

mrBrown

Super-Moderator
Mitarbeiter
Diese Version ist leider etwas zu fortgeschritten für mich. Von den Schlüsselwörtern abstract,enum, protected, @Override habe ich z.B. noch nie gehört (hab ein bisschen im Internet geschaut, aber ich bin jetzt verwirrter als zuvor :))
Dann lass sie weg ;)

Die Variante passend zu deinen Anforderungen:

Java:
public class Toy {
   private String name = null;

    public Toy(String name) {
        this.name = name;
    }

    public String toString() {
        return name;
    }
}


public class RubberDuck extends Toy {

    public RubberDuck() {
        super("RubberDuck");
    }
}

public class PlasticCar extends Toy {

    public PlasticCar() {
        super("PlasticCar");
    }
}

public class TeddyBear extends Toy {
    public TeddyBear() {
        super("TeddyBear");
    }
}

public class ToyFactory {
  
    public static Toy getToy(String type) {
        switch (type) {
        case "plasticcar:
            return new PlasticCar();
        case "rubberduck":
            return new RubberDuck();
        case "teddybear":
            return new TeddyBear();
        }
        throw new IllegalArgumentException();
    }
}

Jede Klasse muss dabei in eine eigene Datei.
 

temi

Top Contributor
eine public toString()-Methode habe ich auch nicht. Was geschieht dort?
Jedes Java-Objekt hat eine "toString()" Methode, die es von "Object" erbt. Standardmäßig wird hier der Hashcode des Objektes ausgegeben. Du kannst die Methode aber auch überschreiben, dann wird das ausgegeben, was du möchtest.

Die "toString()" Methode wird z.B. von "println()" verwendet:

println(toy) ruft intern "toy.toString()" auf.
 

scratchy1

Bekanntes Mitglied
Ach, danke Temi. Ich hab ja schon print(), dann nehm ich einfach das ;).
So jetzt will ich ja sicherstellen, dass niemand was anderes (womöglich bösartiges bestellt). Mein Versuch in der main:
Java:
throws IllegalArgumentException{
            if(Toy.toyname!="RubberDuck"||Toy.toyname!="PlasticCar"||
               Toy.toyname!="TeddyBear") {
                throw new IllegalArgumentException("toy not available");
        }
        }
Also hier sind Syntax-Errata:
on token throws, interface expected und
insert "{" to complete interface body

aber auch the field Toy.toyname is not visible
 

scratchy1

Bekanntes Mitglied
sorry, ich korrigiere mich
Java:
 throws IllegalArgumentException{
            if(toyname!="RubberDuck"&&toyname!="PlasticCar"&&
               toyname!="TeddyBear") {
                throw new IllegalArgumentException("toy not available");
        }
        }
Jetzt habe ich nur noch 2 Syntax-Fehlermeldungen
Insert Interface Body to complete BlockStatements und
Syntax error on token "throws", interface expected
 

mrBrown

Super-Moderator
Mitarbeiter
Hast du den Teil einfach irgendwo eingefügt? Dann ist klar, dass es nicht klappt...

Wie sieht denn der Rest aus?
 

scratchy1

Bekanntes Mitglied
Hallo mrBrown,
ich hab es an den Anfang meiner main-Methode reingeschrieben:
Java:
public class MainToys {

    public static void main(String[] args) {
        // Instanz der Klasse ToyFactory erzeugen
        throws IllegalArgumentException{
            if(toyname!="RubberDuck"&&toyname!="PlasticCar"&&
               toyname!="TeddyBear")
                throw new IllegalArgumentException("toy not available");
        }
       
        ToyFactory factory = new ToyFactory();
        Toy toy = factory.getToy("RubberDuck");
        toy.print();
        Toy toy1=factory.getToy("Puppe");
        toy.print();
   
       

}
}
 

mrBrown

Super-Moderator
Mitarbeiter
throws gibt die Exceptions an, die eine Methode werfen kann, und muss deshalb an der Methodendeklaration stehen: public static void main(String[] args) throws IllegalArgumentException {


toyname ist an der Stelle nicht definiert, und kann daher natürlich auch nicht genutzt werden. Wo soll toyname denn herkommen?

Strings vergleicht man nicht mit == und !=, sondern mit equals.


Der Test in der main-Methode macht generell nicht viel Sinn. Du müsstest so in main und in ToyFactory alle möglichen Toys kennen (was du mit der Factory ja vermeiden willst) und machst so den quasi gleichen Code, den du in der Factory hast (das default: throw new IllegalArgumentException();), überflüssig
 

scratchy1

Bekanntes Mitglied
Ich soll für meine main-Methode deklarieren, dass diese sich abrupt mit einer IllegalArgumentException
beenden kann und auch diesen Fall testen. Mit Hilfe Deiner Tipps habe ichs folgendermassen verbessert:
Java:
public class MainToys {

    public static void main(String[] args) throws IllegalArgumentException{
        
       
            if((!args.equals("RubberDuck"))&&(!args.equals("PlasticCar"))&&
               (!args.equals("TeddyBear")))
                throw new IllegalArgumentException("toy not available");
       
       
        ToyFactory factory = new ToyFactory();
        Toy toy = factory.getToy("RubberDuck");
        toy.print();
        Toy toy1=factory.getToy("Puppe");
        toy.print();
   
       

}
}
 

mrBrown

Super-Moderator
Mitarbeiter
Okay...

args ist ein String-Array, "RubberDuck" ist ein String - die beiden werden niemals gleich sein.

Und der Test ist wie gesagt an der Stelle nicht nötig, da er in factory.getToy auch gemacht wird.
 

scratchy1

Bekanntes Mitglied
Achso ok, dann kann ich das einfach nur deklarieren und nicht testen:
Java:
public class MainToys {

    public static void main(String[] args) throws IllegalArgumentException{
       
       ToyFactory factory = new ToyFactory();
        Toy toy = factory.getToy("RubberDuck");
        toy.print();
        Toy toy1=factory.getToy("Puppe");
        toy.print();
    }
}
 

Blender3D

Top Contributor
Dieses Problem hat man in deiner Variante genauso:
Wenn man davon ausgeht, dass jedes Spielzeug seine eignen Eigenschaften mit sich bringt, dann müsste man bei der ersten Variante 1000 innere Klassen in die Factory einarbeiten. Bei der 2ten Variante beschränkt sich das aber nur auf eine Konstante und eine Instanzierung pro Spielzeug. Des Weiteren stehen die Toys als eigene Klassen zur Verfügung.
Es muss also in der Factory nicht jedes einzelne Toy formuliert werden. --> gewaltiger Unterschied.
 

mihe7

Top Contributor
Vielleicht liegt es daran, dass ich hundemüde bin aber irgendwie verstehe ich den Punkt nicht. Variante 1: 1000 innere Klassen, Variante 2: 1000 Top-Level-Klassen.
 

mihe7

Top Contributor
a) ach, so war "Jedes weiter neue Toy in der Toy Factory eingearbeitet wird" - das ist natürlich richtig
b) mit static class ist das kein Problem :)
 

mrBrown

Super-Moderator
Mitarbeiter
Es stimmt schon das. Das klassische Builder Pattern lagert die Produkte einzeln in Klassen aus, da die einzelnen Produkte im echten Leben sehr komplex werden koennen.
Abgesehen davon, dass es hier um Factorys geht, trifft keins der Pattern eine Aussage, wie man die Klassen in Dateien ablegt. Die klassischen Pattern sind auch älter als Java, von daher könnten die auch gar nicht darauf eingehen :p
 

mrBrown

Super-Moderator
Mitarbeiter
Ich hab das „eingearbeitet“ allerdings auch falsch verstanden - nicht nur als ein „die Klassen stehen da drin und es wird dadurch groß“, sondern eher auf einer Ebene höher, „fügt man eine Klasse hinzu, muss man dazu auch was in der Factory hinzufügen“.
 

cleanairs

Mitglied
Abgesehen davon, dass es hier um Factorys geht, trifft keins der Pattern eine Aussage, wie man die Klassen in Dateien ablegt. Die klassischen Pattern sind auch älter als Java, von daher könnten die auch gar nicht darauf eingehen :p

Hm? Das builder pattern passt hier doch. Er nennt es nur Factory. Das Factory pattern nutzt man nur dann wenn erst zur Laufzeit festgelegt wird welche Objekte erzeugt werden sollen. Das ist hier ja nicht der Fall.
 

mrBrown

Super-Moderator
Mitarbeiter
Jo. Er legt doch aber in der main Methode VOR der Ausführung schon die Objekte fest, also nicht erst zur Laufzeit.
Nein, in der Main steht ein String, da wird keine konkrete Klasse festgelegt. Die Klasse wird erst zur Laufzeit aufgelöst, durch die Factory.

Die Factory könnte zur Laufzeit der Main auch eine völlig andere als zur Compilezeit sein ;)
 

Neue Themen


Oben