外部動作を変更せずにリファクタリングをどこまでプッシュできますか?


27

Martin Fowlerよると、コードのリファクタリングは次のようになります(私の強調):

リファクタリングは、既存のコード本体を再構築し、外部の動作を変更せずに内部構造を変更するための統制された手法です。その中心は、変換を維持する一連の小さな動作です。各変換(「リファクタリング」と呼ばれます)はほとんど行いませんが、一連の変換によって大幅な再構築が行われる可能性があります。それぞれのリファクタリングは小さいので、間違いが起こる可能性は低くなります。また、システムは小さなリファクタリングのたびに完全に機能し続けるため、再構築中にシステムが深刻に破損する可能性が低くなります。

この文脈における「外部行動」とは何ですか?たとえば、移動メソッドのリファクタリングを適用して、あるメソッドを他のクラスに移動すると、外部の動作が変更されたように見えますか?

それで、私はどの時点で変更がリファクタリングでなくなり、さらに何かになるのかを知ることに興味があります。「リファクタリング」という用語は、大きな変更に誤用される可能性があります。別の言葉がありますか

更新。インターフェースに関する多くの興味深い回答がありますが、メソッドのリファクタリングを動かしてインターフェースを変更しませんか?


既存の動作がうまくいかない、または不完全な場合は、それを修正し、削除/書き換えます。その場合、リファクタリングはしていないかもしれませんが、その結果としてシステムが良くなった場合、誰が名前を気にしますか(右?)
ジョブ

2
上司は、リファクタリングの許可が与えられ、書き直したかどうかを気にするかもしれません。
ジェフ

2
リファクタリングの境界は単体テストです。それらによって仕様が作成されている場合、テストを中断しない変更はリファクタリングですか?
ジョージシルバ


詳細を知りたい場合は、このトピックも活発な研究分野です。このトピックに関する多くの科学出版物があります。たとえば、informatik.uni-trier.de
〜ley / db / indices / a

回答:


25

このコンテキストでの「外部」とは、「ユーザーが観察できる」ことを意味します。ユーザーは、アプリケーションの場合は人間であり、パブリックAPIの場合は他のプログラムです。

したがって、メソッドMをクラスAからクラスBに移動し、両方のクラスがアプリケーションの奥深くにあり、変更によるアプリの動作の変化をユーザーが観察できない場合、それを正しくリファクタリングと呼ぶことができます。

OTOH、他の高レベルのサブシステム/コンポーネントがその動作によって動作を変更したり、変更のために破損した場合、それは実際に(通常)ユーザー(または少なくともログをチェックするシステム管理者)に観察可能です。または、クラスがパブリックAPIの一部である場合、MがBではなくクラスAの一部であることに依存するサードパーティコードが存在する可能性があります。したがって、これらのケースはいずれも厳密な意味でリファクタリングされません。

コードのリワークをリファクタリングと呼ぶ傾向がありますが、これは間違っていると思います。

実際、それはリファクタリングがファッショナブルになることの悲しいが期待される結果です。開発者は長年にわたってアドホックな方法でコードのリワークを行ってきましたが、確かに根深い習慣を分析して変更するよりも新しい流行語を学ぶ方が簡単です。

それでは、外部の振る舞いを変えるリワークの正しい言葉は何ですか?

私はそれを再設計と呼びます。

更新

インターフェースに関する多くの興味深い回答がありますが、メソッドのリファクタリングを動かしてインターフェースを変更しませんか?

なにかの?特定のクラス、はい。しかし、これらのクラスは何らかの形で外の世界に直接見えるのでしょうか?彼らはの外部インターフェース(API / GUI)の一部で、あなたのプログラムの中にある、とないので-ない場合はプログラム -変更が行われていない外部の関係者によって観察がある(もちろん変更休憩何か、しない限り)。

:私はこれ以上の深い疑問があることを感じて、それ自体で独立したエンティティとして、特定のクラスが存在していますか?ほとんどの場合、答えはノーです。クラスは、より大きなコンポーネント、クラスとオブジェクトのエコシステムの一部としてのみ存在し、それなしではインスタンス化できず、使用できません。このエコシステムには、その(直接/間接)依存関係だけでなく、それに依存する他のクラス/オブジェクトも含まれます。これは、これらの上位レベルのクラスがないと、クラスに関連付けられた責任がシステムのユーザーにとって無意味/役に立たない可能性があるためです。

たとえば、レンタカーを扱うプロジェクトでは、 Chargeクラス。レンタルステーションのエージェントと顧客は個別の料金で多くのことを行うことができないため、このクラス自体はシステムのユーザーには使用できません。全体としてレンタル契約契約を処理します(これにはさまざまな種類の料金が含まれます) 。ユーザーは主に、これらの料金の合計に関心があり、最終的に支払うことになります。代理店は、さまざまな契約オプション、レンタル期間、車両グループ、保険パッケージ、追加アイテムなどに関心があります。これらは、(洗練されたビジネスルールを介して)現在の料金と最終支払いの計算方法を管理します。これらのうち。そして、国の代表者/ビジネスアナリストは、特定のビジネスルール、それらの相乗効果と効果(会社の収入など)に関心を持っています。

