Skip to content

Beiträge

Code-Refaktorisierung

22. Oktober 2010 • 6 min Lesezeit

Code-Refaktorisierung

Bei einem kürzlichen Projekt wurde ich damit beauftragt, große Teile eines Websystems zu refaktorisieren. Es ist in C# geschrieben. Im Laufe der Zeit waren einige Code-Behind-Dateien auf 4000 Zeilen angewachsen. Das Ziel war es, diese Zahl auf ein wartbareres Niveau zu reduzieren.

In den nächsten Beiträgen habe ich Code-Schnipsel, die ich refaktorisiert habe, zusammengestellt und werde meine Gedanken und wie ich zur Lösung gekommen bin, erklären.

Der erste Code-Schnipsel:

    string tmp = Request.QueryString["st"];
    _varStartRecNum = tmp;
    if ((tmp != null) & (!Page.IsPostBack))
    {
        _varStartRecNum = tmp;
        postBack = true;
    }

    tmp = Request.QueryString["det"];
    if ((tmp != null) & (!Page.IsPostBack))
    {
        _varDetailsRecNum = tmp;
        postBack = true;
    }

    tmp = Request.QueryString["return"];
    if ((tmp != null) & (!Page.IsPostBack))
    {
        postBack = true;
    }

    tmp = Request.QueryString["searchnow"];
    if ((tmp != null) & (!Page.IsPostBack))
    {
        Session["selectedTab"] = "mtf";
        Session["sessionDSProviders"] = null;
        Session["mtfs"] = null;
    }

    tmp = Request.QueryString["displaywalking"];
    if (tmp == "true")
    {
        dispMtf = false;
        postBack = true;
    }

    tmp = Request.QueryString["sb"];

    if ((tmp != null) & (!Page.IsPostBack))
    {
        _varSortBy = tmp;
        postBack = true;
        switch (_varSortBy)
        {
            case "Distance":
            case "Drive time":
                ddlSortBy.SelectedIndex = 0;
                break;
            case "Name":
                ddlSortBy.SelectedIndex = 1;
                break;
            case "Gender":
                ddlSortBy.SelectedIndex = 2;
                break;
            case "Clinic":
                ddlSortBy.SelectedIndex = 3;
                break;
            case "City":
                ddlSortBy.SelectedIndex = 4;
                break;
            case "Description":
                ddlSortBy.SelectedIndex = 5;
                break;
        }
    }

Der obige Code-Schnipsel ist eine Sammlung von if-Anweisungen, die eine Auswertung und eine Ausführung sind. Bei meinem ersten Versuch versuchte ich, die gleiche Auswertung für alle if-Anweisungen zu verwenden, aber dann bemerkte ich, dass eine anders war. Da ich die Absicht des Codes nicht verstehe, bin ich gezwungen, die Logik wörtlich zu bewahren.

Unterschiedliche if-Auswertung:

tmp = Request.QueryString["displaywalking"];
if (tmp == "true")
{
    dispMtf = false;
    postBack = true;
}

Die switch-Anweisung beunruhigte mich. Die Bedingung zum Eintritt in die switch-Anweisung ist die gleiche wie bei den anderen. Ich beschloss, fortzufahren und mich später um die switch-Anweisung zu kümmern.

Der Code verwendet die gleiche Variable, die ‘tmp’-Variable, um verschiedene Abfragewerte abzurufen. Der Wert wird bei jedem Abruf eines Abfragewertes überschrieben. Zur Verdeutlichung erstelle ich eine Variable für jeden Abfragewert:

string st = Request.QueryString["st"];
string det = Request.QueryString["det"];
string @return = Request.QueryString["return"];
string searchNow = Request.QueryString["searchnow"];
string displayWaling = Request.QueryString["displaywalking"];
string sb = Request.QueryString["sb"];

Der nächste Schritt bestand darin, die Auswertung und den Ausdruck zu isolieren, während sie miteinander verbunden bleiben. Wenn eine Auswertung wahr ist, möchte ich ihren entsprechenden Ausdruck ausführen. Ich habe eine Klasse erstellt, die die Zuordnung darstellte.

private class Evaluate
{

    public Func Evaluation { get; set; }

    public Action Expression { get; set; }
}

Jetzt kann ich eine Auswertung erstellen und, wenn sie wahr ist, ihren Ausdruck ausführen.

Das nächste Problem war, wie man die obige Klasse mit allen if-Anweisungen verwendet. Ich war besorgt, dass die Ausdrücke in einer Sammlung unhandlich werden könnten. Der ganze Zweck bestand darin, eine prägnante, skalierbare Lösung zu schaffen. Die bestehende Lösung war weder das eine noch das andere.

var eval = new[]
               {
                   new Evaluate {Evaluation = () => (!string.IsNullOrEmpty(st) && !IsPostBack), Expression = () => { _varStartRecNum = st;postBack = true; }},
                   new Evaluate {Evaluation = () => (!string.IsNullOrEmpty(det) && !IsPostBack), Expression = () => { _varStartRecNum = det;postBack = true; }}, 
                   new Evaluate {Evaluation = () => (!string.IsNullOrEmpty(@return) && !IsPostBack), Expression = () => {postBack = true; }}, 
                   new Evaluate {Evaluation = () => (!string.IsNullOrEmpty(searchNow) && !IsPostBack), Expression = () => {Session["selectedTab"] = "mtf";Session["sessionDSProviders"] = null; Session["mtfs"] = null;}}, 
                   new Evaluate {Evaluation = () => (!string.IsNullOrEmpty(displayWaling)), Expression = () => {dispMtf = false; postBack = true;}}, 
                   new Evaluate {Evaluation = () => (!string.IsNullOrEmpty(sb) && !IsPostBack), Expression = () => {_varSortBy = sb;postBack = true; SetSort(_varSortBy);}}, 
               };

