하고자 하는 것은 주문 시스템을 구현하는 것이다.
maven으로 프로젝트를 구성을 하고, Order 클래스(엔티티)를 만들어준다. UUID를 import 하여 진행을 한다. 5개 식별자를 선언한다.
private final UUID customerid;//주문자
private final List<OrderItem> orderItem;//오더아이템 목록
private FixedAmountVoucher fixedAmountVoucher;//할인 받은 금액
private OrderStatus orderStatus;//오더 상태
FixedAmountVoucher는 원래 단순 할인 금액을 나타내는 discountAmount였으나, 클래스를 따로 구분하여 구현했다.
이로 인한 생성자는 다음과 같다.
this.orderid = orderid;
this.customerid = customerid;
this.orderItem = orderItem;
this.fixedAmountVoucher = new FixedAmountVoucher(discountAmount);//인자 받아서 생성해줘야함.
}
그럼 결제하고자 하는 총액은 어떻게 구하나? 사고자 하는 제품 가격*사고자 하는 개수에 할인금액을 빼준다.
var beforeDiscount = orderItem.stream().map(v->v.getProductPrice()*v.getQuantity())
.reduce(0L, Long::sum);//reduce 메소드는 엘리먼트를 비교하고 컬렉션에서 하나의 값으로 연산한다.
return fixedAmountVoucher.discount(beforeDiscount);
//beforeDiscount-discountAmount 에서, 위처럼 보내면 돼.
}
추가로 orderStatus에 대한 setter도 구현한다.
this.orderStatus = orderStatus;
}
**OrderItem,.java
OrderItem은 List 형식이기 때문에 따로 클래스를 만들어 구현한다.
public final long productPrice;//가격
public final long quantity; //수량
위와 같이 구현하고 생성자와 getter를 구현한다.
**OrderStatus.java
이 클래스는 enum으로 구현한다. 주문 상태에 대한 여러 가지 가능성들을 나열한다.
ACCEPTED,
PAYMENT_REQUIRED,
PAYMENT_CONFIRMED,
PAYMENT_REJECTED,
READY_FOR_DELIVERY,
SHIPPED,
SETTELED,
CANCELLED
}
**FixedAmountVoucher.java
이름에서 유추하듯 고정된 할인 금액을 갖고 있으며, 생성자, 그리고 할인 전 금액을 받아 할인을 적용시키는 discount 메서드를 구현한다.
return beforeDiscount - amount;
}
**OrderTester.java
->지금까지 구현한 것이 정상작동하는지 확인한다.
var customerId= UUID.randomUUID();
var orderItems = new ArrayList<OrderItem>(){{
add(new OrderItem(UUID.randomUUID(), 100L, 1));
//100원짜리 한개. 물품을 추가해준거죠?
}};
var order = new Order(UUID.randomUUID(), customerId, orderItems, 20L);
//w제품 아이디는 랜덤하게, 할인 금액은 20원으로 적용
Assert.isTrue(order.totalAmount()==80L, MessageFormat.format("total Amount : {0}", order.totalAmount()));
//Assert문으로 확인한다.
**여기까지 하고 나서, fixed는 말처럼 정해진 금액만 적용해서 빼주는데, 퍼센트 할인도 적용하려 한다. 각각 만들기보단, 하나의 인터페이스를 두고 implements 하는 게 좋겠다.
**Voucher (interface)
UUID getVoucherID();
long discount(long beforeDiscount);
}
상기 코드로 fixed에도 변화가 필요한데, 그것은 voucherId이다.
생성자와 getter도 생성한다.
**PercentDiscountVoucher
public long discount(long beforeDiscount) {
return beforeDiscount*(percent/100);
}
**Order.java에서
이렇게 적용을 할 수가 있다.
***IOC 제어의 역전 : 객체가 스스로 생성 X, 프레임워크가 제어 권한을 갖고 있다.
**
OrderService : Order에 대한 비즈니스 로직을 담음. -> 위 그림에서 보듯, 오더 레포지토리와 바우처서비스를 사용한다. 이렇게 함으로써 오더를 하나 새로 만들게 된다.
private final OrderRepository orderRepository;
위의 그림대로 두 가지 클래스와 연관된다. 생성자도 만들어주고, 가장 중요한 오더 생성 코드도 구현을 해준다.
var voucher = voucherService.getVoucher(voucherId);
var order = new Order(UUID.randomUUID(), customerID, orderItems, voucher);
orderRepository.insert(order);//새로 저장, 추가해준거죠?
voucherService.useVoucher(voucher);//재사용 못하게 하는 로직
return order;
}
public Order createOrder(UUID customerID, List<OrderItem> orderItems){
var order = new Order(UUID.randomUUID(), customerID, orderItems);
orderRepository.insert(order);//새로 저장, 추가해준거죠?
return order;
}
조금 복잡하지만, 여기서 관건은 바우처가 없을 때가 있을 수 있기 때문에(바우처=할인 여부 및 정보로 여김) 바우처를 전달받지 않고도 오더를 생성해야 한다.
나중에 쓰이겠지만, 만든 오더는
이렇게 저장해 놓는다.
OrdeContext : 애플리케이션의 주요 객체에 대한 생성과 관계설정 : 위 그림처럼 오더 서비스, 바우처 서비스, 오더 레포지토리와 관계를 지닌다.
바우처 서비스를 생성하려면, 바우처 레포지토리를 인자로 전달해야 한다. 고로
//되는 친구니 vRepo를 넣어야겠죠?
return new VoucherService(voucherRepository());
}
return new VoucherRepository() {
@Override
public Optional<Voucher> findById(UUID voucherId) {
return Optional.empty();
}
};
}
이렇게 할 수가 있고,
오더 서비스는 바우처 서비스와 오더 레포를 전달해 줘야 하기에
return new OrderRepository() {
@Override
public void insert(Order order) {
}
};
}
return new OrderService(voucherService(), orderRepository());
}
이런 식으로 구현한다.
**Application Context == IOC Container(IoC 컨테이너는 객체에 대한 생성과 조합이 가능하게 하는 프레임워크)
개별 객체들의 의존이 자동 생성. 레지스터를 하면 의존 관계를 만들고 인스턴스를 만들어준다. appcontext라는 인터페이스가 있다.
ApplicaitonContext는 BeanFactory를 상속하는데 객체에 대 한 생성, 조합, 의존관계설정 등을 제어하는 IoC 기본기능을 BeanFactory에 담당한다.
Bean은 IoC Container에 의해 관리되는 객체를 말합니다. Bean : 정의 방법-> annotation 기반. IOC 컨테이너에서 관리되는 객체가 빈. 빈 정보 -> 설정 메타데이터로부터 받아온다.
스프링의 ApplicaitonContext는 실제 만들어야 할 빈 정보를 Configuration Metadata (설정 메타데이터)로부터 받아온다. 이 메타데이터를 이용해서 IoC 컨테이너에 의해 관리되는 객체들을 생성하고 구성한다.
Configuration Metadata를 XML 기반으로 하거나 Java 파일 기반으로 작성할 수 있다.
**OrderContext->AppConfiguration
public class AppConfiguration{}
내부 메서드들은 @bean 빈 annotation을 붙인다.
**OrderTester
AnnotationConfigApplicationContext 클래스 인스턴스를 가져오고, AppConfiguration.class는 애플리케이션 콘텍스트에 대한 bean 및 기타 설정을 정의하는 구성 클래스를 지정한다.
OrderServiceBean은 메서드를 사용하여 애플리케이션 콘텍스트에서 검색되며, getBean(). 인수 OrderService.class는 검색할 Bean의 유형을 지정한다.
댓글