패턴을 올바르게 사용하려면 상태그리고 전략 Java 응용 프로그램의 핵심에서 Java 프로그래머는 둘 사이의 차이점을 명확하게 이해하는 것이 중요합니다. State 및 Strategy 템플릿은 모두 유사한 구조를 가지고 있으며 둘 다 SOLID 원칙에서 "O"를 나타내는 개방/폐쇄 원칙을 기반으로 하지만 완전히 다릅니다. 의도. 무늬 전략자바에서 캡슐화에 사용클라이언트에 실행 유연성을 제공하는 관련 알고리즘 세트. 클라이언트는 Strategy 개체를 사용하는 클래스의 컨텍스트를 변경하지 않고 런타임에 모든 알고리즘을 선택할 수 있습니다. 몇 가지 인기 있는 패턴 예 전략암호화, 압축 또는 정렬과 같은 알고리즘을 사용하는 코드를 작성하고 있습니다. 반면에 State 패턴을 사용하면 개체가 다른 상태에서 다르게 동작할 수 있습니다. 현실 세계에서 객체는 종종 상태를 가지며 다른 방식으로 동작하기 때문입니다. 다른 주예를 들어 자판기는 hasCoin 상태인 경우에만 상품을 판매하며 코인을 넣을 때까지 판매되지 않습니다. 이제 전략 패턴과 상태 패턴의 차이점을 명확하게 볼 수 있습니다. 의도가 다릅니다. 상태 패턴은 객체가 상태를 관리하는 데 도움이 되는 반면 전략 패턴은 클라이언트가 다른 동작을 선택할 수 있도록 합니다. 보기 쉽지 않은 또 다른 차이점은 누가 행동의 변화를 관리하는지입니다. Strategy 패턴의 경우 컨텍스트에 다른 전략을 제공하는 것은 클라이언트이며, State 패턴에서는 객체 자체의 컨텍스트 또는 상태에 의해 전환이 관리됩니다. 또한 State 객체의 상태 변경을 직접 관리하는 경우 컨텍스트에 대한 참조가 있어야 합니다. 예를 들어 자판기에서는 setState() 메서드를 호출하여 컨텍스트의 현재 상태를 변경할 수 있어야 합니다. 반면에 Strategy 개체는 컨텍스트에 대한 참조를 포함하지 않으며 클라이언트 자체는 선택한 Strategy를 컨텍스트에 전달합니다. 상태 패턴과 전략 패턴의 차이점은 Java 패턴에 대한 인기 있는 인터뷰 질문 중 하나입니다. Java 패턴에 대한 이 기사에서 자세히 살펴보겠습니다. 이러한 패턴에 대한 이해를 높이는 데 도움이 되도록 Java의 전략 패턴과 상태 패턴 간의 유사점과 차이점을 살펴보겠습니다.

상태 패턴과 전략 패턴의 유사점

State 및 Strategy 패턴의 UML 다이어그램을 보면 둘 다 서로 비슷해 보이는 것을 알 수 있습니다. State를 사용하여 동작을 변경하는 객체를 Context 객체라고 하며, 마찬가지로 전략을 사용하여 동작을 변경하는 객체를 Context 객체라고 합니다. 클라이언트는 Context 개체와 상호 작용한다는 것을 기억하십시오. State 패턴의 경우 컨텍스트는 현재 객체로 유지되는 State 객체에 호출 메서드를 위임하는 반면, Strategy 패턴의 경우 컨텍스트는 Strategy 객체를 매개변수로 사용하거나 생성 중에 제공됩니다. 개체 컨텍스트의. Java의 UML 상태 패턴 다이어그램
State 패턴에 대한 이 UML 다이어그램은 Java에서 객체 지향 자동 판매기 디자인을 생성하는 고전적인 문제를 보여줍니다. 자판기의 상태가 인터페이스를 사용하여 표현된 것을 볼 수 있으며, 인터페이스는 특정 상태를 표현하기 위한 구현을 가지고 있습니다. 또한 각 상태에는 컨텍스트에서 호출된 작업의 결과로 다른 상태로 전환하기 위해 개체의 컨텍스트에 대한 참조가 있습니다.
전략 패턴에 대한 이 UML 다이어그램에는 일종의 기능 구현이 포함되어 있습니다. 정렬 알고리즘이 많기 때문에 이 디자인 패턴을 사용하면 클라이언트가 개체를 정렬할 때 알고리즘을 선택할 수 있습니다. 사실로 자바 컬렉션 프레임워크 Java에서 개체를 정렬하는 데 사용되는 Collections.sort() 메서드를 구현하여 이 패턴을 사용합니다. 유일한 차이점은 클라이언트가 정렬 알고리즘을 선택하도록 허용하는 대신 Java에서 Comparator 또는 Comparable 인터페이스의 인스턴스를 전달하여 클라이언트가 비교 전략을 지정할 수 있다는 것입니다. Java에서 이 두 가지 주요 디자인 패턴 사이의 몇 가지 유사점을 살펴보겠습니다.
  1. 상태 및 전략 패턴을 사용하면 사용하는 개체의 컨텍스트에 영향을 주지 않고 새 상태 및 전략을 쉽게 추가할 수 있습니다.

  2. 이 두 가지 모두 코드를 열거나 닫은 상태로 유지합니다. 즉, 디자인은 확장에 열려 있지만 수정에는 닫혀 있습니다. 상태 및 전략 패턴의 경우 개체의 컨텍스트는 수정, 새 상태 또는 새 정책의 도입에 대해 닫혀 있거나 다른 상태의 컨텍스트 또는 최소한의 변경을 수정할 필요가 없습니다.

  3. 객체 컨텍스트가 State 패턴에서 객체의 초기화 상태로 시작하는 것처럼 객체 컨텍스트에도 Java의 Strategy 패턴의 경우 기본 전략이 있습니다.

  4. State 패턴은 다른 객체 상태의 형태로 다른 동작을 나타내는 반면, Strategy 패턴은 다른 객체 전략의 형태로 다른 동작을 나타냅니다.

  5. 전략과 상태 두 패턴 모두 동작을 구현하기 위해 서브클래스에 의존합니다. 각 구체적인 전략은 추상 전략을 확장하며, 각 상태는 상태를 나타내는 데 사용되는 인터페이스 또는 추상 클래스의 하위 클래스입니다.

Java의 전략 패턴과 상태 패턴의 차이점

이제 우리는 State 패턴과 Strategy 패턴이 구조적으로 유사하지만 의도가 다르다는 것을 알고 있습니다. 이러한 디자인 패턴 간의 몇 가지 주요 차이점을 살펴보겠습니다.
  1. 전략 패턴은 관련 알고리즘 세트를 캡슐화하고 클라이언트가 런타임 시 구성 및 위임에도 불구하고 상호 교환 가능한 동작을 사용할 수 있도록 하는 반면, 상태 패턴은 클래스가 다른 상태에서 다른 동작을 나타내는 데 도움이 됩니다.

  2. 상태 패턴과 전략 패턴의 다음 차이점은 상태가 객체의 상태를 캡슐화하는 반면 전략 패턴은 알고리즘 또는 전략을 캡슐화한다는 것입니다. 상태는 객체와 연결되어 있으므로 재사용할 수 없지만 전략이나 알고리즘을 컨텍스트에서 분리하여 재사용할 수 있습니다.

  3. 상태 패턴에서 개인 상태는 상태 전환을 구현하기 위한 컨텍스트에 대한 참조를 포함할 수 있지만 전략에는 사용되는 컨텍스트에 대한 참조가 포함되지 않습니다.

  4. 전략의 구현은 그것을 사용할 객체에 매개변수로 전달할 수 있습니다. 예를 들어 Collection.sort()는 전략인 Comparator를 사용합니다. 반면에 상태는 개체 컨텍스트 자체의 일부이며 시간이 지남에 따라 개체 컨텍스트가 한 상태에서 다른 상태로 전환됩니다.

  5. Strategy와 State는 모두 개방/폐쇄 원칙을 따르지만 각 전략에는 개별 알고리즘이 포함되어 있기 때문에 전략도 단일 책임 원칙을 따르고 서로 다른 전략은 서로 독립적입니다. 한 전략을 변경한다고 해서 다른 전략을 변경할 필요는 없습니다.

  6. 전략 패턴과 상태 패턴의 또 다른 이론적 차이점은 작성자가 개체의 "How" 부분을 정의한다는 것입니다. 예를 들어 "How" 정렬 개체는 데이터를 정렬하는 반면 State 패턴은 "무엇"을 정의하고 개체의 "때" 부분, 예를 들어 개체가 특정 상태에 있을 때 할 수 있는 작업.

  7. 상태 전환의 순서는 상태 패턴에 잘 정의되어 있으며 전략 패턴에는 그러한 요구 사항이 없습니다. 고객은 자신이 선택한 전략의 구현을 자유롭게 선택할 수 있습니다.

  8. 전략 패턴의 일반적인 예는 정렬, 암호화 또는 압축 알고리즘과 같은 알고리즘의 캡슐화입니다. 코드에서 다음을 사용해야 하는 경우 다른 종류관련 알고리즘의 경우 전략 패턴 사용을 고려해야 합니다. 반면에 중첩된 조건문 없이 상태 및 상태 전환을 관리해야 하는 경우 State 패턴을 사용할 가능성을 인식하는 것은 매우 쉽습니다. State 패턴은 사용하기에 적합한 패턴입니다.

  9. 상태와 전략 패턴 간의 마지막이지만 가장 중요한 차이점 중 하나는 전략 변경은 클라이언트가 수행하는 반면 상태 변경은 객체 자체의 컨텍스트 또는 상태에 의해 수행될 수 있다는 것입니다.

모든 것이 다 Java의 상태 패턴과 전략 패턴의 차이점. 내가 말했듯이 둘 다 클래스와 UML 다이어그램에서 비슷해 보이며 둘 다 개방/폐쇄 원칙을 제공하고 동작을 캡슐화합니다. 전략 패턴을 사용하여 런타임에 컨텍스트에 제공되는 알고리즘이나 전략을 캡슐화하고, Java에서 상태 패턴을 사용하여 상태 전환을 제어할 수 있습니다. 원래 "패턴상태".ru 소스

상태 - 다음에 따라 다른 기능을 정의하는 객체의 동작 패턴 내부 상태물체. 사이트 사이트 원본 소스

조건, 작업, 약속

개체가 내부 상태에 따라 동작을 변경할 수 있도록 합니다. 행위는 제한 없이 완전히 임의적으로 변경될 수 있기 때문에 외부에서 객체의 클래스가 변경된 것처럼 보입니다.

동기 부여

수업을 고려하다 TCP연결, 제시 네트워크 연결. 이 클래스의 개체는 여러 상태 중 하나일 수 있습니다. 확립된(설치) 청취(청취), 닫은(닫은). 때 개체 TCP연결다른 객체로부터 요청을 받은 후 현재 상태에 따라 다르게 응답합니다. 예를 들어 요청에 대한 응답 열려 있는(열림) 연결이 상태에 있는지 여부에 따라 다릅니다. 닫은또는 확립된. 상태 패턴은 객체가 어떻게 TCP연결다른 상태에 있을 때 다르게 동작할 수 있습니다. 소스.루

이 패턴의 주요 아이디어는 추상 클래스를 도입하는 것입니다. TCP 상태다른 연결 상태를 나타냅니다. 이 클래스는 다양한 작업 소스를 설명하는 모든 클래스에 공통적인 인터페이스를 선언합니다.ru

