Malos Hábitos de la
Codificación
M.C. Juan Carlos Olivares Rojas
Marzo 2010
Competencias
• Específica: conoce y corrige los errores que
generalmente se cometen al desarrollar código.
• Genéricas
• Instrumentales: Capacidad de análisis y
síntesis, Solución de problemas, Toma de
decisiones.
Competencias
• Interpersonales: Capacidad crítica y autocrítica,
Capacidad
de
trabajar
en
equipo
interdisciplinario, Habilidad para trabajar en un
ambiente laboral, Compromiso ético.
• Sistémicas: Capacidad de aprender, Capacidad
de adaptarse a nuevas situaciones, Capacidad
de generar nuevas ideas (creatividad),
Habilidad para trabajar en forma autónoma,
Preocupación por la calidad.
Temario
• Código duplicado
• Codificación excesiva
• Mal diseño
• Pruebas
• Refactorización de Generalización
• Más patrones de Diseño.
Evidencias
• 30% Prácticas
• 10% Otras actividades en el aula
• 60% Actividad de Evaluación Integral (TeóricoPráctico)
Código duplicado
• Es el principal indicador de un malor olor.
• Una vez detectada el principio básico de
reestructuración es la unificación de todo ese
código duplicado. Generalmente se puede
aplicar el Refactoring de extraer el método.
• De hecho esta reestructuración básica fue
motivo de muchas otras refactorizaciones.
Código duplicado
• ¿cuándo se aplica el refactoring de Pull up?
• Cuando se tiene código duplicado en dos
clases hijas.
• En relaciones de generalización, también se
pueden aplicar otros refactoring básicos cómo
el método de plantilla y la sustitución de
algoritmos.
Codificación excesiva
• La codificación excesiva se da por muchas
razones: malos hábitos de programación,
presión del tiempo de desarrollo, etc.
• El objetivo básico del refactoring es lograr la
simplicidad del código y la codificación
excesiva no ayuda a esto.
• Un ejemplo de esto son los métodos largos, ya
que son más dificiles de entender y por ende
darle mantenimiento.
Codificación excesiva
• ¿qué refactorings se pueden aplicar para evitar
los métodos largos?
•
•
•
•
•
Extraer método
Reemplazar método por objeto método.
Sustitución del algoritmo
Mover método*
Todas los refactoring de simplificación de
condicionales (descomposición condicional,
consolidar condicional, remover banderas, etc.)
Codificación excesiva
• Otros buenos indicadores de malos olores son:
• Clases muy grandes
• Lista de parámetros muy grandes.
• Clases divergentes (una clase es cambiada
constantemente)
• Shotgun surgery (un pequeño cambio afecta a
todos los componentes)
• Clases perezosas.
Codificación excesiva
• Campos temporales
• Cadenas de Mensajes
• La decisión de reestructurar código depende de
los desarrolladores. Para cada indicador debe
de existir una métrica establecida.
• Cada empresa define sus
codificación y de refactoring.
métricas
de
Mal diseño
• Aunque no está enfocado directamente con la
codificación, es el principal mal olor en la
implementación del software.
• Generalmente no se hace diseño y cuando
llega a hacerse se hace muy superficial sólo
por cumplir el requerimiento.
• Ya se ha visto como muchas refactorizaciones
se pueden hacer a través del modelado.
Mal diseño
• En un diagrama de clases en UML es posible
verificar algunos indicadores de malos olores y
poder reestructurarlos antes de codificarlos.
• Existirán indicadores que no son muy visibles
en el modelado como los métodos largos o el
código duplicado.
• Se pueden utilizar otros diagramas de UML
como los de secuencia, actividades, estados,
etc.
Mal diseño
• El modelado es subjetivo. El decir que un
modelo es bueno o malo depende mucho del
criterio del evaluador. Realmente los modelos
deben de ser efectivos a tal punto de que sean
útiles y se programen como deben.
• Cuando se realizó la práctica 4 nos tocó
reestructurar nuestra aplicación a un diseño
predeterminado consumiendo mucho tiempo si
es que nuestro diseño no tenía esa forma.
Pruebas
• Ya se ha hablado de la importancia de las
pruebas unitarias en el refactoring como
proceso elemental.
• Si una aplicación no tiene pruebas unitarias se
deberá hacer un refactoring de creación de
casos de prueba automatizados para cada
funcionalidad del sistema, esto nos permite
entender un código donde previamente no
existe documentación técnica.
Pruebas
• Las pruebas unitarias pueden leer desde un
archivo de Texto todos los casos de prueba y
bien generar la salida en un archivo de Texto.
• Se recomienda que las pruebas unitarias vayan
aplicándose de manera progresiva hasta
finalizar el plan de pruebas.
Actividades
• De tu programa Agenda de la práctica 4
encuentra porciones de código que pudiesen
tener código duplicado (justificar en caso de
que no) y aplica el refactoring adecuado.
• Verifica que tus métodos no sobrepasen de
más de 24 LOCs totales (incluyendo blancos y
comentarios). No se trata de ofuscar el código.
Actividades
• Realiza
pruebas
unitarias
funcionalidad de tu programa.
para
cada
• Las pruebas unitarias deberán realizarse por
cada clase de programa deberá existir una
clase de prueba.
• Cada método y atributo de la clase deberán
utilizarse en las pruebas unitarias.
Ref. en el manejo de Herencia
• La generalización es una herramienta muy
potente que nos permite evitar mucha
duplicación
de
código.
Utilizándola
correctamente podemos conseguir que nuestro
código sea muy fácil de extender. En estas
refactorización encontramos:
•
•
•
•
Subir Atributo,
Subir Metodo,
Subir CodigoConstructor
Bajar Atributo,
Ref. en el manejo de Herencia
•
•
•
•
•
•
•
Bajar Metodo,
Extraer Subclase
Extraer Superclase,
Extraer Interfaz,
Agrupar Jerarquia,
Sustituir Herencia Por Delegacion,
Sustituir Delegacion Por Herencia
• En el caso de los refactorins de subir y bajar
tanto campos como atributos ya se han
comentado con anterioridad.
Pull Up Constructor Body
• Problemática: se tiene constructores en las
subclases donde prácticamente es el mismo
código.
• Solución: crear un constrctor de la clase.
Llamarlo desde las subclases.
class Manager extends Employee...
public Manager (String name, String id, int
grade) {
_name = name;
_id = id;
public Manager (String name, String id, int
_grade = grade;
grade) {
}
super (name, id);
_grade = grade;
}
Pull Up Constructor Body
• Motivación: Los constructores son engañosos.
No son totalmente métodos, por lo que su uso
es restringido y delicado.
• ¿Por qué es necesario definir primero el
constructor de la superclase en la subclase?
• porque al crear una instancia de una clase, se
ejecuta primero el constructor de su
superclase, y después el de ella misma.
Extract Subclass
• Problemática: Una clase tiene características
que son usadas sólo en algunas instancias.
• Solución: Crear una subclase para este
subconjunto de características.
Trabajo
getTotalPrice
getUnitPrice
getEmployee
Trabajo
getTotalPrice
getUnitPrice
Labor
getUnitPrice
getEmployee
Extract Subclass
• Motivación: El principal motivo para utilizar esta
refactorización, es que una clase tenga
comportamiento que es usado por algunas
instancias de la clase y no se use para el resto.
• La principal alternativa para esta refactorización
es Extract Class. Esto significa seleccionar
entre delegación y herencia.
• Extract subclass es usualmente simple para
realizar, pero tiene sus limitaciones.
Extract Superclass
• Problema: se tienen dos clases con
características similares
• Solución: crear una superclase y mover las
características comunes a la superclase.
Department
getTotalAnnualCost()
getName()
getHeadCount()
Employee
getTotalAnnualCost()
getName()
getld()
Party
getTotalAnnualCost( )
getName( )
Department
Employee
getHeadCount( )
getld( )
Extract Superclass
• Motivación: el código duplicado es una de las
principales cosas malas en los sistemas.
• Una forma de código duplicado es tener dos
clases que hacen cosas similares de la misma
manera o de distinta manera. Los objetos
proporcionan mecanismos de ‘construcción’
para simplificar esta situación con herencia.
Extract Interface
• Problema: Varios clientes usan el mismo
subconjunto de una interface, o dos clases
tienen parte de sus interfaces en común
• Solución: Extraer el subconjunto dentro de una
interface
Extract Interface
•
Employee
getRate()
hasSpecialSkill()
getName()
getDepartment()
<<interface>>
Billable
getRate( )
hasSpecialSkill( )
Employee
getRate()
hasSpecialSkill()
getName()
getDepartment()
Extract Interface
• Motivación:
• Las clases son usadas por otras en diferentes
maneras, por lo que son implementadas de
forma diferente para cada clase.
• Cuando una clase necesita comportamientos
de más de una clase, se soporta con lo que se
llama Herencia múltiple. Java tiene herencia
simple, pero permite establecer e implementar
esta clase de requerimientos usando interfaces.
Interfaz vs Clase Abstracta
• Las clases abstractas como su nombre lo
indica son clases que no pueden instanciar
objetos. Por este motivo sólo se utilizan para
definir taxonomía de clases.
• Las interfaces definen las carácterísticas de
una clase pero no la implementan. Las
interfaces sirven para manejar “herencia
múltiple”.
Interfaz vs Clase Abstracta
• Un futbolista tiene ciertas carácterísticas que
no necesariamente definen su personalidad.
Una persona puede tener el comportamiento de
un futbolista. Por este motivo no heredan sino
que implementan una interfaz.
• Las clases abstractas pueden tener métodos
abstractos o no. Cuando un método es
abstracto debe ser redefinido en la subclase.
Interfaz vs Clase Abstracta
• Las interfaces todos sus métodos son
abstractos. Una interface no encapsula datos.
• ¿Cómo se implementaría en Java?
Colapse Hierarchy
• Problema: Una superclase y una subclase no
son muy diferentes
• Solución: mezclar las clases
Empleado
Empleado
Vendedor
Colapse Hierarchy
• Motivación: Refactorizar una jerarquía a
menudo involucra subir métodos y campos, así
como bajar la jerarquía en cuanto a niveles.
• Se tienen que encontrar las subclases que no
están añadiendo algún valor, y es necesario
mezclarlas con otra.
Colapse Hierarchy
• MECANISMO:
• Selecciona que clase será
superclase o alguna subclase.
eliminada:
la
• Utilizar subir campo o método, bajar campo o
método, para mover comportamientos y datos,
de la clase eliminada a la clase con la que será
mezclada.
Colapse Hierarchy
• Compilar y probar cada movimiento.
• Ajustar las referencias a la clase que será
eliminada para usar la clase mezclada. Esto
afectará declaraciones de variables, tipos de
parámetros y constructores.
• Eliminar la clase vacía.
• Compilar y probar
Actividad
• Problemática: los tipos básicos para números
tienen precisiones muy bajas para aplicaciones
científicas.
• Diseñar una biblioteca para el manejo de
números muy grandes. Sólo se distinguirá el
concepto entre números enteros y reales.
• De momento sólo se permitirán las operaciones
de suma y resta.
Actividad
• Las operaciones deberán ser definidas como
estáticas. Se recibirán como argumentos los
objetos de números grandes. Se deberán
sobrecargar los métodos para que se puedan
sumar sin distinción números enteros y reales.
• Se devolverá valores de los mismos datos.
• Se sugieren
interfaces.
utilicen
clases
abstractas
o
Actividad
• El constructor recibirá un String
argumento para construir su valor.
como
• Se deberán realizar casos de prueba que
validen
todas
las
opciones
de
la
implementación.
Rep. Inheritance with Delegation
• Problema: Una subclase usa solamente una
parte de la interfaz de la superclase o no quiere
heredar datos.
• Solución: crear un atributo para la superclase,
ajustando los métodos para se delegados por
la superclase, y remover la subclase.
• Motivación: no siempre la herencia es buena,
sobretodo cuando no se saca provecho.
Rep. Inheritance with Delegation
Rep. Delegation with Inheritance
• Problema: se está usando delegación y
frecuentemente
se
escriben
muchas
delegaciones simples en toda la interfaz.
• Solución: hacer de la clase delegada una
subclase del delegado.
Patrón Estado
• State es un patrón que resuelve la problemática
de que el comportamiento de un objeto
depende de su estado, y sus métodos
contienen la lógica de casos que reflejan las
acciones condicionales dependiendo del
estado.
• Solución: cree clases de estado para cada
estado que implementan una interfaz común.
Patrón Estado
• Utiliza una superclase abstracta (clase de
estado), la cual contiene métodos que
describen los comportamientos para los
estados de un objeto.
• Se maneja una clase de contexto que es la que
sirve de interfaz al cliente.
Patrón Estado
Reeplace Array with Object
• Problema se tienen datos diferentes en un
arreglo. Solución crear un objeto con las
propiedades distintas.
• String[] row = new String[3];
• row [0] = "Liverpool";
• row [1] = "15";
• Performance row = new Performance();
• row.setName("Liverpool");
• row.setWins("15");
Remove Setting Method
• Problema: un método set nunca se utiliza. En
general cualquier método que no se use debe
de eliminarse (tener cuidado con las
referencias).
Hide Method
• Problema: un método sólo es utilizado dentro
de la misma clase.
• Solución: el acceso deberá ser privado en lugar
de público.
Reeplace Exception with Test
• Problema: se deben evitar al máximo las
excepciones ya que generalmente cambian el
transcurso del código.
double getValueForPeriod (int periodNumber) {
try {
return _values[periodNumber];
} catch (ArrayIndexOutOfBoundsException e) {
return 0;}
Reeplace Exception with Test
double getValueForPeriod (int periodNumber) {
if (periodNumber >= _values.length)
return 0;
return _values[periodNumber];
}
Factory Pattern
• El patrón de diseño Fábrica o Factoría: se basa
en el principio de diseño para mantener una
separación de interfaces (separation of
concerns).
• Los
objetos
factoría
separan
las
responsabilidad de la creación compleja de
objetos de apoyo, ocultan la lógica de creación
de la parte compleja.
Factory Pattern
• En su versión más simple o pura se indica el
objeto que se desea construir. Se tiene un
método llamado create o createInstance al cual
se le pasa como parámetro el tipo de dato a
construir.
static Employee create(int type) {
switch (type) {
case ENGINEER: return new Engineer();
case SALESMAN: return new Salesman();
case MANAGER: return new Manager();
Factory Pattern
default:
throw
new
IllegalArgumentException("Incorrect type code
value");
• Nótese que esta implementación es elegante
aunque con frecuencia se crea generalmente
un método por cada tipo de objeto.
class Person...
static Person createMale(){
Factory Pattern
return new Male();}
static Person createFemale() {
return new Female();}
• Reemplazar: Person kent = new Male();
• Con: Person kent = Person.createMale();
Factory Pattern
Introducción a .NET
• .NET (dot-net) es la propuesta de Microsoft para el
desarrollo de aplicaciones orientadas a objetos,
basadas en servicios sobre la red.
• .NET se compone de una máquina virtual y de un
compilador JIT (Just in Time) que permite que el
código sea portable.
• Como todo framework cuenta con un conjunto de
clases predeterminadas dentro del CLR (Common
Language Runtime) que permite la ejecución de
diversos lenguajes (Visual Basic, C++, J#, C# entre
otros).
Introducción a C#
• El lenguaje estrella es C# (leído como c-sharp, c gato o c
almoadilla).
• Es un lenguaje orientado a objetos derivado de C++. Es la
competencia directa a Java (muchos dicen que es un clon
mejorado). Este lenguaje deriva de la versión de Java
propuesta por Microsoft Visual J++.
• Maneja la opción de código seguro (managed code) aunque
puede acceder a código nativo el cual no es seguro (se
recomienda utilizar Visual C++ .NET)
C# versus Java (Similaridades)
• Ambos derivan de C y C++.
• Incluyen características comunes como el recolector de basura,
el uso de lenguaje intermedio: en C# recibe el nombre de
Microsoft Intermediate Language (MSIL), en Java es el
bytecode.
• Maneja herencia múltiple a través del uso de interfaces.
C# versus Java (Diferencias)
• C# contiene más datos primitivos que Java
(enumeraciones*, estructuras).
• Hay sobrecarga de operadores
• Existe el uso de delegados para manejar de forma
segura punteros para el manejo de eventos.
Hola mundo en C#
• El código se puede hacer en un editor de texto plano
using System;
public class HelloWorld {
public static void Main() {
// This is a single line comment.
/*
* This is a multiple line comment.
*/
Console.WriteLine("Hello World!");
}
}
Hola mundo en C#
• Para compilar en mono: mcs
HelloWorld.cs
•
• Para compilar en el SDK de .NET: csc
HelloWorld.cs
• Para correr el programa en Mono: mono
HelloWorld.exe
• En el SDK simplemente doble click o
nombre de programa en línea de
comandos
Tipos Básicos en C#
C# Type .Net Framework Type Signed Bytes
Possible Values
sbyte
System.sbyte
Yes
1
-128 to 127
short
System.Int16
Yes
2
-32768 to 32767
int
System.Int32
Yes
4
231 to 231 - 1
long
System.Int64
Yes
8
263 to 263 - 1
byte
System.Byte
No
1
0 to 255
ushort
System.Uint16
No
2
0 to 65535
uint
System.Uint32
No
4
0 to 232 - 1
ulong
System.Uint64
No
8
0 to 264 - 1
float
System.Single
Yes
4
±1.5 x 10-45 to ±3.4 x 1038 with 7
significant figures
double
System.Double
Yes
8
±5.0 x 10-324 to ±1.7 x 10308 with 15
or 16 significant figures
decimal
System.Decimal
Yes
12
±1.0 x 10-28 to ±7.9 x 1028 with 28 or
29 significant figures
char
System.Char
N/A
2
Any Unicode character
bool
System.Boolean
N/A
1/2
true or false
Arreglos
• C# soporta dos tipos de arreglos multidimensional:
rectangular y jagged.
• Cuando son rectangulares tienen el mismo tamaño.
• Cuando son
irregular:
jagged
tienen
un
tamaño
int[][] jag = new int[2][];
jag[0] = new int [4];
jag[1] = new int [6];
int[][] jag = new int[][] {new int[] {1, 2, 3, 4}, new int[] {5, 6, 7, 8, 9, 10}};
Operadores
• Son prácticamente los mismos de C, C++ y Java.
• Para realizar la sobrecarga de operador se puede
aplicar:
•
•
•
public static bool operator == (Value a, Value b) {
return a.Int == b.Int
}
• Where an operator is one of a logical pair, both
operators should be overwritten if any one is.
Ciclos
• Cuenta con las mismas estructuras de repetición
que C++/Java adicionando el ciclo foreach:
•
foreach (variable1 in variable2) statement[s]
•
•
•
int[] a = new int[]{1,2,3};
foreach (int b in a)
System.Console.WriteLine(b);
Namespaces
• Son el equivalente a los packages en Java. Sirven
para agrupar un conjunto de clases. Ejemplo:
• namespace myapi {
•
namespace math {
•
public class Adder { // insert code here }
•
}
•
}
Declaración de Clases
• Se asemeja mucho a C++. Para indicar herencia se
utiliza el operador “:”. Ejemplo:
•
public class DrawingRectangle : Rectangle
• Las variables constantes se definen con const.
Entrada/Salida
•
•
using System;
using System.IO;
•
•
•
•
•
•
class DisplayFile
{
static void Main(string[] args)
{
StreamReader r = new StreamReader(args[0]);
string line;
•
•
Console.Write("Out File Name: ");
StreamWriter w = new StreamWriter(Console.ReadLine());
•
•
•
•
•
•
•
•
while((line = r.ReadLine()) != null) {
Console.WriteLine(line);
w.WriteLine(line);
}
r.Close();
w.Close();
}
}
Excepciones
• Se manejan de manera muy similar a Java:
• try {
• res = (num / 0);
• catch (System.DivideByZeroException e) {
•
Console.WriteLine("Error: an attempt to divide by zero");
• }
• }
• Se puede realizar autodocumentación del código:
• /// <summary>
•
/// The myClass class represents an arbitrary class
•
/// </summary>
• Se utliza el parámetro /doc:myfile.xml en csc
DLL
• La creación de librerías es simplemente muy sencillo
solo se deberá indicar al compilador la opcion
/target:library clase.cs a nuestra clase pura.
• Para ligar la dll a nuestro programa se deberá indicar
el parámetro /reference:mibiblioteca.dll programa.cs
desde el compilador.
• Para ejecutar código inseguro se utiliza la palabra
clave unsafe{} dentro del código nativo.
• C# cuenta con goto.
II. Hello World
using System.Windows.Forms;
using System.Drawing;
class MyForm:Form{
public static void Main(){
Application.Run(new MyForm());
}
protected override void OnPaint(PaintEventArgs e){
e.Graphics.DrawString("Hello World!", new Font("Arial", 35),
Brushes.Blue, 10, 100);
}
}
III. C# Language
3.Expressions and operators
Práctica 9
• En algunas ocasiones es necesario convertir un
lenguaje de un paradigma a otro lenguaje de otro
paradigma.
• Aspect Oriented Programming es un nuevo enfoque
Referenicas
• Fowler, M. (1999), Refactoring, Adison-Wesley.
• Sanchez, M., (2009), Material del Curso de
Reestructuración
de
Códigos,
Instituto
Tecnológico de Morelia.
Dudas