Sök…


Introduktion

Refactoring avser modifiering av befintlig kod till en förbättrad version. Även om refactoring ofta görs när man ändrar kod för att lägga till funktioner eller fixa buggar, hänvisar termen särskilt till att förbättra koden utan att nödvändigtvis lägga till funktioner eller fixa buggar.

Refactoring gå igenom

Här är ett program som kan dra nytta av refactoring. Det är ett enkelt program som använder C ++ 11 som är avsett att beräkna och skriva ut alla primtal från 1 till 100 och baseras på ett program som publicerades på CodeReview för granskning.

#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";
}

Utgången från detta program ser ut så här:

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

Det första vi märker är att programmet inte lyckas skriva ut 2 vilket är ett primtal. Vi kan helt enkelt lägga till en kodrad för att helt enkelt skriva ut den där konstanten utan att ändra resten av programmet, men det kan vara snyggare att refaktorera programmet för att dela upp det i två delar - en som skapar listan över primtal med en annan som skriver ut dem . Så här kan se ut:

#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';
}

Försöker vi den här versionen ser vi att den verkligen fungerar korrekt nu:

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

Nästa steg är att lägga märke till att det andra if klausulen egentligen inte behövs. Logiken i slingan letar efter primära faktorer för varje givet nummer upp till kvadratroten för det numret. Detta fungerar eftersom om det finns några primära faktorer för ett nummer måste minst en av dem vara mindre än eller lika med kvadratroten för det numret. Omarbetar just den funktionen (resten av programmet förblir densamma) får vi detta resultat:

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;
}

Vi kan gå längre och ändra variabla namn för att vara lite mer beskrivande. Till exempel primecount är egentligen inte en räkning av primtal. Istället är det en indexvariabel i vektorn av kända primetter. Även om no ibland används som en förkortning för "nummer", i matematisk skrivning, är det vanligare att använda n . Vi kan också göra några ändringar genom att eliminera break och genom att förklara variabler närmare var de används.

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;
}

Vi kan också refactor main att använda en "range-for" för att göra det lite snyggare:

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

Detta är bara ett sätt refactoring kan göras. Andra kan göra olika val. Syftet med refactoring förblir emellertid detsamma, vilket är att förbättra läsbarheten och möjligen prestandan för koden utan att nödvändigtvis lägga till funktioner.

Gå till sanering

I C ++ -kodbaser som tidigare var C, kan man hitta mönstret för att goto cleanup . Eftersom goto kommandot gör arbetsflödet för en funktion svårare att förstå, undviks detta ofta. Ofta kan det ersättas av returförklaringar, slingor, funktioner. Men med goto cleanup måste man bli av med saneringslogiken.

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;
}

I C ++ kan man använda RAII för att lösa problemet:

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;
}

Från denna tidpunkt kunde man fortsätta refakturera den faktiska koden. Till exempel genom att ersätta VectorRAII med std::unique_ptr eller std::vector .



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow