wake-up-neo.com

Konstruktorüberladung in Java - Best Practice

Es gibt einige ähnliche Themen, aber ich habe keine passende Antwort gefunden.

Ich würde gerne wissen, was die beste Methode für das Überladen von Konstruktoren in Java ist. Ich habe bereits meine eigenen Gedanken zu diesem Thema, aber ich hätte gerne mehr Ratschläge.

Ich beziehe mich sowohl auf das Überladen von Konstruktoren in einer einfachen Klasse als auch auf das Überladen von Konstruktoren, während eine bereits überladene Klasse geerbt wird (dh die Basisklasse verfügt über überladene Konstruktoren).

Vielen Dank :)

99
Eyal Roth

Während es keine "offiziellen Richtlinien" gibt, folge ich dem Prinzip von KISS und DRY. Machen Sie die überladenen Konstruktoren so einfach wie möglich, und der einfachste Weg ist, dass sie nur diese (...) aufrufen. Auf diese Weise müssen Sie die Parameter nur einmal und nur einmal überprüfen und handhaben.

public class Simple {

    public Simple() {
        this(null);
    }

    public Simple(Resource r) {
        this(r, null);
    }

    public Simple(Resource r1, Resource r2) {
        // Guard statements, initialize resources or throw exceptions if
        // the resources are wrong
        if (r1 == null) {
            r1 = new Resource();
        }
        if (r2 == null) {
            r2 = new Resource();
        }

        // do whatever with resources
    }

}

Vom Standpunkt des Unit-Tests aus wird es einfach, die Klasse zu testen, da Sie die Ressourcen in sie einfügen können. Wenn die Klasse über viele Ressourcen verfügt (oder Kollaborateure, wie es einige OO-Geeks nennen), sollten Sie eines der beiden folgenden Dinge berücksichtigen:

Erstellen Sie eine Parameterklasse

public class SimpleParams {
    Resource r1;
    Resource r2;
    // Imagine there are setters and getters here but I'm too lazy 
    // to write it out. you can make it the parameter class 
    // "immutable" if you don't have setters and only set the 
    // resources through the SimpleParams constructor
}

Der Konstruktor in Simple muss entweder nur den Parameter SimpleParams aufteilen:

public Simple(SimpleParams params) {
    this(params.getR1(), params.getR2());
}

… Oder mach SimpleParams zu einem Attribut:

public Simple(Resource r1, Resource r2) {
    this(new SimpleParams(r1, r2));
}

public Simple(SimpleParams params) {
    this.params = params;
}

Mache eine Fabrikklasse

Erstellen Sie eine Factory-Klasse, die die Ressourcen für Sie initialisiert. Dies ist günstig, wenn das Initialisieren der Ressourcen etwas schwierig ist:

public interface ResourceFactory {
    public Resource createR1();
    public Resource createR2();
}

Der Konstruktor wird dann auf die gleiche Weise ausgeführt wie bei der Parameterklasse:

public Simple(ResourceFactory factory) {
    this(factory.createR1(), factory.createR2());
} 

Bilde eine Kombination aus beiden

Ja ... Sie können beide Möglichkeiten kombinieren, je nachdem, was für Sie gerade einfacher ist. Parameterklassen und einfache Factory-Klassen sind in Anbetracht der Simple -Klasse fast dasselbe wie sie.

164
Spoike

Ich denke, es ist am besten, wenn Sie einen einzigen primären Konstruktor haben, auf den sich die überladenen Konstruktoren beziehen, indem Sie this() mit den entsprechenden Parametervorgaben aufrufen. Der Grund dafür ist, dass dadurch viel klarer wird, was der built-Zustand des Objekts ist - wirklich können Sie sich den primären Konstruktor als den einzigen reellen Konstruktor vorstellen, die anderen delegieren nur daran

Ein Beispiel dafür könnte JTable sein - der primäre Konstruktor nimmt eine TableModel (plus Spalten- und Auswahlmodelle) und die anderen Konstruktoren nennen diesen primären Konstruktor.

Für Unterklassen wo die Superklasse bereits Konstruktoren überladen hat, neige ich zu der Annahme, dass es sinnvoll ist, alle Konstruktoren der übergeordneten Klasse als primary zu behandeln, und es ist absolut legitim, keinen einzigen primären Konstruktor zu haben . Wenn Sie beispielsweise Exception erweitern, stelle ich oft 3 Konstruktoren zur Verfügung, wobei einer nur eine String-Nachricht enthält, einer eine Throwable-Ursache und der andere beide. Jeder dieser Konstruktoren ruft direkt super auf.

73
oxbow_lakes

Wenn Sie über eine sehr komplexe Klasse mit vielen Optionen verfügen, von denen nur einige Kombinationen gültig sind, sollten Sie einen Builder verwenden. Funktioniert sowohl codweise als auch logisch sehr gut.

Der Builder ist eine verschachtelte Klasse, deren Methoden nur zum Festlegen von Feldern dienen. Der ComplexClass-Konstruktor akzeptiert dann nur einen solchen Builder als Argument. 


Bearbeiten: Der ComplexClass-Konstruktor kann sicherstellen, dass der Status im Builder gültig ist. Dies ist sehr schwierig, wenn Sie einfach Setter für ComplexClass verwenden.

Es hängt wirklich von der Art der Klassen ab, da nicht alle Klassen gleich erstellt werden.

Als allgemeine Richtlinie würde ich zwei Optionen vorschlagen:

  • Für value & immutable Klassen (Exception, Integer, DTOs und dergleichen) verwenden Sie single primary constructor, wie in der obigen Antwort vorgeschlagen
  • Für alles andere (Session-Beans, Services, veränderliche Objekte, JPA & JAXB-Entitäten usw.) verwenden Sie nur default constructor mit sinnvollen Standardwerten für alle Eigenschaften, sodass er ohne zusätzliche Konfiguration verwendet werden kann
2

Nun, hier ist ein Beispiel für überladene Konstruktoren.

public class Employee
{
   private String name;
   private int age;

   public Employee()
   {
      System.out.println("We are inside Employee() constructor");
   }

   public Employee(String name)
   {
      System.out.println("We are inside Employee(String name) constructor");
      this.name = name;
   }

   public Employee(String name, int age)
   {
      System.out.println("We are inside Employee(String name, int age) constructor");
      this.name = name;
      this.age = age;
   }

   public Employee(int age)
   {
      System.out.println("We are inside Employee(int age) constructor");
      this.age = age; 
   }

   public String getName()
   {
      return name;
   }

   public void setName(String name)
   {
      this.name = name;
   }

   public int getAge()
   {
      return age;
   }

   public void setAge(int age)
   {
      this.age = age;
   }
}

Im obigen Beispiel sehen Sie überladene Konstruktoren. Der Name der Konstruktoren ist derselbe, aber jeder Konstruktor hat andere Parameter.

Hier sind einige Ressourcen, die die Konstruktorüberladung in Java näher beleuchten.

Konstruktoren .

Konstruktor-Erklärung .

0
Shiva