다이나믹 스코프를 가진 언어로 어떻게 리팩토링합니까?


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! 이제 보다는 a인쇄합니다 .oopsqux


이것을 의사 언어 영역에서 다시 가져 오면 이것은 다른 구문으로도 MUMPS의 동작 방식과 정확히 동일합니다.

MUMPS의 최신 ( "현대") 버전에는 소위 NEW문이 포함되어 있는데,이를 통해 변수가 수신자로부터 발신자에게 누출되는 것을 방지 할 수 있습니다. 우리가했던 경우에 따라서 위의 첫 번째 예에서, NEW y = "tetanus"foo(), 다음 print(y)bar()(명시 적으로 다른 것으로 설정하지 않으면 MUMPS에, 모든 이름은 빈 문자열을 가리) 아무것도 출력하지 않을 것이다. 우리가있는 경우 :하지만 수신자에게 발신자로부터 누출 변수를 방지 할 수있는 건 아무것도 없다 function p() { NEW x = 3; q(); print(x); }, 우리가 알고있는 모두를 위해, q()돌연변이 수 x를 명시 적으로 수신되지에도 불구하고, x매개 변수로는. 이것은 여전히 ​​나쁜 상황이지만, 예전 처럼 나쁘지는 않습니다 .

이러한 위험을 염두에두고 어떻게 MUMPS 또는 다른 언어로 동적 범위를 지정하여 코드를 안전하게 리팩터링 할 수 있습니까?

리팩토링을보다 쉽게하기위한 명백한 모범 사례가 있습니다. 예를 들어, NEW직접 초기화 하거나 명시적인 매개 변수로 전달 된 것 이외의 함수에서 변수를 사용하지 않고 함수의 호출자로부터 내재적으로 전달 된 매개 변수를 명시 적으로 문서화하는 것과 같습니다 . 그러나 수십 년 전 ~ 10 8 -LOC 코드베이스에서 이들은 종종 가지고 있지 않은 사치입니다.

물론, 어휘 범위를 가진 언어에서 리팩토링에 대한 모든 모범 사례는 동적 범위-쓰기 테스트 등의 언어에도 적용 할 수 있습니다. 그렇다면 리팩토링시 동적 범위 코드의 취약성 증가와 관련된 위험을 어떻게 완화 할 수 있을까요?

동적 언어로 작성된 코드를 탐색하고 리팩터링 하는 방법은 이 질문과 비슷한 제목을 갖지만 전적으로 관련이 없습니다.



@gnat 나는 그 질문 / 답변 이이 질문과 어떻게 관련이 있는지 모르겠습니다.
senshin

1
@gnat 당신은 그 대답이 "다른 프로세스와 다른 무거운 물건을 사용하는 것"이라고 말하는가? 내 말은, 아마도 틀렸을 수도 있지만, 특히 유용하지 않다는 점에서 너무 일반적입니다.
senshin

2
솔직히 말해서, "변수에 실제로 범위 지정 규칙이있는 언어로 전환"또는 "모든 변수 앞에 파일 및 / 또는 메서드 이름이 접두어있는 헝가리 표기법의 자식을 사용하는 것"외에는 이에 대한 대답이 없다고 생각합니다. 유형이나 종류보다 " 당신이 설명하는 문제는 정말 끔찍 합니다. 좋은 해결책을 상상할 수 없습니다 .
Ixrec

4
적어도 불쾌한 질병의 이름을 따서 허위 광고를 한 MUMPS를 비난 할 수 없습니다.
Carson63000

답변:


4

와.

나는 언어로서 MUMPS를 모른다. 그래서 나는 나의 의견이 여기에 적용되는지 모른다. 일반적으로 말하기-당신은 내부에서 리팩토링해야합니다. 전역 상태 (전역 변수)의 소비자 (리더)는 매개 변수를 사용하여 메소드 / 함수 / 프로 시저로 리팩토링되어야합니다. 리팩토링 후 메소드 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를 지구 상태를 오염시키는 것으로 간주해야합니다. 오염 된 요소가 하나만있는 경우 다음 리팩토링을 고려하십시오.

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