最近、このクラスをリファクタリングし、そのフィールドとメソッドのほとんどの名前を変更しました(標準のJava命名規則に従います。これは前任者によって完全に無視されていました)。またStringcharフィールドをより適切なタイプに置き換えるためのリファクタリングをさらに計画enumしていbooleanます。これらはすべてクラスのインターフェイスを確実に変更しますが、(私が正しく仕事をすれば)アプリのユーザーには表示されません。彼らは確かに電荷の概念を知っているにもかかわらず、個々の電荷がどのように表されるかを気にしません。ドメインコンセプトを表していない他の100のクラスを例として選択することもできたので、エンドユーザーには概念的にも見えなくなりましたが、コンセプトレベルで少なくともある程度の可視性がある例を選択する方が面白いと思いました。これは、クラスインターフェースがドメインコンセプトの表現にすぎず(実際には)、実際のものではないことを示しています*。概念に影響を与えることなく表現を変更できます。また、ユーザーは概念を理解しているだけです。概念と表現の間のマッピングを行うのは私たちの仕事です。

* そして、私たちのクラスが表すドメインモデルは、それ自体が「本物」の近似表現にすぎないことを簡単に追加できます...


3
ニッチピッキング、私は「デザイン変更」は再設計ではないと言うでしょう。再設計は非常に重要に聞こえます。
user606723

4
面白い-あなたの例のクラスAのコードの「既存の体」であり、方法Mがパブリックであるならば、Aの外部の動作が変更されているあなたは、おそらく全体的なシステムがリファクタリングされているのに対し、そのクラスAは、再設計されたと言うことができるように。。
saus

私はユーザーに観察可能が好きです。だからこそ、単体テストの破壊は兆候ではなく、エンドツーエンドまたは統合テストが兆候だと思います。
アンディWiesendanger

8

外部とは、単にその真の言語的意味におけるインターフェースを意味します。この例では牛を考えてみましょう。野菜を食べて、戻り値として牛乳を摂取する限り、その内臓がどのように機能するかは気にしません。神が牛の内臓を変えて、血が青くなり、入り口と出口(口と牛乳)が変わらない限り、リファクタリングと見なすことができます。


1
良い答えだとは思わない。リファクタリングは、APIの変更を意味します。ただし、エンドユーザーや入力/ファイルの読み取り/生成方法に影響を与えることはありません。
rds

3

私にとって、リファクタリングは、テストや正式な仕様によって境界が設定されたときに最も生産的/快適になりました。

  • これらの境界は十分に硬直しているので、たまに交差すればすぐに検出されるので、回復するために多くの変更をロールバックする必要はありません。一方、これらは、無関係な動作の変更を心配することなくコードを改善するのに十分な余裕を与えます。

  • 私が特に好きなのは、これらの種類の境界がいわば適応的であるということです。つまり、1)変更を行い、仕様/テストに準拠していることを確認します。次に、2)QAまたはユーザーテストに渡されます。ここで注意してください。仕様/テストで何かが欠落しているため、まだ失敗する可能性があります。OK、3a)テストに合格したら、完了です。それ以外の場合、3b)テストが失敗した場合、4)変更ロールバックし 5)テストを追加するか、仕様を明確にして、次回この間違いが繰り返されないようにします。テストが成功したか失敗したかに関係なく、私何かをます-コード/テスト/仕様のいずれかが改善されることに注意してください-私の努力は完全に無駄になりません。

他の種類の境界については、これまでのところあまり運がありませんでした。

「ユーザーに観測可能」は、従うべき規律がある場合に安全な賭けです -私にとって、既存のテストの分析/新しいテストの作成に常に多大な労力を費やしている-多すぎる労力。私がこのアプローチについて嫌うもう一つのことは、盲目的にそれに従うことは、あまりにも制限的であることが判明するかもしれないということです。-データの読み込みには2秒ではなく3秒かかるため、この変更は禁止されています。-ユーザー/ UXの専門家にこれが関連するかどうかを確認してください。-まさか、観察可能な行動の変更は禁止されています。安全?あなたは賭けます!生産的?あんまり。

私が試したもう1つの方法は、コードロジックを保持することです(読みながら理解する方法)。最も基本的な(通常はあまり実りのない)変更を除いて、これは常にワームの缶だった...またはバグの缶を言うべきですか?回帰バグを意味します。スパゲッティコードを操作するときに重要なものを壊すのは簡単すぎます。


3

