반복자를 디자인 패턴으로 만드는 것은 무엇입니까?


9

다른 유사한 구조와 비교할 때 Iterator를 특별하게 만드는 것이 무엇인지 궁금해했으며 Gang of Four가 그것을 디자인 패턴으로 나열했습니다.

반복자는 다형성 (공통 인터페이스를 가진 컬렉션의 계층)과 우려의 분리를 기반으로합니다 (컬렉션에 대한 반복은 데이터의 구성 방식과 독립적이어야 함).

그러나 컬렉션 계층을 예를 들어 수학 객체 (정수, 부동 소수점, 복소수, 행렬 등)의 계층과 이러한 객체에 대한 일부 관련 작업 (예 : 전원 함수)을 나타내는 클래스로 반복자를 대체하면 어떻게 될까요? 클래스 다이어그램은 동일합니다.

우리는 아마도 Writer, Painter, Encoder와 같은 더 많은 유사한 예제를 찾을 수있을 것입니다. 그러나 나는 이것들 중 어떤 것도 디자인 패턴이라고 들었습니다.

그렇다면 Iterator가 특별한 이유는 무엇입니까?

컬렉션 내에 현재 위치를 저장하기 위해 변경 가능한 상태가 필요하기 때문에 더 복잡하다는 사실입니까? 그러나 변경 가능한 상태는 일반적으로 바람직하지 않은 것으로 간주됩니다.


요점을 명확히하기 위해 좀 더 자세한 예를 들어 보겠습니다.

디자인 문제는 다음과 같습니다.

클래스의 계층 구조와이 클래스의 객체에 정의 된 작업이 있다고 가정 해 봅시다. 이 작업의 인터페이스는 각 클래스마다 동일하지만 구현 방법이 완전히 다를 수 있습니다. 또한 다른 매개 변수를 사용하여 동일한 객체에 작업을 여러 번 적용하는 것이 합리적이라고 가정합니다.

다음은 디자인 문제에 대한 합리적인 해결책입니다 (실제로 반복자 패턴의 일반화).

우려를 분리하기 위해 작업 구현을 원래 클래스 계층 (오퍼랜드 개체)에 함수로 추가해서는 안됩니다. 동일한 피연산자에 연산을 여러 번 적용하려고하므로 함수가 아니라 피연산자에 대한 참조를 보유하는 객체로 표시되어야합니다. 따라서 피연산자 객체는 작업을 나타내는 객체를 반환하는 함수를 제공해야합니다. 이 객체는 실제 작업을 수행하는 기능을 제공합니다.

예를 들면 :

MathObject파생 클래스 MyInteger와 와 기본 클래스 또는 인터페이스 (어리석은 이름, 누군가가 더 나은 아이디어를 가지고 있습니다.)가 있습니다 MyMatrix. 각각에 대해 정사각형, 큐브 등의 계산을 허용 MathObject하는 작업 Power을 정의해야합니다. 그래서 우리는 (Java로) 쓸 수 있습니다 :

MathObject i = new MyInteger(5);
Power powerOfFive = i.getPower();
MyInteger square = powerOfFive.calculate(2); // should return 25
MyInteger cube = powerOfFive.calculate(3); // should return 125

5
"클래스 다이어그램은 같을 것입니다"-그래서 무엇? 디자인 패턴은 클래스 다이어그램이 아닙니다. 반복되는 문제에 대한 일련의 솔루션에 대한 높은 수준의 추상화입니다.
Doc Brown

@DocBrown : 그렇지만 수학 연산, 파일에 객체 쓰기, 그래픽 출력 또는 반복과 같은 반복되는 데이터 인코딩 문제가 아닌가?
Frank Puffer

디자인 패턴의 선택은 주관적입니다 (즉, "디자이너"또는 디자인을 판단하는 사람들의 눈에). 디자인 패턴의 이름은 도메인에 구애받지 않기위한 것입니다 (그래서 우리는 도메인에 따라 다르다는 생각에 산만하지 않습니다). 내 의견으로는 인용 할 근거가 없습니다.
rwong

@FrankPuffer 파일에 객체를 쓰는 일반적인 솔루션의 개요를 제시하면 솔루션을 적어두고 객체 작성 패턴이라고 부르면 도움이됩니다.
Brandin

3
당신은 이것을 지나치게 생각하고 있습니다. 디자인 패턴은 일반적인 컴퓨팅 문제에 대한 잘 알려진 솔루션이며 그게 전부입니다. 패턴이 제공하는 이점을 인식하고 적용 할 수있을 때 패턴을 사용합니다.
Robert Harvey

