이 반 패턴의 이름은? 지역 변수로서의 필드 [닫기]


68

검토중인 일부 코드에서 다음과 같은 도덕적 인 내용을보고 있습니다.

public class Foo
{
    private Bar bar;

    public MethodA()
    {
        bar = new Bar();
        bar.A();
        bar = null;
    }

    public MethodB()
    {
        bar = new Bar();
        bar.B();
        bar = null;
    }
}

이 필드 bar논리적 으로 지역 변수입니다. 값은 메소드 호출에서 지속되지 않습니다. 그러나 많은 메소드 Foo가 유형의 오브젝트를 필요로하기 Bar때문에 원래 코드 작성자는 유형 필드를 작성했습니다 Bar.

  1. 이것은 분명히 나쁘지 않습니까?
  2. 이 반 패턴의 이름이 있습니까?

60
글쎄요, 그것은 새로운 것입니다. 이 클래스가 다중 스레드 컨텍스트에서 사용되지 않기를 바랍니다.
TMN

8
정말 드문 일입니까? 나는 내 경력에서 이것을 여러 번 보았다.
JSB ձոգչ

32
@TMN 스레드 안전하지는 않지만 더 나쁜 것은 재진입도 아닙니다. 어떤 코드 경우 MethodA또는 MethodB원인 MethodA또는이 MethodB어떤 방식으로 호출 할 (그리고 bar의 경우, 다시 사용 bar.{A,B}()되는 범죄자) 당신은 어떤 동시성없이 유사한 문제가있다.

73
누군가 할 수있는 모든 멍청한 일에 대한 이름이 있어야합니까? 그냥 나쁜 생각이라고 부릅니다.
Caleb

74
매달려있는 개인용 안티 패턴이라고 부를 수는 없습니다.
psr

답변:


86

이것은 분명히 나쁘지 않습니까?

네.

  • 메소드를 재진입 할 ​​수 없게하므로 동일한 인스턴스에서 재귀 적으로 또는 다중 스레드 컨텍스트에서 호출되는 경우 문제가됩니다.

  • 다시 호출을 잊어 버린 경우 한 통화에서 다른 통화로의 상태가 누출됨을 의미합니다.

  • 코드가 실제로 무엇을하는지 확인하기 위해 위의 내용을 확인해야하기 때문에 코드를 이해하기 어렵습니다. (로컬 변수 사용과 대조)

  • Foo인스턴스를 필요한 것보다 크게 만듭니다 . (그리고 N 변수에 대해 이것을하는 것을 상상해보십시오 ...)

이 반 패턴의 이름이 있습니까?

IMO는 반 패턴이라고 부를 자격이 없습니다. 그것은 나쁜 코딩 습관 / Java 구문 / 쓰레기 코드의 오용입니다. 학생이 강의를 건너 뛰거나 프로그래밍에 적성하지 않은 경우 학부 코드에서 볼 수있는 종류입니다. 프로덕션 코드에서 볼 수 있다면 더 많은 코드 검토를 수행해야한다는 신호입니다 ...


기록을 작성하는 올바른 방법은 다음과 같습니다.

public class Foo
{
    public methodA()
    {
        Bar bar = new Bar();  // Use a >>local<< variable!!
        bar.a();
    }

    // Or more concisely (in this case) ...
    public methodB()
    {
        new Bar().b();
    }
}

Java 식별자에 허용되는 스타일 규칙에 맞게 메서드 및 변수 이름도 수정했습니다.


11
"제작 코드에서 볼 수 있다면 채용 프로세스를 재검토 해야한다는 신호입니다 "
Beta

@Beta-그것도 활발한 코드 검토를 통해 나쁜 코딩 습관을 교정 할 수 있어야합니다.
Stephen C

더 많은 코드 리뷰에 대한 +1. 사람들이 생각하는 것에도 불구하고, 고용 연습을 개선해도 이런 종류의 문제를 예방할 수는 없습니다. 고용은 헛소리입니다.
mattnz

@StephenC "이 방법은 재진입 할 ​​수 없으며, 동일한 인스턴스에서 재귀 적으로 또는 다중 스레드 컨텍스트에서 호출되는 경우 문제가됩니다." . 재 입력이 무엇인지 알고 있지만 메소드가 잠금을 사용하지 않으므로 여기에 대한 요점을 볼 수 없습니까? 설명해 주시겠습니까?
Geek

@Geek-특정 예에 너무 집중하고 있습니다. 여러 스레드에서 또는 재귀 적으로 메서드를 호출 할 수 있는 일반적인 경우 에 대해 이야기하고 있습니다.
Stephen C

39

변수에 대해 불필요한 큰 범위라고 부릅니다. 이 설정은 또한 다중 스레드가 MethodA 및 MethodB에 액세스하는 경우 경쟁 조건을 허용합니다.


12
특히 "가변 범위"는 여기서 문제의 이름이라고 생각합니다. 이 사람은 지역 변수가 무엇이며 언제 어떻게 사용하는지 알아야합니다. 긍정적 인 패턴 또는 일반적인 규칙은 각 변수에 대해 사용 가능한 가장 작은 범위를 사용하고 필요에 따라 넓히는 것입니다. 분명히,이 오류는 나쁜 스타일이 아닙니다. 이와 같은 경쟁 조건을 코딩하는 것은 오류입니다.
GlenPeterson

30

이것은로 알려진 일반적인 패턴의 특정한 경우입니다 global doorknobbing. 집을 지을 때는 손잡이를 하나만 사서 주위에 두는 것이 좋습니다. 누군가가 문이나 찬장을 사용하고 싶을 때, 그들은 단지 하나의 글로벌 문 손잡이를 잡습니다. 손잡이가 비싸면 좋은 디자인이 될 수 있습니다.

불행히도 친구가 와서 손잡이가 동시에 두 곳에서 사용되면 현실이 산산조각납니다. 문 손잡이가 너무 비싸서 하나만 감당할 수 있다면 사람들이 문 손잡이를 안전하게 돌릴 수있는 시스템을 구축하는 것이 좋습니다.

이 경우 도어 손잡이는 참조 용이므로 저렴합니다. 손잡이가 실제 객체이고 참조가 아닌 객체를 재사용하는 경우에는 값이 충분할 수 있습니다 prudent global doorknob. 그러나 값이 싸면이라고합니다 cheapskate global doorknob.

귀하의 예는 cheapskate다양합니다.


19

여기서 가장 큰 관심사는 동시성입니다. Foo를 여러 스레드에서 사용하는 경우 문제가 있습니다.

그 외에도 지역 변수는 자체 수명주기를 완벽하게 관리합니다. 더 이상 유용하지 않을 때 무효화해야하는 인스턴스 변수로 바꾸면 오류가 발생합니다.


15

"가변 재사용"이라는 측면이있는 "부적절한 범위 지정"사례입니다.


7

안티 패턴이 아닙니다. 반 패턴에는 좋은 생각처럼 보이게하는 속성이있어 사람들이 의도적으로이를 수행하게합니다. 그것들은 패턴으로 계획된 다음 아주 잘못됩니다.

또한 어떤 부분이 패턴, 반 패턴 또는 일반적으로 잘못 적용되는 패턴인지에 대해 논쟁을 불러 일으키는 부분이기도합니다.

이것은 단지 잘못이다.

조금 더 추가합니다.

이 코드는 미신적이거나 기껏해야화물 컬트 관행입니다.

미신은 분명한 정당화없이 행해지는 것입니다. 실제와 관련이있을 수 있지만 연결은 논리적이지 않습니다.

카고 컬트 관행은 지식이 풍부한 소스에서 배운 것을 복사하려고 시도하지만 실제로는 프로세스가 아닌 표면 인공물을 복사하는 것입니다 (파푸아 뉴기니 컬트의 이름을 따서 명 명함) 2 차 세계 대전 일본과 미국 비행기가 돌아 오기를 희망하면서 대나무로 된 비행기 조종 라디오.

이 두 경우 모두 실제 사례는 없습니다.

안티 패턴은 소규모 (처리해야 할 추가 사례를 처리하기위한 추가 브랜치, 스파게티 코드로 이어짐) 또는 매우 의도적으로 패턴을 구현하는 대규모에서 합리적인 개선을 시도합니다. 일부는 쓰기 전용 (예 : 로깅 객체 또는 읽기 전용 (예 : 구성 설정 객체)을 제외한 일부는 싱글 톤을 설명하고 일부는이를 비난 할 수도 있음) 또는 다른 곳에서 해결하는 경우 잘못된 문제 (.NET이 처음 등장했을 때 MS는 관리되지 않는 필드와 일회용 관리 필드가 모두있을 때 처리하는 패턴을 권장했습니다. 실제로 그 상황을 잘 처리하지만 실제 문제는 같은 클래스의 두 유형의 필드).

따라서 반 패턴은 언어, 문제 영역 및 사용 가능한 라이브러리를 잘 알고있는 현명한 사람이 의도적으로 할 수있는 일이며, 여전히 단점을 압도하는 단점이 있습니다.

우리 중 어느 누구도 주어진 언어, 문제 영역 및 사용 가능한 라이브러리에 대해 잘 알지 못하기 때문에 합리적인 솔루션에서 다른 솔루션으로 갈 때 누군가가 무언가를 놓칠 수 있기 때문에 (예를 들어, 필드에 무언가를 잘 사용하기 위해 저장하기 시작한 다음 시도) 그것을 리팩토링하지만 작업을 완료하지 않으면 문제와 같은 코드로 끝날 것입니다.) 우리는 때때로 학습에서 물건을 놓치므로 언젠가는 미신적이거나화물이 많은 코드를 만들었습니다. . 좋은 점은 실제로 안티 패턴보다 식별하고 수정하는 것이 더 명확하다는 것입니다. 진정한 반 패턴은 반 패턴이 아닐 수도 있고, 매력적인 품질을 가지고 있거나, 불량으로 식별 된 경우에도 적어도 반항 할 수있는 방법을 가지고 있습니다. 다른쪽으로 이어집니다).


1
그러나 사람들 이것이 코드 재사용의 한 형태라고 생각하기 때문에 이것이 좋은 생각이라고 생각합니다.
JSB ձոգչ

초보자는 종종 두 변수를 선언하면 어떻게 든 두 배의 공간이 필요하므로 변수를 재사용하는 것을 선호한다고 생각하는 것 같습니다. 이것은 메모리 모델 (무료 저장소 대 스택, 스택 위치 재사용 가능성)에 대한 오해와 모든 컴파일러가 수행 할 수있는 최적화를 보여줍니다. 초보자가 변수 재사용을 좋은 아이디어로 생각할 수 있으므로 반 패턴으로 분류 할 수 있다고 주장합니다.
Philipp

그것은 반 패턴이 아니라 미신이됩니다.
존 한나

3

(1) 좋지 않다고 말할 것입니다.

스택이 로컬 변수를 사용하여 자동으로 수행 할 수 있도록 수동 하우스 유지를 수행합니다.

"새"를 각 메소드 호출이라고 부르므로 성능상의 이점은 없습니다.

편집 : 막대 포인터가 항상 클래스 수명 동안 메모리를 차지하기 때문에 클래스의 메모리 공간이 더 큽니다. 막대 포인터가 로컬 인 경우 메소드 호출 수명 동안 메모리 만 사용합니다. 포인터에 대한 기억은 바다에서 떨어질 뿐이지 만 여전히 불필요합니다.

바는 클래스의 다른 메소드에 표시됩니다. bar를 사용하지 않는 메소드는이를 볼 수 없습니다.

상태 기반 오류. 이 특별한 경우에는 "new"가 매번 호출되므로 상태 기반 오류는 멀티 스레딩에서만 발생해야합니다.

(2) 실제로는 하나의 문제가 아니라 몇 가지 문제이므로 이름이 있는지 모르겠습니다.
-자동 사용 가능시 수동 하우스 키핑-
불필요한 상태-
수명 이상 오래 살 수 있는 상태-
가시성 또는 범위가 너무 큼


