외부 동작을 변경하지 않고 리팩토링을 얼마나 멀리 푸시 할 수 있습니까?


27

Martin Fowler에 따르면 코드 리팩토링은 (강조 광산)입니다.

리팩토링은 기존 코드를 재구성하여 외부 동작을 변경하지 않고 내부 구조 를 변경 하는 훈련 된 기술입니다 . 그 핵심은 변화를 보존하는 일련의 작은 행동입니다. 각 변환 ( '리팩토링'이라고 함)은 거의 수행하지 않지만 일련의 변환은 상당한 구조 조정을 생성 할 수 있습니다. 각 리팩토링은 작기 때문에 잘못 될 가능성이 적습니다. 시스템은 또한 각각의 작은 리팩토링 후에도 완벽하게 작동하여 재구성 중에 시스템이 심각하게 손상 될 가능성을 줄입니다.

이 문맥에서 "외부 행동"이란 무엇입니까? 예를 들어 이동 메소드 리팩토링을 적용 하고 일부 메소드를 다른 클래스로 이동하면 외부 동작을 변경하는 것처럼 보이지 않습니다.

따라서 변경이 리팩터링을 중지하고 더 많은 부분이되는 시점을 파악하는 데 관심이 있습니다. "리팩토링"이라는 용어는 더 큰 변화에 잘못 사용될 수 있습니다. 다른 단어가 있습니까?

최신 정보. 인터페이스에 대한 흥미로운 답변이 많이 있지만 메소드 리팩토링으로 인터페이스를 변경하지 않습니까?


기존 동작이 빨라지거나 불완전한 경우 수정하고 삭제 / 다시 작성하십시오. 그때 당신은 리팩토링을하지 않을 수도 있지만, 그 결과 시스템이 더 나아질 경우 누가 그 이름이 무엇인지에 관심이 있습니다.
Job

2
리팩터링 권한이 부여되었고 재 작성을 수행 한 경우 관리자가 관리 할 수 ​​있습니다.
JeffO

2
리팩토링의 경계는 단위 테스트입니다. 사양에 따라 작성된 사양이 있다면 테스트를 중단하지 않는 변경 사항은 리팩토링입니까?
George Silva


자세한 내용을 알아 보려면이 주제도 활발한 연구 분야입니다. 이 주제에 관한 많은 과학적 출판물이 있습니다. 예 : informatik.uni-trier.de/~ley/db/indices/a-tree/s/…
Skarab

답변:


25

이 문맥에서 "외부"는 "사용자가 볼 수 있음"을 의미합니다. 응용 프로그램의 경우 사용자이거나 공용 API의 경우 다른 프로그램 일 수 있습니다.

따라서 메서드 M을 클래스 A에서 클래스 B로 옮기고 두 클래스가 응용 프로그램 내부에 있고 변경으로 인해 응용 프로그램의 동작 변화를 관찰 할 수없는 사용자는 리팩토링이라고 할 수 있습니다.

OTOH의 다른 상위 레벨 하위 시스템 / 구성 요소가 변경으로 인해 동작이나 변경이 발생하는 경우 실제로는 사용자 (또는 일반적으로 로그를 확인하는 시스템 관리자)에게 있습니다. 또는 클래스가 공개 API의 일부인 경우 B가 아닌 클래스 A의 일부인 M에 의존하는 타사 코드가있을 수 있습니다. 따라서 이러한 두 가지 경우 모두 엄격한 의미에서 리팩토링하지 않습니다.

리팩토링으로 코드 재 작업을 호출하는 경향이 있습니다.

사실, 그것은 리팩토링이 유행이되어 슬프지만 기대되는 결과입니다. 개발자들은 연령에 따라 임시 방식으로 코드 재 작업을 수행해 왔으며, 뿌리 깊은 습관을 분석하고 변경하는 것보다 새로운 유행어를 배우는 것이 훨씬 쉽습니다.

그렇다면 외부 행동을 변화시키는 재 작업의 올바른 단어는 무엇입니까?

