부트캠프 일지

부트캠프 85일차 후기

여름하인 2024. 3. 29. 21:09

드디어 프론트엔드 작업을 마쳤다. 생각 외로 많은 시간이 걸려버렸지만 겨우 작업을 마쳤다. 이 자바스크립트란 언어는 매우 널럴한 언어인데, 함수에 변수 갯수를 잘못넣어도 나머지 변수를 undefined로 채우기에, IDE 내에서의 오류 검출이 다른 언어에 비해 개인적으로 힘든 것 같다.. 프론트엔드를 고르지 않은 것은 좋은 선택이였던 것 같다.

 

오늘 모의면접을 보았는데, 얘기를 나누다 보니 디자인 패턴에 대한 이야기가 나왔다. 디자인 패턴에는 많은 패턴이 있어서(옵저버 패턴, 템플릿 메소드 패턴, 체인 패턴, 프록시 패턴 등등) 다 다루지는 않을 것이고, 면접에서 나온 커맨드 패턴과 전략 패턴과 차이점에 대해 간략하게 정리를 해볼려고 한다.

 

전략 패턴은 전략 행위를 클래스화하고, 인터페이스를 정의한 뒤에, 객체의 행위를 실행 중에 동적으로 바꿀 때, 클래스를 바꿈으로써, 행위의 수정이 가능하게 하는 패턴이다.

 

밑의 코드는 위키피디아에 수록된 코드를 가져왔다.

public class StrategyPatternWiki
{
    public static void Main(String[] args)
    {
        Customer firstCustomer = new Customer(new NormalStrategy());

        // Normal billing
        firstCustomer.Add(1.0, 1);

        // Start Happy Hour
        firstCustomer.Strategy = new HappyHourStrategy();
        firstCustomer.Add(1.0, 2);

        // New Customer
        Customer secondCustomer = new Customer(new HappyHourStrategy());
        secondCustomer.Add(0.8, 1);
        // The Customer pays
        firstCustomer.PrintBill();

        // End Happy Hour
        secondCustomer.Strategy = new NormalStrategy();
        secondCustomer.Add(1.3, 2);
        secondCustomer.Add(2.5, 1);
        secondCustomer.PrintBill();
    }
}


class Customer
{
    private IList<double> drinks;

    // Get/Set Strategy
    public IBillingStrategy Strategy { get; set; }

    public Customer(IBillingStrategy strategy)
    {
        this.drinks = new List<double>();
        this.Strategy = strategy;
    }

    public void Add(double price, int quantity)
    {
        drinks.Add(Strategy.GetActPrice(price * quantity));
    }

    // Payment of bill
    public void PrintBill()
    {
        double sum = 0;
        foreach (double i in drinks)
        {
            sum += i;
        }
        Console.WriteLine("Total due: " + sum);
        drinks.Clear();
    }
}

interface IBillingStrategy
{
    double GetActPrice(double rawPrice);
}

// Normal billing strategy (unchanged price)
class NormalStrategy : IBillingStrategy
{
    public double GetActPrice(double rawPrice)
    {
        return rawPrice;
    }

}

// Strategy for Happy hour (50% discount)
class HappyHourStrategy : IBillingStrategy
{

    public double GetActPrice(double rawPrice)
    {
        return rawPrice * 0.5;
    }
}

고객이 물건을 계산할 때, 평소에는 물건 가격을 그대로 계산하는 전략인, new NormalStrategy()를 통해 고객의 계산 전략을 세웠다. 하지만, HappyHour일 때엔 물건의 가격을 반값으로 줄여주는 new HappyHourStrategy()로 전략을 바꿔서 물건의 가격을 계산한다. 이것이 전략 패턴이다.

- https://ko.wikipedia.org/wiki/%EC%A0%84%EB%9E%B5_%ED%8C%A8%ED%84%B4

 

전략 패턴 - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전. 전략 패턴(strategy pattern) 또는 정책 패턴(policy pattern)은 실행 중에 알고리즘을 선택할 수 있게 하는 행위 소프트웨어 디자인 패턴이다. 전략 패턴은 특정한 계열

ko.wikipedia.org

커맨드 패턴은 실행될 기능을 클래스로 만들어서 캡슐화하고 매개변수화해서 객체 간의 의존성을 낮추는 디자인 패턴이다. 구성 요소엔 Command, ConcreteCommand, Client, Invoker, Receiver 등이 있다.

 

Command는 작업 수행을 위한 인터페이스이다. ConcreteCommand는 Command 인터페이스로 구현된 실제 기능이고, Invoker는 Receiver에게 기능의 실행을 요청한다. Client는 Command 객체를 생성하고 Receiver를 설정하며. Receiver로 실제 일을 처리한다.

