Está en la página 1de 16

Tarea 1: Bad Smell

Bad Smell - “Envy Feature”


Equipo: Armagedon
Tema: 7 - Envy Feature
Integrantes:
1. Quinteros Gutierrez Fernando
2. Valdivia Viscarra Nicolas Mateo
3. Vargas Orellana José Roberto
4. Vargas Luizaga Cristian Joel

Feature envy es un bad smell en el que una clase "envidia" a otra clase tal cual como su
nombre lo menciona en la traducción al español “Envidia de clase”. Sucede comúnmente
cuando el programador expone el funcionamiento interno de otras clases usando muchas
funcionalidades de otras clases relacionándose con problemas de acoplamiento y cohesión,
con los cuales puede llegar a provocar efectos en cascada. El nombre del bad smell debido a
que la clase en cuestión quiere ser la otra clase tanto que usa sus métodos en exceso. Es
común que se produzca esta práctica cuando movamos propiedades a otra clase, es cuando
nos debería interesar también mover algunas funcionalidades que estén relacionadas con la
clase de origen.

Una de las características más comunes


para poder identificar este bad smell es
cuando se ve un "método en el lugar
equivocado", usa una cantidad grande de
métodos y campos de otra clase usándose
más de donde está definido.
La principal ganancia es una mejor organización del código debido a que estará centralizado
las características de nuestro interés en una sola clase permitiendo que sea sencillo de
mantener debido a que los atributos y métodos estarán en la misma clase. Es vital ignorar esta
práctica cuando el objetivo principal es tener la capacidad de cambiar dinámicamente el
comportamiento de la funcionalidad sin tener muchas complicaciones.

Refactorización sobre “Envy Feature”


Move Method

“Move Method” se aplica cuando un método se usa más en otra clase ya sea en una relación
de herencia o de utilidad, que en la propia clase donde se creó.

Para implementar esta técnica se crea un nuevo método en las clases o clase que más usa el
método, o colocar como una referencia dentro de un método creado dentro de las demás
clases para dar más utilidad al nuevo método creado. Se refactoriza para crear una coherencia
interna dentro de la clase y también reduce la dependencia entre clases.

Procedimiento

1. Inspeccione las características utilizadas por el método anterior en su clase, en


caso de utilizar algún método o propiedad mover junto a método a trasladar. A
veces es mucho más fácil mover una gran cantidad de métodos que establecer
relaciones entre ellos en diferentes clases, de tal forma que se evite “Feature
Envy”

Asegúrese de que el método no esté declarado en superclases y subclases. Si este es el caso,


tendrá que abstenerse de moverse o implementar una especie de polimorfismo en la clase
receptora para garantizar la funcionalidad variable de un método dividido entre las clases
donantes.

2. Declare el nuevo método en la clase receptora. Es posible que desee dar un nuevo
nombre al método que sea más apropiado para él en la nueva clase.
3. Decida cómo se referirá a la clase destinataria. Es posible que ya tenga un campo o
método que devuelva un objeto apropiado, pero si no es así, deberá escribir un nuevo
método o campo para almacenar el objeto de la clase destinataria.

Ahora tiene una forma de hacer referencia al objeto destinatario y un nuevo método en su
clase. Con todo esto en su haber, puede convertir el método antiguo en una referencia al
nuevo método.

4. Eche un vistazo: ¿puede eliminar el método anterior por completo? Si es así, coloque
una referencia al nuevo método en todos los lugares que usan el antiguo.
Extract Method

Con “Extract Method” agrupas un fragmento de código y lo mueves a un método separado


que realiza la misma función, y se reemplaza el código original con una llamada al método
creado.

Procedimiento

1. Identificar fragmento de código a extraer.


2. Copiar el fragmento a un método nuevo.
3. Identificar las variables que se declaran fuera del método nuevo que son necesarias
para realizar las operaciones y pasarlas como argumentos (no se debe pasar un arreglo
como argumento).
4. Observar las variables provisionales que solo se utilizan en el nuevo método, en este
caso deben ser declaradas dentro del método.
5. Identificar si alguna de las variables locales que son usadas posteriormente es
modificada por el método, en este caso el método tiene que devolver dicho valor. Si
son muchas las variables modificadas o resulta muy complicado retornar los valores
no se debe usar “Extract Method”.
6. Llamar al método.

