Notice
Recent Posts
Recent Comments
Link
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

개발공부일지

TypeScript - 전략패턴(Strategy Pattern) 본문

TypeScript

TypeScript - 전략패턴(Strategy Pattern)

보람- 2024. 1. 5. 14:57

목차

1. 전략패턴(Strategy Pattern)이란

2. 활용1 (로그인 로직)

3. 활용2 (상품 할 로직)


 

 

 

 

1. 전략패턴(Strategy Pattern)이란

 

- 객체지향적으로 프로그래밍할때 가장 많이 사용하는 패턴 중 하나.

- 클래스의 재사용성을 높이고, 클래스 각각의 내용을 따로 분리해서 만들어 사용하므로 유지보수 용이함.

   → 새로운 기능을 추가해야한다면, 새로운 클래스를 만들어주어 사용!

- 각각의 전략(strategy)을 구현하는 인터페이스를 만들고, 그걸 구체적인 클래스가 구현!

 

 

 

 

 

2. 활용1 (로그인 로직)

 

  ① 로그인 진행에 대한 각각의 interface를 만들고

더보기
// 로그인 진행
interface AuthProps {
  email: string;
  password: string;
}

// 로그인 요청 후 응답 객체형태
interface AuthentResponse {
  success: boolean;
  message?: string;
}

interface Authenticator {
  // 검증에 대한 요청 함수, AuthentResponse 형태로 반환받음
  authenticator(credentials: AuthProps): Promise<AuthentResponse>;
  // 함수의 내용은 interface에 정의할 수 없음 (어떤 함수나 변수가 포함될지 정의만 가능!)
}

interface LoginService {
  // 로그인 로직 처리할 함수
  login(type: string, credentials: AuthProps): Promise<AuthentResponse>;
}

 

 

 

  ② 만들어둔 interface에 따라 클래스로 로그인 방식을 구현

더보기
// 카카오 로그인
class KakaoAuthenticator implements Authenticator {
  async authenticator(credentials: AuthProps): Promise<AuthentResponse> {
    console.log("kakao 로그인");
    return { success: true };
  }
}

// 이메일 로그인
class EmailAuthenticator implements Authenticator {
  async authenticator(credentials: AuthProps): Promise<AuthentResponse> {
    console.log("email 로그인");
    return { success: true };
  }
}

// 로그인 서비스 구조 상속
class Login implements LoginService {
  constructor(private readonly strategy : Strategy ) {}
  async login(
    type: "email" | "kakao",
    credentials: AuthProps
  ): Promise<AuthentResponse> {
    const result = this.startegy[type].authenticator(credentials);
    return result;
  }
}

 

 

 

  ③ 로그인 로직 실행과 email일때 kakao 로그인일때 클래스 동적할당

더보기
interface Strategy {
  email: EmailAuthenticator;
  kakao: KakaoAuthenticator;
}

class Auth {
  constructor(
    private readonly authProps: AuthProps,
    private readonly loginService: LoginService
  ) {
    this.authProps = authProps;
  }

  public async login() {
    console.log(this);
    const result = await this.loginService.login("email", this.authProps);
    console.log(result); // { success: true } 객체 반환
  }
}

// 로그인 로직 실행
const authProps: AuthProps = {
  email: "boram@naver.com",
  password: "1234",
};

// email, kakao 로그인 클래스 동적할당
const _email = new EmailAuthenticator();
const _kakao = new KakaoAuthenticator();

const _strategy : Strategy = {
  email: _email,
  kakao: _kakao,
};

const _loginService = new Login(_strategy);

const auth = new Auth(authProps, _loginService);
auth.login();

 

 

 

- npx ts-node 실행해보면 email로그인이 나온다!

Auth {
  authProps: { email: 'boram@naver.com', password: '1234' },
  loginService: Login {
    strategy : { email: EmailAuthenticator {}, kakao: KakaoAuthenticator {} }
  }
}
email 로그인
{ success: true }

 

 

 

- 만약 다른방식의 로그인을 추가해야한다면 → interface Strategy 안에 추가하고, 클래스를 만들어주어 사용하면된다!