답변:


9

GoF 책의 패턴 대부분은 다음과 같은 공통점이 있습니다.

  • 객체 지향 수단을 사용하여 기본 설계 문제 를 해결 합니다.
  • 사람들은 종종 임의의 프로그램에서 이런 종류의 문제에 직면하게됩니다.
  • 그것들은 종종 코드를 더 SOLID로 만들어 재사용 성을 높이는 레시피입니다.
  • 그들은 이러한 문제에 대한 정식 해결책을 제시합니다

이 패턴들에 의해 해결 된 문제는 너무 기본적이어서 많은 개발자들이 주로 프로그래밍 언어 기능 누락에 대한 해결 방법 으로 이해합니다. IMHO는 유효한 관점입니다. 오늘과 같은 기능).

반복자 패턴은이 설명에 잘 맞습니다. 특정 도메인과 독립적으로 매우 자주 발생하는 기본 문제를 해결하며, 스스로 작성했을 때 "문제의 분리"에 대한 좋은 예입니다. 아시다시피 직접 반복자 지원은 오늘날 많은 현대 프로그래밍 언어에서 찾을 수 있습니다.

이제 이것을 선택한 문제와 비교하십시오.

  • 파일에 쓰기-그것은 단순히 "기본"충분하지 않은 IMHO입니다. 매우 구체적인 문제입니다. 좋은 정식 해결책도 없습니다. 파일에 쓰는 방법에는 여러 가지가 있으며 명확한 "모범 사례"는 없습니다.
  • Painter, Encoder : 여러분이 염두에두고 있던 그 문제는 도메인에 독립적이지 않은 나에게 훨씬 덜 기본적으로 보입니다.
  • 다른 종류의 물체에 "파워"기능을 사용할 수 있습니다. 언뜻 보면 패턴에 걸 맞는 가치가 있지만 제안 된 솔루션은 저를 설득하지 못합니다. 반복자 패턴. 엔지니어링 계산으로 많은 코드를 구현했지만 전력 함수 객체와 비슷한 접근 방식이 도움이 된 상황을 기억할 수 없습니다 (그러나 반복자는 매일 처리 해야하는 것입니다).

또한, 나는 당신의 전력 함수 예제에서 전략 패턴이나 명령 패턴의 응용으로 해석 될 수없는 것을 보지 못했습니다. 이것은 기본 부분이 이미 GoF 책에 있음을 의미합니다. 더 나은 솔루션은 연산자 오버로딩 또는 확장 방법을 포함 할 수 있지만, 언어 기능에 따라 달라 지므로 "Gang"에서 사용하는 "OO"가 제공 할 수없는 것입니다.


The problems solved by these patterns are so basic that many developers think their main purpose is to be workarounds for missing programming language features-소프트웨어 개발자들이 일상적으로 20 년이 지난 소프트웨어 디자인 패턴을 사용하면서도 최신 코드를 작성하고 있다고 생각하는 아이러니 한 존재.
Robert Harvey

