Dispose pattern
In object-oriented programming, the dispose pattern is a design pattern for resource management. In this pattern, a resource is held by an object, and released by calling a conventional method – usually called
close
, dispose
, free
, release
depending on the language – which releases any resources the object is holding onto. Many languages offer language constructs to avoid having to call the dispose method explicitly in common situations.The dispose pattern is primarily used in languages whose runtime environment have automatic garbage collection.
Motivation
Wrapping resources in objects
Wrapping resources in objects is the object-oriented form of encapsulation, and underlies the dispose pattern.Resources are typically represented by handles, concretely usually integers, which are used to communicate with an external system that provides the resource. For example, files are provided by the operating system, which in many systems represents open files with a file descriptor.
These handles can be used directly, by storing the value in a variable and passing it as an argument to functions that use the resource. However, it is frequently useful to abstract from the handle itself, and to store additional auxiliary data with the handle, so handles can be stored as a field in a record, along with other data; if this in an opaque data type, then this provides information hiding and the user is abstracted from the actual representation.
For example, in C file input/output, files are represented by objects of the
FILE
type, which stores an handle to the file, together with auxiliary information like I/O mode and position in the stream. These objects are created by calling fopen
, which acquires the resource and returns a pointer to it; the resource is released by calling fclose
on a pointer to the FILE
object. In code:FILE *f = fopen;
// Do something with f.
fclose;
Note that
fclose
is a function with a FILE *
parameter. In object-oriented programming, this is instead an instance method on a file object, as in Python:f = open
- Do something with f.
This is precisely the dispose pattern, and only differs in syntax and code structure from traditional file opening and closing. Other resources can be managed in exactly the same way: being acquired in a constructor or factory, and released by an explicit
close
or dispose
method.Prompt release
The fundamental problem that freeing resources aims to solve is that resources are expensive, and thus should be released promptly. Further, some finalization work is sometimes needed, particularly for I/O, such as flushing buffers to ensure that all data is actually written.If a resource is unlimited or effectively unlimited, and no explicit finalization is necessary, it is not important to release it, and in fact short-lived programs often do not explicitly release resources: due to short run time, they are unlikely to exhaust resources, and they rely on the runtime system or operating system to do any finalization.
However, in general resources must be managed. Explicit disposal means that resource finalization and release is deterministic and prompt: the
dispose
method does not complete until these are done.An alternative to requiring explicit disposal is to tie resource management to object lifetime: resources are acquired during object creation, and released during object destruction. This approach is known as the Resource Acquisition Is Initialization idiom, and is used in languages with deterministic memory management. In this case, in the example above, the resource is acquired when the file object is created, and when the scope of the variable
f
is exited, the file object that f
refers to is destroyed, and as part of this, the resource is released.RAII relies on object lifetime being deterministic; however, with automatic memory management, object lifetime is not a concern of the programmer: objects are destroyed at some point after they are no longer used, but when is abstracted. Indeed, lifetime is often not deterministic, though it may be, notably if reference counting is used. Indeed, in some cases there is no guarantee that objects will ever be finalized: when the program terminates, it may not finalize the objects, and instead just let the operating system reclaim memory; if finalization is required, data loss can occur.
Thus by not coupling resource management to object lifetime, the dispose pattern allows resources to be released promptly, while giving implementation flexibility for memory management. The cost of this is that resources must be managed manually, which can be tedious and error-prone.
Early exit
A key problem with the dispose pattern is that if thedispose
method is not called, the resource is leaked. A common cause of this is early exit from a function, due to an early return or exception.For example:
def func:
f = open
if a:
return x
f.close
return y
If the function returns at the first return, the file is never closed and the resource is leaked.
def func:
f = open
g # Do something with f that may raise an exception.
f.close
If the intervening code raises an exception, the function exits early and the file is never closed, so the resource is leaked.
Both of these can be handled by a
try...finally
construct, which ensures that the finally clause is always executed on exit:def func:
try:
f = open
# Do something.
finally:
f.close
More generically:
Resource resource = getResource;
try finally
The
try...finally
construct is necessary for proper exception safety, since the finally
block enables execution of cleanup logic regardless of if an exception is thrown or not in the try
block.One disadvantage of this approach is that it requires the programmer to explicitly add cleanup code in a
finally
block. This leads to code size bloat, and failure to do so will lead to resource leakage in the program.Language constructs
To make the safe use of the dispose pattern less verbose, several languages have some kind of built-in support for resources held and released in the same block of code.The C# language features the
using
statement that automatically calls the Dispose
method on an object that implements the IDisposable
interface:using )
which is equal to:
Resource resource = GetResource
try
finally
Similarly, the Python language has a
with
statement that can be used to similar effect with a context manager object. The context manager protocol requires implementing __enter__
and __exit__
methods which get automatically called by the with
statement construct, to prevent duplication of code that would otherwise occur with the try
/finally
pattern.with resource_context_manager as resource:
# Perform actions with the resource.
...
- Perform other actions where the resource is guaranteed to be deallocated.
The Java language introduced a new syntax called
try
-with-resources in Java version 7. It can be used on objects that implement the AutoCloseable interface ):try catch // try
Problems
Beyond the key problem of correct resource management in the presence of returns and exceptions, and heap-based resource management, there are many further complexities associated with the dispose pattern. These problems are largely avoided by RAII. However, in common simple use these complexities do not arise: acquire a single resource, do something with it, automatically release it.A fundamental problem is that having a resource is no longer a class invariant, so the resource may not be available when the object tries to use it, for example trying to read from a closed file. This means that all methods on the object that use the resource potentially fail, concretely usually by returning an error or raising an exception. In practice this is minor, as use of resources can usually fail for other reasons as well, so these methods already might fail, and not having a resource just adds another possible failure. A standard way to implement this is to add a boolean field to the object, called
disposed
, which is set to true by dispose
, and checked by a guard clause to all methods, raising an exception if the object has been disposed.Further, it is possible to call
dispose
on an object more than once. While this may indicate a programming error, it is simpler, more robust, and thus usually preferable for dispose
to be idempotent. This is easily implemented by using the same boolean disposed
field and checking it in a guard clause at the start of dispose
, in that case returning immediately, rather than raising an exception. Java distinguishes disposable types from disposable types where dispose is idempotent.Disposal in the presence of inheritance and composition of objects that hold resources have analogous problems to destruction/finalization. Further, since the dispose pattern usually does not have language support for this, boilerplate code is necessary. Firstly, if a derived class overrides a
dispose
method in the base class, the overriding method in the derived class generally needs to call the dispose
method in the base class, in order to properly release resources held in the base. Secondly, if an object has a "has a" relationship with another object that holds a resource, should the indirectly using object be disposable? This corresponds to whether the relationship is owning or viewing, or even just communicating, and both conventions are found. If the indirect use is responsible for the resource, it must be disposable, and dispose the owned objects when it is disposed.Composition provides encapsulation, but at the cost of considerable complexity when there are further relationships between objects, while aggregation is considerably simpler, at the cost of lacking encapsulation. In.NET, convention is to only have direct user of resources be responsible: "You should implement IDisposable only if your type uses unmanaged resources directly." See resource management for details, and further examples.