(최근에 이벤트 루프를 디자인 패턴으로 잘못 설명하여 다음엔 그러지 않기 위하여 이 포스트를 포스팅 합니다.)
디자인 패턴이란 객체 지향 프로그래밍 설계를 할 때 사용되는 패턴으로 자주 발생하는 문제를 피하기 위해 사용됩니다.
제가 이해한 흐름은 객체 지향 프로그래밍 설계를 할때 반복하여 발생하는 문제들에 직면하는 상황이 생겼습니다.
이러한 문제를 해결하는 방식들은 고착화 되었을 것입니다.
예를 들면 '메모리를 save하기 위해 전역 범위에 객체 인스턴스를 생성해두고 의존성이 주입되면 그때 이미 생성된 인스턴스를 참조할 수 있도록 하는 방법'이 있을 것입니다.
자주 발생하는 문제를 해결하기 위해 위와 같이 매번 말하는 방법도 있을 수 있고 이러한 패턴에 이름을 붙여 좀 더 간단하게 소통할 수 도 있을 것입니다.
농구를 예로 들면 가드가 숫자 1을 손가락으로 가리키면 픽&롤, 2를 가리키면 픽&팝, 3을 가리키면 엘레베이터 스크린 등의 동작을 수행하는 예시가 있을 수 있을것 같습니다.
디자인 패턴의 맹신 보다는 코드베이스의 간결성을 추구하는 것이 중요하다 합니다.
디자인 패턴 적용을 하지 않아도 되는 부분에서는 사용하지 말아야 한다는 의미인것 같습니다.
디자인 패턴이 왜 효율적인지를 이해하는 것이 중요하고 모든 상황의 해결책이 아니라는 것을 아는 것이 중요하다 합니다.
디자인 패턴은 여러가지 패턴이 있습니다.
생성 패턴, 구조 패턴, 행위 패턴 등이 있는데
여러 패턴에 대한 지식이 현재는 없어 제 기준 친숙한 두개의 패턴만 언급을 하려 합니다.
(공부를 해 나가는 대로 여러 패턴들은 추가될 예정입니다.)
생성 패턴에서의 추상 팩토리 패턴과 싱글턴 패턴 입니다.
팩토리 패턴
객체를 생성하는 인터페이스만 정의합니다. 인스턴스를 만드는 클래스는 서브 클래스에서 결정합니다.
프레임 워크처럼 기본 정보만 제공하고 활용은 스스로 하여라 라고 이해하였습니다.
저는 프레임 워크를 강아지 집으로 이해하고 있습니다.
프레임 워크를 이용하게 되면 아무 색깔도 칠해져 있지 않은 나무 그대로의 강아지 집을 전달 받습니다.
이후 라이브러리 or 코드를 이용하여 이 강아지 집을 색칠, 커스텀 하여 강아지 집을 완성하는 흐름입니다.
저는 알고리즘 cli를 만들때 여러 사이트들이 동일한 메서드로 동작할 수 있도록 설계를 하고 싶었습니다.
그래서 추상 클래스 하나를 만들고 모든 사이트 class는 해당 추상 클래스를 상속받고 추상 메서드를 구현하게 하여
인스턴스의 생성은 서브 클래스에서 결정하도록 하였습니다.
추상 클래스 / 추상 메서드
import { InfoForGenerateFile } from "../interface";
export abstract class AbstractGenerator {
constructor() {}
public abstract generate({site, language, identifier}: InfoForGenerateFile) : void;
}
사이트 클래스들(서브 클래스들)
import { programmersJavascriptTemplate, programmersPythonTemplate, programmersJavaTemplate } from '../templates/index';
import { IndividualGeneratorInputProps } from './../interface/index';
import { AbstractGenerator } from "./abstract.generator";
export class ProgrammersGenerator implements AbstractGenerator {
public generate({language}: IndividualGeneratorInputProps): string | Error {
switch(language) {
case "python" :
return programmersPythonTemplate();
case "javascript" :
return programmersJavascriptTemplate();
case "java" :
return programmersJavaTemplate();
default :
return new Error('input wrong language');
}
}
}
import { swExpertAcademyJavaTemplate, swExpertAcademyPythonTemplate } from '../templates/index';
import { IndividualGeneratorInputProps } from './../interface/index';
import { AbstractGenerator } from "./abstract.generator";
export class SwExpertAcademyGenerator extends AbstractGenerator {
public generate({language}: IndividualGeneratorInputProps): string | Error {
switch(language) {
case "python" :
return swExpertAcademyPythonTemplate();
case "java" :
return swExpertAcademyJavaTemplate();
default :
return new Error('only python and java work')
}
}
}
import { baekjoonJavascriptTemplate, baekjoonPythonTemplate, baekjoonJavaTemplate } from '../templates/index';
import { IndividualGeneratorInputProps } from "../interface";
import { AbstractGenerator } from "./abstract.generator";
export class BaekjoonGenerator implements AbstractGenerator {
public generate({language, identifier}: IndividualGeneratorInputProps): string | Error {
switch(language) {
case "python" :
return baekjoonPythonTemplate(identifier);
case "javascript" :
return baekjoonJavascriptTemplate();
case "java" :
return baekjoonJavaTemplate();
default :
return new Error("input wrong language")
}
}
}
이렇게 사용함으로써 사이트가 추가되더라도 동일한 메서드를 구현하고 있음을 보장 받을 수 있었고
공통된 기능이 추가되야 한다면 추상 클래스에 추상메서드를 추가해줌으로써
모든 사이트에 구현되어야 하는게 있다라는 것을 빠짐없이 알릴 수 있다고 생각합니다.
싱글턴 패턴
싱글턴 패턴은 객체의 인스턴스가 오직 1개만 생성되는 패턴입니다.
객체가 필요할 때마다 인스턴스를 생성하고 활용해야 한다면 중복된 객체가 생성될 수도 있고 메모리가 hard하게 사용될 수도 있습니다.
이러한 관점에서 싱글턴 패턴이 등장하였다고 알고 있습니다.
// 객체를 미리 생성해두고 항상 가져다 사용하는 방식
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {
// 생성자는 외부에서 직접 참조할 수 없도록 private
}
public static Singleton getInsance() {
return instance;
}
public void say() {
System.out.println("hi, there")
}
}
nestjs에서는 싱글턴 패턴을 사용한다고 합니다.
코드상에 존재하는 모듈들을 맨 처음 구동시 인스턴스화 합니다.
그리고 service, repository 클래스 들에서는 의존성 주입을 통해 어떤 객체가 필요한지를 nest에게 알립니다.
이럴때 두가지 방법이 있습니다.
매번 객체를 생성하는 것과 미리 전역 범위에 띄워놓고 생성되어 있는 객체를 참조하게 하여 원하는 동작을 수행할 수 있습니다.
이때 두번째 방법인 싱글턴 패턴을 이용합니다.
nestjs에서는 모듈 클래스에 토큰을 추가하여 이 토큰을 기반으로 전역범위에 생성된 모듈에 접근하고 생성을 합니다.
싱글턴 패턴을 사용하면 장/단이 있습니다.
* 메모리를 아낄수 있습니다.
* 데이터의 공유가 쉽습니다. 하나의 객체에 속성들을 업데이트 한다면 객체를 공유하는 여러곳에서 변경된 정보에 접근할 수 있습니다.
하지만
동시성 문제가 발생할 수 있습니다.
생성된 객체들은 전역에서 접근이 가능합니다.
여러 스레드에서 하나의 객체에 접근할 때 순서, 자원의 점유 등으로 인하여 동시성 문제가 발생하여 에러가 발생할 수 있습니다.
동시성 문제에 관하여는 따로 포스팅 하겠습니다.
nestjs에서 이러한 단점에도 싱글턴 패턴을 사용하였습니다.
node는 싱글스레드로 동작을 하게 되므로 동시성 문제에서 피해갈 수 있어 선택하지 않았을까 생각합니다.
(공부 이후에 해당 내용들은 수정될 수 있습니다.)
출처: https://tecoble.techcourse.co.kr/post/2020-11-07-singleton/
https://namu.wiki/w/%EB%94%94%EC%9E%90%EC%9D%B8%20%ED%8C%A8%ED%84%B4
'CS' 카테고리의 다른 글
멀티 프로세스, 싱글 스레드, 멀티 스레드 🔥 (0) | 2022.03.30 |
---|---|
프로세스, 스레드 🔥 (0) | 2022.03.30 |
CQS 원칙 - Command Query Separation (0) | 2022.03.29 |
컴퓨터는 어떻게 켜질까?[컴퓨터 부팅 과정] (2) | 2021.12.12 |