위키피디아의 수록된 코드를 살펴보자.

 

#include <iostream>
#include <vector>
#include <string>

using namespace std;

class Command{
  public:
   virtual void execute(void) =0;
   virtual ~Command(void){};
};

class Ingredient : public Command {
  public:
   Ingredient(string amount, string ingredient){
      _ingredient = ingredient;
      _amount = amount;
   }
   void execute(void){
      cout << " *Add " << _amount << " of " << _ingredient << endl;
   }
  private:
   string _ingredient;
   string _amount;
};

class Step : public Command {
  public:
   Step(string action, string time){
      _action= action;
      _time= time;
   }
   void execute(void){
      cout << " *" << _action << " for " << _time << endl;
   }
  private:
   string _time;
   string _action;
};

class CmdStack{
  public:
   void add(Command *c) {
      commands.push_back(c);
   }
   void createRecipe(void){
      for(vector<Command*>::size_type x=0;x<commands.size();x++){
         commands[x]->execute();
      }
   }
   void undo(void){
      if(commands.size() >= 1) {
         commands.pop_back();
      }
      else {
         cout << "Can't undo" << endl;
      }
   }
  private:
   vector<Command*> commands;
};

int main(void) {
   CmdStack list;

   //Create ingredients
   Ingredient first("2 tablespoons", "vegetable oil");
   Ingredient second("3 cups", "rice");
   Ingredient third("1 bottle","Ketchup");
   Ingredient fourth("4 ounces", "peas");
   Ingredient fifth("1 teaspoon", "soy sauce");

   //Create Step
   Step step("Stir-fry","3-4 minutes");

   //Create Recipe
   cout << "Recipe for simple Fried Rice" << endl;
   list.add(&first);
   list.add(&second);
   list.add(&step);
   list.add(&third);
   list.undo();
   list.add(&fourth);
   list.add(&fifth);
   list.createRecipe();
   cout << "Enjoy!" << endl;
   return 0;
}

Ingredient, Step은 둘 다 어떤 행동을 정의하고 있다. Ingredient는 재료를 넣는 행위를 정의하고 있고, Step은 요리사가 어떤 행위를 하도록 정의를 하고 있다. 두 행위 모두 execute 인터페이스를 가진 Command를 구현해서 각자의 행위를 묘사한다. 마지막엔 createRecipe를 통해 지금까지 저장된 요리 단계를 execute로 한번에 보여준다. 

 

참조: https://ko.wikipedia.org/wiki/%EC%BB%A4%EB%A7%A8%EB%93%9C_%ED%8C%A8%ED%84%B4

 

커맨드 패턴 - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전. 커맨드 패턴(Command pattern)이란 요청을 객체의 형태로 캡슐화하여 사용자가 보낸 요청을 나중에 이용할 수 있도록 매서드 이름, 매개변수 등 요청에 필요한 정보

ko.wikipedia.org

이 두 패턴은 객체의 행위를 클래스화한다는 점에서 유사하고 이번 모의면접에서 이 두 차이점이 헷갈렸다. 그래서 이번에 공부를 하게 되었다. 결론은 전략패턴은 "어떻게"에 집중한다. 즉, 어떤 행위를 할 때, 어떤 전략을 사용해서 어떻게 결과를 얻을지에 초첨이 맞춰져있다. 즉, 최단거리를 찾을 때, 브루트포스를 쓸 지, 벨만-포드를 쓸 지, 아니면, 다익스트라를 쓸 지를 고민하는 것이 전략 패턴이다.

 

반면에 커맨드 패턴은 "무엇"에 더 집중한다. 어떻게 할 지는 정해졌고, 무엇을 할 것인지가 중요한 것이 커맨드 패턴이다. 즉석 복권, 번호식 복권이 있다고 했을 때, 무엇을 긁을지를 고민하는 것이 이 커맨드 패턴이다. 이 두 패턴의 차이점을 잘 기억해두자.

 

참조: https://tecoble.techcourse.co.kr/post/2021-10-04-strategy-command-pattern/

 

전략패턴과 커맨드패턴

tecoble.techcourse.co.kr

다음주엔 자잘한 디버깅과 미루어둔 면접 연습을 할 예정이다. 두괄식으로 말을 하는 연습을 해보자. 몇 번 연습을 해봤지만, 막상 모의면접에 들어가니 연습한 대로 되지 않았다. 생각보단 더 연습이 필요할 것 같다.