폐기 패턴

Dispose pattern

객체 지향 프로그래밍에서 폐기 패턴은 리소스 관리를 위한 설계 패턴입니다.이 패턴에서 리소스객체에 의해 유지되며 통상적인 메서드를 호출하여 해방됩니다.close,dispose,free,release언어에 따라 달라집니다.이것에 의해, 오브젝트가 보관 유지되고 있는 모든 리소스가 해방됩니다.많은 언어는 일반적인 상황에서 폐기 메서드를 명시적으로 호출할 필요가 없도록 언어 구성을 제공합니다.

폐기 패턴은 런타임 환경자동 가비지 수집이 있는 언어에서 주로 사용됩니다(아래 동기 참조).

동기

리소스를 개체로 래핑하는 중

개체로 리소스를 래핑하는 것은 개체 지향 캡슐화 형식이며 폐기 패턴의 기반이 됩니다.

리소스는 일반적으로 핸들(추상 참조), 구체적으로는 정수(정수)로 나타나며 리소스를 제공하는 외부 시스템과의 통신에 사용됩니다.예를 들어, 파일은 운영 체제(특히 파일 시스템)에 의해 제공되며, 많은 시스템에서는 열린 파일을 파일 기술자(파일을 나타내는 정수)로 나타냅니다.

이러한 핸들은 값을 변수에 저장하고 리소스를 사용하는 함수에 인수로 전달함으로써 직접 사용할 수 있습니다.단, 핸들 자체에서 추상화(예를 들어 운영체제에 따라 파일이 다르게 표시되는 경우)하고 핸들과 함께 추가 보조 데이터를 저장함으로써 핸들이 다른 데이터와 함께 레코드에 필드로 저장될 수 있습니다. 불투명한 데이터 유형일 경우 정보 숨김과사용자는 실제 표현에서 추상화됩니다.

예를 들어, C 파일 입출력에서는 파일은 의 오브젝트로 표시됩니다.FILEtype(혼란스럽게 "파일 핸들"이라고 함): 이것은 언어 수준의 추상화입니다.이러한 추상화는 I/O 모드(읽기, 쓰기) 및 스트림의 위치와 같은 보조 정보와 함께 파일(파일 기술자 등)에 대한 핸들을 저장합니다.이러한 오브젝트는 콜에 의해 작성됩니다.fopen(개체 지향 용어로 컨스트럭터) 자원을 취득하여 포인터를 반환한다.리소스는 호출에 의해 해방된다.fclose에 대한 포인터로FILE오브젝트[1]코드:

파일 *f = 열리다(파일명, 모드); // f를 사용하여 작업을 수행합니다. fclose(f); 

주의:fclose를 가진 함수입니다.FILE *파라미터를 지정합니다.객체 지향 프로그래밍에서는 Python에서와 같이 파일 객체의 인스턴스 메서드입니다.

f = 열다.(파일명) # f로 뭔가를 해라. f.가까운.() 

이는 정확히 폐기 패턴이며 기존의 파일 열기 및 닫기와는 구문 및 코드[a] 구조만 다릅니다.다른 리소스는 정확히 동일한 방법으로 관리할 수 있습니다. 즉, 컨스트럭터 또는 팩토리에서 구입하여 명시적인 사용자가 배포하는 것입니다.close또는dispose방법.

신속한 릴리스

자원 해방은 자원 비용이 많이 들기 때문에(예를 들어 열려 있는 파일 수에 제한이 있을 수 있음) 즉시 해방되어야 한다는 근본적인 문제입니다.또한 모든 데이터가 실제로 기록되도록 버퍼를 플러싱하는 등의 I/O에 대해 일부 마무리 작업이 필요할 수 있습니다.

리소스가 무제한 또는 사실상 무제한인 경우 리소스를 명시적으로 종료할 필요가 없습니다.또한 실제로 단명 프로그램에서는 리소스를 명시적으로 해방하지 않는 경우가 많습니다.단시간 실행으로 인해 리소스를 소진할 가능성은 거의 없으며 런타임 시스템 또는 운영체제에 의존하여 최종화를 수행합니다.