Ejemplo
Debido a la pandemia del Coronavirus, los hospitales requirieron un sistema eficiente para
obtener la información de los pacientes registrados. En el cual debían obtener la información
más completa del paciente donde debía especificar el nombre completo de la persona,
Localidad (País, Ciudad y dirección), números de teléfono (Casa y Celular) y además de
calcular el precio por los días hospitalizado si el costo del día es 1500 bolivianos, cabe aclarar
que los pacientes deben tener un porcentaje de descuento y se debe imprimir el costo a pagar
incluyendo el descuento. Debido a la agilidad que se necesitó el software resultante contiene
muchos bad Smells, la mayoría de tipo “Envy Feature” se pidió una actualización para el
sistema de tal forma que sea escalable para que próximos desarrolladores puedan mantener el
código.
Análisis y Refactorización
En la clase hospital tenemos dos métodos los cuales son: imprimirPaciente y
calcularPrecioPaciente. En los cuales podemos observar que la clase hospital está usando en
exceso métodos de la clase paciente, lo cual nos indica que es el bad smell “Feature Envy”.

public class Hospital


{
private ArrayList <Paciente> pacientes;
private int precioDia;

public Hospital(){
precioDia = 1500 ;
pacientes = new ArrayList <Paciente> ();
}

public void imprimirPaciente (String nombrePaciente){


Paciente paciente = buscarPaciente (nombrePaciente);
System.out.println ("El nombre del paciente es: " + paciente.getNombreCompleto());
System.out.println ("La localidad del paciente es: "
+ paciente.getLocalidad().getPais()
+ "/" +paciente.getLocalidad().getDireccion()
+ "/" + paciente.getLocalidad().getCiudad());
System.out.println("El numero fijo es: " + paciente.getNumeros().getCasa());
System.out.println("El numero de celular es: " + paciente.getNumeros().getCelular());
}

public void calcularPrecioPaciente(String nombre){


Paciente datosPaciente = buscarPaciente(nombre);
int precio = datosPaciente.getDiasInternado() * precioDia;
double precioDescuento = precio * (datosPaciente.getDescuento() * 0.01);
double precioTotal = precio - precioDescuento;
System.out.println("El precio total del paciente es: " + precioTotal);
}
// ...
}

En el código mencionado se contempla un code smell “feature envy” por la presencia de


múltiples llamadas métodos y atributos de la clase “Persona” en el método imprimirPaciente
de la clase “Hospital”.

Podemos observar la presencia de otro bad smell en el método “calcularPrecioPaciente”, por


lo que dicho método, no debería encontrarse almacenado en la clase actual y que lo más
recomendable sería que el método se encuentre en la clase paciente, el cual para más
optimización de código sería bueno que sea separada en dos métodos.

Refactorización:
Para la refactorización se realizó un análisis de ambos métodos, es de vital importancia
mantener la búsqueda del paciente en ambos, pero se extraerán las operaciones principales y
se moverán al método con el cual se está usando más atributos o métodos, en este caso la
clase paciente. Para ello se comienza con el método de imprimirPaciente, el primer paso
será copiar el método a la clase paciente, posteriormente se cambiará las llamadas de los
getters por las mismas propiedades. También se renombrará el nombre del método dentro la
clase paciente de “imprimirPaciente” a “imprimir” para una mejor comprensión del mismo en
invocaciones de otros lados. En esta parte podemos observar de una manera a grandes rasgos
la parte de “extract method” y “move method”.

public class Hospital


{
// ...
public void imprimirPaciente(String nombrePaciente){
Paciente paciente = buscarPaciente (nombrePaciente);
paciente.imprimir();
}
}

public class Paciente


{
public void imprimir (){
System.out.println ("El nombre del paciente es: " + nombreCompleto);
System.out.println ("La localidad del paciente es: "
+ localidad.getPais()
+ "/" + localidad.getDireccion()
+ "/" + localidad.getCiudad());
System.out.println("El numero fijo es: " + numeros.getCasa());
System.out.println("El numero de celular es: " + numeros.getCelular());
}
}

Una vez completada la refactorización de este método, se prosigue con el siguiente método
de interés, el cual sería “calcularPrecioPaciente”. Al igual que el anterior ejemplo es
necesario copiar el método, a la clase Paciente de la clase Hospital, de igual forma
renombrándolo, pero se mantendrá el método de la clase hospital debido a que es necesario
obtener mediante una búsqueda el paciente. En este caso es buena práctica separar la lógica
de la función de clase para crear un código más mantenible. Por lo cual se agregara el método
“calcularPrecio” el cual retornara el precio descontando el descuento del paciente internado.
Finalmente tendremos el método “imprimirPrecio” el cual llamará al método “calcularPrecio”
e imprimirá el resultado, este será invocado en Hospital.