Es stellte sich besser heraus als erwartet. Ein Nachteil meiner Lösung ist, dass Sie, wenn Sie nicht wissen, wie man Delegaten verwendet, beim Verwalten des obigen Codes in Schwierigkeiten geraten.

Das letzte Hindernis war die switch-Anweisung. Sie sollte nicht elegant in meine anonyme Sammlung passen, aber das musste sie auch nicht:

private void SetSort(string sortBy)
{
    switch (sortBy)
    {
        case "Distance":
        case "Drive time":
            ddlSortBy.SelectedIndex = 0;
            break;
        case "Name":
            ddlSortBy.SelectedIndex = 1;
            break;
        case "Gender":
            ddlSortBy.SelectedIndex = 2;
            break;
        case "Clinic":
            ddlSortBy.SelectedIndex = 3;
            break;
        case "City":
            ddlSortBy.SelectedIndex = 4;
            break;
        case "Description":
            ddlSortBy.SelectedIndex = 5;
            break;
    }
}

Durch die Kapselung in eine Methode konnte ich die Methode im Ausdruck referenzieren. Es funktionierte sehr schön.

new Evaluate {Evaluation = () => (!string.IsNullOrEmpty(sb) && !IsPostBack), Expression = () => {_varSortBy = sb;postBack = true; SetSort(_varSortBy);}

Die letzte Komponente ist die Iteration über die Sammlung:

foreach (var evaluate in eval.Where(evaluate => evaluate.Evaluation()))
{
    evaluate.Expression();
}

Die vollständige Lösung:

private class Evaluate
{
    public Func Evaluation { get; set; }

    public Action Expression { get; set; }
}

private void SetSort(string sortBy)
{
    switch (sortBy)
    {
        case "Distance":
        case "Drive time":
            ddlSortBy.SelectedIndex = 0;
            break;
        case "Name":
            ddlSortBy.SelectedIndex = 1;
            break;
        case "Gender":
            ddlSortBy.SelectedIndex = 2;
            break;
        case "Clinic":
            ddlSortBy.SelectedIndex = 3;
            break;
        case "City":
            ddlSortBy.SelectedIndex = 4;
            break;
        case "Description":
            ddlSortBy.SelectedIndex = 5;
            break;
    }
}

private void EvaluateQueryParameters()
{
    string st = Request.QueryString["st"];
    string det = Request.QueryString["det"];
    string @return = Request.QueryString["return"];
    string searchNow = Request.QueryString["searchnow"];
    string displayWaling = Request.QueryString["displaywalking"];
    string sb = Request.QueryString["sb"];

    var eval = new[]
                   {
                       new Evaluate {Evaluation = () => (!string.IsNullOrEmpty(st) && !IsPostBack), Expression = () => { _varStartRecNum = st;postBack = true; }},
                       new Evaluate {Evaluation = () => (!string.IsNullOrEmpty(det) && !IsPostBack), Expression = () => { _varStartRecNum = det;postBack = true; }}, 
                       new Evaluate {Evaluation = () => (!string.IsNullOrEmpty(@return) && !IsPostBack), Expression = () => {postBack = true; }}, 
                       new Evaluate {Evaluation = () => (!string.IsNullOrEmpty(searchNow) && !IsPostBack), Expression = () => {Session["selectedTab"] = "mtf";Session["sessionDSProviders"] = null; Session["mtfs"] = null;}}, 
                       new Evaluate {Evaluation = () => (!string.IsNullOrEmpty(displayWaling)), Expression = () => {dispMtf = false; postBack = true;}}, 
                       new Evaluate {Evaluation = () => (!string.IsNullOrEmpty(sb) && !IsPostBack), Expression = () => {_varSortBy = sb;postBack = true; SetSort(_varSortBy);}}, 
                   };

    foreach (var evaluate in eval.Where(evaluate => evaluate.Evaluation()))
    {
        evaluate.Expression();
    }
}

Am Ende gefällt mir diese Lösung besser als die ursprüngliche. Einer der Nachteile ist das Niveau, auf dem sie geschrieben ist. Ich wollte eine einfachere Lösung schaffen, die jeder Entwickler warten kann. Es gibt nichts Schwieriges an dem obigen Code; ich erstelle eine Sammlung und iteriere über sie. Die Verwirrung entsteht durch die Auswertung und die Ausdrücke. Es ist kein Anfängerthema.

Autor: Chuck Conway ist ein KI-Ingenieur mit fast 30 Jahren Erfahrung in der Softwareentwicklung. Er entwickelt praktische KI-Systeme – Content-Pipelines, Infrastruktur-Agenten und Tools, die echte Probleme lösen – und teilt seine Erkenntnisse unterwegs. Verbinden Sie sich mit ihm in den sozialen Medien: X (@chuckconway) oder besuchen Sie ihn auf YouTube und auf SubStack.

↑ Nach oben

Das könnte dir auch gefallen