Il pourrait être utile d'envisager l'utilisation d'une méthode d'usine statique au lieu d'un constructeur.
Je dis que au lieu de mais il est évident que vous ne pouvez pas remplacer le constructeur. Ce que vous pouvez faire, cependant, c'est cacher le constructeur derrière une méthode d'usine statique. De cette façon, nous publions la méthode d'usine statique en tant que partie de l'API de la classe, mais en même temps nous cachons le constructeur en le rendant privé ou privé de paquetage.
Il s'agit d'une solution relativement simple, surtout si on la compare au modèle Builder (tel qu'il est présenté dans l'ouvrage de Joshua Bloch intitulé Java efficace 2e édition - Attention, la bande des quatre Modèles de conception définit un modèle de conception complètement différent portant le même nom, ce qui peut prêter à confusion) qui implique la création d'une classe imbriquée, d'un objet constructeur, etc.
Cette approche ajoute une couche supplémentaire d'abstraction entre vous et votre client, renforçant l'encapsulation et facilitant les changements ultérieurs. Elle vous donne également le contrôle de l'instance - puisque les objets sont instanciés à l'intérieur de la classe, c'est vous, et non le client, qui décidez du moment et de la manière dont ces objets sont créés.
Enfin, il facilite les tests - en fournissant un constructeur muet, qui assigne simplement les valeurs aux champs, sans effectuer de logique ou de validation, il vous permet d'introduire un état invalide dans votre système afin de tester son comportement et ses réactions. Vous ne pourrez pas le faire si vous validez les données dans le constructeur.
Vous trouverez de plus amples informations à ce sujet dans l'ouvrage de Joshua Bloch (déjà mentionné), intitulé Java efficace 2e édition - c'est un outil important dans la boîte à outils de tous les développeurs et il n'est pas étonnant qu'il fasse l'objet du premier chapitre du livre ;-)
En suivant votre exemple :
public class Book {
private static final String DEFAULT_TITLE = "The Importance of Being Ernest";
private final String title;
private final String isbn;
private Book(String title, String isbn) {
this.title = title;
this.isbn = isbn;
}
public static Book createBook(String title, String isbn) {
return new Book(title, isbn);
}
public static Book createBookWithDefaultTitle(String isbn) {
return new Book(DEFAULT_TITLE, isbn);
}
...
}
Quelle que soit la méthode choisie, il est bon d'en avoir une. principal qui attribue aveuglément toutes les valeurs, même s'il est utilisé par d'autres constructeurs.