tutorial oxygen interfaz interfaces instalar grafica español ejemplos diseño java coding-style builder code-cleanup object-construction

java - oxygen - ¿Cómo inicio clases con muchos campos de una manera elegante?



windowbuilder eclipse photon (6)

En mi aplicación, tengo que instanciar muchos tipos diferentes de objetos. Cada tipo contiene algunos campos y debe agregarse a un tipo que contiene. ¿Cómo puedo hacer esto de una manera elegante?

Mi paso de inicialización actual se ve algo como esto:

public void testRequest() { //All these below used classes are generated classes from xsd schema file. CheckRequest checkRequest = new CheckRequest(); Offers offers = new Offers(); Offer offer = new Offer(); HotelOnly hotelOnly = new HotelOnly(); Hotel hotel = new Hotel(); Hotels hotels = new Hotels(); Touroperator touroperator = new Touroperator(); Provider provider = new Provider(); Rooms rooms = new Rooms(); Room room = new Room(); PersonAssignments personAssignments = new PersonAssignments(); PersonAssignment personAssignment = new PersonAssignment(); Persons persons = new Persons(); Person person = new Person(); Amounts amounts = new Amounts(); offers.getOffer().add(offer); offer.setHotelOnly(hotelOnly); room.setRoomCode("roomcode"); rooms.getRoom().add(room); hotels.getHotel().add(hotel); hotel.setRooms(rooms); hotelOnly.setHotels(hotels); checkRequest.setOffers(offers); // ...and so on and so on }

Realmente quiero evitar escribir código como este, porque es un poco complicado tener que crear una instancia de cada objeto por separado y luego inicializar cada campo en varias líneas de código (por ejemplo, tener que llamar a new Offer() y luego setHotelOnly(hotelOnly) y luego add(offer) ).

¿Qué métodos elegantes puedo usar en lugar de lo que tengo? ¿Hay alguna " Factories " que pueda ser usada? ¿Tiene alguna referencia / ejemplo para evitar escribir código como este?

Estoy realmente interesado en implementar código limpio.

Contexto:

Estoy desarrollando una aplicación RestClient para enviar solicitudes de publicaciones a un servicio web.

La API se representa como un archivo de xsd schema y creé todos los objetos con JAXB

Antes de enviar una solicitud, tengo que crear una instancia de muchos Objetos porque tienen dependencias entre sí. (Una oferta tiene hoteles, un hotel tiene habitaciones, una habitación tiene personas ... y estas clases son las generadas)

Gracias por tu ayuda.


Idealmente, un objeto no debería preocuparse por crear instancias de sus dependencias. Solo debe preocuparse por las cosas que se supone que debe hacer con ellos. ¿Has considerado algún marco de inyección de dependencia? Spring o Google''s Juice son bastante versátiles y tienen una huella pequeña.

La idea es simple: usted declara las dependencias y deja que el marco decida cuándo / cómo / dónde crearlas y las "inyecte" en sus clases.

Si no desea utilizar ningún marco, puede tomar notas de diseño de ellos e intentar emular sus patrones de diseño y modificarlo para su caso de uso.

Además, puede simplificar las cosas hasta cierto punto haciendo un uso adecuado de las Colecciones. Por ejemplo, ¿qué característica adicional tienen las Offers aparte de almacenar una colección de Offer ? No estoy seguro de cuáles son sus limitaciones pero, si puede hacer que esa parte sea un poco más limpia, tendría enormes ganancias en todos los lugares donde está creando una instancia de los objetos.


Puede usar un constructor o un patrón de constructor o una variación del patrón de constructor para solucionar el problema de tener demasiados campos en su paso de inicialización.

Voy a extender un poco su ejemplo para demostrar mi punto de por qué estas opciones son útiles.

Entendiendo tu ejemplo:

Digamos que una Offer es simplemente una clase contenedora para 4 campos:

public class Offer { private int price; private Date dateOfOffer; private double duration; private HotelOnly hotelOnly; // etc. for as many or as few fields as you need public int getPrice() { return price; } public Date getDateOfOffer() { return dateOfOffer; } // etc. }

Tal como se muestra en su ejemplo, para establecer valores en estos campos, use los configuradores:

public void setHotelOnly(HotelOnly hotelOnly) { this.hotelOnly = hotelOnly; }

Desafortunadamente, esto significa que si necesita una oferta con valores en todos los campos, tiene que hacer lo que tiene:

Offers offers = new Offers(); Offer offer = new Offer(); offer.setPrice(price); offer.setDateOfOffer(date); offer.setDuration(duration); offer.setHotelOnly(hotelOnly); offers.add(offer);

Ahora veamos cómo mejorar esto.

Opción 1: ¡Constructores!

Un constructor que no sea el constructor predeterminado (el constructor predeterminado actualmente es Offer() ) es útil para inicializar los valores de los campos en su clase.

Una versión de Offer usando constructores se vería así:

public class Offer { private int price; private Date dateOfOffer; //etc. // CONSTRUCTOR public Offer(int price, Date dateOfOffer, double duration, HotelOnly hotelOnly) { this.price = price; this.dateOfOffer = dateOfOffer; //etc. } // Your getters and/or setters }

Ahora, podemos inicializarlo en una línea!

Offers offers = new Offers(); Offer offer = new Offer(price, date, duration, hotelOnly); offers.add(offer);

Aún mejor, si nunca usa otra offer no sea esa única línea: offers.add(offer); ¡Ni siquiera necesitas guardarlo en una variable!

Offers offers = new Offers(); offers.add( new Offer(price, date, duration, hotelOnly) ); // Works the same as above

Opción 2: Patrón de constructor

Un patrón de construcción es útil si desea la opción de tener valores predeterminados para cualquiera de sus campos.

El problema que resuelve un patrón de constructor es el siguiente código desordenado:

public class Offer { private int price; private Date dateOfOffer; // etc. // The original constructor. Sets all the fields to the specified values public Offer(int price, Date dateOfOffer, double duration, HotelOnly hotelOnly) { this.price = price; this.dateOfOffer = dateOfOffer; // etc. } // A constructor that uses default values for all of the fields public Offer() { // Calls the top constructor with default values this(100, new Date("10-13-2015"), 14.5, new HotelOnly()); } // A constructor that uses default values for all of the fields except price public Offer(int price) { // Calls the top constructor with default values, except price this(price, new Date("10-13-2015"), 14.5, new HotelOnly()); } // A constructor that uses default values for all of the fields except Date and HotelOnly public Offer(Date date, HotelOnly hotelOnly) { this(100, date, 14.5, hotelOnly); } // A bunch more constructors of different combinations of default and specified values }

¿Ves lo desordenado que puede llegar?

El patrón de construcción es otra clase que pones dentro de tu clase.

public class Offer { private int price; // etc. public Offer(int price, ...) { // Same from above } public static class OfferBuilder { private int buildPrice = 100; private Date buildDate = new Date("10-13-2015"); // etc. Initialize all these new "build" fields with default values public OfferBuilder setPrice(int price) { // Overrides the default value this.buildPrice = price; // Why this is here will become evident later return this; } public OfferBuilder setDateOfOffer(Date date) { this.buildDate = date; return this; } // etc. for each field public Offer build() { // Builds an offer with whatever values are stored return new Offer(price, date, duration, hotelOnly); } } }

Ahora, no puede tener que tener tantos constructores, pero aún puede elegir qué valores desea dejar predeterminados y cuáles inicializar.

Offers offers = new Offers(); offers.add(new OfferBuilder().setPrice(20).setHotelOnly(hotelOnly).build()); offers.add(new OfferBuilder().setDuration(14.5).setDate(new Date("10-14-2015")).setPrice(200).build()); offers.add(new OfferBuilder().build());

Esa última oferta es simplemente una con todos los valores por defecto. Los demás son valores predeterminados excepto los que establezco.

¿Ves cómo eso hace las cosas más fáciles?

Opción 3: Variación del patrón del constructor

También puede usar el patrón de construcción simplemente haciendo que sus configuradores actuales devuelvan el mismo objeto de Oferta. Es exactamente lo mismo, excepto sin la clase extra OfferBuilder .

Advertencia: como indica el usuario WW a continuación, esta opción rompe JavaBeans, una convención de programación estándar para clases de contenedor, como Oferta . Por lo tanto, no debe usar esto para fines profesionales y debe limitar su uso en sus propias prácticas.

public class Offer { private int price = 100; private Date date = new Date("10-13-2015"); // etc. Initialize with default values // Don''t make any constructors // Have a getter for each field public int getPrice() { return price; } // Make your setters return the same object public Offer setPrice(int price) { // The same structure as in the builder class this.price = price; return this; } // etc. for each field // No need for OfferBuilder class or build() method }

Y tu nuevo código de inicialización es

Offers offers = new Offers(); offers.add(new Offer().setPrice(20).setHotelOnly(hotelOnly)); offers.add(new Offer().setDuration(14.5).setDate(new Date("10-14-2015")).setPrice(200)); offers.add(new Offer());

Esa última oferta es simplemente una con todos los valores por defecto. Los demás son valores predeterminados excepto los que establezco.

Entonces, si bien es mucho trabajo, si desea limpiar su paso de inicialización, necesita usar una de estas opciones para cada una de sus clases que tienen campos en ellas. Luego use los métodos de inicialización que incluí con cada método.

¡Buena suerte! ¿Algo de esto necesita más explicación?


Siempre he preferido usar builder-pattern-with-a-twist porque proporciona mucho más que el enfoque básico del patrón de constructor.

Pero, ¿qué sucede cuando quiere decirle al usuario que debe llamar a un método de construcción u otro, ya que es crucial para la clase que está tratando de construir?

Piense en un constructor para un componente de URL. ¿Cómo se podría pensar en los métodos del generador para encapsular el acceso a los atributos de URL, son igualmente importantes, interactúan entre sí, etc.? Mientras que los parámetros de consulta o el fragmento son opcionales, el nombre de host no lo es; podría decir que también se requiere un protocolo, pero para eso puede tener un valor predeterminado significativo, como http, ¿verdad?

De todos modos, no sé si esto tiene sentido para su problema particular, pero pensé que valdría la pena mencionar para que otros lo echen un vistazo.


Dozer framework proporciona una buena forma de copiar valores desde el objeto ws a su dto. Aquí hay otro example . Además, si los nombres de getter / setter son los mismos de ambas clases, no necesita un convertidor personalizado



Simplemente proporciono esta respuesta porque se mencionó en un comentario y creo que también debería formar parte de esta enumeración de patrones de diseño.

Patrón de diseño de objeto nulo

Intención

La intención de un objeto nulo es encapsular la ausencia de un objeto proporcionando una alternativa sustituible que ofrezca un comportamiento por defecto adecuado para no hacer nada. En definitiva, un diseño donde "nada vendrá de nada".

Usa el patrón Objeto nulo cuando

  • un objeto requiere un colaborador. El patrón de Objeto nulo no introduce esta colaboración, hace uso de una colaboración que ya existe
  • Algunas instancias de colaboradores no deberían hacer nada.
  • Desea abstraer el manejo de null lejos del cliente.

Aquí encontrará la parte completa del patrón de diseño "Objeto nulo"