export interface Startegy {
  email: EmailAuthenticator;
  kakao: KakaoAuthenticator;
  github: GithubAuthenticator;
}

export class GithubAuthenticator implements Authenticator {
  async authenticator(credentials: AuthProps): Promise<AuthentResponse> {
    console.log("github 로그인");
    return { success: true };
  }
}

 

 

 

 

 

3. 활용2 (상품 할 로직)

 

 3-1 interface로 상품의 이름과 가격을 받고, class 상품으로 이름, 가격, 할인까지 조정

더보기
interface IProduct {
  name: string;
  price: number;
}

class Product {
  private name: string;
  private price: number;
  private discount: number;

  constructor(name: string, price: number) {
    this.name = name;
    this.price = price;
    this.discount = 0;
  }

  getName(): string {
    return this.name;
  }

  getPrice(): number {
    return this.price - this.discount;
  }

  getProduct(): IProduct {
    return { name: this.name, price: this.getPrice() };
  }

  // 상품 할인가 조정하는 함수
  setDiscountAmount(amount: number): void {
    this.discount = amount;
  }
}

const product = new Product("사탕", 1000);
console.log(product.getProduct());
product.setDiscountAmount(200);
console.log(product.getProduct());

 

- npx ts-node로 실행하면 상품의 이름과 가격이 나옴

{ name: '사탕', price: 1000 }
{ name: '사탕', price: 800 }

 

 

  3-2 기존 할인 조정에 퍼센트할인, 이벤트할인이 추가되었을 때

더보기
// 할인하는 함수

interface Discount {
  getDiscountPrice(price: number): number;
}

// 상품의가격에서 금액(정수)을 빼주는
class FlatDiscount implements Discount {
  private amount: number;

  constructor(amount: number) {
    this.amount = amount;
  }
  getDiscountPrice(price: number): number {
    return price - this.amount;
  }
}

// 할인율로 가격 수정
class PercentDiscount implements Discount {
  private amount: number;

  constructor(amount: number) {
    this.amount = amount;
  }
  getDiscountPrice(price: number): number {
    return price * (1 - this.amount / 100);
  }
}

// event 할인
class FlatPercentDiscount implements Discount {
  private flatAmount: number;
  private percent: number;

  constructor(flatAmount: number, percent: number) {
    this.flatAmount = flatAmount;
    this.percent = percent;
  }

  getDiscountPrice(price: number): number {
    const flatDiscountAmount = price - this.flatAmount;
    return flatDiscountAmount * (1 - this.percent / 100);
  }
}

class Product2 {
  private name: string;
  private price: number;

  constructor(name: string, price: number) {
    (this.name = name), (this.price = price);
  }

  getName(): string {
    return this.name;
  }

  getPrice(): number {
    return this.price;
  }
}

class ProductDiscount {
  private product: Product2;
  private discount: Discount;

  constructor(product: Product2, discount: Discount) {
    this.product = product;
    this.discount = discount;
  }

  getPrice(): void {
    console.log(this.discount.getDiscountPrice(this.product.getPrice()));
  }
}

const _product = new Product2("비트코인", 100000);
const _product2 = new Product2("이더리움", 20000);

const productDiscount = new FlatDiscount(10000);
const productDiscountPercent = new PercentDiscount(10);
const productDiscountFlatPercent = new FlatPercentDiscount(10000, 10);

const productService = new ProductDiscount(_product, productDiscountPercent);
productService.getPrice();

const productService2 = new ProductDiscount(_product2, productDiscount);
productService2.getPrice();

const productService3 = new ProductDiscount(
  _product2,
  productDiscountFlatPercent
);
productService3.getPrice();

 

 


 

'TypeScript' 카테고리의 다른 글

Typescript - 2) more on design patterns with TS  (0) 2024.01.11
Typescript - design patterns with TS ①  (0) 2024.01.11
TypeScript - 제네릭(generics), tuple, interface  (0) 2024.01.05
TypeScript  (0) 2024.01.04