リファクタリング本は、あなたができることをそのメッセージにはかなり強いですあなたはユニットテストのカバレッジを持っている場合にのみリファクタリング行います

したがって、ユニットテストを壊さない限り、リファクタリングしていると言えます。テストを中断すると、リファクタリングは行われなくなります。

しかし、クラスやメンバーの名前を変更するような単純なリファクタリングはどうですか?彼らはテストを破りませんか?

はい、彼らはそうします、そして、それぞれの場合において、あなたはそのブレークが重要であるかどうかを考慮する必要があります。あなたの場合はSUTは、パブリックAPIである/ SDKは、[名前の変更は、実際に、破壊変化です。そうでなければ、おそらく大丈夫です。

ただし、実際に気にする動作を変更したためではなく、テストが壊れやすいテストであるため、テストが頻繁に中断することを考慮してください。


3

このコンテキストで「外部動作」を定義する最良の方法は、「テストケース」です。

コードをリファクタリングし、テストケース(リファクタリングの前に定義された)をパスし続ける場合、リファクタリングは外部の動作を変更していません。1つ以上のテストケースが失敗した場合、外部の動作変更されています。

少なくとも、それはトピック(たとえば、ファウラーの)で出版されたさまざまな本の私の理解です。


2

境界は、プロジェクトを開発、保守、サポートする人と、サポーター、メンテナー、開発者以外のユーザーである人との間の境界線になります。したがって、外部の世界から見ると、振る舞いは同じように見えますが、振る舞いの背後にある内部構造は変化しています。

したがって、ユーザーに表示されるものでない限り、クラス間で関数を移動しても問題はありません。

コードのリワークによって外部の動作が変更されたり、新しい関数が追加されたり、既存の関数が削除されない限り、リワークをリファクタリングと呼んでもかまいません。


同意しない。データアクセスコード全体をnHibernateに置き換えても、外部の動作は変わりませんが、ファウラーの「規律あるテクニック」には従いません。これはリエンジニアリングであり、それをリファクタリングと呼ぶことは、関連するリスク要因を隠します。
pdr

1

すべての敬意を払って、クラスのユーザーはクラスで構築されたアプリケーションのエンドユーザーではなく、リファクタリングされているクラスを使用することで呼び出されるか継承することによって実装されるクラスであることを覚えておく必要があります。

「外部の振る舞いを変えてはならない」と言うとき、ユーザーに関する限り、クラスは以前とまったく同じように振る舞うことを意味します。元の(リファクタリングされていない)実装は単一のクラスであり、新しい(リファクタリングされた)実装にはクラスが構築される1つ以上のスーパークラスがありますが、ユーザーは内部(実装)インターフェイスのみが表示されます。

そのため、クラスに「doSomethingAmazing」というメソッドがある場合、それが参照しているクラスによって実装されているか、そのクラスが構築されているスーパークラスによって実装されているかはユーザーには関係ありません。ユーザーにとって重要なのは、新しい(リファクタリングされた)「doSomethingAmazing」の結果が古い(リファクタリングされていない)「doSomethingAmazing」の結果と同じであることだけです。

ただし、多くの場合リファクタリングと呼ばれるものは真のリファクタリングではなく、コードを修正して新しい機能を追加するのを容易にするために行われる再実装です。そのため、この(擬似)リファクタリングの後者のケースでは、新しい(リファクタリングされた)コードは実際には何か違うこと、またはおそらく古いものよりも何かをします。


「OKボタンを押してもよろしいですか」というダイアログをポップアップするためにWindowsフォームが使用された場合はどうなりますか?そして、私はそれを削除することに決めましたが、それはほとんど良い結果を出せず、ユーザーを悩ますので、コードをリファクタリングし、再設計し、修正し、デバッグしましたか?
ジョブ

@job:新しい仕様に合わせてプログラムを変更しました。
-jmoreno

私見、あなたはここで異なる基準を混ぜているかもしれません。コードをゼロから書き直すことは確かにリファクタリングではありませんが、これは外部の動作を変更したかどうかに関係なくそうです。また、クラスインターフェイスの変更がリファクタリングではない場合、どうしてMove Method et al。リファクタリングカタログに存在する?
ペテルトレック

@PéterTörök-「クラスインターフェイスの変更」の意味に完全に依存します。継承を実装するOOP言語では、クラスのインターフェイスには、すべての先祖によってクラス自体によって実装されるものだけが含まれます。クラスインターフェイスの変更とは、インターフェイスへのメソッドの削除/追加(またはメソッドのシグネチャ、つまり渡されるパラメーターのタイプの数)の変更を意味します。リファクタリングとは、メソッド、クラス、またはスーパークラスに誰が応答しているかを意味します。
ジークハンセル

私見-この質問全体は難解すぎて、プログラマーにとって有用な価値がないかもしれません。
ジークハンセル

1

「外部の振る舞い」では、主にパブリックインターフェースについて話していますが、これにはシステムの出力/アーティファクトも含まれます。(つまり、ファイルを生成するメソッドがある場合、ファイルの形式を変更すると外部の動作が変更されます)

e:「移動方法」は外部の振る舞いの変化だと思います。ここで、ファウラーは、野生にリリースされた既存のコードベースについて話していることに留意してください。状況に応じて、変更が外部クライアントに影響を与えないことを確認し、陽気な方法で進めることができる場合があります。

e2:「それで、外部の振る舞いを変えるリワークの正しい言葉は何ですか?」-APIリファクタリング、重大な変更など...まだリファクタリングされていますが、クライアントとすでに荒れているパブリックインターフェイスをリファクタリングするためのベストプラクティスに従っていないだけです。


しかし、彼がパブリックインターフェイスについて話している場合、「メソッドの移動」リファクタリングについてはどうでしょうか。
SiberianGuy

@Idsa回答を編集して、質問に対処しました。

@kekela、それはまだ私には不明であるところ、この「リファクタリング」のものが終了
SiberianGuy

@idsa投稿した定義によると、パブリックインターフェースを変更するとすぐにリファクタリングが停止します。(1つのクラスから別のクラスにパブリックメソッドを移動することがこの例です)

0

「移動方法」はリファクタリング手法であり、それ自体ではリファクタリングではありません。リファクタリングは、いくつかのリファクタリング手法をクラスに適用するプロセスです。そこで、「移動メソッド」をクラスに適用したと言うと、実際には「リファクタリング(クラス)」という意味ではなく、「そのクラスにリファクタリング手法を適用した」という意味です。リファクタリングは、最も純粋な意味で、ブラックボックスと見なすことができるアプリケーションデザインの一部に、より具体的にはデザインに適用されます。

クラスのコンテキストで使用される「リファクタリング」は「リファクタリング手法」を意味すると言うことができます。したがって、「メソッドの移動」はプロセスのリファクタリングの定義を壊しません。同じページで、デザインのコンテキストでの「リファクタリング」は、コード内の既存の機能を壊すことはなく、デザインを「壊す」だけです(とにかくその目的です)。

結論として、質問で言及されている「境界」は、リファクタリングプロセスをリファクタリングプロセスと混同する場合(mix:D)交差します。

PS:いい質問ですね。


それを数回読んで、まだそれを得ることはありません
SiberianGuy

あなたはどの部分を理解しませんでしたか?(定義が適用されるプロセスのリファクタリングまたは定義が適用されないテクニック、たとえば、メソッドのリファクタリングがあります。したがって、move-methodはリファクタリングの定義を壊しません-処理するか、境界を超えないようにします)。私はあなたの懸念があなたの例に存在すべきではないと言っています。リファクタリングの境界は曖昧なものではありません。何かの定義を他の何かに適用しているだけです。
ベラン

0

数値の因数分解について話す場合、整数を乗算すると開始数に等しい整数のグループを記述します。この定義をファクタリングに使用し、プログラミング用語のリファクタリングに適用すると、リファクタリングはプログラムを最小の論理ユニットに分割し、プログラムとして実行されたときに同じ出力を生成します(同じ入力が与えられた場合) )元のプログラムとして。


