tipos - Patrón de constructor en Java efectivo
metodos en java (8)
Recientemente comencé a leer Effective Java de Joshua Bloch. Encontré la idea del patrón Builder [Artículo 2 en el libro] realmente interesante. Traté de implementarlo en mi proyecto, pero hubo errores de compilación. Siguiente es en esencia lo que estaba tratando de hacer:
La clase con múltiples atributos y su clase de constructor:
public class NutritionalFacts {
private int sodium;
private int fat;
private int carbo;
public class Builder {
private int sodium;
private int fat;
private int carbo;
public Builder(int s) {
this.sodium = s;
}
public Builder fat(int f) {
this.fat = f;
return this;
}
public Builder carbo(int c) {
this.carbo = c;
return this;
}
public NutritionalFacts build() {
return new NutritionalFacts(this);
}
}
private NutritionalFacts(Builder b) {
this.sodium = b.sodium;
this.fat = b.fat;
this.carbo = b.carbo;
}
}
Clase donde intento usar la clase anterior:
public class Main {
public static void main(String args[]) {
NutritionalFacts n =
new NutritionalFacts.Builder(10).carbo(23).fat(1).build();
}
}
Recibo el siguiente error de compilación:
se necesita una instancia adjunta que contenga effectivejava.BuilderPattern.NutritionalFacts.Builder NutritionalFacts n = new NutritionalFacts.Builder (10) .carbo (23) .fat (1) .build ();
No entiendo lo que significa el mensaje. Por favor explique. El código anterior es similar al ejemplo sugerido por Bloch en su libro.
Debe declarar la clase interna del generador como static
.
Consulte alguna documentación para las clases internas no estáticas y las clases internas estáticas .
Básicamente, las instancias de clases internas no estáticas no pueden existir sin una instancia de clase externa adjunta.
Debes hacer que la clase de Constructor sea estática y también debes hacer que los campos sean definitivos y obtengan esos valores. No proporcione setters a esos valores. De esta manera, tu clase será perfectamente inmutable.
public class NutritionalFacts {
private final int sodium;
private final int fat;
private final int carbo;
public int getSodium(){
return sodium;
}
public int getfat(){
return fat;
}
public int getCarbo(){
return carbo;
}
public static class Builder {
private int sodium;
private int fat;
private int carbo;
public Builder sodium(int s) {
this.sodium = s;
return this;
}
public Builder fat(int f) {
this.fat = f;
return this;
}
public Builder carbo(int c) {
this.carbo = c;
return this;
}
public NutritionalFacts build() {
return new NutritionalFacts(this);
}
}
private NutritionalFacts(Builder b) {
this.sodium = b.sodium;
this.fat = b.fat;
this.carbo = b.carbo;
}
}
Y ahora puede establecer las propiedades de la siguiente manera:
NutritionalFacts n = new NutritionalFacts.Builder().sodium(10).carbo(15).
fat(5).build();
Está intentando acceder a una clase no estática de forma estática. Cambie Builder
a static class Builder
y debería funcionar.
El uso de ejemplo que das falla porque no hay ninguna instancia de Builder
presente. Una clase estática para todos los propósitos prácticos siempre se instancia. Si no lo convierte en estático, tendría que decir:
Widget = new Widget.Builder(10).setparm1(1).setparm2(3).build();
Porque necesitarías construir un nuevo Builder
cada vez.
Esto significa que no puede crear el tipo de adjuntar. Esto significa que primero debe cerar una instancia de clase "principal" y luego desde esta instancia puede crear instancias de clase anidadas.
NutritionalFacts n = new NutritionalFacts()
Builder b = new n.Builder(10).carbo(23).fat(1).build();
Haga el constructor una clase static
. Entonces funcionará. Si no es estático, requerirá una instancia de su clase propietaria, y el punto es no tener una instancia de él, e incluso prohibir la creación de instancias sin el constructor.
public class NutritionFacts {
public static class Builder {
}
}
Referencia: clases anidadas
La clase de Constructor debe ser estática. No tengo tiempo ahora mismo para probar el código más allá de eso, pero si no funciona, avíseme y volveré a analizarlo.
Para generar un generador interno en Intellij IDEA, consulte este complemento: https://github.com/analytically/innerbuilder
Personalmente prefiero usar el otro enfoque, cuando tienes 2 clases diferentes. Entonces no necesitas ninguna clase estática. Esto es básicamente para evitar escribir Class.Builder
cuando tiene que crear una nueva instancia.
public class Person {
private String attr1;
private String attr2;
private String attr3;
// package access
Person(PersonBuilder builder) {
this.attr1 = builder.getAttr1();
// ...
}
// ...
// getters and setters
}
public class PersonBuilder (
private String attr1;
private String attr2;
private String attr3;
// constructor with required attribute
public PersonBuilder(String attr1) {
this.attr1 = attr1;
}
public PersonBuilder setAttr2(String attr2) {
this.attr2 = attr2;
return this;
}
public PersonBuilder setAttr3(String attr3) {
this.attr3 = attr3;
return this;
}
public Person build() {
return new Person(this);
}
// ....
}
Entonces, puedes usar tu constructor así:
Person person = new PersonBuilder("attr1")
.setAttr2("attr2")
.build();