State Pattern
The State pattern is a behavioral design pattern that allows an object to alter its behavior when its internal state changes. This pattern is beneficial when an object’s behavior depends on its state and must change its behavior at runtime based on that state.
The State Pattern suggests that an object’s behavior should be a function of its state, allowing it to change its behavior at runtime without altering its class. At any given moment, a program can exist in one of several finite states and transition between them. Each distinct state is encapsulated in its class, with all state classes implementing a common interface. This approach enables seamless behavior changes by swapping out the state object through the shared interface, effectively changing the object’s behavior based on its current state. By doing so, the pattern promotes flexibility, maintainability, and adherence to the Open/Closed principle, as new states can be added without modifying existing code.
Structure
The State pattern consists of the following key components:
- Context: is the class that contains the state and can have its behavior changed
- State: is an interface that defines methods that each concrete implementation should define
- Concrete State: are concrete implementations of the State interface, providing specific behavior for each state
When to use the State Pattern?
- When you want to change the behavior of an object based on its state
- When you want to get rid of conditional statements for changing the behavior based on state
- When you want to add new states without breaking the existing code (
Open/Closed Principle
) - When you want to put different states in separate classes (
Single Responsibility Principle
)
Implementation Example
The code below demonstrates the State design pattern for a blog post’s lifecycle in Java. It defines a BlogPost
class (the context) and various states (DraftState
, UnderReviewState
, PublishedState
, and ArchivedState
) that implement the BlogPostState
interface. Each state handles transitions and operations differently, allowing the blog post to change its behavior based on its current state. The Solution
class demonstrates the usage by creating a blog post and performing various operations on it, showcasing how the post’s behavior changes as it moves through different states. This pattern provides a clean and extensible way to manage complex state-dependent behaviors without relying on numerous conditional statements.
// Client class
public class Solution {
public static void main(String[] args) {
BlogPost post = new BlogPost();
post.setContent("This is a sample blog post for iamnishantrao.com");
System.out.println();
post.publish();
post.submitForReview();
post.publish();
post.submitForReview();
post.publish();
post.archive();
post.submitForReview();
post.archive();
post.publish();
post.draft();
post.submitForReview();
post.delete();
}
}
// Context
class BlogPost {
private BlogPostState draftState;
private BlogPostState underReviewState;
private BlogPostState publishedState;
private BlogPostState archivedState;
private BlogPostState currentState;
private String content;
public BlogPost() {
draftState = new DraftState();
underReviewState = new UnderReviewState();
publishedState = new PublishedState();
archivedState = new ArchivedState();
currentState = draftState;
}
public void setContent(String content) {
this.content = content;
System.out.println("Created new blog post with content: (" + content + ")");
}
public void draft() {
System.out.println("Trying to set the blog post as draft...");
currentState.draft(this);
}
public void submitForReview() {
System.out.println("Trying to submit the blog post for review...");
currentState.submitForReview(this);
}
public void publish() {
System.out.println("Trying to publish the blog post...");
currentState.publish(this);
}
public void archive() {
System.out.println("Trying to archieve the blog post...");
currentState.archive(this);
}
public void delete() {
System.out.println("Trying to delete the blog post...");
currentState.delete(this);
}
public void setState(BlogPostState state) {
this.currentState = state;
}
public BlogPostState getDraftState() {
return draftState;
}
public BlogPostState getUnderReviewState() {
return underReviewState;
}
public BlogPostState getPublishedState() {
return publishedState;
}
public BlogPostState getArchivedState() {
return archivedState;
}
}
// State interface
interface BlogPostState {
void draft(BlogPost post);
void submitForReview(BlogPost post);
void publish(BlogPost post);
void archive(BlogPost post);
void delete(BlogPost post);
}
// Concrete States
class DraftState implements BlogPostState {
public void draft(BlogPost post) {
System.out.println("Post is already in draft state!\n");
}
public void submitForReview(BlogPost post) {
System.out.println("Submitting post for review!\n");
post.setState(post.getUnderReviewState());
}
public void publish(BlogPost post) {
System.out.println("Cannot publish directly from draft. Please submit for review first!\n");
}
public void archive(BlogPost post) {
System.out.println("Archiving draft post!\n");
post.setState(post.getArchivedState());
}
public void delete(BlogPost post) {
System.out.println("Deleting draft post!\n");
post.setState(null);
}
}
class UnderReviewState implements BlogPostState {
public void draft(BlogPost post) {
System.out.println("Moving post back to draft!\n");
post.setState(post.getDraftState());
}
public void submitForReview(BlogPost post) {
System.out.println("Post is already under review!\n");
}
public void publish(BlogPost post) {
System.out.println("Publishing reviewed post!\n");
post.setState(post.getPublishedState());
}
public void archive(BlogPost post) {
System.out.println("Cannot archive a post under review!\n");
}
public void delete(BlogPost post) {
System.out.println("Deleting post under review!\n");
post.setState(null);
}
}
class PublishedState implements BlogPostState {
public void draft(BlogPost post) {
System.out.println("Moving published post back to draft!\n");
post.setState(post.getDraftState());
}
public void submitForReview(BlogPost post) {
System.out.println("Cannot submit a published post for review!\n");
}
public void publish(BlogPost post) {
System.out.println("Post is already published!\n");
}
public void archive(BlogPost post) {
System.out.println("Archiving published post!\n");
post.setState(post.getArchivedState());
}
public void delete(BlogPost post) {
System.out.println("Deleting published post!\n");
post.setState(null);
}
}
class ArchivedState implements BlogPostState {
public void draft(BlogPost post) {
System.out.println("Moving archived post back to draft!\n");
post.setState(post.getDraftState());
}
public void submitForReview(BlogPost post) {
System.out.println("Cannot submit an archived post for review!\n");
}
public void publish(BlogPost post) {
System.out.println("Publishing archived post!\n");
post.setState(post.getPublishedState());
}
public void archive(BlogPost post) {
System.out.println("Post is already archived!\n");
}
public void delete(BlogPost post) {
System.out.println("Deleting archived post!\n");
post.setState(null);
}
}
The output of the following program will be:
Created new blog post with content: (This is a sample blog post for iamnishantrao.com)
Trying to publish the blog post...
Cannot publish directly from draft. Please submit for review first!
Trying to submit the blog post for review...
Submitting post for review!
Trying to publish the blog post...
Publishing reviewed post!
Trying to submit the blog post for review...
Cannot submit a published post for review!
Trying to publish the blog post...
Post is already published!
Trying to archieve the blog post...
Archiving published post!
Trying to submit the blog post for review...
Cannot submit an archived post for review!
Trying to archieve the blog post...
Post is already archived!
Trying to publish the blog post...
Publishing archived post!
Trying to set the blog post as draft...
Moving published post back to draft!
Trying to submit the blog post for review...
Submitting post for review!
Trying to delete the blog post...
Deleting post under review!
Enjoyed the read? Give it a thumbs up 👍🏻!