상태. 이러한 하위 클래스에서 TCP 상태상태별 동작이 구현됩니다. 예를 들어 수업에서 TCP설정됨그리고 TCP닫힘상태별 동작 구현 확립된그리고 닫은각기. 사이트 사이트 원본 소스

사이트 원본 소스 사이트

수업 TCP연결상태 객체(하위 클래스의 인스턴스)를 저장합니다. TCP 상태) 연결의 현재 상태를 나타내고 모든 상태별 요청을 이 개체에 위임합니다. TCP연결자체 하위 클래스 인스턴스를 사용합니다. TCP 상태충분히 간단합니다: 통합 인터페이스의 메소드 호출 TCP 상태, 무엇에 따라 이 순간특정 하위 클래스가 저장됨 TCP 상태-a - 결과가 다릅니다. 실제로는 이 연결 상태와 관련된 작업이 수행됩니다. original.ru 소스

연결 상태가 변경될 때마다TCP연결상태 객체를 변경합니다. 예를 들어, 설정된 연결이 닫힐 때 TCP연결클래스의 인스턴스를 대체 TCP설정됨사례 TCP닫힘. 사이트 원본 소스 사이트

적용의 징후, 패턴의 사용 State(State)

다음과 같은 경우 상태 패턴을 사용합니다. 소스 original.ru
  1. 개체의 동작이 상태에 따라 달라지며 런타임에 변경되어야 하는 경우. .ru
  2. opcode에 다중 분기 작업이 있는 경우 조건문, 분기 선택은 상태에 따라 다릅니다. 일반적으로 이 경우 상태는 열거된 상수로 표시됩니다. 종종 동일한 조건문 구조가 여러 명령문에서 반복됩니다. 상태 패턴은 각 분기가 별도의 클래스에 배치되도록 제안합니다. 이를 통해 개체의 상태를 다른 개체와 독립적으로 변경할 수 있는 독립 개체로 취급할 수 있습니다. 소스 original.ru

해결책

사이트 소스 사이트 원본

original.ru

상태 패턴의 참가자

소스 original.ru
  1. 문맥(TCPConnection) - 컨텍스트.
    클라이언트에 대한 단일 인터페이스를 정의합니다.
    하위 클래스의 인스턴스를 저장합니다. 콘크리트 상태, 현재 상태를 정의합니다. original.ru
  2. 상태(TCPState) - 상태.
    컨텍스트의 특정 상태와 관련된 동작을 캡슐화하기 위한 인터페이스를 정의합니다. 소스 original.ru
  3. 서브클래스 콘크리트 상태(TCPEstablished, TCPListen, TCPClosed) - 특정 상태.
    각 하위 클래스는 일부 컨텍스트 상태와 관련된 동작을 구현합니다. 문맥. 사이트 사이트 원본 소스

State 패턴 사용 방식

수업 문맥현재 객체에 요청을 위임합니다. 콘크리트 상태. 사이트 사이트 원본 소스

컨텍스트는 객체에 대한 인수로 자신을 전달할 수 있습니다. 상태요청을 처리할 사람입니다. 이것은 상태 객체( 콘크리트 상태) 필요한 경우 컨텍스트에 액세스합니다. 사이트 원본 소스 사이트

문맥클라이언트를 위한 기본 인터페이스입니다. 클라이언트는 상태 개체로 컨텍스트를 구성할 수 있습니다. 상태(더 정확하게 콘크리트 상태). 컨텍스트가 구성되면 클라이언트는 더 이상 상태 개체와 직접 통신할 필요가 없습니다(공통 인터페이스를 통해서만 상태). 사이트 소스 사이트 원본

동시에, 문맥, 또는 하위 클래스 자체 콘크리트 상태상태 변화가 어떤 조건에서 어떤 순서로 발생하는지 결정할 수 있습니다. .ru 소스

State 패턴의 구현에 관한 질문