나는 그것을 재 설계 라고 부를 것이다 .

최신 정보

인터페이스에 대한 흥미로운 답변이 많이 있지만 메소드 리팩토링으로 인터페이스를 변경하지 않습니까?

어떤? 특정 수업입니다. 그러나이 수업은 어떤 식 으로든 외부 세계에 직접 표시됩니까? 그들은의 외부 인터페이스 (API / GUI)의 일부 프로그램 내부에, 그리고 때문에 - 그렇지 않으면 프로그램 - 변화가 이루어지지 외부 자에 의한 관찰 할 수있다 (물론 변화 나누기 뭔가,하지 않는 한).

나는 이것보다 더 깊은 질문이 있다고 생각합니다 . 특정 클래스가 독립적 인 독립 체로서 존재합니까? 대부분의 경우 대답은 ' 아니오'입니다 . 클래스는 더 큰 구성 요소, 클래스 및 객체의 에코 시스템의 일부로 만 존재하며 인스턴스가 없으면 인스턴스화 할 수없고 사용할 수 없습니다. 이 생태계에는 (직접 / 간접) 종속성뿐만 아니라 그에 의존하는 다른 클래스 / 객체도 포함됩니다. 이러한 높은 수준의 클래스가 없으면 클래스와 관련된 책임이 시스템 사용자에게 무의미하거나 쓸모가 없기 때문입니다.

예를 들어 렌터카를 다루는 프로젝트에는 Charge수업. 이 클래스는 렌탈 스테이션 에이전트와 고객이 개별 요금으로 많은 것을 할 수 없기 때문에 시스템 사용자에게는 그다지 쓸모가 없습니다. . 사용자는 대부분이 요금의 총액에 관심이 있으며 결국 지불해야합니다. 상담원은 다양한 계약 옵션, 대여 기간, 차량 그룹, 보험 패키지, 추가 품목 등에 대해 관심이 있으며, 정교한 비즈니스 규칙을 통해 존재하는 요금과 최종 지불 방법을 결정합니다. 이 중. 또한 국가 대표 / 비즈니스 분석가는 특정 비즈니스 규칙, 시너지 및 효과 (회사의 수입 등)에 관심을 갖습니다.

최근에 나는이 클래스를 리팩토링하여 대부분의 필드와 메소드의 이름을 변경했다 (전임자가 완전히 무시한 표준 Java 명명 규칙을 따랐다). 나는 또한 더 교체 리팩토링을 계획 String하고 char더 적절한있는 필드 enumboolean유형. 이 모든 것이 확실히 클래스의 인터페이스를 바꿀 것이지만 (내 일을 올바르게하면) 우리 앱의 사용자에게는 아무것도 보이지 않을 것입니다. 그들 중 아무도 그들이 확실히의 개념을 알고에도 불구하고, 표현하는 방법을 개별 요금에 대한 관심 없다 요금. 도메인 개념을 나타내지 않는 백 개의 다른 클래스를 예로들 수 있었으므로 최종 사용자에게는 개념적으로 볼 수 없었지만 개념 수준에서 최소한의 가시성이있는 예제를 선택하는 것이 더 흥미 로웠다고 생각했습니다. 이것은 클래스 인터페이스가 도메인 개념의 표현 일 뿐이며 실제 상황이 아니라는 점을 잘 보여줍니다. 개념에 영향을주지 않고 표현을 변경할 수 있습니다. 그리고 사용자는 그 개념을 가지고 있고 이해할뿐입니다. 개념과 표현 사이의 매핑을하는 것이 우리의 임무입니다.

* 하나는 쉽게 우리의 클래스가 나타내는 도메인 모델은, 그 자체 일부 "진짜"의 대략적인 표현 인 것을 추가 할 수 있습니다 ..


3
nitpicking, 나는 '디자인 변경'이 재 설계가 아니라고 말할 것입니다. 재 설계 소리가 너무 큽니다.
user606723

