메모이제이션(memoization) 패턴
패턴의 이름대로 '메모'를 하는 것이 특징입니다. 메모를 하는 대상은 바로 함수 또는 객체입니다. 이 패턴의 활용 용도는 일반적인 프로그래밍 언어에서 캐시를 사용하는 것처럼 사용한다고 생각하면 됩니다.
적용예시1. 메모이제이션 패턴을 이용한 캐시 구현
특정 아이템을 검색하려고 할 때, 검색 결과를 ID 기반으로 캐시 처리하는 메모이제이션 패턴입니다. 메모이제이션 패턴의 메모를 하는 대상은 변수가 될 수도 있고 함수가 될 수도 있습니다. 아래 소스에서 검색하는 함수가 searchItem() 이라면 이 함수 자체에 cache라는 속성을 객체로 추가하고, cache에는 아이템이 있으면 캐시에 저장된 객체를 반환하고 없으면 서버로 요청합니다.
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<input type="text" id="itemId">
<button id="search">Search</button>
<script>
(function () {
let inputItemId = document.getElementById("itemId");
function searchItem(id) {
let xhr;
if (searchItem.cache.hasOwnProperty(id)) {
return searchItem.cache[id];
}
xhr = new XMLHttpRequest();
xhr.open("GET", "/searchItem");
xhr.onload = function () {
let item = JSON.parse(xhr.responseText);
searchItem.cache[item.id] = item;
}
xhr.send();
}
searchItem.cache = {};
document.getElementById("search").addEventListener("click", function () {
searchItem(searchItem.value);
});
}());
</script>
</body>
</html>
적용예시2. 메모이제이션 패턴으로 피보나치 수열 구현
메모이제이션 패턴은 피보나치 수열의 입력값을 메모에 저장하여 메모에 있으면 다시 불러오는 식으로 동작합니다. 이러한 식으로 이전에 계산된 결과를 토대로 새로운 결과를 계산할 수 있을 때, 캐시 등을 이용하면 그것을 동적 프로그래밍(dynamic programming)이라고 합니다.
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<script>
(function () {
function fibonacci(n) {
if (n === 0 || n ===1) {
return n;
} else {
return fibonacci(n - 1) +fibonacci(n - 2);
}
}
let fibonacciMemo = (function () {
return function (n) {
let result = fibonacciMemo.memo[n];
if (typeof result !== 'number') {
result = fibonacciMemo(n - 1) + fibonacciMemo(n - 2);
fibonacciMemo.memo[n] = result;
}
return result;
};
}());
fibonacciMemo.memo = [0, 1];
let testNum = 40,
start, end;
// 최초 호출
start = Date.now();
console.log(fibonacci(testNum)); //==>> 102334155
end = Date.now();
console.log(`Elapse time of ${((end-start)/1000).toFixed(2)} seconds for recursive fibonacci(${testNum})`);
//==>> Elapse time of 1.93 seconds for recursive fibonacci(40)
//참고. 소요시간은 조금씩 달라질 수 있습니다.
// 두 번째 호출 : 0초 소요
start = Date.now();
console.log(fibonacciMemo(testNum)); //==>> 102334155
end = Date.now();
console.log(`Elapsed time of ${((end-start)/1000).toFixed(2)} seconds for recursive fibonacciMemo(${testNum})`);
//==>> Elapse time of 0.00 seconds for recursive fibonacci(40)
// 세 번째 호출 : 0초 소요
start = Date.now();
console.log(fibonacciMemo(testNum)); //==>> 102334155
end = Date.now();
console.log(`Elapsed time of ${((end-start)/1000).toFixed(2)} seconds for recursive fibonacciMemo(${testNum})`);
//==>> Elapse time of 0.00 seconds for recursive fibonacci(40)
}());
</script>
</body>
</html>
적용예시3. Function.prototype 활용
이러한 메모이제이션 패턴을 일반적으로 사용할 수 있도록 Function의 프로토타입에 추가해서 사용하면 복잡하지 않고 편리하게 활용할 수 있습니다. Function.prototype에 추가하는 memoize() 함수는 재귀가 아닌 일반적인 산술 처리나 XMLHttpRequest 등과 같이 캐시를 활용할 수 있는 함수에 추가 기능으로 사용하면 좋고, 재귀 함수는 직접 메모이제이션 패턴을 설계하여 사용하는 것이 더 좋은 성능을 보여줍니다.
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<script>
(function () {
Function.prototype.memoize = function () {
let _this = this,
memo = {};
return function () {
let argsString = JSON.stringify(arguments),
returnValue;
if (memo.hasOwnProperty(argsString)) {
return memo[argsString];
} else {
returnValue = _this.apply(this, arguments);
memo[argsString] = returnValue;
return returnValue;
}
}
};
function fibonacci(n) {
if (n === 0 || n === 1) {
return n;
} else {
return fibonacci(n - 1) + fibonacci(n - 2);
}
}
let fibonacciMemo = fibonacci.memoize();
let testNum = 40,
start, end;
// 최초 조회
start = Date.now();
console.log(fibonacciMemo(testNum));
end = Date.now();
console.log(`Elapsed time of ${((end-start)/1000).toFixed(2)} seconds for recursive fibonacciMemo(${testNum}) for the first time`);
//==>> Elapsed time of 1.97 seconds for recursive fibonacciMemo(40) for the first time
// 두 번째 조회
start = Date.now();
console.log(fibonacciMemo(testNum));
end = Date.now();
console.log(`Elapsed time of ${((end-start)/1000).toFixed(2)} seconds for recursive fibonacciMemo(${testNum}) for the first time`);
//==>> Elapsed time of 0.00 seconds for recursive fibonacciMemo(40) for the first time
}());
</script>
</body>
</html>
#
참고도서 : 속깊은 자바스크립트
이전글 :
2021/02/10 - [Study Note/Javascript] - javascript #디자인패턴 - 폼 검증을 위한 데커레이터 패턴(decorator pattern)
javascript #디자인패턴 - 폼 검증을 위한 데커레이터 패턴(decorator pattern)
데커레이터 패턴 프락시 패턴은 호출되는 객체가 아닌 별도의 중간자 역할을 수행하는 프락시 객체가 추가 기능을 수행하는 패턴이라고 한다면, 데커레이터(decorator) 패턴은 호출 대상이 되는
simuing.tistory.com
2021/02/10 - [Study Note/Javascript] - javascript #디자인패턴 - 객체 기반 데커레이터 패턴(decorator pattern)
javascript #디자인패턴 - 객체 기반 데커레이터 패턴(decorator pattern)
하나의 객체에 여러 가지 기능들을 추가함으로써 기존의 객체에 추가로 꾸며진 객체를 만들어낼 수 있습니다. 데커레이터 패턴을 이용해 각기 다른 부품으로 이루어진 컴퓨터의 가격을 알아봅
simuing.tistory.com
2021/02/10 - [Study Note/Javascript] - javascript #디자인패턴 - 함수 기반 데커레이터 패턴(decorator pattern)
javascript #디자인패턴 - 함수 기반 데커레이터 패턴(decorator pattern)
프락시 패턴에서 살펴봤던 래퍼 기능을 데커레이터 패턴과 함께 사용하면, 해당 함수가 호출되기 전에 여러 가지 함수가 호출될 수 있도록 응용할 수 있습니다. 이렇게 응용할 수 있는 상황은
simuing.tistory.com
2021/02/10 - [Study Note/Javascript] - javascript #디자인패턴 - Init-time branching 패턴
javascript #디자인패턴 - Init-time branching 패턴
Init-time branching 패턴 Init-time branching 패턴은 이름 그대로 초기화 단계에서 분기하여 같은 함수를 환경에 따라 다르게 정의하는 것을 의미합니다. 보통 웹페이지가 처음 열릴 때 실행된다고 해서 I
simuing.tistory.com
2021/02/12 - [Study Note/Javascript] - javascript #디자인패턴 - Self-defining function 패턴
javascript #디자인패턴 - Self-defining function 패턴
Self-defining function 패턴 앞서 살펴본 Init-time branching 패턴은 처음 웹페이지 초기화 단계에서 컴퓨팅 자원을 소모하여 향후 어떠한 방법으로 함수가 호출될 지 결정하는 것이었다면, Self-defining funct
simuing.tistory.com
'Study Note > Javascript' 카테고리의 다른 글
javascript #디자인패턴 - 콜백 패턴(callback pattern) (0) | 2021.02.13 |
---|---|
javascript #디자인패턴 - Self-invoking constructor 패턴 (0) | 2021.02.13 |
javascript #디자인패턴 - Self-defining function 패턴 (0) | 2021.02.12 |
javascript #디자인패턴 - Init-time branching 패턴 (0) | 2021.02.10 |
javascript #디자인패턴 - 함수 기반 데커레이터 패턴(decorator pattern) (0) | 2021.02.10 |
댓글