태그 보관물: object-oriented

object-oriented

클래스 속성이 클래스의 새 인스턴스를 만들어 반환하는 경우 안티 패턴입니까? createReciprocalHeading()의 새 인스턴스를 자동으로 만들어서 사용자. 하지만, 내

나는 Heading몇 가지 일을 하는 클래스를 가지고 있지만 현재 제목 값의 반대를 반환 할 수 있어야하며, 결국 Heading클래스 자체 의 새 인스턴스를 생성하여 사용해야 합니다.

reciprocal현재 값의 반대 제목을 반환하기 위해 간단한 속성을 호출 한 다음 수동으로 제목 클래스의 새 인스턴스를 만들거나 제목 클래스 createReciprocalHeading()의 새 인스턴스를 자동으로 만들어서 사용자.

하지만, 내 동료 중 하나는 클래스 만들기 위해 저를 추천 재산 이라고 reciprocal는 getter 메소드를 통해 클래스 자체의 새로운 인스턴스 반환합니다.

내 질문은 : 클래스의 속성이 그렇게 행동하는 것이 안티 패턴이 아닙니까?

특히 다음과 같은 이유로 직관적이지 않습니다.

  1. 내 생각에 클래스의 속성이 클래스의 새 인스턴스를 반환해서는 안되며
  2. 이 속성의 이름은 reciprocal개발자가 IDE의 도움을 받거나 게터 서명을 확인하지 않고도 동작을 완전히 이해하는 데 도움이되지 않습니다.

클래스 속성이해야 할 일이 너무 엄격합니까, 아니면 유효한 관심사입니까? 나는 항상 필드와 속성을 통해 클래스의 상태와 메서드를 통해 동작을 관리하려고 노력했지만 이것이 클래스 속성의 정의에 어떻게 적합한 지 알 수 없습니다.



답변

Copy () 또는 Clone ()과 같은 것은 알 수 없지만 그래도 걱정할 것 같습니다.

예를 들어 :

h2 = h1.reciprocal
h2.Name = "hello world"
h1.reciprocal.Name = ?

이 속성은 매번 새 개체라는 경고를 표시하는 것이 좋습니다.

다음과 같이 가정 할 수도 있습니다.

h2.reciprocal == h1

하나. 표제 클래스가 변경 불가능한 값 유형 인 경우 이러한 관계를 구현할 수 있으며 상호는 조작에 대한 좋은 이름 일 수 있습니다.


답변

클래스의 인터페이스는 클래스 사용자가 클래스 작동 방식에 대한 가정을하도록합니다.

이러한 가정 중 많은 것이 맞고 틀린 것이 거의 없다면 인터페이스가 좋습니다.

이러한 가정 중 다수가 틀렸고 옳지 않은 경우가 많으면 인터페이스가 엉망입니다.

속성에 대한 일반적인 가정은 get 함수를 호출하는 것이 저렴하다는 것입니다. 속성에 대한 또 다른 일반적인 가정은 get 함수를 연속으로 두 번 호출하면 같은 것을 반환한다는 것입니다.


기대치를 변경하기 위해 일관성을 사용하여이 문제를 해결할 수 있습니다. 예를 들어, 당신이 필요로하는 작은 3D 라이브러리 Vector, Ray Matrix등 당신은 같은 같은 게터 것을 할 수 있습니다 Vector.normal와는 Matrix.inverse거의 항상 비싸다. 사용자 인터페이스는 지속적으로 비용이 속성을 사용 불행히도 경우에도 인터페이스는 기껏해야 하나 직관적으로 사용됩니다 Vector.create_normal()Matrix.create_inverse()– 아직 난 기대를 변경 한 후, 속성을 사용하여보다 직관적 인 인터페이스를 생성하는 할 수있는 강력한 인수 알고.


답변

속성은 매우 빠르게 반환되고 반복 된 호출로 동일한 값을 반환해야하며 값을 가져 오는 데 부작용이 없어야합니다. 여기서 설명하는 내용은 속성으로 구현 해서는 안됩니다 .

직감이 전반적으로 정확합니다. 팩토리 메소드는 반복 호출로 동일한 값을 리턴하지 않습니다. 매번 새로운 인스턴스를 반환합니다. 이것은 재산으로 거의 실격됩니다. 나중에 개발할 때 네트워크 종속성과 같은 인스턴스 생성에 가중치가 추가되는 것도 빠르지 않습니다.

속성은 일반적으로 클래스의 현재 상태의 일부를 가져 오거나 설정하는 매우 간단한 작업이어야합니다. 공장과 유사한 작업은이 기준을 충족하지 않습니다.


답변

“속성”을 구성하는 것은 언어 별 질문이며,“속성”의 호출자가 기대하는 것은 언어 별 질문이기 때문에 이것에 대한 언어 독립적 인 대답이 없다고 생각합니다. 나는 이것에 대해 생각하는 가장 유익한 방법은 발신자의 관점에서 어떻게 보이는지 생각하는 것이라고 생각합니다.

C #에서 속성은 (전통적으로) 대문자로 표시되지만 (메소드와 유사하지만) 괄호 (공개 인스턴스 변수와 같이)가 없다는 점에서 차별화됩니다. 설명서가없는 다음 코드가 표시되면 무엇을 기대하십니까?

var reciprocalHeading = myHeading.Reciprocal;

상대 C # 초보자이지만 Microsoft의 Property Usage Guidelines를 읽는 사람 은 다음 Reciprocal과 같은 것들을 기대 합니다.

  1. Heading수업 의 논리적 인 데이터 멤버이다
  2. 내가 값을 캐시 할 필요가 없도록 호출 비용이 저렴합니다.
  3. 관찰 가능한 부작용이 없음
  4. 연속해서 두 번 호출하면 동일한 결과를 생성합니다
  5. (아마) ReciprocalChanged이벤트를 제안하십시오

이 가정들 중 (3)과 (4)는 아마도 맞을 것입니다 ( 이완의 답변Heading 에서와 같이 불변의 값 유형 이라고 가정 ) (어떤 불구하고 의미 론적 의미를 가지고 제목을 아마해야 이벤트). 이것은 C # API에서 “가상 수를 얻거나 계산하는 것”이 속성으로 구현 되어서는 안되며 특히 계산이 저렴하고 불변 인 경우 경계선 인 경우를 제안합니다 .HeadingChangedHeading

(그러나 속성을 호출하면 (2)가 아니라 새 인스턴스를 생성 하는지 여부와 관련이 없습니다. CLR에서 객체를 만드는 것 자체는 꽤 저렴합니다.)

Java에서 특성은 메소드 이름 지정 규칙입니다. 내가 보면

Heading reciprocalHeading = myHeading.getReciprocal();

내 기대는 위의 것과 비슷합니다 (명확하게 명시되지 않은 경우). 나는 전화가 저렴하고 dem 등하 며 부작용이 없을 것으로 기대합니다. 그러나, 자바 빈즈 프레임 워크 외부에서 “속성”의 개념이 자바의 의미, 특히 결코 상응하는 불변의 특성을 고려할 때 모든하지 setReciprocal()getXXX()규칙은 지금은 다소 구식이다. 에서 효과적인 자바 , 두 번째 버전 (현재 이미 8 년 이상 된) :

boolean호출 된 객체 의 비 기능 또는 속성 을 반환하는 메소드 는 일반적으로 명사, 명사구 또는 동사로 시작하는 동사구로 명명됩니다 get… 으로 시작하는 세 번째 형식 만 get허용되지만이 주장에 대한 근거는 거의 없다고 주장하는 성명서가 있습니다. 처음 두 형식은 일반적으로 더 읽기 쉬운 코드로 이어집니다… (p. 239)

그렇다면 현대적이고 유창한 API에서 볼 것으로 예상됩니다.

Heading reciprocalHeading = myHeading.reciprocal();

이것은 호출이 저렴하고 dem 등성이 있으며 부작용이 없음을 다시 제안하지만 새로운 계산이 수행되는지 또는 새 객체가 작성되는지에 대해서는 아무 말도하지 않습니다. 이건 괜찮아; 좋은 API에서는 신경 쓰지 않아야합니다.

루비에는 속성 같은 것이 없습니다. “속성”이 있지만 내가 볼 경우

reciprocalHeading = my_heading.reciprocal

나는 간단한 접근 자 메소드 @reciprocal를 통해 인스턴스 변수에 액세스 attr_reader하는지 또는 비싼 계산을 수행하는 메소드를 호출하는지 여부를 즉시 알 수있는 방법이 없습니다 . 그러나 메소드 이름이 단순한 명사라는 사실은 calcReciprocal다시 말하지만 호출이 적어도 싸고 아마도 부작용이 없음을 시사합니다.

스칼라에서 명명 규칙은 부작용이있는 메소드는 괄호를 사용하고 그렇지 않은 메소드는 그렇지 않지만

val reciprocal = heading.reciprocal

다음 중 하나 일 수 있습니다.

// immutable public value initialized at creation time
val reciprocal: Heading = …

// immutable public value initialized on first use
lazy val reciprocal: Heading = …

// public method, probably recalculating on each invocation
def reciprocal: Heading = …

// as above, with parentheses that, by convention, the caller
// should only omit if they know the method has no side effects
def reciprocal(): Heading = …

(주 스칼라는 것을 허용 그럼에도 불구하고 각종 물건 낙담 에 의해 스타일 가이드를 .이 스칼라 내 주요 불만 중 하나입니다.)

괄호가 없으면 전화에 부작용이 없다는 것을 알 수 있습니다. 이 이름은 통화가 상대적으로 저렴해야 함을 나타냅니다. 그 외에도 나는 그것이 어떻게 가치를 얻는 지 상관하지 않습니다 .

한마디로 : 사용중인 언어와 다른 프로그래머가 API에 어떤 기대를 가져다 줄지 알 수 있습니다. 그 밖의 모든 것은 구현 세부 사항입니다.


답변

다른 사람들이 말했듯이 동일한 클래스의 인스턴스를 반환하는 것은 다소 일반적인 패턴입니다.

이름 지정은 언어의 이름 지정 규칙과 밀접한 관련이 있습니다.

예를 들어, Java에서는 아마도 그것이 호출 될 것으로 기대합니다. getReciprocal();

즉, 나는 가변 대 불변의 객체를 고려할 것 입니다.

불변의 것을 사용하면 상황이 매우 쉽고 동일한 객체를 반환하거나하지 않아도 상처를 입지 않습니다.

으로 변경할 것, 이것은 아주 무서운 얻을 수 있습니다.

b = a.reciprocal
a += 1
b = what?

이제 b는 무엇을 말하는가? a의 원래 값의 역수? 아니면 변경된 것의 역수? 이것은 예가 너무 짧을 수 있지만 요점을 알 수 있습니다.

이러한 경우 발생하는 상황을 알려주 는 더 나은 이름 을 찾으십시오. 예를 들어 createReciprocal()더 나은 선택 일 수 있습니다.

그러나 그것은 실제로 상황에 달려 있습니다.


답변

단일 책임과 명확성을 위해 객체를 가져 와서 그 역을 반환하는 ReverseHeadingFactory가 있습니다. 이것은 새로운 객체가 리턴되고 있음을 더 명확하게 할 것이며, 리버스를 생성하는 코드가 다른 코드로부터 캡슐화되었다는 것을 의미합니다.


답변

따라 다릅니다. 나는 일반적으로 속성이 인스턴스의 일부인 것을 반환하기를 기대하므로 동일한 클래스의 다른 인스턴스를 반환하는 것은 조금 특이합니다.

그러나 예를 들어 문자열 클래스에는 “firstWord”, “lastWord”속성이 있거나 유니 코드 “firstLetter”, “lastLetter”를 처리하는 경우 전체 문자열 객체 (일반적으로 작은 객체)가됩니다.