0

リファクタリングの境界は、特定のリファクタリングに固有のものです。答えは1つだけではなく、すべてを網羅することができます。1つの理由は、リファクタリングという用語自体が非特異的であることです。

リファクタリングできます:

  • ソースコード
  • プログラムアーキテクチャ
  • UIデザイン/ユーザーインタラクション

そして、私は他のいくつかのことを確信しています。

おそらく、リファクタリングは次のように定義できます。

「特定のシステムのエントロピーを減少させるアクションまたはアクションのセット」


0

リファクタリングできる範囲には確かに限界があります。参照:2つのアルゴリズムが同じ場合

人々は通常、アルゴリズムを、それを実装するプログラムよりも抽象的であると考えています。この考えを形式化する自然な方法は、アルゴリズムが適切な等価関係に関してプログラムの等価クラスであるということです。そのような等価関係は存在しないと主張します。

だから、行き過ぎないでください。そうしないと、結果に自信が持てません。一方で、経験上、あるアルゴリズムを別のアルゴリズムに置き換えることができ、同じ答えを、時にはより速く得ることができます。それが美しさですね。


0

「外部」の意味を考えることができます

毎日の立ち上がりの外部。

したがって、豚に影響を与えないシステムへの変更は、リファクタリングとみなすことができます。

クラスがチームによって構築および保守されている単一のシステムでのみ使用されている場合、クラスインターフェイスを変更しても問題はありません。ただし、クラスがすべての.netプログラマーによって使用される.netフレームワークのパブリッククラスである場合、それは非常に異なる問題です。

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