woowacourse / tecoble-comments

0 stars 0 forks source link

post/2021-10-04-strategy-command-pattern/ #50

Open utterances-bot opened 2 years ago

utterances-bot commented 2 years ago

전략패턴과 커맨드패턴

인터페이스를 구현하여 사용하는 두 가지 패턴인 전략 패턴과 커맨드 패턴. 자주 사용하지만, 이 둘의 차이점은 무엇일까? 알쏭달쏭한 전략 패턴과 커맨드 패턴의 차이점을 알아보는 전략 패턴과 커맨드 패턴 글입니다.

https://tecoble.techcourse.co.kr/post/2021-10-04-strategy-command-pattern/?utterances=2695977b5a68eb2f1b527f2bp5k55NZEx9w7BQcQEjNQSikmYq2nEK86vIXjKKd0ByWDfJoSzk4lzY8AlRouFVT8mzBn1%2Bbznx36cxFH5kARdFsq5VUmFdhufv0%2FEW8IJyW1XxZ%2BxngRQXUGuUc%3D

leeseojune53 commented 2 years ago

안녕하세요 궁금한 점이 생겨 댓글 남기게되었습니다.

InstantScratch 즉석복권긁기커맨드 = new InstantScratch(즉석복권, 와이비통장);

위 코드에서 InstantScratch보다는 LotteryCommand가 더 적합하지않나요?

또, 위의 로또 예시를 전략패턴으로 변경해도 별다른 문제가 발생할 것 같지 않습니다.

생성 시 NumberLottery에 당첨번호와 나의 번호를 넣는다면 scratch를 오버로딩할 필요가 없을 것 같습니다.

xron2929 commented 2 years ago

어찌되었든 간에 당사자님이 강조해주고 싶었던 말씀은 코드가 복잡해지면 그걸 메소드로 전부 사용해버리면 하나의 class가 너무 많아지니까요 가령. 선물을 보내는 class인데 거기서 선물은 상품권,돈,꽃,치킨,라면,케이크,피자,초밥,과자,수박,음료수,스테이크,화장품,책,짜장면 이렇게 많은데, 여기서 선물을 보내기만 한다면 전략 패턴이겠죠.. 근데 여기서, 선물을 직접 줄지 온라인으로 줄지 방법을 나눠야 하고, 온라인, 오프라인에 관한 상품권 클래스 음식 구매나 화장품 배달에 대해서만 배달가능한지에 대한 관련된 클래스 (신라호텔 케이크같이 고급이기 때문에 예약제라 불가능할 수도 있겠죠) 가 이미 따른 곳에서 갖다 쓰는 것 때문에 써져있어서 그냥 쉽게 가져다 쓸 수 있는 경우면 오버로딩해서 메소드(음식 음식객체)
메소드(화장품 화장품객체) 메소드(상품권 상품권객체) 메소드(int money) 이런식으로 쓸 수도 있겠죠 여기서 음식class랑 화장품class랑 상품권class에 함수를 추가하고 money class를 추가하시는 것도 방법이겠죠 그렇지만 여기서 많은 음식,화장품,상품권class에 이미 객체랑 메소드가 충분히 많은 상태에서 메소드를 더 추가한다면 이는 곧 하나의 class에 너무 많은 역할을 부여하고, 이미 음식 화장품 상품권이 다른 interface를 참조하고 있는 상태인 경우라면... 각각의 class도 선물인터페이스도 구현해야하니.. oop의 법칙에 위반되는 사항이겠죠..

xron2929 commented 2 years ago

따라서 선물이라는 인터페이스에서 음식 화장품 상품권으로 구현을 추가로 더하는 경우가 나은 환경도 존재하지만, 기존에 이미 많은 역할이 부여된 상태라면 목적이라는 대상을 선물 구현 클래스의 메소드로 오버로딩 해서 따로 구현하는 경우도 확실히 좋아보입니다

leeseojune53 commented 2 years ago

제가 제대로 이해를 했는지 궁금하네요 우선 위에서 이야기해주신 배달가능한지 여부에 대해 관련된 클래스에서 음식, 화장품 등의 메소드를 오버로딩하여 구현할 경우에는 한 클래스가 지는 책임이 너무 많아지므로, 커멘드 패턴에서 receiver 쪽에서 구현하는게 더 좋다라고 이해하면 맞는걸까요?

