mirror of
https://github.com/tiennm99/java-design-patterns.git
synced 2026-10-14 02:16:06 +00:00
docs: update raii
This commit is contained in:
1 parent
f6d77a684e
commit
a8433cc634
3 files changed
+91
-89
No files matched your search
@@ -1,132 +1,135 @@
|
|||||||
---
|
---
|
||||||
title: Resource Acquisition Is Initialization
|
title: Resource Acquisition Is Initialization
|
||||||
category: Idiom
|
category: Resource management
|
||||||
language: en
|
language: en
|
||||||
tag:
|
tag:
|
||||||
- Data access
|
- Encapsulation
|
||||||
|
- Memory management
|
||||||
|
- Resource management
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Also known as
|
||||||
|
|
||||||
|
* RAII
|
||||||
|
* Scope-based Resource Management
|
||||||
|
|
||||||
## Intent
|
## Intent
|
||||||
Resource Acquisition Is Initialization pattern can be used to implement exception safe resource management.
|
|
||||||
|
Ensure that resources are properly released when they are no longer needed by tying the resource management to object lifetime.
|
||||||
|
|
||||||
## Explanation
|
## Explanation
|
||||||
|
|
||||||
Real-world example
|
Real-world example
|
||||||
> If you're renting a car, picking it up for use is initialising the object and once done, you would return the rental.
|
|
||||||
> If you forgot to return it or something happens, the rental service will ensure that the car is retrieved or
|
> In a car rental service, each car represents a resource. Using the RAII pattern, when a customer rents a car (acquires the resource), the car is marked as rented. When the customer returns the car (the object goes out of scope), the car is automatically made available for the next customer. This ensures that cars are properly managed and available without manual intervention for checking availability or returns.
|
||||||
> equivalent property loss is compensated for.
|
|
||||||
|
|
||||||
In plain words
|
In plain words
|
||||||
> Resource Acquisition is Initialization allows for exception-safe resource handling and means that objects are able to
|
|
||||||
> manage themselves without other code to inform them that a clean-up is required after use.
|
> Resource Acquisition is Initialization allows for exception-safe resource handling and means that objects are able to manage themselves without other code to inform them that a clean-up is required after use.
|
||||||
|
|
||||||
Wikipedia says
|
Wikipedia says
|
||||||
> Resource acquisition is initialization (RAII) is a programming idiom used in several object-oriented, statically
|
|
||||||
> typed programming languages to describe a particular language behavior. Resource allocation (or acquisition) is done
|
> Resource acquisition is initialization (RAII) is a programming idiom used in several object-oriented, statically typed programming languages to describe a particular language behavior. Resource allocation (or acquisition) is done during object creation (specifically initialization), by the constructor, while resource deallocation (release) is done during object destruction (specifically finalization), by the destructor.
|
||||||
> during object creation (specifically initialization), by the constructor, while resource deallocation (release) is
|
|
||||||
> done during object destruction (specifically finalization), by the destructor.
|
|
||||||
|
|
||||||
**Programmatic Example**
|
**Programmatic Example**
|
||||||
Let us implement the car rental example with the RAII pattern. Here's our object class Car:
|
|
||||||
|
The RAII pattern is a common idiom used in software design where the acquisition of a resource is done during object creation (initialization), and the release of the resource is done during object destruction. This pattern is particularly useful in dealing with resource leaks and is critical in writing exception-safe code in C++. In Java, RAII is achieved with try-with-resources statement and interfaces `java.io.Closeable` and `AutoCloseable`.
|
||||||
|
|
||||||
```java
|
```java
|
||||||
import java.util.Date;
|
// This is an example of a resource class that implements the AutoCloseable interface.
|
||||||
|
// The resource is acquired in the constructor and released in the close method.
|
||||||
|
|
||||||
class Car {
|
@Slf4j
|
||||||
private String registrationNumber;
|
public class SlidingDoor implements AutoCloseable {
|
||||||
private boolean isRented;
|
|
||||||
|
|
||||||
public Car(String registrationNumber) {
|
public SlidingDoor() {
|
||||||
this.registrationNumber = registrationNumber;
|
LOGGER.info("Sliding door opens."); // Resource acquisition is done here
|
||||||
this.isRented = false;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void rent() {
|
@Override
|
||||||
if (!isRented) {
|
public void close() {
|
||||||
isRented = true;
|
LOGGER.info("Sliding door closes."); // Resource release is done here
|
||||||
System.out.println("Car " + registrationNumber + " has been rented.");
|
}
|
||||||
} else {
|
|
||||||
System.out.println("Car " + registrationNumber + " is already rented.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void returnCar() {
|
|
||||||
if (isRented) {
|
|
||||||
isRented = false;
|
|
||||||
System.out.println("Car " + registrationNumber + " has been returned.");
|
|
||||||
} else {
|
|
||||||
System.out.println("Car " + registrationNumber + " is not rented.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
We then implement the CarRental class that handles the Object's initialisation and return.
|
In the above code, `SlidingDoor` is a resource that implements the `AutoCloseable` interface. The resource (in this case, a door) is "acquired" in the constructor (the door is opened), and "released" in the `close` method (the door is closed).
|
||||||
|
|
||||||
```java
|
```java
|
||||||
class CarRental {
|
// This is another example of a resource class that implements the Closeable interface.
|
||||||
private Car car;
|
// The resource is acquired in the constructor and released in the close method.
|
||||||
private Date rentalDate;
|
|
||||||
|
|
||||||
public CarRental(Car car) {
|
@Slf4j
|
||||||
this.car = car;
|
public class TreasureChest implements Closeable {
|
||||||
car.rent();
|
|
||||||
rentalDate = new Date();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void returnCar() {
|
public TreasureChest() {
|
||||||
car.returnCar();
|
LOGGER.info("Treasure chest opens."); // Resource acquisition is done here
|
||||||
rentalDate = null;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void drive() {
|
@Override
|
||||||
if (rentalDate != null) {
|
public void close() {
|
||||||
System.out.println("Driving the rented car with registration number: " + car.registrationNumber);
|
LOGGER.info("Treasure chest closes."); // Resource release is done here
|
||||||
} else {
|
}
|
||||||
System.out.println("Cannot drive. Car is not rented.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
If we run the following examples:
|
Similarly, `TreasureChest` is another resource that implements the `Closeable` interface. The resource (a treasure chest) is "acquired" in the constructor (the chest is opened), and "released" in the `close` method (the chest is closed).
|
||||||
```
|
|
||||||
public static void main(String[] args) {
|
|
||||||
Car car1 = new Car("XYZ-123");
|
|
||||||
Car car2 = new Car("ABC-789");
|
|
||||||
|
|
||||||
CarRental rental1 = new CarRental(car1);
|
```java
|
||||||
CarRental rental2 = new CarRental(car2);
|
// This is an example of how to use the RAII pattern in Java using the try-with-resources statement.
|
||||||
|
|
||||||
rental1.drive();
|
@Slf4j
|
||||||
rental2.drive();
|
public class App {
|
||||||
|
|
||||||
rental1.returnCar();
|
public static void main(String[] args) {
|
||||||
rental2.returnCar();
|
|
||||||
|
|
||||||
rental1.drive();
|
try (var ignored = new SlidingDoor()) {
|
||||||
rental2.drive();
|
LOGGER.info("Walking in.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try (var ignored = new TreasureChest()) {
|
||||||
|
LOGGER.info("Looting contents.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
We can expect the following program outputs:
|
In the `main` method of the `App` class, we see the RAII pattern in action. The `try-with-resources` statement is used to ensure that each resource is closed at the end of the statement. This is where the `AutoCloseable` or `Closeable` interfaces come into play. When the `try` block is exited (either normally or via an exception), the `close` method of the resource is automatically called, thus ensuring the resource is properly released.
|
||||||
```
|
|
||||||
Car XYZ-123 has been rented.
|
|
||||||
Car ABC-789 has been rented.
|
|
||||||
Driving the rented car with registration number: XYZ-123
|
|
||||||
Driving the rented car with registration number: ABC-789
|
|
||||||
Car XYZ-123 has been returned.
|
|
||||||
Car ABC-789 has been returned.
|
|
||||||
Cannot drive. Car is not rented.
|
|
||||||
Cannot drive. Car is not rented.
|
|
||||||
```
|
|
||||||
|
|
||||||
## Class diagram
|
## Class diagram
|
||||||

|
|
||||||
|

|
||||||
|
|
||||||
## Applicability
|
## Applicability
|
||||||
Use the Resource Acquisition Is Initialization pattern when
|
|
||||||
|
|
||||||
* You have resources that must be closed in every condition
|
* Use RAII when resources such as file handles, network connections, or memory need to be managed and automatically released.
|
||||||
|
* Suitable in environments where deterministic resource management is crucial, such as real-time systems or applications with strict resource constraints.
|
||||||
|
|
||||||
|
## Known Uses
|
||||||
|
|
||||||
|
* Java `try-with-resources` statement: Ensures that resources are closed automatically at the end of the statement.
|
||||||
|
* Database connections: Using connection pools where the connection is obtained at the beginning of a scope and released at the end.
|
||||||
|
* File I/O: Automatically closing files using `try-with-resources`.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
Benefits:
|
||||||
|
|
||||||
|
* Automatic and deterministic resource management.
|
||||||
|
* Reduces the likelihood of resource leaks.
|
||||||
|
* Enhances code readability and maintainability by clearly defining the scope of resource usage.
|
||||||
|
|
||||||
|
Trade-offs:
|
||||||
|
|
||||||
|
* May introduce complexity in understanding object lifetimes.
|
||||||
|
* Requires careful design to ensure all resources are correctly encapsulated.
|
||||||
|
|
||||||
|
## Related Patterns
|
||||||
|
|
||||||
|
* Object Pool: Manages a pool of reusable objects to optimize resource allocation and performance, often used for resources that are expensive to create and manage.
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
* [Resource acquisition is initialization] (https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization)
|
|
||||||
|
* [Effective Java](https://amzn.to/4cGk2Jz)
|
||||||
|
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
|
||||||
|
* [Resource acquisition is initialization](https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization)
|
||||||
+1
-1
@@ -51,7 +51,7 @@ public class App {
|
|||||||
/**
|
/**
|
||||||
* Program entry point.
|
* Program entry point.
|
||||||
*/
|
*/
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) {
|
||||||
|
|
||||||
try (var ignored = new SlidingDoor()) {
|
try (var ignored = new SlidingDoor()) {
|
||||||
LOGGER.info("Walking in.");
|
LOGGER.info("Walking in.");
|
||||||
|
|||||||
+1
-2
@@ -68,7 +68,7 @@ class ClosableTest {
|
|||||||
/**
|
/**
|
||||||
* Logging Appender Implementation
|
* Logging Appender Implementation
|
||||||
*/
|
*/
|
||||||
class InMemoryAppender extends AppenderBase<ILoggingEvent> {
|
static class InMemoryAppender extends AppenderBase<ILoggingEvent> {
|
||||||
private final List<ILoggingEvent> log = new LinkedList<>();
|
private final List<ILoggingEvent> log = new LinkedList<>();
|
||||||
|
|
||||||
public InMemoryAppender() {
|
public InMemoryAppender() {
|
||||||
@@ -85,5 +85,4 @@ class ClosableTest {
|
|||||||
return log.stream().anyMatch(event -> event.getMessage().equals(message));
|
return log.stream().anyMatch(event -> event.getMessage().equals(message));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user