커맨드패턴
배달앱을 만들어보려고 하는데 문제가 있다.
지금 새로 앱을 만들 예정이라서 아직 스토어를 등록한 사장님이 한 분도 안계시기 때문이다.
새로운 사장님들은 지속적으로 스토어를 등록하실 건데 말이다.
또 스토어별로 주문옵션이 되게 상이할 것 같다는 느낌이 들었다.
예측할 수 없는 변화에 대해서 어떻게 최대한 유연하게 반응할 수 있는 앱을 만들 수 있을까?
음 일단 변화에 대해 유연한 구조를 설계하기위해서는 필요한 것들이 무엇일까?
일단 서로 서로를 최소한으로 알거나 몰라도 되는 경우에는 아예 분리시켜버리는 게 좋을 수 있다.
배달앱은 스토어를 모르게 하고 하나의 배달주문이 발생하면 그 주문에 있는 스토어를 자세히는 모르되
주문에 적혀있는 스토어로 연결만 해주면 될 것같다.
배달주문은 다양한 형태로 존재할 것이다.
근데 배달주문을 어떻게 일반화하여서 표현할 수 있을까?
배달은 일종의 요청이다. 요청은 요청을 받을 상대가 있어야한다.
요청은 커맨드 객체
라는 말로 표현할 것이고, 요청을 받을 상대는 리시버 객체
로 표현할 것이다.
public interface Command {
void execute();
}
주문은 주문을 했을 때 주문을 받은 스토어가 해야할 일이 있을 것이다.
하지만 주문을 받는 스토어의 종류는 다양할 것이라는 것과 , 또 주문을 받은 스토어가 해야할 일이 너무 다양하기 때문에 그 부분을 정해놓고 앱을 만드는 게 확장면에서는 좋지 않을 것 같다는 생각이 들었다.
강조된 부분은 앞으로 변할 수 있는 부분이라는 생각이 들었다. 이 부분을 캡슐화하여 숨기고, 그 캡슐화된 하나의 방법만을 사용하게 된다면, 내부가 바뀌었다고 외부가 알아야할 일은 없을 것 같다.
execute()
라는 하나의 메소드로 주문(요청,호출) 자체를 캡슐화할 것이다.
오..
말하던 도중 스토어등록을 원하시는 사장님이 생겼다.
public class 서브웨이 {
class 샌드위치 {
String 메뉴이름;
public 샌드위치(String 메뉴이름){
this.메뉴이름 = 메뉴이름;
}
public String toString(){
return this.메뉴이름+"🥙";
}
}
public void 샌드위치주문(String 메뉴이름) {
샌드위치 주문메뉴 = new 샌드위치(메뉴이름);
System.out.println(주문메뉴+"배달하였습니다.");
}
}
샌드위치를 판매하는 가게가 등록 되었다.
사장님이 주문을 기다리시니 어서 앱에서 주문을 받을 수 있게 해야겠다.
일단 주문을 캡슐화한 Command
를 구현한 하나의 주문 객체를 만들 것이다.
public class 샌드위치주문 implements Command {
서브웨이 서브웨이;
String 메뉴이름;
public 샌드위치주문(서브웨이 서브웨이, String 메뉴이름) {
this.서브웨이 = 서브웨이;
this.메뉴이름 = 메뉴이름;
}
@Override
public void execute() {
서브웨이.샌드위치주문(메뉴이름);
}
}
샌드위치 가게라는 걸 추상화하면 좋겠지만, 이번 주제에서는 요청,호출 을 추상화하는 걸 중점적으로 할 것이기에
예제가 복잡해지는 걸 방지하고자 이런 식으로 구현을 했다.
그럼 이제 배달앱은 저 주문을 받아서 사장님이 영업하시는 스토어로 메시지를 무사히 전달하면 될 것같다.
이제 내가 만든 배달앱을 공개하겠다.
public class 배달부_엉이 {
Command 주문;
public void set주문(Command 주문) {
this.주문 = 주문;
}
public void 주문요청(){
주문.execute();
}
}
ㅎ ㅡㅎ 내가 만들어낸 배달부_엉이 라는 배달앱이다.
주문이 들어오면 주문에 써있는 행동을 그대로 수행해주는 시시한 앱이다.
하지만 주문 조건만 맞는 다면, 어떤 주문이라도 잘 해결할 수있는 훌륭한 앱이다.
- 주문조건 : 인터페이스 구현 을 이야기한다.
주문을 한번 해보자.
public class Main {
public static void main(String [] args) {
// 배달앱을 앱스토어에서 받았다!
배달부_엉이 배달앱 = new 배달부_엉이();
// 메뉴를 고르고 주문항목을 선택했다.
Command 주문 = new 샌드위치주문(new 서브웨이(), "치킨로티세리");
// 배달앱에 장바구니에 주문을 넣어두고
배달앱.set주문(주문);
// 결제와 동시에 주문을 했다.
배달앱.주문요청();
}
}
만족스러운 주문이었다.
두개의 다른 메뉴를 주문한다면???
두 메뉴 모두 따듯하게 배달되는 걸 볼 수있다.
아직 배달부_엉이앱(커맨드패턴)
의 강력함을 다 보여주지 않았다.
바로 새로운 스토어가 계속적으로 생겨도 , 배달앱은 문제없이 잘 될 것이라는 거다.
축하할 일이 생겼다.👏🎉🎊
배달부_엉이앱에 애플스토어⌚️💻📱(new Receiver) 가 입점하고 싶다는 문의가 들어왔다.
우리는 이제 이런 제품을 살 수있다.
class 아이폰 {
String 색상;
int 시리즈;
int 용량;
public 아이폰(String 색상, int 시리즈, int 용량) {
this.색상 = 색상;
this.시리즈 = 시리즈;
this.용량 = 용량;
}
public String toString() {
return "아이폰"+시리즈+"📱"+색상+"("+용량+")";
}
}
class 애플워치 {
String 소재;
int 시리즈;
int 용량;
public 애플워치(String 소재, int 시리즈, int 용량) {
this.소재 = 소재;
this.시리즈 = 시리즈;
this.용량 = 용량;
}
public String toString() {
return "애플워치"+시리즈+"⌚️"+소재+"("+용량+")";
}
}
class 맥북 {
String 색상;
String 라인;
int 용량;
boolean 키보드마우스추가;
public 맥북(String 색상, String 라인, int 용량,boolean 키보드마우스추가) {
this.색상 = 색상;
this.라인 = 라인;
this.용량 = 용량;
this.키보드마우스추가 = 키보드마우스추가;
}
public String toString() {
String 키보드마우스 = 키보드마우스추가 ? "🖱⌨️" : "";
return "맥북"+라인+"💻"+색상+"("+용량+")" + 키보드마우스;
}
}
그리고 이번에 입점한 애플스토어이다.
public class 애플스토어 {
public 아이폰 아이폰주문(String 색상, int 시리즈, int 용량) {
return new 아이폰(색상,시리즈,용량);
}
public 맥북 맥북주문(String 색상, String 라인, int 용량,boolean 키보드마우스추가여부) {
return new 맥북(색상,라인,용량,키보드마우스추가여부);
}
public 애플워치 애플워치주문(String 소재, int 시리즈, int 용량) {
return new 애플워치(소재,시리즈,용량);
}
}
맥북이 없는 부엉이는 신난 눈치이다.
맥북을 어디한번 주문해볼까?
그럼 주문객체(커맨드객체)
만 만들면 되겠다.
public class 아이폰주문 implements Command {
애플스토어 주문상점;
String 색상;
int 시리즈;
int 용량;
주문자 주문자;
public 아이폰주문(애플스토어 주문상점, String 색상, int 시리즈, int 용량, 주문자 주문자) {
this.주문상점 = 주문상점;
this.색상 = 색상;
this.시리즈 = 시리즈;
this.용량 = 용량;
this.주문자 = 주문자;
}
@Override
public void execute() {
주문자.받다(주문상점.아이폰주문(색상,시리즈,용량));
}
}
public class 맥북주문 implements Command {
애플스토어 주문상점;
String 색상;
String 라인;
int 용량;
boolean 키보드마우스추가;
주문자 주문자;
public 맥북주문(애플스토어 주문상점, String 색상, String 라인, int 용량, boolean 키보드마우스추가, 주문자 주문자) {
this.주문상점 = 주문상점;
this.색상 = 색상;
this.라인 = 라인;
this.용량 = 용량;
this.키보드마우스추가 = 키보드마우스추가;
this.주문자 = 주문자;
}
@Override
public void execute() {
주문자.받다(주문상점.맥북주문(색상,라인,용량,키보드마우스추가));
}
}
복잡하기때문에 이번에는 애플워치는 별로 구미가 당기지 않기때문에 다른 두개에 대해서만 구현해보겠다.
이번에는 주문자의 주소를 입력해서 주문한 주소로 제품이 도착할 수 있게 앱을 업그레이드 해보았다.
이렇게 다양한 주문이 앞으로도 계속 생길 수 있지만 , 배달앱은 계속 유지가 가능할 것이다.
해당하는 주문(커맨드객체
)만 계속 만들어 낸다면 말이다!!
💳💶💷💴💸💵주문신청~~~~
public static void main(String [] args) {
배달부_엉이 배달앱 = new 배달부_엉이();
애플스토어 코드스쿼드점 = new 애플스토어();
주문자 부엉이 = new 주문자("🦉");
Command 아이폰주문 = new 아이폰주문(코드스쿼드점,"블랙",11,64,부엉이);
배달앱.set주문(아이폰주문);
배달앱.주문요청();
주문자 블루 = new 주문자("BLU");
Command 맥북주문 = new 맥북주문(코드스쿼드점,"블랙","Pro",512,true,블루);
배달앱.set주문(맥북주문);
배달앱.주문요청();
}
주문완료!
UML