Il y a quelques sujets similaires à celui-ci, mais je n'ai pas pu en trouver un avec une réponse suffisante.
Je voudrais savoir quelle est la meilleure pratique pour la surcharge de constructeur en Java. J'ai déjà mes propres idées sur le sujet, mais j'aimerais entendre davantage de conseils.
Je parle de la surcharge de constructeur dans une classe simple et de la surcharge de constructeur en héritant d'une classe déjà surchargée (ce qui signifie que la classe de base a des constructeurs surchargés).
Merci :)
Bien qu'il n'y ait pas de "directives officielles", je suis le principe de KISS et DRY. Rendez les constructeurs surchargés aussi simples que possible, et le plus simple est qu’ils n’appellent que cela (...). De cette façon, vous ne devez vérifier et gérer les paramètres qu’une seule fois.
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
}
}
Du point de vue des tests unitaires, il deviendra facile de tester la classe puisque vous pouvez y mettre les ressources. Si la classe dispose de nombreuses ressources (ou de collaborateurs comme l'appellent certains geeks), prenez en compte l'une de ces deux choses:
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
}
Le constructeur dans Simple seulement doit scinder le paramètre SimpleParams
:
public Simple(SimpleParams params) {
this(params.getR1(), params.getR2());
}
… Ou faites de SimpleParams
un attribut:
public Simple(Resource r1, Resource r2) {
this(new SimpleParams(r1, r2));
}
public Simple(SimpleParams params) {
this.params = params;
}
Créez une classe fabrique qui initialise les ressources pour vous, ce qui est avantageux si l’initialisation des ressources est un peu difficile:
public interface ResourceFactory {
public Resource createR1();
public Resource createR2();
}
Le constructeur se fait alors de la même manière qu'avec la classe de paramètres:
public Simple(ResourceFactory factory) {
this(factory.createR1(), factory.createR2());
}
Ouais ... vous pouvez combiner les deux manières en fonction de ce qui est plus facile pour vous à ce moment-là. Les classes de paramètres et les classes d'usine simples sont à peu près la même chose compte tenu de la classe Simple
utilisée de la même manière.
Je pense que la meilleure pratique consiste à avoir un seul constructeur principal auquel les constructeurs surchargés se réfèrent en appelant this()
avec les paramètres par défaut appropriés. La raison en est que cela rend beaucoup plus clair ce qu'est l'état construit de l'objet. En réalité, vous pouvez considérer le constructeur principal comme le constructeur réel seulement, les autres ne faisant que le déléguer.
Un exemple de ceci pourrait être JTable
- le constructeur principal prend une TableModel
(plus des modèles de colonne et de sélection) et les autres constructeurs appellent ce constructeur principal.
Pour les sous-classes où la super-classe a déjà des constructeurs surchargés, j'aurais tendance à supposer qu'il est raisonnable de traiter n'importe quel constructeur de la classe parente comme primaire et pense qu'il est parfaitement légitime de ne pas avoir un seul constructeur primaire. . Par exemple, lors de l'extension de Exception
, je fournis souvent 3 constructeurs, un prenant seulement un message String
, un prenant une cause Throwable
et l'autre prenant les deux. Chacun de ces constructeurs appelle directement super
.
Si vous avez une classe très complexe avec beaucoup d'options dont seules certaines combinaisons sont valides, envisagez d'utiliser un générateur. Fonctionne très bien aussi bien codément que logiquement.
Le générateur est une classe imbriquée dont les méthodes sont uniquement conçues pour définir des champs, puis le constructeur ComplexClass prend uniquement un tel générateur en tant qu'argument.
Édition: le constructeur ComplexClass peut s'assurer que l'état dans le générateur est valide. Ceci est très difficile à faire si vous utilisez uniquement des setters sur ComplexClass.
Cela dépend vraiment du type de classes car toutes les classes ne sont pas créées égales.
Comme ligne directrice générale, je suggérerais 2 options:
Eh bien, voici un exemple pour les constructeurs surchargés.
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;
}
}
Dans l'exemple ci-dessus, vous pouvez voir les constructeurs surchargés. Le nom des constructeurs est identique mais chaque constructeur a des paramètres différents.
Voici quelques ressources qui jettent plus de lumière sur la surcharge de constructeur en Java,