2

나는 그것을 "방황 제노 포브"라고 부릅니다. 혼자 남겨두고 싶지만 그것이 어디에 있는지 또는 무엇인지 알지 못한다. 따라서 다른 사람들이 말한 것처럼 나쁜 것입니다.


2

여기에서 그레인에 반대하지만 주어진 예제가 현재의 형식과 같은 좋은 코딩 스타일이라고 생각하지는 않지만 단위 테스트를 수행하는 동안 패턴 이 존재합니다.

단위 테스트를 수행하는 매우 표준적인 방법

public class BarTester
{
    private Bar bar;

    public Setup() { bar = new Bar(); }
    public Teardown() { bar = null; }

    public TestMethodA()
    {
        bar.A();        
    }

    public TestMethodB()
    {
        bar.B();
    }
}

이 OP 코드와 동등한 리팩토링입니다.

public class BarTester
{
    private Bar bar;

    public TestMethodA()
    {
        bar = new Bar();
        bar.A();
        bar = null;
    }

    public TestMethodB()
    {
        bar = new Bar();
        bar.B();
        bar = null;
    }
}

나는 OP의 예제에서 주어진 코드를 작성하지 않을 것이며 리팩토링을 비명을 지르지 만 패턴유효하지 않거나 반 패턴 이 아닌 것으로 간주합니다 .


이 경우 고정구는 각 테스트마다 독립적으로 인스턴스화되며 가변 재사용 또는 동시성 관련 문제는 발생하지 않습니다. 일반적인 경우 (단위 테스트 외부) 각 객체에 대해 최대 하나의 메소드가 호출되는지 여부는 알 수 없습니다.
Philipp

1
@Philipp-재사용 또는 동시성 문제에 대해 이의를 제기하지는 않지만 OP의 질문은 단위 테스트에 일반적으로 사용되는 패턴에 관한 것이 었습니다. 고정구가 각 테스트에 대해 인스턴스화된다는 사실은 테스트 프레임 워크의 구현 세부 사항입니다. 단위 테스트에서도 패턴은 테스트 프레임 워크가 사용되어야하는대로 사용될 때만 안전합니다. 프로덕션 코드에서이 패턴을 사용할 때도 동일한 추론이 적용됩니다.
Lieven Keersmaekers

설정할 필요가 없습니다 barnullJUnit을 어쨌든 모든 테스트 방법에 대한 새로운 테스트 인스턴스를 생성하고,이.
준수

2

이 코드 냄새는 Beck / Fowler의 Refactoring에서 Temporary Field라고합니다.

http://sourcemaking.com/refactoring/temporary-field

중재자의 요청에 따라 추가 설명을 추가하도록 편집 : 위의 참조 URL 또는 책의 하드 카피에서 코드 냄새에 대한 자세한 내용을 읽을 수 있습니다. OP는이 특정 안티 패턴 / 코드 냄새의 이름을 찾고 있었기 때문에 10 세가 넘는 기존 소스에서 언급되지 않은 참조를 인용하면 도움이 될 것이라고 생각했지만 이 질문에 대한 답을 얻었으므로 답변이 투표되거나 받아 들여질 것 같지 않습니다.

개인적으로 너무 홍보가 아니라면 다가오는 Pluralsight 과정 인 Refactoring Fundamentals에서이 특정 코드 냄새를 참조하고 설명합니다. 2013 년 8 월에 공개 될 것으로 예상됩니다.

더 많은 가치를 창출하는 방법으로이 특정 문제를 해결하는 방법에 대해 이야기하겠습니다. 몇 가지 옵션이 있습니다. 필드가 하나의 방법으로 만 사용되는 경우 로컬 변수로 강등 될 수 있습니다. 그러나 여러 메소드가 필드를 사용하여 통신하는 경우 (매개 변수 대신), 리팩토링에 설명 된 전형적인 경우이며 필드를 매개 변수로 바꾸거나이 코드와 함께 작동하는 메소드가 밀접한 경우 정정 할 수 있습니다. 관련된 클래스 추출은 메소드와 필드를 자체 클래스로 가져 오는 데 사용될 수 있습니다. 필드는 항상 유효한 상태 (건설 중 설정 됨)에 있으며이 새 클래스의 상태를 나타냅니다. 매개 변수 경로로 이동하지만 전달할 값이 너무 많은 경우 다른 옵션은 새 매개 변수 객체를 도입하는 것입니다.