xron2929 commented 2 years ago

네네 말씀하신 내용이 맞는 것 같습니다

hybeom0720 commented 2 years ago

안녕하세요. leeseojune53님! 좋은 질문 감사드립니다. 댓글 알람이 뜨지는 않아서 확인이 늦었습니다. 질문 내용에 답변을 드리도록 하겠습니다.

  1. InstantScratch 즉석복권긁기커맨드 = new InstantScratch(즉석복권, 와이비통장);

-> 이 부분은 말씀하신데로 앞의 타입이 LotteryCommand가 조금 더 설명에 맞는 표현같습니다. 수정 해놓도록 하겠습니다.

  1. xron2929님이 "배달" 을 예시로 하여 상황에 맞게 "어떻게"에 집중을 할 것인지, "무엇"에 집중할 것인지에 대해서 잘 설명을 해주셨습니다.

로또로 돌아가서 설명을 해보자면, NumberLottery에 내 번호와 당첨 번호를 동시에 넣게된다면, 확실하게 전략패턴이 맞을 수 있는 상황이 일것 같습니다. 하지만 제가 전략패턴보다는 커맨드 패턴이 적절하다고 생각한 이유가 2가지가 있습니다.

먼저 실제 상황에 맞는 추상화입니다. 저희가 실제로 로또 복권을 살 때(발행될 때) 에는 당첨 번호가 정해지지 않은 채로 발행이 됩니다. 복권을 사고 나서 한창 뒤인 추첨일날 랜덤으로 당첨 번호가 생성이 됩니다. 이것을 프로그래밍 세계로 옮겨 넣으면, 처음에 NumberLottey 인스턴스를 생성하게 되면 내 번호는 정해져있지만, 당첨 번호는 정해지지 않는 상태(null) 값이 될 것입니다. 발표날이 되면, setter를 통하여 발행되어있는 모든 NumberLottery에 당첨 번호를 일일히 넣어야할 것 같습니다. 이는 조금 부자연스러울 것으로 생각을 하게 되었습니다.

두번째로는 "무엇을"에 집중을 하게된다면 NumberLottery와 당첨 번호를 같이 추상화한 객체(여기서는 LotteryCommand를 구현한 객체가 되겠네요) 를 만들수가 있습니다. 이는 NumberLottery와 InstantScratch 를 같은 이름의 메소드를 통해 일괄적으로 당첨 확인을 실행할 수 있다는 장점이 있습니다. 만약 오버로딩을 활용하여 인터페이스에 두 메소드를 넣게 된다면,

  1. 각각의 당첨확인 객체에 사용하지 않을 메소드도 구현을 해야한다.
  2. 당첨확인 객체의 종류를 확인하고, 그에 맞는 메소드를 실현하는 로직이 추가되어야 한다. 라는 단점이 있을 것 같습니다.

요약하자면, "NumberLottery가 생성될 때, 자신의 당첨 번호를 알고 있는 것이 자연스러운가" 및 "복권 종류와 상관없이 한 메소드 타입을 활용하여 당첨을 확인하는 행위에 집중하자" 에 집중을 하였고, 이는 전략패턴보다는 커맨드 패턴이 조금 더 적절하지 않을까? 라는 결론으로 이어지게 되었습니다.

끝으로 디자인 패턴을 선택해서 활용하는 것도 좋지만, leesojune53 님이 든 의문처럼 각 상황에 맞는 구현을 하다보니까 해당 디자인패턴으로 연결되더라 가 제일 좋은 방향이라고 저도 생각합니다!

다시 한 번 두 분께 오류 지적과 좋은 질문 및 논의 감사드립니다!

nello95 commented 2 years ago

정말 좋은 글 감사드립니다! GoF를 공부하는 중에 의문이었던 점을 정말 시원하게 긁어주셔서 감사 댓글을 달지 않을 수가 없었네요! ㅎㅎ

wingunkh commented 1 year ago

너무 좋은 글 감사합니다!