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.
• 30% Prácticas
Evidencias
• 10% Otras actividades en el aula • 60% Actividad de (Teórico-Práctico)
Evaluación
Integral
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
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á eliminada: la superclase o alguna subclase. • 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 utilicen clases abstractas o interfaces.
Actividad
• El constructor recibirá un String como argumento para construir su valor. • 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.
• State
es
Patrón Estado 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");
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 IllegalArgumentException("Incorrect code value");
new type
• 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();}
â&#x20AC;˘ Reemplazar: Person kent = new Male(); â&#x20AC;˘ 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 jagged tienen un tamaño irregular: 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
• Fowler, M. Wesley.
Referenicas (1999),
Refactoring,
Adison-
• Sanchez, M., (2009), Material del Curso de Reestructuración de Códigos, Instituto Tecnológico de Morelia.
Dudas