Szukaj…


Wprowadzenie

Refaktoryzacja odnosi się do modyfikacji istniejącego kodu w ulepszoną wersję. Chociaż refaktoryzacja jest często przeprowadzana podczas zmiany kodu w celu dodania funkcji lub naprawienia błędów, termin ten dotyczy w szczególności ulepszania kodu bez konieczności dodawania funkcji lub naprawiania błędów.

Przeprowadź refaktoryzację

Oto program, który może skorzystać z refaktoryzacji. Jest to prosty program wykorzystujący C ++ 11, który ma na celu obliczenie i wydrukowanie wszystkich liczb pierwszych od 1 do 100 i jest oparty na programie opublikowanym do przeglądu w CodeReview .

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

Dane wyjściowe tego programu wyglądają następująco:

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

Pierwszą rzeczą, którą zauważamy, jest to, że program nie drukuje 2 który jest liczbą pierwszą. Moglibyśmy po prostu dodać wiersz kodu, aby po prostu wydrukować tę stałą bez modyfikowania reszty programu, ale fajniej byłoby zrefaktoryzować program i podzielić go na dwie części - jedną, która tworzy listę liczb pierwszych i drugą, która je drukuje . Oto jak to może wyglądać:

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

Po wypróbowaniu tej wersji widzimy, że rzeczywiście działa teraz poprawnie:

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

Następnym krokiem jest zauważenie, że druga klauzula if nie jest tak naprawdę potrzebna. Logika w pętli wyszukuje czynniki pierwsze każdej podanej liczby do pierwiastka kwadratowego z tej liczby. Działa to, ponieważ jeśli istnieją jakieś czynniki pierwsze liczby, przynajmniej jeden z nich musi być mniejszy lub równy pierwiastkowi kwadratowemu tej liczby. Przerabiając tylko tę funkcję (reszta programu pozostaje taka sama) otrzymujemy ten wynik:

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

Możemy pójść dalej, zmieniając nazwy zmiennych, aby były bardziej opisowe. Na przykład liczba primecount nie jest tak naprawdę liczbą liczb pierwszych. Zamiast tego jest to zmienna indeksu w wektorze znanych liczb pierwszych. Ponadto, chociaż no jest czasami używane jako skrót od „liczby”, w piśmie matematycznym częściej używa się n . Możemy również wprowadzić pewne modyfikacje, eliminując break i deklarując zmienne bliżej miejsca ich użycia.

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

Możemy również refaktoryzować main aby użyć „zasięgu”, aby był nieco bardziej uporządkowany:

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

Jest to tylko jeden ze sposobów refaktoryzacji. Inni mogą dokonywać różnych wyborów. Jednak cel refaktoryzacji pozostaje ten sam, czyli poprawa czytelności i być może wydajności kodu bez konieczności dodawania funkcji.

Idź do czyszczenia

W bazach kodu C ++, które były C, można znaleźć wzorzec goto cleanup . Ponieważ polecenie goto utrudnia zrozumienie przepływu pracy funkcji, często tego się unika. Często można go zastąpić instrukcjami zwrotnymi, pętlami, funkcjami. goto cleanup należy pozbyć się logiki czyszczenia.

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

W C ++ można użyć RAII, aby rozwiązać ten problem:

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

Od tego momentu można kontynuować refaktoryzację rzeczywistego kodu. Na przykład zastępując VectorRAII przez std::unique_ptr lub std::vector .



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow