動的スコープを持つ言語でどのように安全にリファクタリングしますか?


13

動的スコープを持つ言語で動作しない幸運をお持ちの方のために、それがどのように機能するかについて少しおさらいさせてください。次のように動作する「RUBELLA」と呼ばれる擬似言語を想像してください。

function foo() {
    print(x); // not defined locally => uses whatever value `x` has in the calling context
    y = "tetanus";
}
function bar() {
    x = "measles";
    foo();
    print(y); // not defined locally, but set by the call to `foo()`
}
bar(); // prints "measles" followed by "tetanus"

つまり、変数はコールスタックを上下に自由に伝播します-で定義されたすべての変数は、fooその呼び出し元に表示され、呼び出し元によって変更可能barです。逆もまた真です。これは、コードのリファクタリングに深刻な影響を及ぼします。次のコードがあると想像してください。

function a() { // defined in file A
    x = "qux";
    b();
}
function b() { // defined in file B
    c();
}
function c() { // defined in file C
    print(x);
}

これで、への呼び出しa()は印刷されますqux。しかし、その後、いつか、b少し変更する必要があると判断します。すべての呼び出しコンテキスト(実際にはコードベースの外にあるものもあります)を知っているわけではありませんが、それは大丈夫です-変更は完全に内部になりますよbね?したがって、次のように書き換えます。

function b() {
    x = "oops";
    c();
}

そして、ローカル変数を定義したばかりなので、何も変更していないと思うかもしれません。しかし、実際には、あなたは壊れていaます!今でaoopsなく、印刷しquxます。


疑似言語の領域からこれを取り戻すと、異なる構文ではありますが、これがまさにMUMPSの動作です。

MUMPSのモダン(「モダン」)バージョンには、いわゆるNEWステートメントが含まれています。これにより、変数が呼び出し先から呼び出し元に漏れるのを防ぐことができます。したがって、上記の最初の例でinを実行NEW y = "tetanus"したfoo()場合、print(y)in bar()は何も出力しません(MUMPSでは、明示的に他の値に設定しない限り、すべての名前は空の文字列を指します)。しかし、変数が呼び出し元から呼び出し先に漏れることを防ぐことができるものは何もありません:function p() { NEW x = 3; q(); print(x); }知っている限り、パラメータとして明示的に受け取っていなくても、q()変異する可能性があります。これはまだ悪い状況ですが、おそらく以前ほど悪くはありません。xx

これらの危険性を念頭に置いて、MUMPSまたは他の言語のコードを動的スコープで安全にリファクタリングするにはどうすればよいですか?

そこに簡単リファクタリング作るためのいくつかの明白な良い慣行は、あなたが(初期設定以外の機能で変数を使用したことがないように、あるNEW自分は)または明示的なパラメータとして渡され、明示的パラメータ文書化されている暗黙のうちに、関数の呼び出し元から渡されたし。しかし、数十年前の〜10 8 -LOCコードベースでは、これらはしばしば持っていない贅沢品です。

そしてもちろん、字句スコープを持つ言語でのリファクタリングの基本的にすべての優れた実践は、動的スコープを持つ言語にも適用できます-テストの書き込みなど。質問は次のとおりです。リファクタリング時に、動的スコープのコードの脆弱性の増加に特に関連するリスクをどのように軽減しますか?

動的言語で記述されたコードどのようにナビゲートおよびリファクタリングしますか?は、この質問と似たタイトルを持っていますが、まったく無関係です。)



@gnat私はその質問/その答えがこの質問にどのように関連しているか見ていません。
戦神

1
@gnatあなたは答えが「異なるプロセスと他の重いものを使う」と言っているのですか?つまり、それはおそらく間違いではありませんが、特に有用ではないという点では一般的すぎます。
戦神

2
正直なところ、「変数に実際にスコープ規則がある言語に切り替える」または「すべての変数の前にファイル名やメソッド名が付いているハンガリー語表記のろくでなしの子を使用する」以外にこれに対する答えがあるとは思わないタイプまたは種類よりも」。あなたが説明する問題はただとてもひどいもので良い解決策を想像することはできません。
Ixrec

4
少なくとも、厄介な病気にちなんで名付けられたために、おたふく風邪の偽広告を非難することはできません。
Carson63000

回答:


4

ワオ。

私はムンプスを言語として知らないので、私のコメントがここに当てはまるかどうかわかりません。一般的に言えば、あなたは裏からリファクタリングしなければなりません。グローバル状態(グローバル変数)のコンシューマー(リーダー)は、パラメーターを使用してメソッド/関数/プロシージャにリファクタリングする必要があります。リファクタリング後、メソッドcは次のようになります。

function c(c_scope_x) {
   print c(c_scope_x);
}

cのすべての使用法に書き換える必要があります(これは機械的なタスクです)

c(x)

これは、ローカル状態を使用してグローバル状態から「内部」コードを分離するためです。それが終わったら、bを次のように書き換える必要があります。

