Buscar..


Introducción

Refactorización se refiere a la modificación del código existente en una versión mejorada. Aunque la refactorización a menudo se realiza mientras se cambia el código para agregar características o corregir errores, el término en particular se refiere a mejorar el código sin necesariamente agregar características o corregir errores.

Recorrer la refactorización

Aquí hay un programa que podría beneficiarse de la refactorización. Es un programa simple que utiliza C ++ 11, cuyo objetivo es calcular e imprimir todos los números primos del 1 al 100 y se basa en un programa que se publicó en CodeReview para su revisión.

#include <iostream>
#include <vector>
#include <cmath>

int main()
{
    int l = 100;
    bool isprime;
    std::vector<int> primes;
    primes.push_back(2);
    for (int no = 3; no < l; no += 2) {
        isprime = true;
        for (int primecount=0; primes[primecount] <= std::sqrt(no); ++primecount) {
            if (no % primes[primecount] == 0) {
                isprime = false;
                break;
            } else if (primes[primecount] * primes[primecount] > no) {
                std::cout << no << "\n";
                break;
            }
        }
        if (isprime) {
            std::cout << no << " ";
            primes.push_back(no);
        }
    }
    std::cout << "\n";
}

La salida de este programa se ve así:

3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97

Lo primero que notamos es que el programa no imprime 2 que es un número primo. Podríamos simplemente agregar una línea de código para simplemente imprimir esa constante sin modificar el resto del programa, pero sería mejor refactorizar el programa para dividirlo en dos partes: una que crea la lista de números primos y otra que las imprime. . Así es como podría verse:

#include <iostream>
#include <vector>
#include <cmath>

std::vector<int> prime_list(int limit)
{
    bool isprime;
    std::vector<int> primes;
    primes.push_back(2);
    for (int no = 3; no < limit; no += 2) {
        isprime = true;
        for (int primecount=0; primes[primecount] <= std::sqrt(no); ++primecount) {
            if (no % primes[primecount] == 0) {
                isprime = false;
                break;
            } else if (primes[primecount] * primes[primecount] > no) {
                break;
            }
        }
        if (isprime) {
            primes.push_back(no);
        }
    }
    return primes;
}

int main() 
{
    std::vector<int> primes = prime_list(100);
    for (std::size_t i = 0; i < primes.size(); ++i) {
        std::cout << primes[i] << ' ';
    }
    std::cout << '\n';
}

Al probar esta versión, vemos que sí funciona correctamente ahora:

2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97

El siguiente paso es observar que la segunda cláusula if no es realmente necesaria. La lógica en el bucle busca los factores primos de cada número dado hasta la raíz cuadrada de ese número. Esto funciona porque si hay algunos factores primos de un número, al menos uno de ellos debe ser menor o igual a la raíz cuadrada de ese número. Trabajando solo esa función (el resto del programa sigue siendo el mismo) obtenemos este resultado:

std::vector<int> prime_list(int limit)
{
    bool isprime;
    std::vector<int> primes;
    primes.push_back(2);
    for (int no = 3; no < limit; no += 2) {
        isprime = true;
        for (int primecount=0; primes[primecount] <= std::sqrt(no); ++primecount) {
            if (no % primes[primecount] == 0) {
                isprime = false;
                break;
            }
        }
        if (isprime) {
            primes.push_back(no);
        }
    }
    return primes;
}

Podemos ir más lejos, cambiando los nombres de las variables para que sean un poco más descriptivos. Por ejemplo, primecount no es realmente un conteo de primos. En su lugar, es una variable de índice en el vector de números primos conocidos. Asimismo, si bien no se utiliza a veces como una abreviatura de "Número", en la escritura matemática, es más común el uso de n . También podemos hacer algunas modificaciones eliminando la break y declarando las variables más cerca de donde se usan.

std::vector<int> prime_list(int limit)
{
    std::vector<int> primes{2};
    for (int n = 3; n < limit; n += 2) {
        bool isprime = true;
        for (int i=0; isprime && primes[i] <= std::sqrt(n); ++i) {
            isprime &= (n % primes[i] != 0);
        }
        if (isprime) {
            primes.push_back(n);
        }
    }
    return primes;
}

También podemos refactorizar main para usar un "rango para" para hacerlo un poco más limpio:

int main() 
{
    std::vector<int> primes = prime_list(100);
    for (auto p : primes) {
        std::cout << p << ' ';
    }
    std::cout << '\n';
}

Esta es solo una de las formas en que se puede realizar la refactorización. Otros pueden hacer elecciones diferentes. Sin embargo, el propósito de la refactorización sigue siendo el mismo, que es mejorar la legibilidad y posiblemente el rendimiento del código sin necesariamente agregar características.

Ir a la limpieza

En las bases de código C ++ que solían ser C, uno puede encontrar el patrón que se va a goto cleanup . Como el comando goto hace que el flujo de trabajo de una función sea más difícil de entender, a menudo esto se evita. A menudo, puede ser reemplazado por declaraciones de devolución, bucles, funciones. Sin embargo, con la goto cleanup uno necesita deshacerse de la lógica de limpieza.

short calculate(VectorStr **data) {
    short result = FALSE;
    VectorStr *vec = NULL;
    if (!data)
       goto cleanup;  //< Could become return false

    // ... Calculation which 'new's VectorStr

    result = TRUE;
cleanup:
    delete [] vec;
    return result;
}

En C ++ se podría usar RAII para solucionar este problema:

struct VectorRAII final {
    VectorStr *data{nullptr};
    VectorRAII() = default;
    ~VectorRAII() {
        delete [] data;
    }
    VectorRAII(const VectorRAII &) = delete;
};

short calculate(VectorStr **data) {
    VectorRAII vec{};
    if (!data)
       return FALSE;  //< Could become return false

    // ... Calculation which 'new's VectorStr and stores it in vec.data

    return TRUE;
}

A partir de este punto, uno podría continuar refactorizando el código real. Por ejemplo, reemplazando el VectorRAII por std::unique_ptr o std::vector .



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow