외부 동작을 변경하지 않고 리팩토링을 얼마나 멀리 푸시 할 수 있습니까? Fowler에 따르면 코드 리팩토링은 (강조

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

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

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

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

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



답변

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

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

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

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

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

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

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

최신 정보

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

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

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

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

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

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


답변

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


답변

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

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

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

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

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

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


답변

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

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

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

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

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


답변

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

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

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


답변

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

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

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


답변

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

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

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

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