단, 일반적으로 자원을 관리해야 합니다(특히 장수하는 프로그램, 많은 자원을 사용하는 프로그램, 또는 데이터의 기입이 확실히 이루어지도록 안전을 위해서).명시적 폐기란 자원의 최종화와 공개가 결정적이고 신속하게 이루어짐을 의미합니다.dispose이 작업이 완료될 때까지 메서드는 완료되지 않습니다.

명시적 폐기를 요구하는 대신 리소스 관리를 개체의 라이프타임에 연결하는 방법이 있습니다. 즉, 개체 생성 중에 리소스가 취득되고 개체 파괴 중에 리소스가 해방됩니다.이 어프로치는, 자원 취득의 초기화(RAII) 어법으로 알려져, 메모리 관리의 결정성이 있는 언어(C++ 등)로 사용됩니다.이 경우 위의 예에서는 파일개체가 생성되고 변수의 범위가 지정되면 리소스가 취득됩니다.f종료됩니다.f참조가 파기되고 그 일부로서 리소스가 해방됩니다.

RAII는 오브젝트의 라이프 타임이 결정적이라는 점에 의존합니다.그러나 자동 메모리 관리에서는 오브젝트의 라이프 타임이 프로그래머의 관심사가 아닙니다.오브젝트가 사용되지 않게 된 후 어느 시점에서 오브젝트가 파괴되지만 추상화됩니다.실제로, 수명은 종종 결정론적이지 않지만, 특히 기준 계수를 사용하는 경우 그렇다.실제로 오브젝트가 완성된다는 보장이 없는 경우도 있습니다.프로그램이 종료되었을 때 오브젝트가 완성되지 않고 운영체제가 메모리를 회수할 수 있도록 합니다.완료(버퍼 플래시 등)가 필요한 경우 데이터 손실이 발생할 수 있습니다.

따라서 자원 관리와 오브젝트 라이프 타임을 결합하지 않음으로써 폐기 패턴은 메모리 관리를 위한 구현 유연성을 제공하면서 리소스를 신속하게 해방할 수 있습니다.이로 인해 리소스를 수동으로 관리해야 하므로 지루하고 오류가 발생하기 쉽습니다.

조기 종료

폐기 패턴의 주요 문제는 다음과 같습니다.dispose메서드가 호출되지 않고 리소스가 누출됩니다.이 문제의 일반적인 원인은 조기 복귀 또는 예외로 인해 기능에서 조기 종료하는 것입니다.

예를 들어 다음과 같습니다.

방어하다 기능하다(파일명):     f = 열다.(파일명)     한다면 a:         돌아가다 x     f.가까운.()     돌아가다 y 

함수가 처음 반환될 때 반환될 경우 파일은 닫히지 않고 리소스가 누출됩니다.

방어하다 기능하다(파일명):     f = 열다.(파일명)     g(f)  # f로 예외를 일으킬 수 있는 작업을 수행합니다.     f.가까운.() 

간섭하는 코드가 예외를 발생시키면 함수는 조기에 종료되고 파일은 닫히지 않으므로 리소스가 누출됩니다.

이 두 가지를 모두 처리할 수 있습니다.try...finallyconstruct는 final 구가 항상 종료 시 실행되도록 합니다.

방어하다 기능하다(파일명):     해라:         f = 열다.(파일명)         # 어떻게 좀 해봐     마침내.:         f.가까운.() 

보다 일반적인 방법:

자원 자원 = 자원 취득(); 해라 {     // 자원을 취득했습니다.리소스로 액션을 수행합니다.     ... } 마침내. {     // 예외가 발생한 경우에도 리소스를 해제합니다.     자원.처분하다(); } 

try...finally적절한 예외 안전성을 위해 건설이 필요하다.finallyblock은 예외 발생 여부에 관계없이 청소 로직을 실행할 수 있도록 합니다.try차단합니다.

이 접근법의 한 가지 단점은 프로그래머가 명시적으로 정리 코드를 추가할 필요가 있다는 것입니다.finally차단. 코드 크기가 커지며, 그렇지 않으면 프로그램에서 리소스가 누출됩니다.

언어 구성

폐기 패턴을 안전하게 사용하기 위해 여러 언어에는 동일한 코드 블록에 저장 및 릴리스된 리소스에 대한 일종의 지원이 포함되어 있습니다.

C# 언어에는 다음과 같은 기능이 있습니다.using[2] 자동으로 호출하는 스테이트먼트Dispose실장하는 오브젝트에 대한 메서드IDisposable 인터페이스:

사용. (자원 자원 = 자원 취득()) {     // 리소스로 작업을 수행합니다.     ... } 

이는 다음과 같습니다.

자원 자원 = 자원 취득() 해라  {     // 리소스로 작업을 수행합니다.     ... } 마침내.  {     // 리소스가 취득되지 않았거나 이미 해방되었을 수 있습니다.     한다면 (자원 != 무효)          ((아이포지터블)자원).폐기하다();  } 

마찬가지로 Python 언어에는with콘텍스트 매니저 오브젝트와 같은 효과를 얻을 수 있는 스테이트먼트입니다.콘텍스트 매니저 프로토콜을 사용하려면__enter__그리고.__exit__에 의해 자동으로 호출되는 메서드withstatement constructure를 사용하여 발생 가능한 코드의 중복을 방지하기 위해try/finally패턴입니다.[3]

와 함께 resource_manager() ~하듯이 자원:     # 리소스로 액션을 수행합니다.     ... # 자원 할당 해제가 보증되는 다른 액션을 수행합니다. ... 

자바어에는 새로운 구문이 도입되었다.try- Java 버전7의 [4]리소스 포함.AutoCloseable 인터페이스를 구현하는 객체(메서드 close()를 정의함)에서 사용할 수 있습니다.

해라 (출력 스트림 x = 신규 출력 스트림(...)) {     // x로 작업합니다. } 또 만나 (IOException(IOException) ex) {     // 예외 처리    // 리소스 x가 자동으로 닫힙니다. } // 시험해 보다 

문제

반환 및 예외 발생 시 올바른 리소스 관리 및 힙 기반 리소스 관리(개체가 생성된 곳과 다른 범위로 표시됨)라는 주요 문제 외에도 폐기 패턴과 관련된 많은 복잡성이 있습니다.이러한 문제는 RAII에 의해 대부분 회피된다.그러나 일반적으로 단순하게 사용할 경우 이러한 복잡성은 발생하지 않습니다. 즉, 단일 리소스를 획득하여 작업을 수행하고 자동으로 해제하는 것입니다.

근본적인 문제는 리소스를 보유하는 것이 더 이상 클래스 불변성이 아니라는 것입니다(리소스는 오브젝트 작성부터 폐기될 때까지 유지되지만 이 시점에서는 오브젝트가 리소스를 사용하려고 할 때(예를 들어 닫힌 파일에서 읽으려고 할 때) 리소스를 사용할 수 없을 수 있습니다).즉, 리소스를 사용하는 개체의 모든 메서드는 일반적으로 오류를 반환하거나 예외를 발생시킴으로써 실패할 수 있습니다.일반적으로 리소스 사용도 다른 이유(예를 들어 파일 끝을 지나 읽으려고 시도하는 경우)로 인해 실패할 수 있으므로 실제로 이는 사소한 것입니다. 따라서 이러한 방법은 이미 실패할 수 있으며 리소스가 없으면 또 다른 실패가 발생할 수 있습니다.이를 구현하기 위한 표준 방법은 오브젝트에 부울 필드를 추가하는 것입니다.disposed에 의해 true로 설정됩니다.disposeguard 구에 의해 체크되어 (리소스를 사용하는) 모든 메서드에 대해 예외가 발생합니다(예:ObjectDisposedException.NET)에서 개체를 삭제했는지 확인합니다.[5]

또, 전화도 가능합니다.dispose두 번 이상 어떤 물건에 대해.이는 프로그래밍 오류를 나타낼 수 있지만(리소스를 보유하는 각 오브젝트는 정확히 한 번 폐기해야 함), 이는 보다 단순하고 강력하며, 따라서 일반적으로는 다음과 같은 경우에 적합합니다.disposeidempotent('복수 호출은 [5]1회 호출과 같다'는 의미)입니다.이는 동일한 부울을 사용함으로써 쉽게 구현할 수 있습니다.disposed필드 및 시작 시 가드 절에서 체크합니다.dispose이 경우 예외를 [5]발생시키지 않고 즉시 반환됩니다.Java는 일회용 유형(AutoCloseable을 구현하는 유형)과 폐기 가능성이 있는 일회용 유형(하위 유형 Closeable)을 구분합니다.

자원을 보유하는 오브젝트의 상속 및 구성이 존재하는 경우의 폐기에는 파괴/최종화와 유사한 문제가 있습니다(파괴자 또는 최종자를 사용).또, 통상, 폐기 패턴은 이것에 관한 언어 서포트를 가지지 않기 때문에, 보일러 플레이트 코드가 필요하다.첫째, 파생 클래스가 priority의dispose기본 클래스의 메서드는 일반적으로 파생 클래스의 덮어쓰기 메서드를 호출해야 합니다.dispose기본 클래스의 메서드를 사용하여 기본에 저장된 리소스를 적절히 해방합니다.둘째, 객체가 자원을 보유하는 다른 객체와 "a" 관계를 갖는 경우(즉, 객체가 자원을 직접 사용하는 다른 객체를 통해 간접적으로 자원을 사용하는 경우) 간접적으로 사용하는 객체는 처분할 수 있어야 합니까?이는 관계가 소유(개체 구성)인지 표시(개체 집계)인지 또는 통신(어소시에이션)인지에 대응하며, 양쪽 규칙(간접 사용자가 리소스를 책임지거나 책임지지 않음)이 발견됩니다.간접 사용이 리소스에 대한 책임이 있는 경우 리소스를 처분할 수 있어야 하며, 처분 시 소유 개체를 처분해야 합니다(소유 개체를 파기하거나 완료하는 것과 유사함).

컴포지션(소유)은 캡슐화(사용되는 오브젝트만 추적 필요)를 제공하지만 오브젝트 간에 더 많은 관계가 있는 경우에는 상당히 복잡하지만 집약(표시)은 캡슐화가 부족하기 때문에 상당히 단순해집니다..NET에서는 자원의 다이렉트 유저에게만 책임을 지도록 하고 있습니다.「유형의 관리 대상외의 자원을 직접 사용하는 경우에만 [6]IDisposable을 실장할 필요가 있습니다.자세한 내용은 리소스 관리를 참조하십시오.

「 」를 참조해 주세요.

메모들

  1. ^ 클래스 기반 프로그래밍에서 메서드는 암묵을 사용하여 클래스에 정의됩니다.this또는self매개 변수를 명시적으로 사용하는 함수가 아니라 매개 변수입니다.

레퍼런스

  1. ^ stdio.h – 기본 정의 레퍼런스, 단일 UNIX 사양, 오픈 그룹의 제7호
  2. ^ Microsoft MSDN: 스테이트먼트 사용(C# 레퍼런스)
  3. ^ Guido van Rossum, Nick Coghlan (13 June 2011). "PEP 343: The "with" Statement". Python Software Foundation.
  4. ^ Oracle Java 튜토리얼:자원을 사용한 시행문
  5. ^ a b c "Dispose Pattern".
  6. ^ "IDisposable Interface". Retrieved 2016-04-03.

추가 정보