function b() {
   x="oops"
   print c(x);
}

x = "oops"割り当ては、副作用を抑えるためにあります。ここで、bをグローバル状態を汚染するものと見なす必要があります。汚染された要素が1つしかない場合は、このリファクタリングを検討してください。

function b() {
   x="oops"
   print c(x);
   return x;
}

x = b()でbの各使用を書き換えます。関数bは、このリファクタリングを行うときに、すでにクリーンアップされたメソッドのみを使用する必要があります(ro rename coを明確にすることをお勧めします)。その後、グローバル環境を汚染しないようにbをリファクタリングする必要があります。

function b() {
   newvardefinition b_scoped_x="oops"
   print c_cleaned(b_scoped_x);
   return b_scoped_x;
}

bの名前をb_cleanedに変更します。そのリファクタリングに慣れるには、少し試してみる必要があると思います。もちろん、すべてのメソッドをリファクタリングできるわけではありませんが、内部から始める必要があります。Eclipseとjava(抽出メソッド)と「グローバルステート」、つまりクラスメンバーでそれを試して、アイデアを得てください。

function x() {
  fifth_to_refactor();
  {
    forth_to_refactor()
    ....
    {
      second_to_refactor();
    }
    ...
    third_to_refactor();
  }
  first_to_refactor()
}

hth。

質問:これらの危険性を念頭に置いて、MUMPSまたは他の言語のコードを動的スコープで安全にリファクタリングするにはどうすればよいですか?

  • たぶん他の誰かがヒントを与えることができます。

質問:リファクタリング時の動的スコープコードの脆弱性の増加に特に関連するリスクをどのように軽減しますか?

  • 安全なリファクタリングを行うプログラムを作成します。
  • 安全な候補者/最初の候補者を識別するプログラムを作成します。

ああ、リファクタリングプロセスを自動化しようとすると、MUMPSに固有の障害が1つあります。つまり、大規模なMUMPSコードベースには必然的にeval(MUMPSで呼ばれる)の多くの用途がありEXECUTE、場合によってはサニタイズされたユーザー入力でも使用されることを意味します。
戦神

さて、私の答えは適切でないと考えてください。リファクタリング@グーグルスケールのYouTubeビデオは非常にユニークなアプローチをしたと思います。彼らはclangを使用してASTを解析し、独自の検索エンジンを使用して、コードをリファクタリングするための(隠れた使用法さえ)を見つけました。これは、すべての使用法を見つける方法です。私は、おたふく風のコードの解析と検索のアプローチを意味します。
thepacker

2

あなたの最善の策は、完全なコードベースをあなたの管理下に置き、モジュールとその依存関係の概要を確認することだと思います。

したがって、少なくともグローバル検索を行う可能性があり、コード変更による影響が予想されるシステムの部分に回帰テストを追加する可能性があります。

最初に達成する機会がない場合、私の最善のアドバイスは次のとおりです。他のモジュールによって再利用されたり、他のモジュールに依存していることがわからないモジュールをリファクタリングしないでください。。適切なサイズのコードベースでは、他のモジュールに依存しないモジュールを見つける可能性が高くなります。したがって、Bに依存するmod Aがあり、その逆ではなく、動的スコープの言語であっても、Aに依存する他のモジュールがない場合、Bまたは他のモジュールを壊すことなくAに変更を加えることができます。

これにより、AからBの依存関係をAからB2の依存関係に置き換えることができます。B2は、サニタイズされ、書き換えられたバージョンのBです。B2は、上記のルールを念頭に置いて新しく作成し、コードを作成する必要がありますより進化しやすく、リファクタリングが容易です。


これは良いアドバイスですが、アクセス指定子や他のカプセル化メカニズムの概念がないため、これはMUMPSでは本質的に難しいことを脇に付け加えます。つまり、コードベースで指定するAPIは、事実上、どの関数を呼び出すべきかについてのコード。(もちろん、この特定の難しさは動的なスコープとは無関係です。これを関心のあるポイントとしてメモしているだけです。)
戦神

この記事を読んだ後、私はあなたの仕事をあなたがうらやまないことを確信しています。
Doc Brown

0

明白なことを述べるには:ここでリファクタリングを行う方法は? 非常に慎重に進めてください。

(あなたがそれを説明したように、既存のコードベースの開発と維持は、それをリファクタリングしようとすることは言うまでもなく、十分に難しいはずです。)

ここで、テスト駆動型アプローチを遡及的に適用すると考えています。これには、まずテストを簡単にするために、リファクタリングを開始するときに現在の機能が引き続き機能することを確認するテストスイートを記述することが含まれます。(はい、私はここで鶏と卵の問題を予想しています、あなたのコードが既に変更せずにテストするのに十分なモジュールである場合を除きます。)

その後、他のリファクタリングを続行し、進行中にテストが壊れていないことを確認します。

最後に、新しい機能を期待するテストの作成を開始し、それらのテストを機能させるコードを作成できます。

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