개발공부일지
TypeScript - 전략패턴(Strategy Pattern) 본문
목차
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 |