2017-05-05 3 views
0

Je crée un grand projet pour mon entreprise en ce moment, avec beaucoup de structures de données et shizle. Et je suis à la recherche d'une bonne solution pour mon problème suivant:Alternative au constructeur retournant null

Puisque Java n'a aucune possibilité d'avoir un constructeur de retour nul (au moins mes recherches l'ont dit) j'ai besoin d'une bonne alternative.

Disons que j'ai le code suivant (Juste un exemple Le projet actuel est plus complexe.):

public abstract class SuperClass 
{ 
    public SuperClass(Element element) 
    { 
     if(element != null) 
      readElement(element); 
    } 

    public abstract void readElement(Element element); 
} 

public class Foo extends SuperClass 
{ 
    private Bar bar1; 
    private Bar bar2; 
    private Bar bar3; 
    //... 

    public Foo(Element element) 
    { 
     super(element); 
    } 

    @Override 
    public void readElement(Element element) 
    { 
     this.bar1 = new Bar(element.getChild("bar1")); 
     this.bar2 = new Bar(element.getChild("bar2")); 
     this.bar3 = new Bar(element.getChild("bar3")); 
     //... 
    } 
} 

public class Bar extends SuperClass 
{ 
    private String value; 

    public Bar(Element element) 
    { 
     super(element); 
    } 

    @Override 
    public void readElement(Element element) 
    { 
     this.value = element.getChildText("value"); 
    } 
} 

la Element.getChild fonction (String name) est de jdom2 (XML-Parser), utilisé pour lire des fichiers XML. Il peut et retournera null, si un enfant avec un prénom n'a pas été trouvé. J'ai écrit mon projet sur la base de cet exemple, pensant bêtement, que si la fonction nommée renvoie null, la variable (ici la barre1 par exemple) serait également nulle. Mais puisque la fonction named est entourée d'une "nouvelle barre (...)", elle ne sera pas nulle, elle sera à la place un objet vide. Mais je veux et ai besoin que les variables "vides" soient nulles, ainsi je peux les passer facilement quand je traverse toutes les structures de données dans mon projet. Je sauverait l'objet retourné de la « getChild (...) » fonction dans une variable locale « lElement » et avoir quelque chose comme:

if(lElement != null) 
    bar1 = lElement; 

mais j'ai plus de 50 différentes comme celles de Structures des données mon exemple et plus de suffisamment de variables dans celles-ci, qui sont initialisées par la fonction "readElement (...)". Cette idée prendrait beaucoup trop d'édition et probablement même une bonne quantité de performance. Aussi il semble un peu ... "moche" pour moi. Au moins pour cette quantité de variables. J'ai donc besoin de quelque chose, qui n'a aucun effet sur la performance et qui est aussi facile que d'avoir le constructeur qui retourne null. Je préférerais ne pas trop modifier le code dans ces fonctions. J'ai aussi eu l'idée de laisser la structure de données se mettre à zéro si "Element element" est nul, mais après une recherche rapide, cette idée a été effacée tout de suite ^^. Un objet qui se supprime ne fonctionnera pas et n'est pas logique de toute façon.

Donc, fondamentalement, je pourrais résoudre ce problème moi-même. Mais probablement ce ne serait pas le moyen le plus efficace. Les deux dans l'effort d'éditer le code et dans le code-performance. Par conséquent, je vous demande comment vous allez résoudre ce problème. Ayant l'arrière-pensée de non seulement deux simples structures de données comme dans mon exemple, mais plutôt 50 classes + en utilisant ce système.

J'espère que vous pouvez m'aider, et je m'excuse pour tout mauvais anglais. Je viens d'Allemagne ^^. J'ai codé en Java depuis plus de 5 ans maintenant ("professionnellement" depuis l'année dernière), donc c'est un peu embarrassant pour moi, n'ayant pas pensé à ce problème plus tôt. Mais maintenant il est trop tard pour revenir à quelque chose de totalement différent.

Merci d'avance!

+0

