-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(qmp): nuevo borrador de prendas
- Loading branch information
1 parent
bd35368
commit 0684034
Showing
2 changed files
with
178 additions
and
97 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,158 +1,168 @@ | ||
# Que Me Pongo | ||
# Qué Me Pongo V2 | ||
|
||
## Enunciado | ||
## Nuevos Requerimientos | ||
|
||
QuéMePongo es una empresa dedicada al armado de atuendos adecuados a las condiciones climáticas y preferencias de sus clientes. El servicio que provee se basa en tomar las prendas del guardarropas de une user y generar diferentes combinaciones posibles que cubran las necesidades de les mismes en términos de distintos factores climáticos tales como temperatura, viento, sol, lluvia, etc. Asimismo, se busca que estos atuendos se adecuen de la mejor forma a las sensibilidades particulares respecto de dichos factores de cada user y a sus gustos en el aspecto estético. | ||
Partiendo de la [versión anterior](./01-qmp.md), ahora se pide: | ||
|
||
## Primera Iteración | ||
- Especificar qué trama tiene la tela de una prenda (lisa, rayada, con lunares, a cuadros o un estampado). | ||
|
||
Comenzaremos definiendo que un atuendo es una combinación de prendas que tiene sentido usar juntas. Algunos ejemplos de atuendos podrían ser: | ||
- Crear una prenda especificando primero de qué tipo es. | ||
- Crear una prenda especificando en segundo lugar los aspectos relacionados a su material (colores, material, trama, etc) para evitar elegir materiales inconsistentes con el tipo de prenda. | ||
- Guardar un borrador de la la última prenda que empecé a cargar para continuar después. | ||
- Poder no indicar ninguna trama para una tela, y que por defecto ésta sea lisa. | ||
- Guardar una prenda solamente si esta es válida. | ||
|
||
- tus anteojos de sol favoritos, una remera de mangas cortas azul, el pantalón que te regaló tu mamá y unas zapatillas converse. | ||
- un pañuelo verde, una remera de mangas largas rayada, un pantalón de jean y un par de botas de cuero. | ||
- una musculosa de mickey, una pollera amarilla y unas crocs. | ||
> Por otro lado, el equipo de producto está analizando potenciales futuras funcionalidades para la aplicación y, a fin de tener una estimación de su complejidad, nos pidió que esbocemos una solución a los siguientes requerimientos, orientados a integrar el software con colegios e instituciones privadas: | ||
Como primer paso para generar los atuendos, les users de QuéMePongo identificaron el siguiente requerimiento como principal: | ||
- Poder recibir sugerencias de uniformes armados. | ||
- Que un uniforme siempre conste de una prenda superior, una inferior y un calzado | ||
- Poder configurar diferentes uniformes para distintas instituciones | ||
|
||
> Como user de QuéMePongo, quiero poder cargar prendas válidas para generar atuendos con ellas. | ||
## Solución | ||
|
||
Y luego, al consultar más sobre este requerimiento general, logramos definir que | ||
### Diagrama de classes | ||
|
||
### Como user de QuéMePongo, quiero... | ||
### Atendiendo Requerimientos. | ||
|
||
- especificar qué tipo de prenda estoy cargando (zapatos, camisa de mangas cortas, pantalón, etc). | ||
- identificar a qué categoría pertenece una prenda (parte superior, calzado, parte inferior, accesorios). | ||
- poder indicar de qué tela o material está hecha una prenda | ||
- poder indicar un color principal para mis prendas | ||
- poder indicar, si existe, un color secundario para mis prendas. | ||
- evitar que haya prendas sin tipo, tela, categoría o color primario | ||
- evitar que haya prendas cuya categoría no se condiga con su tipo. (Ej, una remera no puede ser calzado). | ||
> Especificar qué trama tiene la tela de una prenda | ||
## Resolución | ||
Para esto, primero necesitariamos un _dominio_ de tramas | ||
|
||
![Diagrama de Clases](images/que_me_pongo-cd.png) | ||
```java | ||
enum TramaTela{ | ||
LISA, RAYADA, A_CUADROS, LUNARES, ESTAMPADO | ||
} | ||
``` | ||
|
||
Nótese como se dejó, por el momento, de lado el concepto de `Atuendo`, dado que en esta iteración no se especifican requirimientos, ni comportamientos relacionados a él, salvo que se compone por prendas. Sin embargo su adición no debería levantar muchos conflictos pero sí _breaking changes_. | ||
> Crear una prenda especificando primero de qué tipo es. | ||
Utilizamos `enums` para establecer un dominio de datos, si bien en el caso de `Categoría` es más evidente su necesidad, consdieramos establecerlos también para `Color` y `Material`. | ||
Esto advertiría que el primer parametro es el `TipoPrenda`, por lo cual, un constructor podría ser... | ||
|
||
### Atendiendo Requerimientos | ||
```java | ||
class Prenda{ | ||
|
||
> Especificar qué tipo de prenda estoy cargando (zapatos, camisa de mangas cortas, pantalón, etc). | ||
public Prenda(TipoPrenda tipo){ | ||
// Validación de no NULL (Iteración I)... | ||
this.tipo = tipo; | ||
} | ||
} | ||
``` | ||
|
||
Primero definiendo un _dominio de datos_ para la categoría... | ||
> Especificando en segundo lugar los aspectos relacionados a su material | ||
Por lo cual modificado lo anterior... | ||
|
||
```java | ||
public enum Categoria{ | ||
ACCESORIO, | ||
PARTE_SUPERIOR, | ||
PARTE_INFERIOR, | ||
CALZADO | ||
class Prenda{ | ||
|
||
public Prenda(TipoPrenda tipoPrenda, TipoMaterial material, TramaPrenda trama, ...){ | ||
// Validación de no NULL (Iteración I)... | ||
} | ||
} | ||
``` | ||
|
||
Aplica lo mismo a los tipos de prenda. Pero además determinamos la categoría a la que se asocia un tipo. | ||
Luego para: | ||
|
||
```java | ||
public enum Tipo{ | ||
> Evitar elegir materiales inconsistentes con el tipo de prenda | ||
Deberiamos refactorizar nuestro `Material` de la iteración anterior, que era un enum, ahora pasará a llamarse `TipoMaterial`. | ||
|
||
Decidí delegar en el estos si acepta el tipo de trama, para lo cual cada tipo de material poseera una lista de tramas que admite. | ||
|
||
LENTES(Categoria.CALZADO), | ||
// ... TIPO(CATEGORIA) | ||
CAMISA(Categoria.PARTE_SUPERIOR); | ||
```java | ||
enum TipoMaterial{ | ||
// Materiales... | ||
|
||
private Categoria categoria; | ||
List<TramaTela> tramas; | ||
|
||
public Categoria categoria(); | ||
Tipo(Categoria cat){ | ||
categoria = cat; | ||
boolean admiteTrama(TramaTela trama){ | ||
tramas.stream().any( tramaAdmitida -> tramaAdmitida.equals(trama)); | ||
} | ||
} | ||
``` | ||
|
||
Por otro lado, `Material` ahora será una clase que sólo dejaría combinar los tipos de materiales ciertas tramas. Lanzando un `RuntimeException` de no ser compatibles. | ||
|
||
```java | ||
public class Prenda{ | ||
public Tipo tipo; | ||
class Material{ | ||
TipoMaterial material; | ||
TramaTela trama; | ||
|
||
Material(Tipomaterial material, trama){ | ||
if(!this.material.admiteTrama(trama)) | ||
throw new MaterialInvalido(); | ||
} | ||
} | ||
``` | ||
|
||
> Identificar a qué categoría pertenece una prenda (parte superior, calzado, parte inferior, accesorios). | ||
Luego... | ||
Análogamente, cada Tipo de Prenda decidirá si acepta el material. | ||
|
||
```java | ||
public class Prenda{ | ||
public Tipo tipo; | ||
public Categoria categoria(){} | ||
} | ||
``` | ||
enum TipoPrenda{ | ||
// Tipos y categorías resueltas... | ||
|
||
> Poder indicar de qué tela o material está hecha una prenda | ||
List<TipoMaterial> materiales; | ||
|
||
```java | ||
public enum Material{ | ||
TELA1, | ||
MATERIAL1, | ||
// Materiales o Telas... | ||
boolean admiteMaterial(TipoMaterial trama){ | ||
return tramas | ||
.stream() | ||
.any( materialAdmitido -> materialAdmitido.equals(material)); | ||
} | ||
} | ||
``` | ||
|
||
Finalmente la `Prenda` valida si el `TipoPrenda` acepta el `TipoMaterial`, luego `new Material(m, t)` verificará si puede asignarse esa trama a ese material. | ||
|
||
```java | ||
public class Prenda{ | ||
public Tipo tipo; | ||
public Categoria categoria(){} | ||
public Material material; | ||
} | ||
``` | ||
class Prenda{ | ||
|
||
> Poder indicar un color principal para mis prendas y, si existe, un color secundario para mis prendas. | ||
public Prenda(TipoPrenda tipoPrenda, TipoMaterial material, TramaTela trama, ...){ | ||
// Validación de no NULL (Iteración I)... | ||
|
||
Determinamos un dominio para los colores... | ||
// Nueva validación de consistencia | ||
if(!tipoPrenda.admiteMaterial(material)) | ||
throw new PrendaInvalida("El tipo de prenda no admite el material seleccionado."); | ||
|
||
```java | ||
public enum Color{ | ||
NINGUNO | ||
// Colores disponibles | ||
// ... | ||
this.tipoPrenda = tipoPrenda; | ||
// El material válida si admite la trama. | ||
this.material = new Material(material, trama); | ||
// .. | ||
} | ||
} | ||
``` | ||
|
||
Si modificamos nuestra `Prenda` | ||
> Poder no indicar ninguna trama para una tela, y que por defecto ésta sea lisa. | ||
Podemos contar con otro constructor para `Prenda` dónde llame al siguiente pasándole `TramaTela.LISA` como valor default. | ||
|
||
```java | ||
public class Prenda{ | ||
public Tipo tipo; | ||
public Categoria categoria(){} | ||
public Material material; | ||
public Color color1; | ||
public Color color2; | ||
} | ||
public Prenda(TipoPrenda tipoPrenda, TipoMaterial material, Color color1) { | ||
this(tipoPrenda, material, TramaTela.LISA, color1, null); | ||
} | ||
``` | ||
|
||
> Evitar que haya prendas sin tipo, tela, categoría o color primario | ||
> Guardar un borrador de la la última prenda que empecé a cargar para continuar después. | ||
Para ello utilizamos el _keyword_ `final` para declarar una constante que **requiera** ser inicializada. | ||
Asumo que después de la Iteración I, no pueden permanecer algunos campos vacíos, luego para _continuar después_ deberían haber algunos `setters`. | ||
Sin embargo opto por la alternativa de crear una nueva clase `Borrador` que utilice las propiedades de Prenda para luego construir una, y esta verifique que siempre se cree una válida. | ||
|
||
```java | ||
public class Prenda{ | ||
public final Tipo tipo; | ||
public Categoria categoria(){} | ||
public final Color color1; | ||
public Color color2 = Color.NINGUNO; | ||
public final Material material; | ||
} | ||
``` | ||
class Borrador { | ||
|
||
> Evitar que haya prendas cuya categoría no se condiga con su tipo. (Ej, una remera no puede ser calzado). | ||
TipoPrenda tipo; | ||
TipoMaterial material; | ||
TramaTela trama; | ||
Color color1; | ||
Color color2; | ||
|
||
Una propuesta sería que la prenda no posea el atributo de `categoría`, sino que lo consultáse directamente de su tipo | ||
// Setters correspondientes | ||
|
||
```java | ||
public class Prenda{ | ||
//... | ||
public Categoría categoria(){ | ||
return tipo.categoria(); | ||
// Delego en la prenda la validación. Recordemos que de las tramas default se encarga el material. | ||
public Prenda crearPrenda() { | ||
return new Prenda(tipo, material, trama, color1, color2); | ||
} | ||
} | ||
``` | ||
|
||
Entoncés, por definición, la categoría **SIEMPRE** estaría en coincidencia con el `tipo`. | ||
> Guardar una prenda solamente si esta es válida. | ||
|
||
Tomo cómo hipótesis de trabajo que **todas** las instancias de `Prenda` **existentes son válidas** ya que de no serlo no se instanciarían en primer lugar (_fail fast_). |
71 changes: 71 additions & 0 deletions
71
01-lecture/quemepongo/src/main/java/quemepongo/Borrador.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package quemepongo; | ||
|
||
import java.util.Objects; | ||
|
||
/** | ||
* Builder de prendas. | ||
* | ||
* @since 04.27.2021 | ||
* @version 1.0 | ||
*/ | ||
public class Borrador { | ||
|
||
TipoPrenda tipo; | ||
|
||
TipoMaterial material; | ||
|
||
TramaTela trama; | ||
|
||
Color color1; | ||
|
||
Color color2; | ||
|
||
/** | ||
* Borrador desde cero. | ||
* | ||
* @since 1.0 | ||
*/ | ||
public Borrador() { | ||
this(null); | ||
} | ||
|
||
/** | ||
* Borrador de una prenda de acuerdo a un template. | ||
* | ||
* @param template El blueprint de la prenda. | ||
* @since 1.0 | ||
*/ | ||
public Borrador(Prenda template) { | ||
if (!Objects.isNull(template)) { | ||
this.tipo = template.tipo; | ||
this.material = template.material.nombre; | ||
this.trama = template.material.trama; | ||
this.color1 = template.color1; | ||
this.color2 = template.color2; | ||
} | ||
} | ||
|
||
public void especificarTipo(TipoPrenda tipo) { | ||
this.tipo = tipo; | ||
} | ||
|
||
public void especificarMaterial(TipoMaterial material) { | ||
this.material = material; | ||
} | ||
|
||
public void especificarColorPrimario(Color color1) { | ||
this.color1 = color1; | ||
} | ||
|
||
public void especificarColorSecundario(Color color2) { | ||
this.color2 = color2; | ||
} | ||
|
||
public void especificarTrama(TramaTela trama) { | ||
this.trama = trama; | ||
} | ||
|
||
public Prenda crearPrenda() { | ||
return new Prenda(tipo, material, trama, color1, color2); | ||
} | ||
} |