State 패턴의 구현에 관한 질문: original.ru 소스
  1. 상태 간의 전환을 결정하는 요소입니다.
    상태 패턴은 어떤 참가자가 상태 간의 전환을 위한 조건(기준)을 결정하는지에 대해 아무 것도 말하지 않습니다. 기준이 고정되면 클래스에서 직접 구현할 수 있습니다. 문맥. 그러나 일반적으로 보다 유연하고 올바른 접근클래스 자체의 하위 클래스를 허용하는 것입니다. 상태다음 상태와 전환 순간을 결정합니다. 이를 위해 수업에서 문맥개체에서 허용하는 인터페이스를 추가해야 합니다. 상태상태를 설정합니다.
    이 분산된 전환 논리는 수정 및 확장이 더 쉽습니다. 새 하위 클래스를 정의하기만 하면 됩니다. 상태. 탈중앙화의 단점은 각 하위 클래스가 상태(실제로 현재 상태를 전환할 수 있는) 다른 상태의 적어도 하나 이상의 하위 클래스를 "알고" 있어야 하며, 이는 하위 클래스 간에 구현 종속성을 도입합니다. 소스.루

    사이트 소스 사이트 원본
  2. 테이블 대안.
    상태 기반 코드를 구성하는 또 다른 방법이 있습니다. 이것이 유한 상태 기계의 원리입니다. 테이블을 사용하여 입력을 상태 전환에 매핑합니다. 도움을 받아 일부 입력 데이터가 도착할 때 이동하려는 상태를 결정할 수 있습니다. 본질적으로 그렇게 함으로써 조건부 코드를 테이블 조회로 대체합니다.
    자동 장치의 주요 장점은 규칙성입니다. 전환 기준을 변경하려면 코드가 아닌 데이터만 수정하면 됩니다. 그러나 단점도 있습니다.
    - 테이블 조회는 종종 함수 호출보다 덜 효율적입니다.
    - 통일된 표 형식으로 전환 논리를 제시하면 기준이 덜 명확하여 이해하기 더 어려워집니다.
    - 일반적으로 상태 전환에 수반되는 작업을 추가하는 것은 어렵습니다. 테이블 방식은 상태와 이들 사이의 전환을 고려하지만 각 상태 변경에 따라 임의의 계산이 수행될 수 있도록 확장해야 합니다.
    테이블 기반 상태 머신과 상태 패턴의 주요 차이점은 다음과 같이 요약할 수 있습니다. 상태 패턴은 상태별 동작을 모델링하는 반면 표 방식상태 간 전환의 정의를 강조합니다. original.ru 소스

    source.ru 원본
  3. 상태 객체 생성 및 소멸.
    개발 과정에서 일반적으로 다음 중에서 선택해야 합니다.
    - state 객체가 필요할 때 생성하고, 사용 후 즉시 소멸,
    - 미리 그리고 영원히 창조하십시오.

    첫 번째 옵션은 시스템이 어떤 상태에 빠질지 미리 알 수 없고 컨텍스트가 상태를 비교적 드물게 변경하는 경우에 선호됩니다. 그렇게 함으로써 우리는 절대 사용되지 않을 객체를 생성하지 않습니다. 이는 많은 정보가 상태 객체에 저장되어 있는 경우에 필수적입니다. 상태 변경이 자주 발생하여 이를 나타내는 객체를 파괴하고 싶지 않다면(곧 다시 필요할 수 있기 때문에) 두 번째 접근 방식을 사용해야 합니다. 시간은 처음에 한 번만 개체를 ​​만드는 데 소비되고 파괴에는 전혀 소비되지 않습니다. 사실, 이 접근 방식은 시스템이 이론적으로 속할 수 있는 모든 상태에 대한 참조를 컨텍스트에 저장해야 하기 때문에 불편할 수 있습니다. source.ru 원본

    사이트 소스 사이트 원본
  4. 동적 변경 사용.
    런타임에 개체의 클래스를 변경하여 요청 시 동작을 변경할 수 있지만 이는 대부분의 개체 지향 언어에서 지원되지 않습니다. 예외는 이러한 메커니즘을 제공하므로 패턴 상태를 직접 유지 관리하는 Perl, JavaScript 및 기타 스크립트 기반 언어입니다. 이를 통해 객체는 클래스 코드를 변경하여 동작을 변경할 수 있습니다. source.ru 원본

    원본 소스.ru

결과

사용 결과 패턴 상태: original.ru 소스
  1. 상태별 동작을 현지화합니다.
    그리고 상태에 해당하는 부분으로 나눕니다. 상태 패턴은 특정 상태와 관련된 모든 동작을 별도의 개체. 상태 종속 코드는 완전히 클래스의 하위 클래스 중 하나에 있기 때문에 상태, 그런 다음 새 하위 클래스를 생성하여 간단히 새 상태와 전환을 추가할 수 있습니다.
    대신 데이터 멤버를 사용하여 내부 상태를 정의한 다음 개체의 작업을 정의할 수 있습니다. 문맥이 데이터를 확인하십시오. 그러나 이 경우 유사한 조건문 또는 분기문이 클래스 코드 전체에 흩어져 있습니다. 문맥. 동시에 새 상태를 추가하려면 여러 작업을 변경해야 하므로 유지 관리가 어렵습니다. 상태 패턴은 이 문제를 해결하지만 다른 상태에 대한 동작이 여러 하위 클래스에 분산되어 있기 때문에 또 다른 문제도 생성합니다. 상태. 이렇게 하면 클래스 수가 늘어납니다. 물론 한 클래스가 더 간결하지만 상태가 많은 경우 이러한 분포가 더 효율적입니다. 그렇지 않으면 성가신 조건문을 처리해야 하기 때문입니다.
    번거로운 조건문의 존재와 긴 절차의 존재는 바람직하지 않습니다. 그것들은 너무 모놀리식이어서 코드 수정과 확장이 문제가 됩니다. 상태 패턴은 상태 종속 코드를 구조화하는 더 나은 방법을 제공합니다. 상태 전환을 설명하는 논리가 더 이상 모놀리식 문으로 래핑되지 않습니다. 만약에또는 스위치, 그러나 하위 클래스에 분산됨 상태. 각 전환 및 작업을 클래스로 캡슐화하여 상태가 본격적인 개체가 됩니다. 이렇게 하면 코드의 구조가 개선되고 목적이 명확해집니다. 소스 original.ru
  2. 상태 간의 전환을 명시적으로 만듭니다.
    객체가 내부 데이터의 관점에서만 현재 상태를 정의하는 경우 상태 전환에는 명시적인 표현이 없습니다. 일부 변수에 대한 할당으로만 나타납니다. 다른 상태에 대해 별도의 개체를 입력하면 전환이 더 명확해집니다. 또한, 개체 상태컨텍스트를 보호할 수 있습니다 문맥컨텍스트의 관점에서 전환이 원자적 동작이기 때문에 내부 변수의 불일치에서. 전환을 구현하려면 하나의 변수(객체 변수 상태클래스 문맥), 일부가 아닙니다. original.ru 소스
  3. 상태 개체를 공유할 수 있습니다.
    상태 객체에 있는 경우 상태인스턴스 변수가 없습니다. 즉, 그것이 나타내는 상태는 유형 자체에 의해서만 인코딩됩니다. 그러면 다른 컨텍스트가 동일한 객체를 공유할 수 있습니다. 상태. 이러한 방식으로 상태가 분리되면 내부 상태가 없고 동작만 있는 본질적으로 플라이웨이트(플라이웨이트 패턴 참조)입니다. 사이트 소스 원본 사이트

예시