4
흥미롭게도-클래스 A는 '기존 코드 본문'이며, 메소드 M이 공개 인 경우 A의 외부 동작이 변경되므로 클래스 A가 재 설계된 반면 전체 시스템이 리팩터링되고 있다고 말할 수 있습니다.
saus

나는 사용자가 볼 수있는 것을 좋아합니다. 그렇기 때문에 단위 테스트가 중단되었다고 말하는 것이 아니라 종단 간 또는 통합 테스트가 부호가 될 것입니다.
앤디 Wiesendanger

8

외부는 단순히 진정한 언어 적 의미의 인터페이스 를 의미합니다. 이 예제에서는 소를 고려하십시오. 야채를 먹이고 우유를 반환 값으로 얻는 한, 내부 장기가 어떻게 작동하는지는 신경 쓰지 않습니다. 이제 신이 젖소 내부 장기를 바꾸어 혈액의 색이 파란색으로 바뀌면 진입 지점과 종료 지점 (입과 우유)이 변하지 않는 한 리팩토링으로 간주 될 수 있습니다.


1
나는 그것이 좋은 대답이라고 생각하지 않습니다. 리팩토링은 API 변경을 암시 할 수 있습니다. 그러나 최종 사용자 나 입력 / 파일을 읽거나 생성하는 방식에는 영향을 미치지 않습니다.
rds

3

나에게 리팩토링은 테스트 및 / 또는 공식 사양으로 경계를 설정했을 때 가장 생산적이고 편안했습니다.

  • 이 경계는 내가 때때로 교차하면 복구하기 위해 많은 변경 사항을 롤백 할 필요가 없을 정도로 빨리 감지 될 것임을 알기 위해 충분히 단단합니다. 반면, 관련없는 동작 변경에 대한 걱정없이 코드를 개선 할 수있는 충분한 여유가 있습니다.

  • 내가 특히 좋아하는 것은 이러한 종류의 경계가 말하기에 적응 적이라는 것 입니다. 내 말은, 1) 변경을 수행하고 사양 / 테스트를 준수하는지 확인합니다. 그런 다음 2) 품질 보증 또는 사용자 테스트에 전달됩니다. 여기에서 사양 / 테스트에 누락 된 것이있어 여전히 실패 할 수 있습니다. 3a) 테스트를 통과하면 문제가 해결 된 것입니다. 그렇지 않으면 3b) 테스트가 실패하면 4) 변경 사항을 롤백 하고 5) 다음 번 에이 실수가 반복되지 않도록 테스트를 추가하거나 사양을 명시하십시오. 패스를 테스트하거나하는 것은 실패 할 경우에 상관없이, 나는주의 얻을 무언가를 - 중 코드 / 테스트 / 스펙이 향상됩니다 - 내 노력이 전체 폐기물로 전환하지 않습니다.

다른 종류의 경계에 관해서는-지금까지 많은 운이 없었습니다.

"사용자에게 관찰 가능"은 따라야 할 규칙이있는 경우 안전한 방법입니다. 기존의 새로운 테스트를 분석하거나 새로운 테스트를 작성하는 데 많은 노력이 필요할 수 있습니다. 이 접근법에 대해 내가 싫어하는 또 다른 점은 맹목적으로 따르는 것이 너무 제한적일 수 있다는 것입니다. -데이터를로드 할 때 2 대신 3 초가 걸리기 때문에이 변경은 금지되어 있습니다.-사용자 / UX 전문가와 관련이 있는지 여부를 확인하는 것은 어떻습니까? -관찰 할 수있는 행동의 변경은 금지되어 있지 않습니다. 안전한? 내기! 생산적인? 실제로는 아닙니다.

내가 시도한 또 하나는 코드 논리를 유지하는 것입니다 (읽을 때 그것을 이해하는 방식). 가장 기본적인 (그리고 일반적으로 그다지 유익하지 않은) 변화를 제외하고, 이것은 항상 벌레의 깡통이었습니다. 아니면 내가 벌레의 깡통이라고 말해야합니까? 회귀 버그를 의미합니다. 스파게티 코드로 작업 할 때 중요한 것을 깨뜨리는 것은 너무 쉽습니다.


3

리팩토링 책은 당신이 할 수있는 그 메시지에 꽤 강한 에만 단위 테스트 적용 범위가있을 때 리팩토링을 수행 .

따라서 단위 테스트를 중단하지 않는 한 리팩토링 한다고 말할 수 있습니다 . 테스트를 중단하면 더 이상 리팩토링하지 않습니다.

그러나 클래스 또는 멤버 이름 변경과 같은 간단한 리팩토링은 어떻습니까? 그들은 테스트를 중단하지 않습니까?

그렇습니다. 각 경우에 해당 중단이 중요한지 고려해야합니다. 귀하의 경우 SUT는 공개 API는 / SDK는 다음 이름 바꾸기는, 참으로, 주요 변경입니다. 그렇지 않다면 아마 괜찮을 것입니다.

그러나 실제로 관심있는 동작을 변경했기 때문에가 아니라 테스트가 취약한 테스트 이기 때문에 테스트가 중단되는 경우가 종종 있습니다 .


3

이러한 맥락에서 "외부 행동"을 정의하는 가장 좋은 방법은 "테스트 사례"일 수 있습니다.

코드를 리팩터링하고 테스트 사례 (리팩토링 전에 정의)를 계속 통과하면 리팩토링이 외부 동작을 변경하지 않았습니다. 하나 이상의 테스트 사례가 실패 하면 외부 동작 변경 한 것 입니다.