@RobertHarvey : 많은 개발자들이 GoF가 제안한 "OO 방식"으로 오늘날 반복자 패턴을 구현할 것이라고 생각하지 않습니다. 일반적으로 언어 또는 표준 라이브러리 (예 : IEnumerableand #를 사용하여 C #에서 제공)를 통해 구현합니다 yield. 그러나 다른 GoF 패턴의 경우 작성한 내용이 사실 일 수 있습니다.
Doc Brown

1
관련 workarounds for missing programming language features: blog.plover.com/prog/johnson.html
모니카 jrw32982 지원

8

크리스토퍼 알렉산더의 패턴 정의 :

각 패턴은 우리 환경에서 반복해서 발생하는 문제를 설명하고 그 문제에 대한 해결책의 핵심을 설명합니다.

반복자가 해결 한 문제는 무엇입니까?

의도 : 기본 표현을 노출시키지 않고 집계 오브젝트의 요소에 순차적으로 액세스 할 수있는 방법을 제공하십시오.

해당 대상 : 반복자 패턴 사용

  • 내부 표현을 노출시키지 않고 집합 객체의 내용에 액세스
  • 집계 개체의 여러 순회를 지원하기 위해
  • 다른 집계 구조를 순회하기위한 (즉, 다형성 반복을 지원하기위한) 균일 한 인터페이스를 제공합니다.

따라서 반복자 패턴은 정의에 따라 컬렉션에 따라 도메인마다 다르다고 주장 할 수 있습니다. 그리고 그것은 완벽하게 괜찮습니다. 인터프리터 패턴과 같은 다른 패턴은 도메인 별 언어에 따라 도메인마다 다르며 팩토리 패턴은 객체 생성에 따라 도메인별로 다릅니다. 물론 이것은 "도메인 특정"에 대한 다소 어리석은 이해입니다. 반복되는 문제-해결책 쌍인 한 패턴이라고 할 수 있습니다.

그리고 Iterator 패턴이 존재하는 것이 좋습니다. 당신이 그것을 사용하지 않으면 나쁜 일이 발생합니다. 내가 가장 좋아하는 안티 예제는 Perl입니다. 여기서 각 컬렉션 (배열 또는 해시)에는 컬렉션의 일부로 반복자 상태가 포함됩니다. 왜 이것이 나쁜가요? while-each 루프를 사용하여 해시를 쉽게 반복 할 수 있습니다.

while (my ($key, $value) = each %$hash) {
  say "$key => $value";
}

그러나 루프 바디에서 함수를 호출하면 어떻게 될까요?

while (my ($key, $value) = each %$hash) {
  do_something_with($key, $value, $hash);
}

이 함수는 이제 다음을 제외하고 원하는대로 수행 할 수 있습니다.

  • 해시 항목을 추가하거나 삭제하십시오. 이는 반복 순서를 예기치 않게 변경하기 때문에 (C ++에서 말하면 반복자를 무효화합니다).
  • 동일한 반복 상태를 사용하므로 복사하지 않고 동일한 해시 테이블을 반복합니다. 죄송합니다.

호출 된 함수가 반복자를 사용해야하는 경우 루프 동작이 정의되지 않습니다. 문제입니다. 그리고 반복자 패턴에는 해결 방법이 있습니다. 모든 반복 상태를 반복마다 작성된 별도의 객체에 넣습니다.

물론 반복자 패턴은 다른 패턴과 관련이 있습니다. 예를 들어, 반복자는 어떻게 인스턴스화됩니까? 자바에서, 우리는 일반적인이 Iterable<T>Iterator<T>인터페이스를. 구체적인 iterable iter ArrayList<T>는 특정 종류의 iterator를 생성하는 반면 HashSet<T>완전히 다른 iterator 유형을 제공 할 수 있습니다. 그것은 추상 팩토리 패턴을 생각 나게합니다. 여기서 Iterable<T>추상 팩토리이고 Iterator제품입니다.

다형성 반복자는 전략 패턴의 예로서 해석 될 수도 있습니다. 예를 들어, 트리는 다른 종류의 반복자를 제공 할 수 있습니다 (주문, 주문, 주문,…). 외부 적으로 이것들은 모두 반복자 인터페이스를 공유하고 어떤 순서로 요소를 산출합니다. 클라이언트 코드는 특정 트리 탐색 알고리즘이 아닌 반복자 인터페이스에만 의존하면됩니다.

패턴은 서로 독립적으로 존재하지 않습니다. 일부 패턴은 동일한 문제에 대한 다른 솔루션이며 일부 패턴은 다른 컨텍스트에서 동일한 솔루션을 설명합니다. 일부 패턴은 다른 패턴을 의미합니다. 또한 Design Patterns 책의 마지막 페이지를 넘길 때 패턴 공간이 닫히지 않습니다 ( 앞의 질문 인 Gang of Four가“Pattern Space”를 철저히 조사 했습니까? 참조 ). 디자인 패턴 책에 설명 된 패턴은 매우 유연하고 광범위하며 무한한 변형에 개방적이며 존재하는 유일한 패턴은 아닙니다.

나열하는 개념 (쓰기, 페인팅, 인코딩)은 문제-해결 조합을 설명하지 않기 때문에 패턴이 아닙니다. "데이터를 작성해야합니다"또는 "데이터를 인코딩해야합니다"와 같은 작업은 실제로 디자인 문제가 아니며 솔루션을 포함하지 않습니다. “알다, 나는 작가 클래스를 만들 것이다”로 구성된“솔루션”은 의미가 없습니다. 그러나 "반 렌더링 된 그래픽을 화면에 페인트하지 않으려는 경우"와 같은 문제가있는 경우 패턴이 존재할 수 있습니다. "이중 버퍼 그래픽을 사용할 것입니다!"


좋은 답변, 감사합니다. 마지막 단락에서 작성한 내용이 여기에 적용된다는 것을 여전히 확신하지는 못합니다. 의미를 설명하기 위해 질문을 편집했습니다.
Frank Puffer
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.