Manuel Calero Solís
9 min readFeb 25, 2018

Clean Code en Harbour

## Introduction

Principios de ingeniería de software, del libro de Robert C. Martin [ Código limpio ](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882), adaptado para Harbout. Esta no es una guía de estilo. Es una guía para producir software legible, reutilizable y refactorizable. No todos los principios de este documento deben seguirse estrictamente, y aún menos serán universalmente acordados. Estas son pautas y nada más, pero están codificadas durante muchos años de experiencia colectiva por los autores de **Clean Code**.

Inspirado en [clean-code-php](https://github.com/jupeter/clean-code-php)

Basado en la traducción de [Francisco Bórquez](https://github.com/fikoborquez/clean-code-php#usar-encapsulaci%C3%B3n-de-objetos)

## Variables

### Use nombres de variables significativos y fáciles de pronunciar.

**Mal:**

```xbase
$cdt = DateTime();
```

**Bien:**

```xbase
$currentDateTime = DateTime();
```

### Usa el mismo vocabulario para el mismo tipo de variable

**Mal:**

```xbase
getUserInfo()
getUserData()
getUserRecord()
getUserProfile()
```

**Bien:**

```xbase
getUser()
```

### Use los nombres para las búsqueda (parte 1)

Es importante que el código que escribimos sea legible y pueda buscarse. Al * no * nombrar variables que terminan siendo significativas para comprender nuestro programa, molestamos a los futuros lectores de nuestro código. Hagamos que los nombres sean faciles de buscar y entender.

**Mal:**

```xbase
// Que significa realmente el dos
fileHandle = fOpen( “fopen.prg”, 2)
```

**Bien:**

```xbase
fileHandle = fOpen( “fopen.prg”, FO_READWRITE)
```

### Use los nombres para las búsqueda (parte 2)

**Mal:**

```xbase
// What the heck is 4 for?
if (user:access == 4)
// do …
end if
```

**Bien:**

```xbase
#define ACCESS_READ = 1
#define ACCESS_CREATE = 2
#define ACCESS_UPDATE = 4
#define ACCESS_DELETE = 8

if (user:access == ACCESS_UPDATE)
// do …
end if
```

### Usar variables explicativas

**Mal:**

```xbase
programsDirectory := Directory( “*.prg” )

aeval( programsDirectory, {|x| sendFile( x[1], x[2] ) } )
```

**Bien:**

```xbase
programsDirectory := Directory( “*.prg” )

aeval( programsDirectory, {|programFile| sendFile( programFile[F_NAME], programFile[F_SIZE] ) } )
```

### Evite anidar demasiado y regrese pronto (parte 1)

Demasiadas declaraciones ‘if’ no pueden hacer que su código sea difícil de seguir.
Hay autores que llegana a decir que si tu funcion tiene, muchos else (incluso solo uno), debe ser reescrita.

Explícito es mejor que implícito.

**Mal:**

```xbase
function isShopOpen(day)

if !empty(day)
if hb_isstring(day)
day = lower(day)
if (day == ‘friday’)
return .t.
elseif (day == ‘saturday’)
return .t.
elseif (day == ‘sunday’)
return .t.
else
return .f.
end if
else
return .f.
end if
else
return .f.
end if

return .f.
```

**Bien:**

```xbase
function isShopOpen(day)

local openingDays := {‘friday’, ‘saturday’, ‘sunday’}

if empty(day)
return .f.
end if

return ( ascan( openingDays, lower(day) ) == 0 )
```

###Evite anidar demasiado y regrese pronto (parte 2)

**Mal:**

```xbase
function fibonacci( n )

if (n < 50)
if (n != 0)
if (n != 1)
return fibonacci(n — 1) + fibonacci(n — 2)
else
return 1
end if
else
return 0
end if
else
return ‘Not supported’
end if

return n
```

**Bien:**

```xbase
function fibonacci( n )

if (n == 0 .or. n == 1)
return n
end if

if (n > 50)
return ‘Not supported’
end if

return fibonacci(n — 1) + fibonacci(n — 2)

return n

```

### Evitar el mapeo mental

No obligue al lector de su código a traducir lo que significa la variable.
Explícito es mejor que implícito.

**Mal:**

```xbase
local li
local l := {‘Austin’, ‘New York’, ‘San Francisco’}

for (i := 1 to len( l ))
li = l[i]
doStuff()
doSomeOtherStuff()
// …
// …
// …
// Espera, ¿qué es `li`?
dispatch(li)
next
```

**Bien:**

```xbase

local locations := {‘Austin’, ‘New York’, ‘San Francisco’}

foreach location in location
doStuff()
doSomeOtherStuff()
// …
// …
// …
dispatch(location)
next
```

### No agregue contexto innecesario

Si su nombre de clase / objeto le dice algo, no lo repita en su
nombre de la variable.
**Mal:**

```xbase
CLASS Car

DATA carMake
DATA carModel
DATA carColor

//…
ENDCLASS
```

**Bien:**

```xbase
CLASS Car

DATA make
DATA model
DATA color

//…
ENDCLASS
```
## Funciones

### Parametros de función (lo ideal es 2 o menos)

Limitar la cantidad de parámetros de función es increíblemente importante porque hace
que probarla sea más fácil. Tener más de tres conduce a una explosión combinatoria
donde tienes que probar toneladas de casos diferentes con cada argumento por separado.

Cero argumentos es el caso ideal. Uno o dos argumentos están bien, y se deben evitar tres.
Algo más que eso debe consolidarse. Por lo general, si tiene más de dos
argumentos entonces su función está tratando de hacer demasiado. En los casos en que no lo es, la mayoría
del tiempo, un objeto de nivel superior bastará como argumento.

**Mal:**

```xbase
function createMenu(title, body, buttonText, cancellable)

// …
return
```

**Bien:**

```xbase
class MenuConfig

DATA title
DATA body
DATA buttonText
DATA cancellable INIT .f.

end class

config := MenuConfig():New()
config:title := ‘Foo’
config:body := ‘Bar’
config:buttonText := ‘Baz’
config:cancellable := .t.

function createMenu( config )
// …
return
```

### Las funciones deberían hacer una cosa

Esta es, con mucho, la regla más importante en ingeniería de software. Cuando las funciones hacen másnde una cosa, son más difíciles de componer, probar y razonar. Cuando puedes aislar
una función para una sola acción, se pueden refactorizar fácilmente y su código leerá mucho
limpio. Si no quita nada más de esta guía que no sea esto, estará adelante
de muchos desarrolladores.

**Mal:**
```xbase
function emailClients(clients)

foreach client in clients
clientRecord = clientModel():find(client)
if clientRecord:isActive()
email(client)
end if
next
return
```

**Bien:**

```xbase
function emailClients(clients)

local activeClients := activeClients( clients )

aeval( activeClients, {|client| email( client ) } )

return nil

function activeClients( clients )

local activeClients := {}

aeval( clients, {|client| if( isClientActive( client ), aadd( activeClients, client ), ) } )

return ( activeClients )

function isClientActive( client )

if clientModel():find( client )
return ( clientRecord:isActive() )
end if

return ( .f. )

```

### Los nombres de las funciones deberían decir lo que hacen

**Mal:**

```xbase
CLASS Email

//…

METHOD handle()

mail( ::to, ::subject, ::body )

RETURN nil

ENDCLASS

message := Email(…):New()
// ¿Que es esto?
message:handle()
```

**Bien:**

```xbase
CLASS Email

//…

METHOD send()

mail( ::to, ::subject, ::body)

RETURN nil

ENDCLASS

message := Email(…):New()
// Limpio y obvio
message:send()
```

### Las funciones deben tener sólo un nivel de abstracción

Cuando tienes más de un nivel de abstracción usualmente es porque tu función está haciendo demasiado. Separarlas en funciones lleva a la reutilización.

**Mal:**

```xbase
function ExecutorStaments( tokenizedStatement )

local statement
local statements := hb_atokens( tokenizedStatement, “,” )

for each statement in statements
if SQLConexion():Parse( statement )
SQLConexion():Execute( statement )
// …
end if
next

return
```

**Bien:**

Lo mejor es crear una clase que tenga dentro dependencias a otras clases.

```xbase
CLASS Tokenizer

METHOD new()

METHOD tokenizer() inline ( hb_atokens( tokens, “,” ) )

end CLASS

CLASS Conexion

data oConexion

METHOD new()

METHOD Parse( statement ) inline ( ::oConexion:Parse( statement ) )

METHOD Execute( statement ) inline ( ::oConexion:Execute( statement ) )

ENDCLASS

CLASS ExecutorStaments

DATA tokenizer
DATA conexion

METHOD new()

::tokenizer := Tokenizer():New()
::conexion := Conexion():New()

RETURN ( self )

METHOD Execute( statement )

local statement
local statements := ::tokenizer:tokenizer( statement )

for each statement in statements
if ::conexion:Parse( statement )
::conexion:Execute( statement )
end if
next

RETURN ( self )

ENDCLASS
```

### No usar logicos como parámetros de funciones

Los valores logicos le dicen al usuario que la función hace más de una cosa. Las funciones deben hacer sólo una. Divide tus funciones si ellas siguen diferentes caminos basados en un valor booleano.

**Mal:**

```xbase
function createFile(name, temp)

if (temp) {
fcreate( ‘./temp/’ + name)
else
fcreate( name )
end if

return
```

**Bien:**

```xbase
function createFile(name, temp)

fcreate( ‘./temp/’ + name)

return

function createTempFile(name)

fcreate( ‘./temp/’ + name)

return
```

### Evitar efectos secundarios

Una función produce un efecto secundario si hace algo más que tomar un valor y devolver otro. Un efecto secundario puede ser escribir en un archivo, modificar alguna variable global, o accidentalmente darle todo tu dinero a un extraño.

Ahora, ocasionalmente necesitaras los efectos secundarios en un programa. Como los ejemplos anteriores, necesitarás escribir en un archivo. Lo que quieres hacer en esos casos es centralizar donde realizarlos. No tengas muchas funciones y clases que escriban un archivo en particular. Ten un servicio que lo haga. Uno y sólo uno.

El punto principal es evitar trampas comunes como compartir estados entre objetos sin alguna estructura, usar tipos de datos mutables que puedan ser escritos por cualquiera, y no centralizar donde el efecto paralelo ocurre. Si puedes hacerlo, serás más feliz que la mayoría de los demás programadores.

**Mal:**

```xbase
// Variable global referenciada por la siguiente función.
// Si tenemos otra función que use el mismo nombre, ahora será un arreglo y podría romperla.

memvar name

function splitIntoFirstAndLastName()

name = hb_atokens( name, ‘ ‘ )

return nil

name = ‘Manuel Calero’

splitIntoFirstAndLastName()

? ( hb_valtoexp( name ) ) // {‘Manuel’, ‘Calero’}
```

**Bien:**

```xbase
function splitIntoFirstAndLastName( name )

return hb_atokens( name, ‘ ‘ )

name := ‘Manuel Calero’
newName := splitIntoFirstAndLastName(name)

? ( hb_valtoexp( name ) ) // ‘Manuel Calero’
? ( hb_valtoexp( newName ) ) // {‘Manuel’, ‘Calero’}
```

### Don’t write to global functions

Llenar de funciones globales es una mala práctica en muchos lenguajes porque puedes chocar con otra librería.

**Mal:**

```xbase
function config()

return { ‘foo’ => ‘bar’ }
```

**Bien:**

```xbase
CLASS Configuration

DATA configuration INIT {}

METHOD New( configuration )

::configuration := configuration

RETURN ( self )

METHOD get( key )

if hhaskey( ::configuration, key )
RETURN ( hget( ::configuration, key ) )
end if

RETURN ( nil )

ENDCLASS
```

Crea la variable configuration con una instancia de la clase `Configuration`

```xbase
configuration := Configuration():new( {‘foo’ => ‘bar’} )
```

Y ahora puedes usar una instancia de la clase `Configuration` en tu aplicación.

### No usar el patrón Singleton
Singleton es un [anti-patrón](https://en.wikipedia.org/wiki/Singleton_pattern). Citando a Brian Button:

1. Son usados generalmente como una **instancia global**, ¿Por qué eso es malo? Porque escondes las dependencias de tu aplicación en tu código, en lugar de exponerlas. Hacer algo global para evitar pasarlo es una [hediondez de código].(https://en.wikipedia.org/wiki/Code_smell).
2. Violan el [principio de la responsabilidad única]: en virtud del hecho de que ellos controlan su propia creación y ciclo de vida.
3. Inherentemente causan que el código esté estrechamente acoplado. Esto hace que muchas veces sean difíciles de probar, aunque en Harbour no tenemos bancos de pruebas.
Llevan su estado al ciclo de vida de la aplicación. Otro golpe a las pruebas porque puedes terminar con una situación donde las pruebas necesitan ser ordenadas lo cual es un gran no para las pruebas unitarias. ¿Por qué? Porque cada prueba unitaria debe hacerse independiente de la otra.
[Misko Hevery](http://misko.hevery.com/about/) ha realizado unas reflexiones interesantes sobre el [origen del problema](http://misko.hevery.com/2008/08/25/root-cause-of-singletons/).

**Mal:**

```xbase
CLASS DBConnection

CLASSDATA instance

METHOD New( dsn )
// …
RETURN ( self )

METHOD getInstance() INLINE ( if( empty( ::oInstance ), ::oInstance := ::New(), ), ::oInstance )

// …
ENDCLASS

singleton := DBConnection():getInstance()
```

**Bien:**

```xbase
CLASS DBConnection

METHOD New( dsn )
// …
RETURN ( self )

ENDCLASS
```

Crea una instancia de la clase `DBConnection` y configúrala con DSN.

```xbase
connection := DBConnection():New( dsn )
```

Y ahora debes usar la instancia de `DBConnection` en tu aplicación.

### Encapsular condicionales

**Mal:**

```xbase
if ( article:state == ‘published’ )
// …
end if
```

**Bien:**

```xbase
if ( article:isPublished() )
// …
end if
```

### Evitar condicionales negativos

**Mal:**

```xbase
function isNodeNotPresent( node )

// …

if ( !isDOMNodeNotPresent( node ) )

// …

```

**Bien:**

```xbase
function isNodePresent( node )

// …

if (isNodePresent( node ) )

// …

```

### Evitar condicionales

Esta parece ser una tarea imposible. Al escuchar esto por primera vez, la mayoría de la gente dice, “¿cómo se supone que haré algo sin una declaración `if`?” La respuesta es que la mayoría de las veces puedes usar polimorfismo para lograr el mismo resultado.

La segunda pregunta usualmente es, “bien, eso es genial, ¿pero cómo puedo hacerlo?” La respuesta es un concepto de código limpio que ya hemos aprendido: una función debe hacer sólo una cosa. Cuando tienes clases y funciones que usan declaraciones `if`, estás diciéndole al usuario que tu función hace más de una cosa. Recuerda, hacer sólo una cosa.

**Mal:**

```xbase
CLASS Airplane

// …

METHOD getCruisingAltitude()

SWITCH ::type
CASE ‘777’:
RETURN ::getMaxAltitude() — ::getPassengerCount()
CASE ‘Air Force One’:
RETURN ::getMaxAltitude()
CASE ‘Cessna’:
RETURN ::getMaxAltitude() — ::getFuelExpenditure()
END

RETURN ( 0 )

ENDCLASS
```

**Bien:**

```xbase
CLASS Airplane

// …

METHOD getCruisingAltitude() VIRTUAL

ENDCLASS

CLASS Boeing777 FROM Airplane

// …

METHOD getCruisingAltitude()

RETURN ::getMaxAltitude() — ::getPassengerCount()

ENDCLASS

CLASS AirForceOne implements Airplane

// …

METHOD getCruisingAltitude()

RETURN ::getMaxAltitude()

ENDCLASS

CLASS Cessna implements Airplane

// …

METHOD getCruisingAltitude()

RETURN ::getMaxAltitude() — ::getFuelExpenditure()

ENDCLASS
```

### Evitar revisión de tipo

HARBOUR es un lenguaje no tipado, lo que quiere decir que tus funciones pueden tener cualquier tipo de argumento. Algunas veces habrás sentido esta libertad y te habrás tentado a hacer revisión de tipo en tus funciones. Hay muchas maneras de evitar tener que hacerlo.

**Mal:**

```xbase
METHOD travelToTexas( vehicle )

if vehicle:IsDerivedFrom( ‘Bicycle’ )
vehicle:pedalTo( Location():New( ‘texas’ ) )
elseif vehicle:IsDerivedFrom( ‘Car’ )
vehicle:driveTo( Location():New( ‘texas’ ) )
end if

RETURN ( nil )
```

**Bien:**

```xbase
METHOD travelToTexas( vehicle )

vehicle:travelTo( Location():New( ‘texas’ ) )

RETURN ( nil )
```

### Quitar código muerto

El código muerto es tan malo como el código duplicado. No hay motivos para mantenerlo en tu código fuente. Si no está siendo llamado, ¡deshazte del! Siempre estará a salvo en tu versión histórica si aún lo necesitas.

**Mal:**

```xbase
CLASS oldRequestModule( url )

// …

ENDCLASS

CLASS newRequestModule( url )

// …

ENDCLASS

request := newRequestModule():New( requestUrl )

inventoryTracker(‘apples’, request, ‘www.inventory-awesome.io')

```

**Bien:**

```xbase
CLASS RequestModule( url )

// …

ENDCLASS

request := RequestModule():New( requestUrl )

inventoryTracker(‘apples’, request, ‘www.inventory-awesome.io')
```