추출 클래스 리팩토링에는 종종 임시 필드가 필요합니다. 그러나 이것을 알고있는 사람은 아마도이 중간 상태의 코드를 체크인하지 않을 것입니다.
준수

1

나는 당신이 그것을 바로 잡을 때, 이것은 실제로 응집력이 부족하다고 말하고 싶습니다. 그리고 나는 "False Cohesion"이라는 이름을 줄 것입니다. 클래스가 그 멤버들의 필드에서 작동하는 것처럼 보이기 때문에 클래스가 응집력이있는 것처럼 보이기 때문에이 용어를 코인 (또는 어딘가에서 가져 와서 "빌리기")하면이 용어를 사용합니다. 그러나 실제로는 겉보기 응집력이 실제로 거짓임을 의미하지 않습니다.

그것이 나쁜지 아닌지에 관해서는 분명히 말하고 싶습니다. 스티븐 C는 그 이유를 설명하는 훌륭한 일을한다고 생각합니다. 그러나 "반 패턴 (anti-pattern)"이라고 부를 자격이 있든 없든간에, 나는 그것과 다른 코딩 패러다임이 레이블로 할 수 있다고 생각합니다.


1

이미 언급 한 문제 외에도 자동 가비지 수집 (예 : C ++)이없는 환경에서이 방법을 사용하면 사용 후 임시 객체가 해제되지 않으므로 메모리 누수가 발생합니다. (이것은 조금 멀리 가져온 것처럼 보일 수 있지만, 'null'을 'NULL'로 변경하고 코드가 컴파일되는 것을 기쁘게 생각하는 사람들이 있습니다.)


1

이것은 플라이급 패턴의 끔찍한 오용처럼 보입니다. 불행히도 다른 방법으로 동일한 "사물"을 여러 번 사용해야하고 메모리를 절약하거나 성능을 향상시키려는 잘못된 시도 또는 다른 이상한 의사 최적화를 시도하여 개인 필드로 가져와야하는 소수의 개발자에게 알려져 있습니다. Flyweight는 실제로 해결해야 할 문제가 아닌 상황에서만 수행하는 문제를 해결하기 위해 비슷한 문제를 해결하려고합니다.


-1

VBA For Dummies를 첫 번째 제목으로 집어 들고 비틀 거리는 언어로 비교적 큰 시스템을 작성했던 사람이 이전에 작성한 코드를 새로 고칠 때이 문제가 여러 번 발생했습니다.

정말 나쁜 프로그래밍 관행은 옳다고 말하지만, 오늘날 우리 모두가 즐길 수있는 인터넷 및 피어 검토 리소스를 "safer"경로를 따라 안내 할 수있는 자원이없는 결과 일 수 있습니다.

실제로 "antipattern"태그를 사용할 필요는 없지만 OP를 코딩하는 방법에 대한 제안을 확인하는 것이 좋습니다.


1
첫 번째 답변을 입력 해 주셔서 감사합니다. Stack Exchange Programmers에 오신 것을 환영합니다. FAQ programmers.stackexchange.com/faq에 나열된 이유 때문에 다운 투표권이있는 것 같습니다 . FAQ는 효과적인 (그리고 투표 된) 답변에 대한 훌륭한 제안을 제공합니다. 때때로 사람들은 다운 투표에 대한 메모를 추가하여 어떤 식 으로든 잘못된 지, 중복되거나, 증거가없는 의견 또는 이전 답변을 반복하는 나도 나타내는지를 충분히 지적 할 수 있습니다. 대부분의 사람들은 투표를 중단하거나 마감하거나 답변을 삭제 했으므로 걱정하지 마십시오. 그것은 단지 시작의 일부입니다. 어서 오십시오.
DeveloperDon
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.