기술 블로그
[CS] 디자인패턴 본문
1. 디자인패턴
라이브러리/프레임워크: 공통으로 사용될 수 있는 특정 기능을 모듈화한 것
디자인 패턴: 프로그램설계시 문제발생을 예방하고 객체 간 상호 관계를 이용해 해결할 수 있도록 한 규약
싱글톤 패턴
하나의 클래스에 하나의 인스턴스만 가지는 패턴
특징
- 데이터베이스 연결 모듈에 자주 사용
- 인스턴스를 공유하여 인스턴스 생성 비용 감소
단점
- 의존성이 높아짐
- 의존성:소프트웨어 개발에서 한 요소나 모듈이 다른 요소나 모듈에 어떻게 의존하고 있는지를 나타내는 개념
- 다른 클래스나 모듈에서 해당 싱글톤 인스턴스에 접근할 수 있고 이로인해 코드가 더 긴밀히 결합되어 하나의 클래스나 모듈이 변경되면 다른 클래스나 모듈도 영향을 받을 수 있음
- 이는 유지보수와 확장성 측면에서 문제을 일으킬 수 있음
- 메인 모듈이 아닌 의존성 주입자를 통해 하위 묘둘에 간접적으로 의존성을 주입
- 의존성 주입 장점
- 모듈을 쉽게 교체할 수 있어 테스팅과 마이그레이션에 유리
- 추상화 레이어를 통해 의존성 방향이 일관되고 쉽게 추론하며 모듈 간 관계가 명확해짐
- 의존성 주입 단점
- 모듈의 분리로 클래스 수가 늘어나 복잡성 증가 가능성
- 런타임 패널티 증가
- 의존성 주입 장점
- TDD 단위테스트에 비적합
- 독립적이지 못한 인스턴스 때문
class Singleton {
constructor() {
if (!Singleton.instance) { //싱글톤 인스턴스가 없다면
Singleton.instance = this // 이 인스턴스가 싱글톤 인스턴스다.
}
return Singleton.instance //객체가 반환되었으므로 해당 객체가 인스턴스로 반환됨
}
getInstance() { // ge
return this
}
}
const a = new Singleton()
const b = new Singleton()
console.log(a === b) // true
const URL = 'mongodb://localhost:27017/kundolapp'
const createConnection = url => ({"url" : url})
class DB {
constructor(url) {
if (!DB.instance) {
DB.instance = createConnection(url)
}
return DB.instance
}
connect() {
return this.instance
}
}
const a = new DB(URL)
const b = new DB(URL)
console.log(a === b) // true
팩토리 패턴
객체 생성부분을 추상화하고 상위 클래스와 하위 클래스의 역할을 구분
class CoffeeFactory { // 상위 클래스
static createCoffee(type) {
const factory = factoryList[type]
return factory.createCoffee()
}
}
class Latte {
constructor() {
this.name = "latte"
}
}
class Espresso {
constructor() {
this.name = "Espresso"
}
}
class LatteFactory extends CoffeeFactory{ //하위클래스
static createCoffee() {
return new Latte()
}
}
class EspressoFactory extends CoffeeFactory{//하위클래스
static createCoffee() {
return new Espresso()
}
}
const factoryList = { LatteFactory, EspressoFactory }
const main = () => {
// 라떼 커피를 주문한다.
const coffee = CoffeeFactory.createCoffee("LatteFactory")
// 커피 이름을 부른다.
console.log(coffee.name) // latte
}
main()
- 상위클래스 : 뼈대 결정
- 하위클래스 : 구체적 내용결정
전략 패턴(정책 패턴)
객체의 행위를 바꾸고싶을때 ‘직접’수정하지 않고 전략(캡슐화된 알고리즘)을 컨텍스트 안에서 바꾸어 상호 교체가 가능하게 만드는 패턴
var passport = require('passport')
, LocalStrategy = require('passport-local').Strategy;
passport.use(new LocalStrategy(
function(username, password, done) {
User.findOne({ username: username }, function (err, user) {
if (err) { return done(err); }
if (!user) {
return done(null, false, { message: 'Incorrect username.' });
}
if (!user.validPassword(password)) {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, user);
});
}
));
즉 메서드가 인자로 결정되는 객체
옵저버 패턴
주체가 어떤 객체의 상태변화를 관찰하다가 변화가 있을 때 메서드 등을 통해 옵저버 목록에 있는 옵저버들에게 변화를 알려주는 디자인패턴
function createReactiveObject(target, callback) {
const proxy = new Proxy(target, {
set(obj, prop, value){
if(value !== obj[prop]){
const prev = obj[prop]
obj[prop] = value
callback(`${prop}가 [${prev}] >> [${value}] 로 변경되었습니다`)
}
return true
}
})
return proxy
}
const a = {
"형규" : "솔로"
}
const b = createReactiveObject(a, console.log)
b.형규 = "솔로"
b.형규 = "커플"
// 형규가 [솔로] >> [커플] 로 변경되었습니다
자바스크립트에서는 프록시 객체를 사용해 구현할 수 있다.
vue3.0프레임워크에서 ref나 reactive정의는 위의 프록시객체로 구현되었다.
프록시 패턴
대상 객체에 접근하기전 그 흐름을 가로채 대상 객체 앞단의 인터페이스 역할을 함
프록시 서버에 활용
프록시 서버: 서버와 클라이언트 사이에서 클라이언트가 자신을 통해 다른 네트워크 서비스에 간접적으로 접속할 수 있게 해주는 것
- Nginx: node.js 앞단의 프록시서버로 활용되며 다수의 연결을 효과적으로 처리한다.
- cloudflare: ddos공격 방어 및 Https구축에 사용
- 프론트 프록시 서버: cors 해결을 위해 사용
이터레이터 패턴
이터레이터를 사용해 컬렉션의 요소들에 접근하는 디자인 패턴
컬렉션의 형태가 다르더라도 이터레이터를 사용해 순회할 수있다.
const mp = new Map()
mp.set('a', 1)
mp.set('b', 2)
mp.set('cccc', 3)
const st = new Set()
st.add(1)
st.add(2)
st.add(3)
const a = []
for(let i = 0; i < 10; i++)a.push(i)
for(let aa of a) console.log(aa)
for(let a of mp) console.log(a)
for(let a of st) console.log(a)
/*
a, b, c
[ 'a', 1 ]
[ 'b', 2 ]
[ 'c', 3 ]
1
2
3
*/
for of 문 이터레이터 프로토콜을 사용해 순회하는 모습
이터레이터 프로토콜: 이터러블한 객체들은 순회할 때 쓰이는 규칙
이터러블한 객체: 반복 가능한 객체로 배열을 일반화한 객체
노출모듈 패턴
즉시 실행 함수를 통해 접근 제어자를 만드는 패턴
const pukuba = (() => {
const a = 1
const b = () => 2
const public = {
c : 2,
d : () => 3
}
return public
})()
console.log(pukuba)
console.log(pukuba.a)
// { c: 2, d: [Function: d] }
// undefined
위와같이 캡슐화를 즉시실행함수로 구현하기도 함
mvc 패턴
모델 뷰 컨트롤러로 이루어진 디자인 패턴
- 모델
- 데이터, 데이터베이스,상수,변수, 변경시 뷰에 반영
- 뷰
- 사용자 인터페이스 요소
- 컨트롤러
- 하나 이상의 모델과 하나 이상의 뷰를 잇는 디리역할. 메인로직 담당
mvp 패턴
mvc패턴에서 c가 프레젠터로 교체된 패턴
- 뷰와 프레젠터는 일대일 관계로 더 강한 결합을 가짐
- 모델의 직접적 뷰 반영을 방지
mvvm 패턴
mvc패턴에서 c가 viewmodel로 교체된 패턴
- 뷰모델: 뷰를 추상화한 계층
- 커맨드와 데이터 바인딩을 가짐
- 뷰와 뷰모델사이 양방향 바인딩을 지원
- 커맨드와 데이터 바인딩을 가짐
프로그래밍 패러다임
패러다임
- 명령형
- 객체지향형
- 객체의 집합으로 프로그램의 상호작용을 표현
- 특징
- 추상화: 핵심 개념,기능을 간추리는것
- 캡슐화:속성과 메서드를 묶고 일부를 감춤
- 상속: 확장가능함
- 다형성:메서드나 클래스가 다양한 방법으로 동작하는 것 .오버로딩,오버라이딩
- 설계원칙
- 단일책임원칙 : 클래스는 각각 하나의 책임을 가져야한다.
- 개방폐쇄원칙 : 확장에는 열리고 수정에는 닫혀야한다.
- 리스코프치환원칙 : 자식은 부모를 대체할 수 있다.
- 인터페이스 분리원칙 : 인터페이스를 분리할수있으면 분리하라
- 의존역전원칙: 부모나 자식 클래스 모두 추상클래스에 의존해야한다.
- 절차지향형
- 연속적인 계산과정
- 모듈화 어렵, 유지보수성 떨어짐
- 객체지향형
- 선언형: 문제를 풀어는것에 집중하는 패러다임 .
- 함수형
- 순수함수 : 출력이 입력에만 의존하는 것
- 고차함수: 함수가 함수를 값처럼 매개변수로 받아 로직을 생성하는 것
- 언어는 일급객체여야함