" " 섹션의 예제 구현을 고려하십시오. 몇 가지 간단한 TCP 연결 아키텍처를 구축합니다. 이것은 TCP 프로토콜의 단순화된 버전입니다. 물론 전체 프로토콜과 TCP 연결의 모든 상태를 나타내지는 않습니다. 사이트 원본 사이트 소스

먼저 클래스를 정의하자. TCP연결데이터 전송을 위한 인터페이스를 제공하고 상태 변경 요청을 처리하는 TCPConnection . source.ru 원본

멤버 변수에서 상태수업 TCP연결클래스의 인스턴스가 저장됨 TCP 상태. 이 클래스는 클래스에 정의된 상태 변경 인터페이스를 복제합니다. TCP연결. 사이트 소스 원본 사이트

출처 original.ru

TCP연결모든 상태 종속 요청을 상태 저장 인스턴스에 위임합니다. TCP 상태. 게다가 수업시간에 TCP연결수술이 있다 상태 변경, 다른 객체에 대한 포인터를 이 변수에 쓰는 데 사용할 수 있습니다. TCP 상태. 클래스 생성자 TCP연결초기화 상태닫기 상태에 대한 포인터 TCP닫힘(아래에서 정의하겠습니다). 소스.루

사이트 소스 원본 사이트

모든 작업 TCP 상태예를 들어 TCP연결매개 변수로 사용하여 개체를 허용합니다. TCP 상태개체 데이터에 액세스 TCP연결연결 상태를 변경합니다. .ru

클래스 TCP 상태위임된 모든 요청에 ​​대해 기본 동작을 구현했습니다. 또한 객체의 상태를 변경할 수 있습니다. TCP연결수술을 통해 상태 변경. TCP 상태와 같은 패키지에 위치 TCP연결, 따라서 이 작업에도 액세스할 수 있습니다. TCPState . 사이트 사이트 원본 소스

소스.루

하위 클래스에서 TCP 상태상태 종속 동작이 구현되었습니다. TCP 연결은 여러 상태일 수 있습니다. 확립된(설치) 청취(청취), 닫은(닫힘) 등이며 각각에는 자체 하위 클래스가 있습니다. TCP 상태. 단순화를 위해 3개의 하위 클래스만 자세히 고려할 것입니다. TCP설정됨, TCP듣기그리고 TCP닫힘. 사이트 사이트 소스 원본

원본 소스.ru

하위 클래스에서 TCP 상태해당 상태에서 유효한 요청에 대해 상태 종속 동작을 구현합니다. original.ru 소스

사이트 원본 사이트 소스

상태별 작업을 수행한 후 이러한 작업은 사이트 원본 소스 사이트

원인 상태 변경객체의 상태를 변경하려면 TCP연결. 그 자신은 TCP 프로토콜에 대한 정보가 없습니다. 서브클래스입니다 TCP 상태프로토콜이 지시하는 상태 전환 및 작업을 정의합니다. 사이트 사이트 원본 소스

ko 원본

상태 패턴의 주목할만한 용도

Ralph Johnson과 Jonathan Zweig는 상태 패턴을 특성화하고 TCP 프로토콜과 관련하여 설명합니다.
가장 인기 있는 대화식 그리기 프로그램은 직접 조작 작업을 수행하기 위한 "도구"를 제공합니다. 예를 들어, 선 그리기 도구를 사용하면 사용자가 마우스로 임의의 점을 클릭한 다음 마우스를 끌어 해당 점에서 선을 그릴 수 있습니다. 선택 도구를 사용하면 일부 모양을 선택할 수 있습니다. 일반적으로 사용 가능한 모든 도구는 팔레트에 배치됩니다. 사용자의 작업은 도구를 선택하고 적용하는 것이지만 도구를 변경할 때 편집기의 실제 동작은 다릅니다. 그리기 도구로 모양을 만들고 선택 도구로 선택하는 식입니다. original.ru 소스

현재 도구에 대한 편집기 동작의 종속성을 반영하기 위해 상태 패턴을 사용할 수 있습니다. 사이트 사이트 원본 소스

추상 클래스를 정의할 수 있습니다. 도구하위 클래스가 도구별 동작을 구현하는 . 그래픽 편집기는 현재 개체에 대한 링크를 유지합니다. 들어오는 요청을 위임합니다. 도구를 선택하면 편집기에서 다른 개체를 사용하므로 동작이 변경됩니다. 소스.루

이 기술은 프레임에 사용됩니다. 그래픽 편집기핫드로와 유니드로. 이를 통해 고객은 새로운 종류의 도구를 쉽게 정의할 수 있습니다. 에 핫드로수업 드로잉 컨트롤러요청을 현재 객체로 리디렉션 도구. 에 유니드로해당 클래스가 호출됩니다. 뷰어그리고 도구. 아래의 클래스 다이어그램은 클래스 인터페이스의 도식적 표현입니다. 도구

사이트 소스 사이트 원본

상태 패턴의 목적

  • State 패턴을 사용하면 객체가 내부 상태에 따라 동작을 변경할 수 있습니다. 개체의 클래스가 변경된 것 같습니다.
  • 상태 패턴은 상태 머신의 객체 지향 구현입니다.

해결 중인 문제

개체의 동작은 상태에 따라 다르며 프로그램 실행 중에 변경되어야 합니다. 이러한 체계는 일련의 조건문을 적용하여 구현할 수 있습니다. 개체의 현재 상태 분석을 기반으로 특정 작업이 수행됩니다. 그러나 언제 큰 숫자조건문은 코드 전체에 흩어져 있으며 이러한 프로그램은 유지 관리하기 어렵습니다.

상태 패턴 토론

