最近のプロジェクトで、Webシステムの大部分をリファクタリングするタスクを任されました。C#で書かれています。時間が経つにつれて、コードビハインドファイルの一部が4000行まで膨れ上がっていました。目標はこの数を、より保守可能なレベルまで削減することでした。
次のいくつかの投稿では、リファクタリングしたコードのスニペットを取り上げ、私の考えと、どのようにしてその解決策に至ったかを説明します。
最初のコードスニペット:
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;
}
}
上記のコードスニペットは、if文の集合です。これらは評価と実行です。最初の試みでは、すべてのif文に同じ評価を使用しようとしましたが、その後、1つが異なることに気付きました。コードの意図を理解していないため、ロジックをそのまま保持することを余儀なくされました。
異なるif評価:
tmp = Request.QueryString["displaywalking"];
if (tmp == "true")
{
dispMtf = false;
postBack = true;
}
switch文が気になりました。switch文に入るための条件は他のものと同じです。先に進んで、switch文については後で心配することにしました。
このコードは同じ変数「tmp」変数を使用して、異なるクエリ値を取得します。値は各クエリ値の取得で上書きされます。明確にするために、各クエリ値に対して変数を作成しました:
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"];
次のステップは、評価と式を分離しながら、それらを相互に関連付けることでした。評価が真の場合、対応する式を実行したいです。関連付けを表すクラスを作成しました。
private class Evaluate
{
public Func Evaluation { get; set; }
public Action Expression { get; set; }
}
これで評価を作成でき、それが真の場合、対応する式を実行できます。
次の問題は、上記のクラスをすべてのif文でどのように使用するかでした。式がコレクション内で扱いにくくなるのではないかと心配していました。全体的な目的は、簡潔でスケーラブルなソリューションを作成することでした。既存のソリューションはどちらでもありませんでした。
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);}},
};
予想以上にうまくいきました。私のソリューションの1つの欠点は、デリゲートの使い方を知らない場合、上記のコードを保守する際に困ることになるということです。
最後の障害はswitch文でした。それは匿名コレクションに優雅に適合するつもりはありませんでしたが、その必要もありませんでした:
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;
}
}
メソッドにカプセル化することで、式内のメソッドを参照できました。非常にうまく機能しました。
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();
}
完全なソリューション:
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();
}
}
結局のところ、このソリューションは元のものより気に入っています。1つの欠点は、それが書かれているレベルです。任意の開発者が保守できるより単純なソリューションを作成したかったのです。上記のコードに難しいことは何もありません。コレクションを作成して反復処理しているだけです。混乱は評価と式に由来します。これは初心者向けのトピックではありません。
Author: Chuck Conway is an AI Engineer with nearly 30 years of software engineering experience. He builds practical AI systems—content pipelines, infrastructure agents, and tools that solve real problems—and shares what he’s learning along the way. Connect with him on social media: X (@chuckconway) or visit him on YouTube and on SubStack.
著者: Chuck Conwayは、ソフトウェアエンジニアリングの経験が30年近くあるAIエンジニアです。彼は実用的なAIシステム(コンテンツパイプライン、インフラストラクチャエージェント、実際の問題を解決するツール)を構築し、学んだことを共有しています。ソーシャルメディアで彼とつながってください: X (@chuckconway) または YouTube と SubStack で彼を訪問してください。