Au lieu de retourner 'null' vous devriez lancer une exception (d'exécution). Renvoyer 'null' vous oblige à encombrer votre code avec des vérifications *' null' * qui est encore plus moche. –

+0

Peut-être que l'appel d'une méthode abstraite dans un constructeur est un choix malheureux. Voir aussi [this] (http://stackoverflow.com/questions/15327417/is-it-ok-to-call-abstract-method-from-constructor-in-java) SO question. –

+0

@Timothy Utiliser des exceptions me forcerait à encombrer mon code avec des tentatives/attrapés, ce qui à mon avis semble encore pire. Je ne veux pas que toutes les variables soient ignorées, quand la première reçoit le pointeur null. – Apahdos

Répondre

0

Les constructeurs définissent l'initialisation d'un objet arbitraire. Ainsi, ils ne pourraient jamais retourner des valeurs.

Je dirais que l'absence d'une méthode hasElement est un défaut de conception majeur, mais néanmoins si vous insistez pour retourner null, vous pouvez.

Dans tous les cas, vous devriez tirer parti des génériques et des références de méthode (> = Java 8).

Vous pouvez utiliser la méthode wrap dans l'exemple de code pour vos besoins (Utiliser l'héritage pour permettre aux sous-classes de lire les enfants plus facilement).

public void readElement(Element element) { 
    this.bar1 = wrap(element.getChild("bar1"), Bar::new); 
    this.bar2 = wrap(element.getChild("bar1"), Bar::new); 
    this.bar3 = wrap(element.getChild("bar1"), Bar::new); 
} 

protected <T> T wrap(Element element, Function<Element, T> elem) { 
    if (element == null) { 
     return null; // Not a good value 
    } 
    return elem.apply(element); 
} 
+0

hmm. Je n'ai jamais travaillé avec une telle méthode d'enveloppement jusqu'à présent. Que fait exactement la classe Function <>? Ne vais-je pas écrire une nouvelle fonction chaque fois que j'utilise wrap (...) en passant une nouvelle classe locale de la fonction <..>? Parce que la fonction <...> semble être une interface. Aussi (ne sachant pas ce que vous voulez dire avec la fonction hasElement()), je n'ai pas ajouté une telle chose, parce que je pensais que ça coûterait trop de performance et d'effort de programmation pour avoir le zéro à chaque initialisation ... – Apahdos

+0

... une variable. Comme je l'ai dit, ayant plus de 50 structures de données différentes avec chacune jusqu'à 10 variables différentes (dont certaines sont des listes), il deviendrait assez épuisant d'utiliser "if (hasElement (...) {...}" – Apahdos

+0

J'ai nommé la méthode wrap en raison de sa fonctionnalité.Enveloppant l'élément donné dans un autre type (T) .Vous n'écrivez aucune nouvelle fonction en raison du fait que vous pouvez passer le constructeur de la classe (Bar: : new) Et hasElement aurait dû être implémenté dans jdom afin que vous ayez simplifié l'affectation avec element.hasChild ("foo")? new Baz (element.getChild ("foo")): null. J'ai fait – manf

0

Une alternative aux constructeurs retournant null est des méthodes d'usine retournant null.

Bien que vous ne pouvez pas écrire

class SomeClass { 
    public SomeClass(Node node) { 
     if (node.isEmpty()) return null; // Does not compile 
     ... 
    } 
} 

vous pouvez certainement écrire ceci:

class SomeClass { 
    private SomeClass(Node node) { 
     // Self-check: this should never happen 
     if (node.isEmpty()) { 
      throw new IllegalArgumentException("node"); 
     } 
     ... 
    } 
    public static SomeClass makeFromNode(Node node) { 
     if (node.isEmpty()) { 
      return null; 
     } 
     return new SomeClass(node); 
    } 
} 

constructeur Remplacement appels avec des appels à makeFromNode vous permettra de traiter des situations où le nœud est null dans un simple et uniforme façon.

+1

Ah, c'est vraiment une bonne façon de le faire. Ce sera un effort de changer tous mes appels, mais cela ne changera rien à la performance, et semble avoir besoin (au moins je pense) le moindre effort pour changer les appels, puisque j'ai un top-class où Je peux ajouter la fonction statique. Merci beaucoup! J'ai appris quelque chose de nouveau ^^ – Apahdos

+0

Ah ... attends. Ce ne sera peut-être pas si facile. J'ai beaucoup de différentes Datastructures, qui sont appelées par ceci. Ils ont tous un getter différent. Donc, je devrais créer la méthode pour chaque classe manuellement, puisque (dans mon exemple du post original) Superclass n'a pas de fonctions, mais Bar aurait une méthode getValue() par exemple ... Ai-je manqué quelque chose ici, ou Est-ce que je ne serai pas capable d'écrire votre méthode dans une super-classe, donc je n'aurai pas besoin de la faire pour chaque classe manuellement? – Apahdos

+0

@Apahdos Les méthodes statiques ne sont pas substituables, donc la méthode dans la classe de base finirait par appeler le constructeur de la classe de base, ce qui n'est pas ce que vous voulez. Si vous ne voulez pas parcourir toutes les classes et apporter cette modification, vous pouvez créer une classe 'MyClassFactory' séparée avec des méthodes statiques pour chaque sous-classe que vous souhaitez créer. Cependant, vous avez besoin d'une méthode par classe. – dasblinkenlight