I am in the middle of shopping website design. As of now, I am stuck at one point and need a little feedback. Let us say I have an order class defined as follow:
class Order {
Payment payment;
Address ShippingAddress
int orderId;
List<Item> orderItem;
double orderValue;
Buyer buyer;
Date orderDate;
NotificationService notificationService;
Shipment shipment;
List<OrderLog> orderLog;
public OrderStatus placeOrder();
public OrderStatus trackOrder();
public void addOrderLogs();
public PaymentInfo makePayment();
public int createShipment();
}
Does it make sense to have placeOrder()
, makePayment()
etc.,API in order class? Or shall I make a separate OrderManager
that helps to do all order related stuff and order will act as a pojo?
To me first one seems correct because the placeOrder()
, makePayment()
seems to me as behaviours of order and they should be in order class but on other class I think it is too much for an order class to do. Am I violating some SOLID principle by taking one approach over another? Since the OrderManager will also do same thing that we add in this class, do does it make sense to move it out?
placeOrder()
, makePayment()
etc, seems behavious of Order to me. Any thoughts?
On the extended note, how to be confident in deciding what should reside in a class and what shouldn't?
In my opinion, I think:
1. IfOrder
class is a data class, and you will use this class to store to database, I think you should move the functions:
public OrderStatus placeOrder();
public OrderStatus trackOrder();
public void addOrderLogs();
public PaymentInfo makePayment();
public int createShipment();
from Order
class to OrderManager
class, because to implement every function (i.e placeOrder
) maybe you need:
private OrderValidator orderValidator;
private OrderService orderService;
public OrderStatus placeOrder() {
if(orderValidator.validateOrder(this)) {
orderService.saveOrder(this);
return OrderStatus.OK;
}
return OrderStatus.NOK
}
but when you use a JPA (Entity) framework like Hibernate, or Spring JPA, they will load data from database and have no information of orderValidator
or orderService
to set to Order
object. And you will need annotate orderValidtor
and orderService
with @Transient
annotation, it makes you source code is complicated
Order
class is a composite class, maybe you can keep the functions in Order
class and you don't need OrderManager
class anymore, but you should combine your pattern with Factory pattern and Builder pattern like this:
interface IOrder {
Payment getPayment();
Address getShippingAddress();
//....
}
interface OrderValidator {
boolean validateOrder(IOrder order);
}
interface OrderService {
void saveOrder(IOrder order);
}
class OrderBuilder {
private OrderValidator orderValidator;
private OrderService orderService;
private Payment payment;
// other fields
public OrderBuilder orderValidator(OrderValidator orderValidator) {
this.orderValidator = orderValidator;
return this;
}
public OrderBuilder orderService(OrderService orderService) {
this.orderService = orderService;
return this;
}
public OrderBuilder payment(Payment payment) {
this.payment = payment;
return this;
}
public Order build() {
Order order = new Order();
order.setOrderService(orderService);
order.setOrderValidator(orderValidator);
order.setPayment(payment);
//set others
return order;
}
}
// Singleton
class OrderFactory {
private OrderValidator orderValidator;
private OrderService orderService;
public OrderFactory(OrderValidator orderValidator, OrderService orderService) {
this.orderValidator = orderValidator;
this.orderService = orderService;
}
public OrderBuilder newOrderBuilder() {
return new OrderBuilder()
.orderValidator(orderValidator)
.orderService(orderService);
}
}
@Setter
@Getter
class Order implements IOrder
// usage
OrderFactory orderFactory = new OrderFactory(orderValidator, orderService);
Order order1 = orderFactory.newOrderBuilder()
.payment(payment)
// set others
.build();
order1.placeOrder();
Order order2 = orderFactory.newOrderBuilder()
.payment(payment)
// set others
.build();
order2.placeOrder();