Refactoring Workbook
Refactoring Workbook
William C. Wake
Es un buen libro, aunque incompleto y con cierto sesgo “agilista” al intentar justificar ciertas causas de errores frecuentes al carecer de un diseño y una gestión del equipo eficiente. See also “Refactoring” by Martin Fowler.
Refactoring is the art of safely improving the design of existing code keeping the system running at all times. Refactoring provides us with ways to recognize problematic code and five us recipes for improving it.
Actualización
El libro lo puedes descargar en formato PDF gratuitamente desde este enlace.
Sin duda es más fácil escribir código que leerlo; por ello después de cada entrega ya sea por parte del programador o del equipo se debería leer el código que se ha entregado para partiendo de un código que ya funciona llegar a otro que funciona (desarrollar) y es simple de leer (mantener).
Especial atención al “safely” de la definición. Sin ir más lejos en mi actual proyecto intenté refactorizar la capa de control de la aplicación y preparé un carajal curioso : fue demasiado ambicioso, me tomó una semana de trabajo y al liberarlo entró en conflicto con los numerosos cambios que el resto del equipo fue realizando. Tendría que haber dividido la tarea en más pequeñas para conseguir que fuera seguro y no interferir con la marcha normal del proyecto.
Smells are warning signs about potential problems in code; some people prefer to talk about potential problems or flaws.
The Refactoring Cycle
Refactoring works in tiny steps. With a working system, while smells remain :
- Choose the worst smell
- Select a refactoring that will address the smell
- Apply the refactoring
Smells within Classes
- Comments
Symptoms: Comments symbols appear in the code.
Causes: Commnets may be present for the best of reasons: the author realizes that something isn´t as clear as it could be and adds a comment. Some comments are particularly helpful, as those that tell why something is done a particular way or why is’nt or thos that cite algorithms thar are not obvious. Other comments can be reflected just as well in the code itself. For example thie goal of a routine’s name as it can through a comment.
What to do: When a comment explains a block of code you can often use “Extract Method” to pull the block out into a separate method (the comment will often suggest a name for the new method). Whe a comment explains what a method does better than the method’s name, use “Rename Method” using the comment as the basis of the new name. When a comment explains preconditions, consider using “Introduce Assertion” to replace the comment with code.
Payoff: Improves communication. May expose duplication.
Contraindications: Don’t delete comments that are pulling their own weight.
- Long Methods
Symptoms: Large number of lines
Causes: Code is often easier to write than it is to read.
What to do: Use “Extract Method” to break up the method into smaller piedes splitting it into blocks semantically meaningful.
Payoff: Improves communication. May expose duplication. Often helps new classes and abstractions emerge.
Contraindications: Like almost all smells, the length is a warning sign – not a guarrantee – of a problem; Perhaps the best way to express something is using longer methods.
- Large Class
Symptoms: Large number of instance varialbes, and/or methods and/or lines.
Causes: The author keeps adding just one more capability to a class until eventually it grows too big.
What to do: If the class has Long Methods, address that smell first.The most common approaches to break up a class are “Extract Class” if you can identify a new class that has part of this class`s responsibilities, “Extract Subclass” if you can divide responsibilities between the class and a new sublcass and “Extract Interface” if you can identify subsets of features that clients use.
Payoff: Improves communication. May expose duplication.
- Long Parameter List
Symptoms: A method has more than one or two parameters
Causes: You may be trying to minimize coupling between objects letting the caller locate everything, or a programmer generalizes the routine to deal with multiple variations and a lot of control parameters.
What to do: If the parameter value can be obtained from another object this one already knows, “Replace Parameter with Method”; if the parameters come from a single object, try “Preserve Whole Object”; If the data is not from one logical object, you still might group them via “Introduce Parameter Object”.
Payoff: Improves communication. May expose duplication. Often reduces size.
Contraindications: Take a look on dependencies between two classes, or you cann’t design meaningful groups.
- Type Embedded in Name (Including Hungarian)
Symptoms: Names are compound words consisting of a word plus the type of the arguments, for example a method addCourse (Course C); names are in Hungarian notation, where the type of an object is encoded into the name, for example iCount as an integer member variable; variable names reflect theri type rather than their purpose or role.
What to do: “Rename Method” or field or constant to a name that comunicates intent without being so tied to a type.
Payoff: Improves communication. May make it easier to spot duplication.
Contraindications: rarely you might have a class that wants to do the same sort of operations to two different but related types, for expample a Graph class with addPoint() and addLink() methods.
- Uncommunicative Name
Symptoms: a name doesn´t communicate its intent well enough : one or two character namens, names with vowels omitted, numbered variables like pane1, pane2…, odd abbreviations, misleading names.
Causes: you haven’t took a minute, fucking lazy boy/girl.
What to do: Use “Rename Method”
Payoff: Improves communication.
Contraindications: i/j/k are oftenly used for loop indexes and c form characters if their scope is reaseonably short.
- Inconsistent Names
Symptoms: One name is used in one place and a different name is used for the same thing somewhere else, for expample find() and list() for the same basic feature.
Causes: different people may create the classes at differente times, vamos que falta un diseño único documentado y consistente.
What to do: Pick the best name and use “Rename Method” or field or constant to give the same name to the same things. Then look for duplication smell. Note that the Eiffel language uses a common pool of words for the names of its library features.
Payoff: Improves communication. May expose duplication.
- Dead Code
Symptoms: a variable, parameter, field, code fragment, method or class is not used anywhere or just in tests.
Causes: Requirements have changed or new approaches are introduced without adequate cleanup. Complicated logic results in some combinations of conditions that can’t actually happen.
What to do: delete the unused code and any associated test.
Payoff: Improves communication. Improves simplicity. Reduces size.
Contraindications: be aware that code aparently dead can be used dinamically or by another software modules.
- Duplicated Code
Symptoms: Two fragments of code look nearly identical or have nearly identical effects.
Causes: programmers working independently (vamos una mala gestión y una carencia en el diseño), and copy and paste code that is almost right and make slight alterations.
What to do: If the duplication is within the same class, use “Extract Method” to pull the common part out into a separate method; if it is in two related classes use Pull Up Field or Method to bring the common parts together; if it is in two unrelated classes use Extract Class for extracting the common part into a new class, or decide in which class should be.
Payoff: Reduces duplication. Reduces size. Can lead to better abstractions and more flexible code.
- Null Check
Symptoms: there are repeated occurrences of code like this : if (xxx == null) …
Causes: someone decides “we’ll use null to mean the default”
What to do: if there’s a resonable default value, use that. Otherwise “Introdue Null Object” tocreate a default object that you explicitly use. Watch out for a case where null means two or more differente things in different contexts.
Payoff: Reduces duplication. Redeces logic errors and exceptions.
Contraindications: if only occurs in only one place, it’s usually not worth the effort to create a separate Null Object. If you can’t define a safe behavior for each method, you may not be able to use a Null Object. Precisamente por esto prefiero, facilita detectar errores no previsto e interrumpe el flujo de ejecución en caso de error no previsto ni controlado.
- Complicated Boolean Expression
Symptoms: code has complex conditions involving and, or and not.
Causes: injustificable.
What to do: Introduce “Explaining Variable” to make each clause clearer.
Payoff: Improves communication.
Contraindications: if simpler exressions comunicates less well.
Smells between Classes
Objects are about data and behavior together, and your code will be more robust if you organize objects by behavior.
- Primitive Obsession
Symptoms: Uses primitive or near primitive types as int, float, String…; Constants and enumerations representing small integers; String constants representing field names.
Causes:
What to do: For missing objects, encapsulate primitives types into new classes.
Payoff: Improves communication. May expose duplication. Improves flexibility. Often exposes the need for other refactorings.
- Data Class
Symptoms: the class consists only of public data members or of simple getting and setting methods. This lets clients depend on the mutability and representation of the class.
Causes:
What to do: “Encapsultate Fields” to block direct access to the fields allowing access only through getters and setters; “Remove Settings Methods” for any method you can and use “Hide Method” to eliminate access to the getters and setters where they’re not used (aquí no estoy de acuerdo, coste / beneficio no suele merecer nunca salvo en un Façade); “Encapsulate Collection” to remove direct acces to any collection-type fields.
Payoff: Improves communication. May expose duplication.
- Message Chains
Symptoms: Yo see calls like a.b().c().d()
What to do: Use “Hide Delegate” to make the method depend on one object only, so rather a.b().c().d() put the d() method on the “a” object. (Revisar, mi primera idea es que sería mejor crear un nuevo objeto x que contenga el método d(), y que “a” invoque a “x”)
Notes: Follow the object oriented programming maxim Tell, Don´t Ask so instead of asking for objects so that you can manipulate them, you simply tell them to do the manipulation for you, as the Law of Demeter says: A method should only talk to itself, never to strangers.
La curva de aprendizaje es más suave, se puede desarrollar en paralelo reduciendo el tiempo de entrega, es más fácil realizar pruebas unitarias, es más fácil de mantener porque se aislan los errores en módulos.
Pero con qué criterios debemos dividir un sistema monolítico en módulos ? La reutilización es la respuesta, ya que nos permite disminuir el número de módulos necesarios en el sistema e incluso aprovechar módulos para futuros desarrollos.
El desacoplo se consigue con un buen interfaz y la reducción de dependencias externas. Debe ser un criterio complementario a la reutilización a la hora de dividir nuestro sistema en módulos. Por ejemplo si queremos hacer una llamada a base de datos para preguntar el número de usuarios de nuestro sistema, podemos tener un módulo para cada SGDB (uno para MySQL, otro para Oracle, otro para SQL Server…) o un único módulo que obtenga el mismo resultado para cualquier SGBD utilizado. Este módulo será un poco más complejo pero se convierte en mucho más reutilizable.
Ya lo dice el refrán : 

Podemos considerar que una Excepción en programación es una situación poco habitual que provoca resultados inesperados o incoherentes en nuestros métodos, pero que es contemplada en el análisis, diseño y construcción de nuestro sistema : el sistema se puede recuperar de estas situaciones.
Y las situaciones poco habituales que provocan resultados inesperados o incoherentes y que no controlamos ? Eso es un error en tiempo de ejecución en nuestro sistema, que puede desembocar en una inconsistencia de datos en nuestra base de datos o en la finalización inesperada de nuestro sistema.

