ほとんどが1つの正規表現で構成される大きな関数をリファクタリングする必要がありますか?[閉まっている]


15

約100行にわたる関数を作成しました。それを聞いて、あなたはおそらく私に単一の責任について教えて、私にリファクタリングを促すように誘惑されるでしょう。これは私の本能でもありますが、問題は次のとおりです。関数 1つのことを行います。複雑な文字列操作を実行し、関数の本体は主に1つの冗長な正規表現で構成され、文書化された多くの行に分割されます。正規表現を複数の関数に分割すると、実際に言語を切り替えているため、実際に読みやすさが失われ、正規表現が提供する一部の機能を利用できなくなるためです。ここに私の質問があります:

正規表現を使用した文字列操作に関しては、大きな関数本体は依然としてアンチパターンですか?名前付きキャプチャグループは、機能と非常に似た目的を果たしているようです。ところで、正規表現を通るすべてのフローのテストがあります。


3
その大部分がドキュメンテーションであると考えると、あなたの機能に何か問題があるとは思いません。ただし、そもそも大きな正規表現を使用すると、保守性の問題が発生する可能性があります。
ジョエルコーネット14年

2
巨大な正規表現が問題の最善の解決策であると確信していますか?パーサーライブラリや、カスタムファイル形式を標準形式(XML、JSONなど)に置き換えるなど、より単純な代替手段を検討しましたか?
ロルタバック14年

2
この正規表現の変更/強化/簡略化されたバージョンを使用する他の機能はありますか?これは、リファクタリングが行われるべき重要な指標です。そうでない場合は、そのままにしておきます。そのような複雑な文字列操作が必要なのはそれ自体が黄色の旗であり(コンテキストはわかりません、したがって黄色だけです)、関数をリファクタリングすることは、私が感じる罪悪感から償還する儀式のようですそれ;)
コンラッド・モラウスキー14年

8
100行の正規表現で1つのことだけを行うにはどうすればよいですか?
ピーターB 14年