x의 b를 x = b ()로 다시 쓰십시오. 함수 b는이 리팩토링을 수행 할 때 이미 정리 된 메소드 만 사용해야합니다 (ro 이름 바꾸기를 명확하게 할 수 있음). 그런 다음 지구 환경을 오염시키지 않도록 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 관련 장애가 있습니다. 즉, 큰 MUMPS 코드베이스는 필연적 으로 위생적 인 사용자 입력에서도 eval (MUMPS에서 ) 을 많이 사용 EXECUTE합니다. 즉, 함수의 모든 사용법을 정적으로 찾아서 다시 작성할 수는 없습니다.
senshin

내 대답이 적절하지 않다고 생각하십시오. refactoring @ google scale이 매우 독창적이라고 생각한 YouTube 동영상입니다. 그들은 clang을 사용하여 AST를 구문 분석 한 다음 자체 검색 엔진을 사용하여 코드를 리팩터링하는 숨겨진 검색을 찾습니다. 이것은 모든 사용법을 찾는 방법이 될 수 있습니다. 유행성 이하선염 코드에 대한 구문 분석 및 검색 방식을 의미합니다.
thepacker

2

최선의 방법은 전체 코드베이스를 제어하고 모듈과 해당 종속성에 대한 개요를 확인하는 것입니다.

따라서 최소한 전역 검색을 수행하고 코드 변경으로 인해 영향을받을 수있는 시스템 부분에 대해 회귀 테스트를 추가 할 수 있습니다.

첫 번째 목표를 달성 할 기회가 없다면, 최선의 조언은 다음과 같습니다. 다른 모듈에서 재사용하거나 다른 모듈이 의존하는 모듈을 리팩터링하지 마십시오 . 합리적인 크기의 코드베이스에서 다른 모듈에 의존하지 않는 모듈을 찾을 수 있습니다. 따라서 B에 따라 모드 A가 있지만 그 반대의 경우는 아니고 동적으로 범위가 지정된 언어에서도 A에 의존하는 다른 모듈이없는 경우 B 또는 다른 모듈을 중단하지 않고 A를 변경할 수 있습니다.

이것은 A에서 B 로의 종속성을 A에서 B2 로의 종속성으로 대체 할 수있는 기회를 제공합니다. 여기서 B2는 위생 처리되고 다시 작성된 B의 버전입니다. B2는 위에서 언급 한 규칙으로 새로 작성하여 코드를 작성해야합니다. 더 진화 가능하고 리팩토링하기가 더 쉽습니다.


이것은 접근 권고 자나 다른 캡슐화 메커니즘에 대한 개념이 없기 때문에 MUMPS에서 본질적으로 어렵다는 점을 제외하고는 좋은 조언입니다. 코드베이스에서 지정하는 API는 실제로 어떤 함수 를 호출 해야하는지 에 대한 코드 . (물론이 특정 난이도는 동적 범위 지정과 관련이 없습니다. 저는이 점을 관심의 대상으로 작성하고 있습니다.)
senshin

이 기사를 읽은 후에 는 당신의 임무를 부러워하지 않을 것입니다.
Doc Brown

0

명백한 사실을 밝히려면 : 여기에서 리팩토링을 수행하는 방법은 무엇입니까? 매우 신중하게 진행하십시오.

(설명했듯이 기존 코드베이스를 개발하고 유지 관리하는 것은 리팩토링을 시도하는 것뿐만 아니라 충분히 어려워 야합니다.)

나는 여기서 테스트 중심 접근법을 소급 적용 할 것이라고 생각합니다. 여기에는 리팩토링을 시작할 때 먼저 테스트를보다 쉽게하기 위해 현재 기능이 작동하는지 확인하기위한 일련의 테스트 작성이 포함됩니다. (예, 코드가 전혀 변경되지 않고 테스트 할 수있는 모듈 형이 아니면 닭고기와 계란 문제가 발생합니다.)

그런 다음 다른 리팩토링을 진행하면서 테스트를 중단하지 않았는지 확인할 수 있습니다.

마지막으로, 새로운 기능이 필요한 테스트 작성을 시작한 다음 해당 테스트가 작동하도록 코드를 작성할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.