State 패턴은 다음과 같은 방식으로 이 문제를 해결합니다.

  • 외부 세계에 대한 인터페이스를 정의하는 Context 클래스를 소개합니다.
  • 추상 클래스 State 를 소개합니다.
  • State 의 하위 클래스로 상태 머신의 다양한 "상태"를 나타냅니다.
  • Context 클래스에는 상태 머신의 상태가 변경될 때 변경되는 현재 상태에 대한 포인터가 있습니다.

상태 패턴은 새 상태로의 전환 조건이 정확히 정의되는 위치를 정의하지 않습니다. Context 클래스 또는 State 하위 클래스의 두 가지 옵션이 있습니다. 이점 마지막 버전새로운 파생 클래스를 쉽게 추가할 수 있습니다. 단점은 각 State 하위 클래스가 새 상태로 전환하기 위해 이웃에 대해 알아야 하므로 하위 클래스 간에 종속성이 도입된다는 것입니다.

또한 입력 데이터를 상태 전환에 고유하게 매핑하는 테이블 사용을 기반으로 하는 유한 상태 머신의 설계에 대한 대안적인 테이블 지향 접근 방식이 있습니다. 그러나 이 접근 방식은 전환이 실행될 때 액션 실행을 추가하기 어렵다는 단점이 있습니다. 상태 패턴 접근 방식은 데이터 구조 대신 코드를 사용하여 상태 전환을 수행하므로 이러한 작업을 쉽게 추가할 수 있습니다.

상태 패턴의 구조

Context 클래스는 클라이언트에 대한 외부 인터페이스를 정의하고 내부적으로 State 개체의 현재 상태에 대한 참조를 저장합니다. 추상 기본 클래스 State의 인터페이스는 하나를 제외하고 Context 인터페이스와 동일합니다. 추가 매개변수- Context 인스턴스에 대한 포인터. State에서 파생된 클래스는 특정 상태에 특정한 동작을 정의합니다. Context 래퍼 클래스는 수신된 모든 요청을 "현재 상태" 개체에 위임하며, 이 개체는 수신된 추가 매개변수를 사용하여 Context 인스턴스에 액세스할 수 있습니다.

State 패턴을 사용하면 객체가 내부 상태에 따라 동작을 변경할 수 있습니다. 자판기의 작동에서도 비슷한 그림을 볼 수 있습니다. 기계는 상품의 가용성, 받은 코인의 양, 환전 가능성 등에 따라 다른 상태를 가질 수 있습니다. 구매자가 상품을 선택하고 지불한 후 다음 상황(상태)이 가능합니다.

  • 구매자에게 상품을 발행할 필요가 없으며 변경을 발행할 필요가 없습니다.
  • 구매자에게 상품을 주고 변경합니다.
  • 구매자는 충분한 금액의 부족으로 인해 상품을 받지 못할 것입니다.
  • 구매자는 부재로 인해 상품을 받지 못합니다.

상태 패턴 사용

  • 클라이언트에서 "상태 머신"으로 사용할 기존 "래퍼" 컨텍스트 클래스를 정의하거나 새 "래퍼" 컨텍스트 클래스를 정의합니다.
  • Context 클래스의 인터페이스를 모방하는 기본 State 클래스를 만듭니다. 각 메소드는 Context 클래스의 인스턴스인 하나의 추가 매개변수를 취합니다. State 클래스는 유용한 "기본" 동작을 정의할 수 있습니다.
  • 가능한 모든 상태에 대해 State에서 클래스를 파생합니다.
  • "래퍼" 클래스 컨텍스트에는 "현재 상태" 개체에 대한 참조가 있습니다.
  • Context 클래스는 클라이언트로부터 받은 모든 요청을 "현재 상태" 개체에 위임하는 한편 추가 매개변수로 Context 개체의 주소를 전달합니다.
  • 필요한 경우 이 주소를 사용하여 State 클래스의 메서드는 Context 클래스의 "현재 상태"를 변경할 수 있습니다.

상태 패턴의 특징

  • 상태 객체는 종종 외톨이입니다.
  • 플라이웨이트는 상태 개체를 공유할 수 있는 방법과 시기를 보여줍니다.
  • 인터프리터 패턴은 구문 분석할 때 상태를 사용하여 컨텍스트를 정의할 수 있습니다.
  • State 및 Bridge 패턴은 유사한 구조를 가지고 있지만 Bridge는 봉투 클래스("래퍼" 클래스의 유사체) 계층을 허용하지만 State는 허용하지 않습니다. 이러한 패턴은 유사한 구조를 갖지만 다른 용도로 사용됩니다. State는 객체가 내부 상태를 기반으로 동작을 변경할 수 있도록 하는 반면 Bridge는 추상화를 구현과 분리하여 독립적으로 변경할 수 있도록 합니다.
  • State 패턴의 구현은 Strategy 패턴을 기반으로 합니다. 차이점은 목적에 있습니다.

상태 패턴 구현

두 개의 가능한 상태와 두 개의 이벤트가 있는 상태 시스템의 예를 고려하십시오.

#포함 네임스페이스 std 사용 클래스 Machine ( 클래스 State *current; public: Machine(); void setCurrent(State *s) ( current = s; ) void on(); void off(); ); class State ( public: virtual void on(Machine *m) ( cout<< " already ON\n"; } virtual void off(Machine *m) { cout << " already OFF\n"; } }; void Machine::on() { current->이에); ) void Machine::off() ( current->off(this); ) 클래스 ON: public State ( public: ON() ( cout<< " ON-ctor "; }; ~ON() { cout << " dtor-ON\n"; }; void off(Machine *m); }; class OFF: public State { public: OFF() { cout << " OFF-ctor "; }; ~OFF() { cout << " dtor-OFF\n"; }; void on(Machine *m) { cout << " going from OFF to ON"; m->setCurrent(새로운 ON()); 이것을 삭제하십시오; ) ); 무효 ON::off(머신 *m) ( cout<< " going from ON to OFF"; m->setCurrent(새로운 OFF()); 이것을 삭제하십시오; ) Machine::Machine() ( 현재 = 새로운 OFF(); cout<< "\n"; } int main() { void(Machine:: *ptrs)() = { Machine::off, Machine::on }; Machine fsm; int num; while (1) { cout << "Enter 0/1: "; cin >>숫자; (fsm.*ptrs)(); ) )

행동 디자인 패턴. 프로그램을 실행하는 동안 개체가 상태에 따라 동작을 변경해야 하는 경우에 사용됩니다. 고전적인 구현은 모든 메소드와 가능한 각 상태에 대한 하나의 클래스를 포함하는 기본 추상 클래스 또는 인터페이스를 만드는 것을 포함합니다. 패턴은 "조건문을 다형성으로 교체" 조언의 특별한 경우입니다.

모든 것이 책에 따른 것처럼 보이지만 뉘앙스가 있습니다. 주어진 상태와 관련이 없는 메서드를 올바르게 구현하는 방법은 무엇입니까? 예를 들어, 빈 카트에서 항목을 제거하거나 빈 카트에 대한 비용을 지불하려면 어떻게 해야 합니까? 일반적으로 각 상태 클래스는 관련 메서드만 구현하고 그렇지 않으면 InvalidOperationException을 발생시킵니다.

사람을 Liskov로 대체하는 원칙 위반. Yaron Minsky는 다음과 같은 대안적 접근 방식을 제안했습니다. 불법 국가를 대표할 수 없도록 하다. 이를 통해 런타임에서 컴파일 시간으로 오류 검사를 이동할 수 있습니다. 그러나 이 경우 제어 흐름은 다형성을 사용하지 않고 패턴 일치를 기반으로 구성됩니다. 운 좋게, .

F# 주제에 대해 자세히 알아보기 불법 국가를 대표할 수 없도록 하다 Scott Vlashin의 웹사이트에 공개되었습니다.

바구니의 예에서 "상태"의 구현을 고려하십시오. C#에는 기본 제공 공용체 유형이 없습니다. 데이터와 동작을 분리합니다. enum을 사용하여 상태 자체를 코딩하고 별도의 클래스로 동작을 코딩합니다. 편의를 위해 열거형과 해당 동작 클래스, 기본 "상태" 클래스를 바인딩하는 속성을 선언하고 열거형에서 동작 클래스로 전달할 확장 메서드를 추가합니다.

하부 구조

public class StateAttribute: Attribute ( public Type StateType ( get; ) public StateAttribute(Type stateType) ( StateType = stateType ?? throw new ArgumentNullException(nameof(stateType)); ) ) public 추상 클래스 State 여기서 T: class ( protected State(T entity) ( Entity = entity ?? throw new ArgumentNullException(nameof(entity)); ) protected T Entity ( get; ) ) public static class StateCodeExtensions ( public static State 상태로 (이 Enum stateCode, 개체 엔터티) 여기서 T: class // yes, yes 반사가 느립니다. 컴파일 표현식 트리 // 또는 IL Emit으로 교체하면 빠릅니다. => (State ) Activator.CreateInstance(stateCode .GetType() .GetCustomAttribute ().StateType, 엔티티); )

대상 지역

"바구니" 엔터티를 선언해 보겠습니다.

공개 인터페이스 IHasState 여기서 TEntity: 클래스( TStateCode StateCode( get; ) 상태 상태( get; ) ) 공개 부분 클래스 장바구니: IHasState ( 공개 사용자 사용자 ( get; 보호 세트; ) 공개 CartStateCode StateCode ( 가져오기; 보호 세트; ) 공개 상태 상태 => StateCode.ToState (이것); 공개 10진수 합계( get, 보호된 집합, ) 보호된 가상 ICollection 제품( get; set; ) = 새 목록 (); // ORM 전용 Protected Cart() ( ) public Cart(User user) ( User = user ?? throw new ArgumentNullException(nameof(user)); StateCode = StateCode = CartStateCode.Empty; ) public Cart(사용자 사용자, IEnumerable Products) : this(user) ( StateCode = StateCode = CartStateCode.Empty; foreach (제품의 var product) ( Products.Add(product); ) ) public Cart(사용자 사용자, IEnumerable Products, decimal total) : this(user, products) ( if (total<= 0) { throw new ArgumentException(nameof(total)); } Total = total; } }
각 장바구니 상태에 대해 하나의 클래스(비어 있음, 활성 및 지불됨)를 구현해 보겠습니다. 그러나 공통 인터페이스는 선언하지 않습니다. 각 상태가 관련 동작만 구현하도록 합니다. 이것은 EmptyCartState , ActiveCartState 및 PaidCartState 클래스가 동일한 인터페이스를 구현할 수 없다는 것을 의미하지 않습니다. 가능하지만 이러한 인터페이스에는 각 상태에서 사용할 수 있는 메서드만 포함되어야 합니다. 우리의 경우 Add 메서드는 EmptyCartState 및 ActiveCartState에서 사용할 수 있으므로 추상 AddableCartStateBase에서 상속할 수 있습니다. 그러나 미지불 장바구니에만 제품을 추가할 수 있으므로 모든 주에 공통 인터페이스는 없습니다. 따라서 컴파일 단계에서 코드에 InvalidOperationException이 없음을 보장합니다.

Public 부분 클래스 Cart ( public enum CartStateCode: byte ( Empty, Active, Paid ) public interface IAddableCartState ( ActiveCartState Add(Product product); IEnumerable 제품 ( get; ) ) 공용 인터페이스 INotEmptyCartState ( IEnumerable 제품( get; ) 십진수 합계( get; ) 공용 추상 클래스 AddableCartState: 상태 , IAddableCartState ( 보호된 AddableCartState(카트 엔티티): base(entity) ( ) public ActiveCartState Add(제품 제품) ( Entity.Products.Add(product); Entity.StateCode = CartStateCode.Active; return (ActiveCartState)Entity.State; ) 공개 IEnumerable 제품 => Entity.Products; ) public class EmptyCartState: AddableCartState ( public EmptyCartState(Cart 엔터티): base(entity) ( ) ) public class ActiveCartState: AddableCartState, INotEmptyCartState ( public ActiveCartState(Cart 엔터티): base(entity) ( ) public PaidCartState Pay(decimal total) ( Entity.Total = total;Entity.StateCode = CartStateCode.Paid;return (PaidCartState)Entity.State; ) 공개 상태 Remove(제품 제품) ( Entity.Products.Remove(product); if(!Entity.Products.Any()) ( Entity.StateCode = CartStateCode.Empty; ) 반환 Entity.State; ) public EmptyCartState Clear() ( Entity. Products.Clear(), Entity.StateCode = CartStateCode.Empty, 반환(EmptyCartState)Entity.State ) 공개 십진수 합계 => Products.Sum(x => x.Price), ) 공개 클래스 PaidCartState: 상태 , INotEmptyCartState( 공개 IEnumerable 제품 => Entity.Products; 공개 십진수 합계 => Entity.Total; public PaidCartState(장바구니 엔티티) : base(entity) ( ) ) )
상태는 중첩으로 선언됩니다( 중첩) 수업은 우연이 아닙니다. 중첩 클래스는 Cart 클래스의 보호된 멤버에 액세스할 수 있습니다. 즉, 동작을 구현하기 위해 엔터티 캡슐화를 희생할 필요가 없습니다. 엔티티 클래스 파일을 어지럽히지 않기 위해 선언을 Cart.cs와 CartStates.cs로 나누었습니다. 예어부분적인.

Public ActionResult GetViewResult(상태 cartState) ( switch (cartState) ( 케이스 Cart.ActiveCartState 활성 상태: return View("Active", activeState); 케이스 Cart.EmptyCartState emptyState: return View("Empty", emptyState); 케이스 Cart.PaidCartState payCartState: return View(" 유료", payCartState); 기본값: 새로운 InvalidOperationException(); ) )
카트의 상태에 따라 다른 보기를 사용합니다. 빈 카트의 경우 "카트가 비어 있습니다"라는 메시지가 표시됩니다. 활성 장바구니에는 제품 목록, 제품 수를 변경하고 일부를 제거하는 기능, "체크아웃" 버튼 및 구매 총액이 포함됩니다.

유료 장바구니는 활성 장바구니와 동일하게 보이지만 아무 것도 편집할 수 없습니다. 이 사실은 INotEmptyCartState 인터페이스를 강조 표시하여 확인할 수 있습니다. 따라서 Liskov 치환 원칙 위반을 제거했을 뿐만 아니라 인터페이스 분리 원칙을 적용했습니다.

결론

애플리케이션 코드에서 IAddableCartState 및 INotEmptyCartState 인터페이스 참조에 대해 작업하여 장바구니에 항목을 추가하고 장바구니에 항목을 표시하는 코드를 재사용할 수 있습니다. 패턴 매칭은 타입간에 공통점이 없을때만 C#의 제어 흐름에 적합하다고 생각합니다. 다른 경우에는 기본 링크에서 작업하는 것이 더 편리합니다. 엔터티의 동작을 인코딩할 때뿐만 아니라 .

개체가 내부 상태에 따라 동작을 변경할 수 있도록 합니다. 외부에서 개체의 클래스가 변경된 것처럼 보입니다.

"상태" 패턴에는 모든 유효한 작업에 대한 기본 클래스 또는 인터페이스 할당과 각 가능한 상태에 대한 상속인이 포함됩니다.

상태 패턴을 사용하는 경우

    객체의 동작이 해당 상태에 의존해야 하고 런타임에 동적으로 변경될 수 있는 경우

    여러 조건 구문이 객체 메소드의 코드에서 사용될 때, 그 선택은 객체의 현재 상태에 따라 다릅니다.

"상태" 패턴의 UML 다이어그램:

C#에서 상태 패턴 구현

시스템 사용; 네임스페이스 DoFactory.GangOfFour.State.Structural( /// /// Structural용 MainApp 시작 클래스 /// 상태 디자인 패턴. ///클래스 MainApp( /// /// 콘솔 애플리케이션의 진입점. /// static void Main() ( // 상태의 컨텍스트 설정 Context c = new Context(new ConcreteStateA()); // 상태를 전환하는 요청 발행 c.Request(); c.Request(); c.Request() ;c.Request(); // 사용자를 기다립니다. Console.ReadKey(); ) ) /// /// "상태" 추상 클래스 ///추상 클래스 상태 ( public abstract void Handle(Context context); ) /// class ConcreteStateA: State ( public override void Handle(Context context) ( context.State = new ConcreteStateB(); ) ) /// /// "ConcreteState" 클래스 /// class ConcreteStateB: State ( public override void Handle(Context context) ( context.State = new ConcreteStateA(); ) ) /// /// "컨텍스트" 클래스 /// class Context ( private State _state; // 생성자 public Context(State state) ( this.State = state; ) // 상태를 가져오거나 설정합니다. public State State ( get ( return _state; ) set ( _state = value; Console.WriteLine ("상태: " + _state.GetType().Name); ) ) 공개 무효 Request() ( _state.Handle(this); ) ) )

실생활의 State 패턴의 예

.NET Framework의 예

  • CommunicationObject 구현 상태 머신 WCF 클라이언트 상태 간 전환: Created, Opening, Opened, Closing, Closed 및 Faulted.
  • 작업은 작업 상태(Created, WaitingForActivation, WaitingToRun, Running, Run ToCompletion, Canceled, Faulted) 간의 전환을 위한 상태 시스템을 구현합니다.