Skip to content

Instantly share code, notes, and snippets.

@flbulgarelli
Created June 24, 2022 21:52
Show Gist options
  • Save flbulgarelli/3a09fb7450b54ecb696bb74324239cc4 to your computer and use it in GitHub Desktop.
Save flbulgarelli/3a09fb7450b54ecb696bb74324239cc4 to your computer and use it in GitHub Desktop.
QMP5
// ## Antes de empezar
// El código y el diagrama
class Prenda {
}
class Guardarropa {
private String nombre;
private List<Prenda> prendas;
}
class Usuario {
private Guardarropa guardarropa;
}
// ## Punto 1: Guardarropa múltiples
//
// Como usuarie de QuéMePongo, quiero poder manejar varios guardarropa para separar mis prendas según diversos criterios (ropa de viaje, ropa de entrecasa, etc).
class Usuario {
private List<Guardarropa> guardarropas;
}
dani.crearGuardarropa("deportivo")
dani.agregarGuardarropa(new Guardarropa("deportivo"))
// * Los criterios no son parte del software
// * Es un requerimiento trivial
// ## Punto 2: Guardarropas compartidos
// Como usuarie de QuéMePongo, quiero poder crear guardarropas compartidos con otros usuaries (ej, ropa que comparto con mi hermane).
// Opción 1: para tener guardarropas compartidos,
// simplemente compartimos al guardarropas
Guardarropa guardarropa = feli.crearGuardarropa()
guardarropa.compartirCon(dani)
// o incluso...
dani.agregarGuardarropa(guarropa)
class Guardarropa {
void compatirCon(usuarie) {
usuarie.agregarGuardarropa(this);
}
}
// Opción 2: Agregar a todos los guardarropas una lista de usuaries
Guardarropa guardarropa = feli.crearGuardarropa()
guardarropa.compartirCon(dani)
class Usuario {
void crearGuardarropa() {
guardarropa = new Guardarropa()
guardarropa.compartirCon(this)
guardarropas.add(guardarropa)
return guardarropa
}
}
class Guardarropa {
// ¿Pero no hace falta une admin?
List<Usuario> colaboradores;
void compatirCon(usuarie) {
colaboradores.add(usuarie);
usuarie.agregarGuardarropa(this);
}
}
// y probablemente haya más grises que no estoy contemplando en esta solución....
// Opción 3: Subclasificar
feli.crearGuardarropaCompartidoCon(dani)
// o incluso...
feli.agregarGuardarropa(new GuardarropaCompartido([dani]))
// ## Punto 3 y 4: Las propuestas de agregar y quitar
// Como usuarie de QuéMePongo, quiero que otro usuario me proponga tentativamente agregar una prenda al guardarropas.
// Como usuarie de QuéMePongo, quiero que otro usuario me proponga tentativamente quitar una prenda del guardarropas.
// * ¿Dónde va? ¿Usuario o guardarropas? ¿A quien afecta? ¿Hubiera sido lo mismo si hubieramos tenido sólo un guardarropas?
// * Se puede hacer bien más sencillo
guardarropa.agregarPropuestaDeAgregado(saco)
guardarropa.agregarPropuestaDeQuitado(saco)
// o incluso...
guardarropa.proponerAgregar(saco)
guardarropa.proponerRemover(saco)
// quizás podría ser algo así... (sólo la implementación nos dirá luego si tiene sentido)
saco.ofrecerAgregarA(guardarropa)
saco.ofrecerQuitarA(guardarropa)
class Guardarropa {
List<Prenda> prendas;
// y demás atributos con los que ya contábamos...
List<Prenda> propuestasDeAgregado;
void proponerAgregar(Prenda prenda) {
propuestasDeAgregado.add(prenda)
}
}
// pero cuando hay que extender se vuelve repetitivo y disminuye su mantenibilidad...
guardarropa.proponerRemover(saco)
class Guardarropa {
List<Prenda> propuestasDeAgregado;
List<Prenda> propuestasDeRemocion;
void proponerAgregar(Prenda prenda) {
propuestasDeAgregado.add(prenda)
}
void proponerRemover(Prenda prenda) {
propuestasDeRemocion.remove(prenda)
}
}
// pero aún así, funciona, ¿no?
Prenda saco = new Prenda(...)
juli.proponerAgregar(dani, saco)
class Usuarie {
void proponerAgregar(destinatarie, prenda) {
destinatarie.proponerAgregar(prenda)
}
}
// no vamos a ir por acá, porque esto termina siendo un missplaced method
// que parte de la confusión entre actor y objeto
// Punto 5: listar, aceptar y rechazar
// Como usuarie de QuéMePongo, necesito ver todas las propuestas de modificación (agregar o quitar prendas) del guardarropas y poder aceptarlas o rechazarlas.
// * Listar no necesita mucho más
// * Pero ahora sí hay comportamiento (y polimórfico)
class Guardarropa {
List<Propuesta> propuestas;
// BEGIN OPCIONAL
Propuesta proponerAgregar(Prenda prenda) {
Propuesta propuesta = new PropuestaAgregar(prenda);
this.proponer(propuesta)
return propuesta
}
Propuesta proponerRemover(Prenda prenda) {
Propuesta propuesta = new PropuestaRemover(prenda)
this.proponer(propuesta)
return propuesta
}
// END OPCIONAL
void proponer(Propuesta propuesta) {
propuestas.add(propuesta)
}
}
guardarropa.proponerAgregar(saco)
guardarropa.proponerRemover(saco)
interface Propuesta {
void aceptar(Guardarropa guardarropa)
void rechazar(Guardarropa guardarropa)
}
class PropuestaAgregar implements Propuesta {
private Prenda prenda;
constructor(prenda) { ... }
void aceptar(guardarropa) {
guardarropas.agregarPrenda(prenda)
}
}
class PropuestaRemover implements Propuesta {
private Prenda prenda;
constructor(prenda) { ... }
void aceptar(guardarropa) {
guardarropas.quitarPrenda(prenda)
}
}
// opción 1
// t_0
propuesta = guardarropa.proponerAgregar(saco)
// t_1
propuesta.aceptar(guardarropa)
// nota: si el guardarropa hubiera sido estado de la propuesta, se podría haber omitido el argumento guardarropa, así:
// propuesta.aceptar()
// opción 2
// a favor: nos hace más fácil validar y/ o remover las propuestas
// en contra: no nos permite hacer nada que no pudieramos hacer de la otra forma
//
// De todas formas, aunque podría ser, en principio no hay en este ejercicio un buen motivo para hacer validaciones
// t_0
propuesta = guardarropa.proponerAgregar(saco)
// t_1
guardarropa.aceptarPropuesta(propuesta)
class Guardarropa {
void aceptarPropuesta(propuesta) {
propuesta.aceptar(this)
propuestas.remove(propuesta)
}
void rechazarPropuesta(propuesta) {
propuesta.rechazar(this)
propuestas.remove(propuesta)
}
// NOTA MENTAL: esta idea de remover también sería necesaria en la variante anterior
// ¿es necesario?
// ¿el flujo no podría garantizar que siempre se reciban propuestas pendientes?
// No estamos diciendo que SIEMPRE será innecesario,
// pero ojo que no es un requerimiento explícito en este punto
boolean estaPendiente(propuesta) {
return propuestas.contains(propuesta)
}
}
// variantes erróneas:
class Guardarropa {
void aceptarPropuesta(propuesta) {
propuesta = propuestas.find { it => it == propuesta } // MAL MAL MAL A NIVEL OBJETOS
propuesta.aceptar(this)
propuestas.remove(propuesta)
}
}
class Guardarropa {
void aceptarPropuesta(propuestaId) {
propuesta = propuestas.find { it => it.id == propuestaId } // MAL MAL MAL porque estamos mezclando ideas de persistencia que podŕiamos resolver de formas más sencillas y automáticas
propuesta.aceptar(this)
propuestas.remove(propuesta)
}
}
// * Marcar vs sacar de colecciones
// * ¿Es necesario mantener despues de ejecutar?
// * hacer o no hacer, esa es la pregunta
// * Acá podría surgir una abstracta
class PropuestaDeAgregado implements Propuesta {
...
void rechazar(...) {
// hacer o no hacer, esa es la cuestión
}
}
// * Nota lateral: IInterfaz -.-
// * ¿Dobles relaciones?
// Punto 5: arrepintiéndonos
// Como usuarie de QuéMePongo, quiero poder deshacer las propuestas de modificación que haya aceptado.
// A nivel implementación el deshacer
// se trata de simplemente jugar con el add y remove de la lista
// de propuestas y tener un mensaje adicional que hace lo contrario al
// aceptar
// * Es otra justificación para esta estructura, pero no es necesario
// * ¿Funcionará? Depende del orden
// * Nota final sobre el asincronismo
@flbulgarelli
Copy link
Author

diagrama

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment