Skip to content

Commit cc6bf12

Browse files
committed
SOPE Project Phase 2 completed - state design pattern
1 parent 575f1a3 commit cc6bf12

8 files changed

Lines changed: 172 additions & 0 deletions

File tree

Projects/SOPE/src/main/java/com/gautam/SOPE/orderengine/controller/OrderController.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.gautam.SOPE.orderengine.controller;
22

3+
import com.gautam.SOPE.orderengine.model.Order;
34
import com.gautam.SOPE.orderengine.service.PaymentService;
45
import org.springframework.web.bind.annotation.*;
56

@@ -18,6 +19,21 @@ public OrderController(PaymentService paymentService) {
1819
@PostMapping("/checkout")
1920
public String checkout(@RequestParam String type, @RequestParam BigDecimal amount) {
2021
paymentService.checkout(type, amount);
22+
23+
// Simulation
24+
Order order = new Order();
25+
order.printStatus(); // Output: Order is currently in [NEW] state...
26+
27+
order.nextState(); // Output: Order payment processed...
28+
order.printStatus(); // Output: Order is currently in [PAID] state...
29+
30+
order.nextState(); // Output: Order has been shipped!
31+
order.printStatus(); // Output: Order is currently in [SHIPPED] state.
32+
33+
order.nextState(); // Output: Order is already shipped...
34+
2135
return "Payment processed via " + type;
2236
}
37+
38+
2339
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package com.gautam.SOPE.orderengine.model;
2+
3+
import com.gautam.SOPE.orderengine.state.OrderState;
4+
import com.gautam.SOPE.orderengine.state.NewState;
5+
6+
public class Order {
7+
8+
private OrderState state;
9+
10+
// Default constructor sets initial state
11+
public Order() {
12+
this.state = new NewState();
13+
}
14+
15+
public void setState(OrderState state) {
16+
this.state = state;
17+
}
18+
19+
public OrderState getState() {
20+
return state;
21+
}
22+
23+
// The "Request" methods that delegate to the current state
24+
public void nextState() {
25+
state.next(this);
26+
}
27+
28+
public void printStatus() {
29+
state.printStatus();
30+
}
31+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.gautam.SOPE.orderengine.state;
2+
3+
import com.gautam.SOPE.orderengine.model.Order;
4+
5+
public class NewState implements OrderState {
6+
@Override
7+
public void next(Order order) {
8+
System.out.println("Order payment processed successfully.");
9+
order.setState(new PaidState());
10+
}
11+
12+
@Override
13+
public void printStatus() {
14+
System.out.println("Order is currently in [NEW] state. Waiting for payment.");
15+
}
16+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.gautam.SOPE.orderengine.state;
2+
3+
import com.gautam.SOPE.orderengine.model.Order;
4+
5+
public interface OrderState {
6+
void next(Order order);
7+
void printStatus();
8+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.gautam.SOPE.orderengine.state;
2+
3+
import com.gautam.SOPE.orderengine.model.Order;
4+
5+
public class PaidState implements OrderState {
6+
@Override
7+
public void next(Order order) {
8+
System.out.println("Order has been shipped!");
9+
order.setState(new ShippedState());
10+
}
11+
12+
@Override
13+
public void printStatus() {
14+
System.out.println("Order is currently in [PAID] state. Preparing for shipment.");
15+
}
16+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.gautam.SOPE.orderengine.state;
2+
3+
import com.gautam.SOPE.orderengine.model.Order;
4+
5+
public class ShippedState implements OrderState {
6+
@Override
7+
public void next(Order order) {
8+
System.out.println("Order is already shipped. No further steps.");
9+
}
10+
11+
@Override
12+
public void printStatus() {
13+
System.out.println("Order is currently in [SHIPPED] state.");
14+
}
15+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.gautam.SOPE.orderengine.util;
2+
3+
import com.gautam.SOPE.orderengine.state.*;
4+
import jakarta.persistence.AttributeConverter;
5+
import jakarta.persistence.Converter;
6+
7+
@Converter(autoApply = true)
8+
public class OrderStateConverter implements AttributeConverter<OrderState, String> {
9+
10+
@Override
11+
public String convertToDatabaseColumn(OrderState state) {
12+
if (state instanceof NewState) return "NEW";
13+
if (state instanceof PaidState) return "PAID";
14+
if (state instanceof ShippedState) return "SHIPPED";
15+
return "UNKNOWN";
16+
}
17+
18+
@Override
19+
public OrderState convertToEntityAttribute(String dbData) {
20+
if ("NEW".equals(dbData)) return new NewState();
21+
if ("PAID".equals(dbData)) return new PaidState();
22+
if ("SHIPPED".equals(dbData)) return new ShippedState();
23+
return new NewState(); // Default fallback
24+
}
25+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package com.gautam.SOPE.orderengine.model;
2+
3+
import com.gautam.SOPE.orderengine.state.NewState;
4+
import com.gautam.SOPE.orderengine.state.PaidState;
5+
import com.gautam.SOPE.orderengine.state.ShippedState;
6+
import org.junit.jupiter.api.Test;
7+
import static org.junit.jupiter.api.Assertions.*;
8+
9+
class OrderStateTest {
10+
11+
@Test
12+
void testOrderLifecycleFlow() {
13+
// 1. Setup - Create the object
14+
Order order = new Order();
15+
16+
// 2. Assert Initial State
17+
// explicitly check the class type to verify the state
18+
assertTrue(order.getState() instanceof NewState, "Order should start in NEW state");
19+
20+
// 3. Act - Transition to next state
21+
order.nextState();
22+
23+
// 4. Assert - Verify transition to PAID
24+
assertTrue(order.getState() instanceof PaidState, "Order should transition to PAID state after first next()");
25+
26+
// 5. Act - Transition to next state
27+
order.nextState();
28+
29+
// 6. Assert - Verify transition to SHIPPED
30+
assertTrue(order.getState() instanceof ShippedState, "Order should transition to SHIPPED state");
31+
}
32+
33+
@Test
34+
void testShippedStateIsFinal() {
35+
// Setup an order manually in the SHIPPED state to save time
36+
Order order = new Order();
37+
order.setState(new ShippedState());
38+
39+
// Act - Try to move past the final state
40+
order.nextState();
41+
42+
// Assert - Should still be SHIPPED (idempotency)
43+
assertTrue(order.getState() instanceof ShippedState, "Order should remain in SHIPPED state");
44+
}
45+
}

0 commit comments

Comments
 (0)