java - pattern - Patrón de generador vs. objeto de configuración
factory pattern java (9)
El patrón de constructor es popular para crear objetos inmutables, pero hay algunos gastos generales de programación para crear un constructor. Así que me pregunto por qué no simplemente usando un objeto de configuración.
El uso de un constructor se vería así:
Product p = Product.Builder.name("Vodka").alcohol(0.38).size(0.7).price(17.99).build();
Es obvio que esto es muy legible y conciso, pero tienes que implementar el constructor:
public class Product {
public final String name;
public final float alcohol;
public final float size;
public final float price;
private Product(Builder builder) {
this.name = builder.name;
this.alcohol = builder.alcohol;
this.size = builder.size;
this.price = builder.price;
}
public static class Builder {
private String name;
private float alcohol;
private float size;
private float price;
// mandatory
public static Builder name(String name) {
Builder b = new Builder();
b.name = name;
return b;
}
public Builder alcohol(float alcohol) {
this.alcohol = alcohol;
return.this;
}
public Builder size(float size) {
this.size = size;
return.this;
}
public Builder price(float price) {
this.price = price;
return.this;
}
public Product build() {
return new Product(this);
}
}
}
Mi idea es reducir el código usando un objeto de configuración simple como este:
class ProductConfig {
public String name;
public float alcohol;
public float size;
public float price;
// name is still mandatory
public ProductConfig(String name) {
this.name = name;
}
}
public class Product {
public final String name;
public final float alcohol;
public final float size;
public final float price;
public Product(ProductConfig config) {
this.name = config.name;
this.alcohol = config.alcohol;
this.size = config.size;
this.price = config.price;
}
}
Uso:
ProductConfig config = new ProductConfig("Vodka");
config.alcohol = 0.38;
config.size = 0.7;
config.price = 17.99;
Product p = new Product(config);
Este uso necesita unas pocas líneas más, pero también es muy legible, pero la implementación es mucho más simple y quizás sea más fácil de entender para alguien que no está familiarizado con el patrón de constructor. Por cierto, ¿hay un nombre para este patrón?
¿Hay un inconveniente en el enfoque de configuración que he pasado por alto?
¿Has considerado usar builder-builder ?
Creo que el constructor (con prefijos como "Con") lee más natualmente / con fluidez.
¿Qué problema intentas resolver con tu patrón? El patrón de construcción se usa para objetos con muchos parámetros (opcionales) para evitar toneladas de constructores diferentes o muy largos. También mantiene su objeto en un estado consistente (vs. patrón javabean) durante la construcción.
La diferencia entre el constructor y el "objeto de configuración" (se siente como un buen nombre) es que todavía tiene que crear el objeto con los mismos parámetros por constructor o getter / setter. Esto a) no resuelve el problema del constructor o b) mantiene el objeto de configuración en un estado inconsistente. Los estados inconsistentes del objeto de configuración realmente no lo dañan, pero podría pasar un objeto de configuración sin terminar como parámetro. [El enlace de Michids a tipos fantasmas parece resolver este problema, pero de nuevo mata la legibilidad ( new Foo<TRUE,TRUE, TRUE, FALSE, TRUE>
un poco apesta).) Esa es la gran ventaja del patrón de creación: puede validar sus parámetros antes de crear un objeto y puede devolver cualquier subtipo (actúa como una fábrica).
Los objetos de configuración son válidos para conjuntos de parámetros que son todos obligatorios. He visto este patrón muchas veces en .NET o java antes.
El patrón de configuración y el patrón de construcción son funcionalmente equivalentes. Ambos resuelven los mismos problemas.
Eliminar la necesidad de múltiples firmas de constructores
Permitir que los campos se establezcan solo durante la construcción
Permitir que los consumidores solo establezcan los valores que les interesan y tienen valores predeterminados lógicos para los otros valores
Cualquier cosa que desee hacer en uno de estos patrones puede hacer en el otro, como permitir que solo se establezca el estado con métodos que realicen la validación y el estado de configuración con lógica encapsulada. La única diferencia real es si te gusta crear objetos con el new
término clave o si te gusta llamar a un método .build()
.
El patrón del constructor mejora el desacoplamiento: su Producto puede ser una interfaz y la única clase que conoce la implementación (o implementaciones, en algunos casos) es el constructor. Si el constructor también implementa una interfaz, entonces puede inyectar esto en su código para aumentar aún más el desacoplamiento.
Este desacoplamiento significa que su código es más fácil de mantener y de probar.
El principal inconveniente es que no está en el libro de Joshua, por lo que los drones no pueden envolver sus cabezas alrededor de él.
Está utilizando un objeto de valor simple para mantener varios argumentos que necesita una función (/ method / constructor), no hay nada de malo en eso, se ha hecho durante años. Mientras no hayamos nombrado parámetros opcionales, tendremos que idear soluciones como esta: es una pena, no algunos malditos inventos brillantes de los dioses en el Sol.
La diferencia real es que usted expone los campos directamente. Joshua nunca tendría un campo público mutable, pero escribe API que serán utilizadas por millones de personas, la mayoría de las cuales son idiotas, y las API deben ser seguras para evolucionar durante las próximas décadas, y pueden asignar muchos meses de personal solo para diseñar una clase simple
¿Quiénes somos nosotros para emular eso?
En mi opinión, el patrón del constructor es mucho más robusto si tiene cosas como la validación, etc.
El patrón de construcción en su caso se puede cambiar para hacer lo siguiente:
Product p = new ProductBuilder("pName").alcohol(0.38).size(0.7).price(17.99).build();
El método build()
puede hacer todas las cosas de validación que se necesitan para su constructor. El patrón del constructor también tiene varias ventajas de diseño (todas las cuales pueden no ser aplicables en su caso). Para personas de la tercera edad, consulte esta question
Está perdiendo varias ventajas del patrón de constructor, como ya se ha señalado (lo nuevo es feo y más difícil de mantener y filtra detalles en comparación con un constructor limpio).
Sin embargo, el que más echo de menos es que el patrón de constructor se puede usar para proporcionar lo que se denomina "interfaces fluidas" .
En lugar de esto:
ProductConfig config = new ProductConfig("Vodka");
config.alcohol = 0.38;
config.size = 0.7;
config.price = 17.99;
Product p = new Product(config);
Tu puedes hacer:
ProductFactory.create()
.drink("Vodka")
.whereAlcohoolLevelIs(0.38)
.inABottleSized(0.7)
.pricedAt(17.99)
.build();
No a todos les gustan las interfaces fluidas, pero definitivamente son un muy buen uso del patrón de constructor (todas las interfaces fluidas deben usar el patrón de constructor, pero no todos los patrones de constructor son interfaces fluidas).
Algunas grandes colecciones de Java, como las colecciones de Google, hacen un uso muy liberal y muy bueno de las "interfaces fluidas" . Elegiría estos días cualquier día con su enfoque de "caracteres más fáciles de escribir / menos" :
No debe utilizar el campo público, sino protegido o privado. Para acceder a continuación, debe utilizar los captadores y setter para mantener la encapsulación.
Personalmente, creo que el patrón de construcción a primera vista le ofrece un código más limpio donde se utilizan estos objetos. Por otro lado, el hecho de no tener captadores / setters no será muy utilizable por muchos marcos que esperan que los getters / set de camel case. Este es un serio retroceso que siento.
Lo que también me gusta con los captadores / setters es que ves claramente lo que estás haciendo: get o set. Siento que con el constructor estoy perdiendo un poco de claridad intuitiva aquí.
Sé que mucha gente ha leído un libro específico y que ahora, de repente, el patrón del constructor ha disfrutado de la exageración, como si fuera el nuevo iPhone. Sin embargo, no soy un adoptante temprano. Solo uso "la nueva forma" cuando realmente demuestra un gran ahorro de tiempo en cualquier territorio, ya sea rendimiento, mantenimiento, codificación ...
Mi experiencia práctica es que, por lo general, soy mejor con los getters / setters y los constructores. Me permite reutilizar estos POJO''s para cualquier propósito.
Aunque veo el propósito de su objeto de configuración, también creo que es aún más costoso que el constructor, ¿y para qué? ¿Qué está mal con los setters?
Tal vez necesitamos inventar una cláusula WITH: ejemplo, digamos que tienes
public Class FooBar() {
private String foo;
public void setFoo(String bar) {
this.foo = bar;
}
public String getFoo() {
return this.foo;
}
}
public static void main(String []args) {
FooBar fuBar = new FooBar();
String myBar;
with fuBar {
setFoo("bar");
myBar = getFoo();
}
}
Ah, no sé ... Creo que esto aún puede resultar en una escritura de código más rápida sin toda la molestia de la clase interna. ¿Alguien tiene conexiones con el gurú de Java de Oracle?
No se ve tan limpio como usar un objeto con un constructor, pero se ahorra tiempo de construcción del constructor. Y aún puede usar la clase como un pojo / bean regular que puede usarse en marcos ...
¿Les gustaría esta cláusula o piensan que preferiría apestar? Aclamaciones