public class Paciente


{
// ...
private double calcularPrecio (int precioDia) {
int precio = diasInternado * precioDia;
double precioDescuento = precio * (descuento * 0.01);
double precioTotal = precio - precioDescuento;
return precioTotal;
}

public void imprimirPrecio(int precioDia){


System.out.println("El precio total del paciente es : "+ calcularPrecio (precioDia));
}
}

public class Hospital


{
// ...
public void calcularPrecioPaciente(String nombre){
Paciente datosPaciente = buscarPaciente(nombre);
datosPaciente.imprimirPrecio(precioDia);
}
}
Ejemplo Código completo
Código no refactorizado
Clase Hospital
import java.util.ArrayList;
public class Hospital
{
private ArrayList <Paciente> pacientes;
private int precioDia;

public Hospital(){
precioDia = 1500 ;
pacientes = new ArrayList <Paciente> ();
}

public void imprimirPaciente (String nombrePaciente){


Paciente paciente = buscarPaciente (nombrePaciente);
System.out.println ("El nombre del paciente es: " + paciente.getNombreCompleto());
System.out.println ("La localidad del paciente es: "
+ paciente.getLocalidad().getPais()
+ "/" +paciente.getLocalidad().getDireccion()
+ "/" + paciente.getLocalidad().getCiudad());
System.out.println("El numero fijo es: " + paciente.getNumeros().getCasa());
System.out.println("El numero de celular es: " + paciente.getNumeros().getCelular());
}

public Paciente buscarPaciente (String nombre){


Paciente paciente = new Paciente();
for (int i = 0; i < pacientes.size(); i++){
if (pacientes.get(i).getNombreCompleto().equals(nombre)){
paciente = pacientes.get(i);
}
}
return paciente;
}

public void calcularPrecioPaciente(String nombre){


Paciente datosPaciente = buscarPaciente(nombre);
int precio = datosPaciente.getDiasInternado() * precioDia;
double precioDescuento = precio * (datosPaciente.getDescuento() * 0.01);
double precioTotal = precio - precioDescuento;
System.out.println("El precio total del paciente es : " + precioTotal);
}

public void internarNuevoPaciente (Paciente pacienteNuevo){


pacientes.add(pacienteNuevo);
}
}
Clase Paciente
public class Paciente
{
private String nombreCompleto;
private Localidad localidad;
private Numeros numeros;
private int diasInternado;
private int descuento;

public Paciente(String nombreCompleto, Localidad localidad , Numeros numeros, int diasInternado,


int descuento){
this.nombreCompleto = nombreCompleto;
this.localidad = localidad ;
this.numeros = numeros ;
this.diasInternado = diasInternado;
this.descuento = descuento;
}

public Paciente(){
this.nombreCompleto = "";
this.localidad = new Localidad();
this.numeros = new Numeros();
this.diasInternado = 0;
this.descuento = 0;
}

public String getNombreCompleto (){


return this.nombreCompleto;
}

public void setNombreCompleto(String nombreCompleto){


this.nombreCompleto = nombreCompleto;
}

public int getDiasInternado (){


return diasInternado;
}

public void setDiasInternado (int diasInternado){


this.diasInternado = diasInternado;
}

public int getDescuento (){


return descuento;
}

public void setDescuento (int descuento){


this.descuento = descuento;
}

public Localidad getLocalidad (){


return localidad;
}

public Numeros getNumeros () {


return numeros;
}
}
Clase Localidad
public class Localidad
{
private String pais;
private String ciudad;
private String direccion;

public Localidad(){
this.pais = "";
this.ciudad = "";
this.direccion = "";
}

public Localidad(String pais, String ciudad, String direccion){


this.pais = pais;
this.ciudad = ciudad;
this.direccion = direccion;
}

public String getPais(){


return pais;
}

public void setPais(String pais){


this.pais = pais;
}

public String getCiudad(){


return ciudad;
}

public void setCiudad(String ciudad){


this.ciudad = ciudad;
}

public String getDireccion(){


return direccion;
}

public void setDireccion(String direccion){


this.direccion = direccion;
}
}
Clase Numeros
public class Numeros
{
private int casa;
private int celular;

public Numeros (){


this.celular = 0;
this.casa = 0;
}

public Numeros (int celular, int casa){


this.celular = celular;
this.casa = casa;
}

public int getCelular (){


return this.celular;
}

public void setCelular (int celular){


this.celular = celular;
}

public int getCasa (){


return this.casa;
}

public void setCasa (int casa){


this.casa = casa;
}
}
Código refactorizado
Clase Hospital
import java.util.ArrayList;