적어도 그것은 주제에 관해 출판 된 다양한 책들 (예를 들어, Fowler 's)에 대한 나의 이해입니다.


2

경계는 프로젝트를 개발, 유지 보수, 지원하는 사람과 지지자, 유지 관리자, 개발자 이외의 사용자 인 사용자를 구분하는 선이됩니다. 따라서 외부 세계에서는 동작이 동일하게 보이지만 동작 뒤에있는 내부 구조는 변경되었습니다.

따라서 사용자가 보지 않는 한 클래스간에 함수를 이동하는 것이 좋습니다.

코드 재 작업이 외부 동작을 변경하지 않거나 새로운 기능을 추가하거나 기존 기능을 제거하지 않는 한 재 작업을 리팩토링이라고 부르는 것이 좋습니다.


동의하지 않는다. 전체 데이터 액세스 코드를 nHibernate로 바꾸면 외부 동작은 변경되지 않지만 Fowler의 "훈련 된 기술"을 따르지 않습니다. 이것은 리엔지니어링하고 리팩토링이라고 부르는 것은 관련된 위험 요소를 숨 깁니다.
pdr

1

모든면에서 클래스의 사용자는 클래스로 빌드 된 애플리케이션의 최종 사용자가 아니라 리팩토링되는 클래스를 호출하거나 상속하여 구현 된 클래스라는 점을 기억해야합니다. .

"외부 행동이 바뀌지 않아야한다"고 말하면 사용자에 관한 한 클래스는 이전과 동일하게 동작한다는 것을 의미합니다. 원래의 (리팩토링되지 않은) 구현은 단일 클래스였으며 새로운 (리팩토링 된) 구현에는 클래스가 구축 된 하나 이상의 수퍼 클래스가 있지만 사용자는 내부 (구현)를 볼 수 없습니다. 인터페이스 만 참조하십시오.

따라서 클래스에 "doSomethingAmazing"이라는 메소드가있는 경우 참조하는 클래스 또는 해당 클래스가 빌드 된 수퍼 클래스에 의해 구현되는지 여부는 사용자에게 중요하지 않습니다. 사용자에게 중요한 것은 새로운 (리팩토링 된) "doSomethingAmazing"이 이전 (리팩터링되지 않은) "doSomethingAmazing"과 동일한 결과를 가져야한다는 것입니다.

그러나 많은 경우에 리팩토링이라고하는 것은 진정한 리팩토링이 아니라 코드를보다 쉽게 ​​수정하여 새로운 기능을 추가하기 위해 수행 된 재 연산입니다. 따라서 (의사) 리팩토링의 후자의 경우, 새로운 (리팩토링 된) 코드는 실제로 다른 것 또는 아마도 이전 것보다 더 많은 것을 수행합니다.


"OK 버튼을 누르시겠습니까?"대화 상자를 팝업하는 데 Windows 양식이 사용 된 경우 어떻게됩니까? 나는 그것을 거의 달성하지 못하고 사용자를 귀찮게하기 때문에 제거하기로 결정한 다음 코드를 리팩터링하고, 다시 디자인하고, 수정하고, 디버깅 한 것입니까?
Job

@job : 새로운 사양에 맞게 프로그램을 변경했습니다.
jmoreno

IMHO 여기에서 다른 기준을 섞을 수 있습니다. 코드를 처음부터 다시 작성하는 것은 실제로 리팩토링이 아니지만 외부 동작의 변경 여부에 관계없이 적용됩니다. 또한 클래스 인터페이스를 변경하는 것이 리팩토링이 아닌 경우 Move Method et al. 리팩토링 카탈로그에 존재 합니까?
Péter Török

@Peter Török-상속을 구현하는 OOP 언어에서 클래스의 인터페이스에는 모든 조상이 클래스 자체에서 구현 한 것이 아니라 클래스의 인터페이스가 포함되므로 "클래스 인터페이스 변경"의 의미에 전적으로 의존합니다. 클래스 인터페이스를 변경하는 것은 인터페이스에 메소드를 제거 / 추가하는 것 (또는 메소드의 서명, 즉 전달 된 매개 변수의 수)을 변경하는 것을 의미합니다. 리팩토링은 메소드, 클래스 또는 수퍼 클래스에 응답하는 사람을 의미합니다.
Zeke Hansell

IMHO-이 전체 질문은 프로그래머에게 유용한 가치가 되기에는 너무 난해 할 수 있습니다.
Zeke Hansell

1

"외부 행동"에 의해 그는 주로 공개 인터페이스에 대해 이야기하고 있지만, 이것은 또한 시스템의 출력 / 아티팩트도 포함합니다. (즉, 파일을 생성하는 메소드가 있으며 파일 형식을 변경하면 외부 동작이 변경됩니다)

e : "이동 방법"이 외부 행동의 변화라고 생각합니다. Fowler가 야생으로 출시 된 기존 코드베이스에 대해 이야기하고 있음을 명심하십시오. 상황에 따라 변경 사항으로 인해 외부 클라이언트가 중단되지 않는지 확인하고 즐거운 방법으로 진행할 수 있습니다.

e2 : "따라서 외부 행동을 바꾸는 재 작업의 올바른 단어는 무엇입니까?" -API 리팩토링, 주요 변경 사항 등은 여전히 ​​리팩토링이며, 이미 클라이언트와 야생 상태 인 공용 인터페이스를 리팩토링하는 모범 사례를 따르지 않습니다.


그러나 공개 인터페이스에 대해 이야기하고 있다면 "Move method"리팩토링은 어떻습니까?
SiberianGuy

@Idsa 귀하의 질문에 대한 답변을 편집했습니다.

@kekela,하지만 여전히 내게 불분명 곳 "리팩토링"일의 끝
SiberianGuy

@idsa 게시 한 정의에 따르면 공용 인터페이스를 변경하는 순간 리팩토링이 중단됩니다. (한 클래스에서 다른 클래스로 공용 메소드를 이동하는 것이 이에 대한 예입니다.)

0

"이동 방법"은 자체 리팩토링이 아니라 리팩토링 기술입니다. 리팩토링은 여러 리팩토링 기술을 클래스에 적용하는 프로세스입니다. 거기에, 당신이 말할 때, "move method"를 클래스에 적용했을 때, 실제로 "I refactored (클래스)"를 의미하는 것이 아니라, 실제로 "그 클래스에 리팩토링 기술을 적용했습니다"를 의미합니다. 리팩토링은 가장 순수한 의미로 블랙 박스로 볼 수있는 응용 프로그램 디자인의 일부에 디자인 또는보다 구체적으로 적용됩니다.

클래스 컨텍스트에서 사용되는 "리팩토링"은 "리팩토링 기술"을 의미하므로 "이동 메소드"가 리팩토링 프로세스의 정의를 위반하지는 않습니다. 같은 페이지에서 디자인의 맥락에서 "리팩토링"은 코드의 기존 기능을 손상시키지 않고 디자인을 "깨뜨립니다"(어쨌든 목적 임).

결론적으로, 리팩토링 기술과 리팩토링 프로세스를 혼동하면 (혼합 : D) 문제에서 언급 한 "경계"가 교차됩니다.

PS : 좋은 질문입니다.


여러 번 읽어도 여전히 이해가되지 않습니다
SiberianGuy

어떤 부분을 이해하지 못했습니까? (리팩토링-정의가 적용되는 프로세스 또는 기술을 리팩토링하는 방법이 있습니다. 예를 들어, 정의가 적용되지 않는 이동 방법입니다. 따라서 이동 방법은 프로세스 또는 경계를 넘지 않아야 함). 나는 당신이 당신의 모범을 위해 존재해서는 안되는 우려를 말하고 있습니다. 리팩토링의 경계는 모호한 것이 아닙니다. 당신은 무언가의 정의를 다른 것에 적용하고 있습니다.
Belun

0

팩토링 숫자에 대해 이야기한다면, 곱하면 시작 숫자와 같은 정수 그룹을 설명합니다. 이 팩토링 정의를 가져와 프로그래밍 용어 리 팩터에 적용하면 리팩토링은 프로그램을 프로그램으로 실행할 때 동일한 출력을 생성하도록 프로그램을 가장 작은 논리 단위로 세분화합니다. )를 원래 프로그램으로 사용하십시오.


