메모이제이션(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)
2021/02/10 - [Study Note/Javascript] - javascript #디자인패턴 - 객체 기반 데커레이터 패턴(decorator pattern)
2021/02/10 - [Study Note/Javascript] - javascript #디자인패턴 - 함수 기반 데커레이터 패턴(decorator pattern)
2021/02/10 - [Study Note/Javascript] - javascript #디자인패턴 - Init-time branching 패턴
2021/02/12 - [Study Note/Javascript] - javascript #디자인패턴 - Self-defining function 패턴
'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 |
댓글