public class Hospital


{
private ArrayList <Paciente> pacientes;
private int precioDia;

public Hospital(){
precioDia = 1500 ;
pacientes = new ArrayList <Paciente> ();
}

public void imprimirPaciente(String nombrePaciente){


Paciente paciente = buscarPaciente (nombrePaciente);
paciente.imprimir();
}

public Paciente buscarPaciente (String nombre){


Paciente paciente = new Paciente();
for (int i = 0; i < pacientes.size(); i++){
if (pacientes.get(i).getNombreCompleto().equals(nombre)){
paciente = pacientes.get(i);
}
}
return paciente;
}

public void calcularPrecioPaciente(String nombre){


Paciente datosPaciente = buscarPaciente(nombre);
datosPaciente.imprimirPrecio(precioDia);
}

public void internarNuevoPaciente (Paciente pacienteNuevo){


pacientes.add(pacienteNuevo);
}
}
Clase Paciente

public class Paciente


{
private String nombreCompleto;
private Localidad localidad;
private Numeros numeros;
private int diasInternado;
private int descuento;

public Paciente(String nombreCompleto, Localidad localidad , Numeros numeros, int


diasInternado, int descuento){
this.nombreCompleto = nombreCompleto;
this.localidad = localidad ;
this.numeros = numeros ;
this.diasInternado = diasInternado;
this.descuento = descuento;
}

public Paciente(){
this.nombreCompleto = "";
this.localidad = new Localidad();
this.numeros = new Numeros();
this.diasInternado = 0;
this.descuento = 0;
}

public void imprimir (){


System.out.println ("El nombre del paciente es: " + nombreCompleto);
System.out.println ("La localidad del paciente es: "
+ localidad.getPais()
+ "/" + localidad.getDireccion()
+ "/" + localidad.getCiudad());
System.out.println("El numero fijo es: " + numeros.getCasa());
System.out.println("El numero de celular es: " + numeros.getCelular());
}

private double calcularPrecio (int precioDia) {


int precio = diasInternado * precioDia;
double precioDescuento = precio * (descuento * 0.01);
double precioTotal = precio - precioDescuento;
return precioTotal;
}

public void imprimirPrecio(int precioDia){


System.out.println("El precio total del paciente es : "+ calcularPrecio (precioDia));
}
public String getNombreCompleto (){
return this.nombreCompleto;
}

public void setNombreCompleto(String nombreCompleto){


this.nombreCompleto = nombreCompleto;
}

public int getDiasInternado (){


return diasInternado;
}

public void setDiasInternado (int diasInternado){


this.diasInternado = diasInternado;
}

public int getDescuento (){


return descuento;
}

public void setDescuento (int descuento){


this.descuento = descuento;
}

public Localidad getLocalidad (){


return localidad;
}

public Numeros getNumeros () {


return numeros;
}
}
Clase Localidad
public class Localidad
{
private String pais;
private String ciudad;
private String direccion;

public Localidad(){
this.pais = "";
this.ciudad = "";
this.direccion = "";
}

public Localidad(String pais, String ciudad, String direccion){


this.pais = pais;
this.ciudad = ciudad;
this.direccion = direccion;
}

public String getPais(){


return pais;
}

public void setPais(String pais){


this.pais = pais;
}

public String getCiudad(){


return ciudad;
}

public void setCiudad(String ciudad){


this.ciudad = ciudad;
}

public String getDireccion(){


return direccion;
}

public void setDireccion(String direccion){


this.direccion = direccion;
}
}
Clase Numeros
public class Numeros
{
private int casa;
private int celular;

public Numeros (){


this.celular = 0;
this.casa = 0;
}

public Numeros (int celular, int casa){


this.celular = celular;
this.casa = casa;
}

public int getCelular (){


return this.celular;
}

public void setCelular (int celular){


this.celular = celular;
}

public int getCasa (){


return this.casa;
}

public void setCasa (int casa){


this.casa = casa;
}
}

También podría gustarte