@lortabac:入力はユーザーが作成したテキスト(
プローゼ

回答:


36

あなたが遭遇しているのは、合理的な意思決定よりも「ベストプラクティス」を装ってガイドラインを奴隷的に守ることを好む人々に耳を傾けることから生じる認知的不協和です。

あなたは明らかに宿題をしました:

  • 関数の目的が理解されています。
  • その実装の仕組みが理解されています(つまり、読み取り可能)。
  • 実装の完全なカバレッジテストがあります。
  • これらのテストは合格です。つまり、実装が正しいと信じています。

これらの点のいずれかが真実ではない場合、私はあなたの機能が仕事を必要とすることを言うために一列になります。そのため、コードを現状のままにしておくことに1つの投票があります。

2番目の投票は、あなたのオプションとそれぞれから得られる(そして失う)ものを見ることに由来します。

  • リファクタリング。これにより、関数の長さに関する誰かの考えを順守し、可読性を犠牲にします。
  • 何もしない。 これにより、既存の可読性が維持され、関数の長さに関する誰かの考えへの準拠が犠牲になります。

この決定は、読みやすさまたは長さのどちらを重視するかによって決まります。長さはいいが、読みやすさは重要で、いつでも前者を後者に引き継ぐと信じているキャンプに落ちます。

結論:破損していない場合は修正しないでください。


10
「破損していない場合は修正しないでください」の+1
ジョルジオ14年

確かに。Sandy Metzのルール(gist.github.com/henrik/4509394)はすばらしく、すべてですが、youtube.com / watch?v = VO-NvnZfMA4#t = 1379で、彼女がどのようになったのか、なぜ人々が取っているのかについて語っていますあまりにも真剣に。
アマダン14年

@Amdan:ビデオからの追加のコンテキストで、Metzがしたことは理にかなっています。片方の顧客がより合理的な中間にドラッグする方法として、もう一方の端で極端な行動に対抗するために、一方の顧客が意図的に極端であるという彼女の推奨。その議論の残りの部分は、私の答えの根底にあるものです。信仰ではなく、推論が最善の行動方針を決定する方法です。
Blrfl

19

正直なところ、あなたの機能は「一つのことをする」かもしれませんが、あなたが自分で述べたように

正規表現を複数の関数に分割し始めることができますが、

つまり、正規表現コードは多くのことを実行します。そして、それはより小さく、個別にテスト可能なユニットに分解できると思います。ただし、これが良いアイデアである場合、答えるのは簡単ではありません(特に実際のコードを見ずに)。そして、正しい答えは「はい」でも「いいえ」でもないかもしれませんが、「まだではありませんが、次回はその正規表現で何かを変更する必要があります」。

しかし、実際には言語を切り替えるので、実際にはそのように読みやすさが失われるように感じます

そして、これが核となるポイントです-あなたは正規表現言語で書かれたコードを持っています。この言語は、それ自体では抽象化の優れた手段を提供しません(また、「名前付きキャプチャグループ」を関数の代替として考えていません)。したがって、「正規表現言語で」リファクタリングすることは実際には不可能であり、小さな正規表現をホスト言語と織り交ぜると実際に読みやすさが向上しない場合があります(少なくとも、そう感じますが、疑問があります。 。だからここに私のアドバイスがあります

  • コードを別の上級開発者に見せてください(おそらく/codereview//にあります)に見せて、他の人があなたのやり方で読みやすさについて考えていることを確認してください。他の人があなたほど読みやすい100行の正規表現を見つけられないかもしれないという考えを受け入れてください。「小さな破片に簡単に壊れない」という概念は、2番目の目で克服できます。

  • 実際の進化を観察します-新しい要件が到着し、それらを実装してテストする必要があるとき、あなたの光沢のある正規表現はまだとてもよく見えますか?あなたの正規表現が機能する限り、私はそれに触れることはありませんが、何かを変更する必要があるときはいつでも、この1つの大きなブロックにすべてを入れることが本当に良いアイデアであるかどうかを再検討します-そして(真剣に!)小さいピースは良いオプションではありません。

  • 保守性を観察してください-現在のフォームで正規表現を効果的にデバッグできますか?特に、何かを変更する必要があり、テストで何かがおかしいと言われた後、根本原因を見つけるのに役立つreg expデバッガーがありますか?デバッグが困難になった場合、設計を再検討する機会にもなります。


名前付きキャプチャグループ(実際にはキャプチャグループ全般)は、final / write-once変数、またはおそらくマクロに最も似ていると思います。正規表現プロセッサから返された一致オブジェクトから、または後で正規表現自体で、一致の特定の部分を参照できます。
JAB

4

1つのことを行うより長い関数が、作業単位を処理する最も適切な方法である場合があります。(お気に入りのクエリ言語を使用して)データベースのクエリを処理し始めると、非常に長い関数に簡単にアクセスできます。関数(またはメソッド)を読みやすくして、その目的に限定することは、関数の最も望ましい結果だと考えています。

コードサイズに関しては、長さは任意の「標準」です。C#の100行関数が長いと見なされる場合、アセンブリの一部のバージョンではごくわずかです。レポートの非常に複雑なデータセットを返す200行のコード範囲に十分収まるSQLクエリを見てきました。

完全に動作するコード、それは合理的にできる限り単純ですであり、それが目標です。

長いからといって変更しないでください。


3

常に正規表現をサブ正規表現に分割し、最終的な表現を徐々に構成することができます。これは、特に同じサブパターンが何度も繰り返される場合に、非常に大きなパターンの理解を助けることができます。たとえば、Perlの場合。

my $start_re = qr/(?:\w+\.\w+)/;
my $middle_re = qr/(?:DOG)|(?:CAT)/;
my $end_re = qr/ => \d+/;

my $final_re = $start_re . $middle_re . $end_re;
# or: 
# my $final_re = qr/${start_re}${middle_re}${end_re}/

詳細フラグを使用します。これは、提案されているものよりもさらに便利です。
DudeOnRock 14年

1

それが壊れている場合、私はそれを破ると言うでしょう。保守性の観点とおそらくは持続可能性の観点から、それを壊すことは理にかなっていますが、もちろん、関数の自然さ、入力をどのように取得し、何を返すかを考慮する必要があります。

ストリーミングチャンクデータをオブジェクトに解析していたのを覚えているので、基本的には2つの主要な部分に分割しました。1つはエンコードされたテキストから文字列の完全なユニットを構築し、それらは(異なるオブジェクトのランダムプロパティである可能性があります)オブジェクトの更新または作成よりも。

また、各主要部分をいくつかのより小さくより具体的な機能に分割することができたため、最後に5つの異なる機能を使用して全体を実行し、いくつかの機能を異なる場所で再利用できました。


1

あなたが考慮してもしなくてもよいことの1つは、その言語で正規表現を使用する代わりに、使用している言語で小さなパーサーを書くことです。これは読みやすく、テストしやすく、保守しやすいかもしれません。


私はこれについて自分で考えました。問題は、入力が散文であり、コンテキストとフォーマットからヒントを得ていることです。このようなパーサーを作成できる場合は、それについて詳しく知りたいと思います!自分で何も見つかりませんでした。
DudeOnRock

1
正規表現で解析できる場合は、解析できます。あなたの応答は、あなたが解析に精通していないかもしれないように私に思わせます。その場合は、正規表現を使用することをお勧めします。それか、新しいスキルを学びます。
トーマスエディング14年

新しいスキルを学びたいです。提案できる優れたリソースはありますか?その背後にある理論にも興味があります。
DudeOnRock 14年

1

ほとんどの場合、巨大な正規表現は悪い選択です。私の経験では、開発者が構文解析に慣れていないためによく使用されます(Thomas Edingの回答を参照)。

とにかく、正規表現ベースのソリューションに固執したいとします。

実際のコードがわからないので、考えられる2つのシナリオを調べます。

  • 正規表現は単純です(多くのリテラルマッチングといくつかの選択肢)

    この場合、単一の正規表現によって提供される高度な機能は必須ではありません。これは、それを分割することで恩恵を受ける可能性が高いことを意味しています。

  • 正規表現は複雑です(多くの選択肢)

    この場合、おそらく何百万もの可能性のあるフローがあるため、現実的には完全なテストカバレッジを得ることができません。そのため、テストするには、分割する必要があります。

想像力に欠けるかもしれませんが、100行の正規表現が適切なソリューションである現実の状況は考えられません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.