0

리팩토링의 경계는 주어진 리팩토링에 따라 다릅니다. 답은 하나도 없으며 모든 것을 포함합니다. 한 가지 이유는 리팩토링이라는 용어 자체가 비특이적이기 때문입니다.

리팩토링 할 수 있습니다.

  • 소스 코드
  • 프로그램 아키텍처
  • UI 디자인 / 사용자 상호 작용

그리고 나는 다른 몇 가지 확신합니다.

아마도 리 팩터는 다음과 같이 정의 될 수 있습니다

"특정 시스템에서 엔트로피를 감소시키는 동작 또는 동작 집합"


0

리팩터링 할 수있는 거리에는 확실히 한계가 있습니다. 참조 : 두 알고리즘은 언제 동일합니까?

사람들은 일반적으로 알고리즘을 구현하는 프로그램보다 알고리즘을 더 추상적 인 것으로 간주합니다. 이 아이디어를 공식화하는 자연스러운 방법은 알고리즘이 적절한 동등성 관계와 관련하여 프로그램의 동등성 클래스라는 것입니다. 우리는 그러한 동등성 관계가 존재하지 않는다고 주장합니다.

따라서 너무 멀리 가지 마십시오. 결과에 대한 확신이 없습니다. 반면에 경험에 따르면 한 알고리즘을 다른 알고리즘으로 대체하고 동일한 답변을 얻을 수 있으며 때로는 더 빠릅니다. 그게 아름다움이야?


0

"외부"의미를 생각할 수 있습니다

매일 외부에 서십시오.

따라서 돼지에 영향을 미치지 않는 시스템의 변경은 리팩토링으로 간주 될 수 있습니다.

클래스가 팀에서 구축하고 유지 관리하는 단일 시스템에서만 클래스를 사용하는 경우 클래스 인터페이스 변경은 문제가되지 않습니다. 그러나 클래스가 모든 .net 프로그래머가 사용하는 .net 프레임 워크의 공용 클래스 인 경우에는 매우 다릅니다.

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