์ถ์ฒ: Holub on Patterns by Allen Holub
์ฑ
์์๋ ๋งํ๋ค. ๋ง์ ๊ฐ๋ฐ์๋ค์ด ์ธํฐํ์ด์ค ๋ณด๋ค๋ ์์์ ์ฌ์ฉํ์ฌ ๊ฐ๋ฐํ๋ค๊ณ ... ๊ทธ๋ ๋ค! ์ฌ์ค์ด๋ค. ๋๋ ์ฌ์ง๊ป ์ธํฐํ์ด์ค๋ก ๋ฌด์ฅํ ์ฝ๋๋ฅผ ๋ณด์ง ๋ชปํ๋ค.
์ธ์ ๋ ๊ฐ๋ฐ์ ํ ๋ '์ด๋ผ~ ๊ฐ์ ์ผ ํ๋๋ฐ? ์ด๊ฑฐ Base ํด๋์ค ๋ง๋ค์ด์ ์๋ก ์ฌ๋ ค์ผ ๊ฒ ๋๋ฐ?' ์ผ๋ง์ ํ๋ ์ฃผ์ง ์๊ณ ์คํํ๋ค. ๋คํ์ฑ์ ์ฌ์ฉํ๋ ์ฝ๋๋ฅผ ์์ฑํ๋ค. '์์ฐ~! ํ๊ฒฐ ๊น๋ํด ์ก๋๊ฑธ?' ํ์ง๋ง ์ค์ฐ์ด์๋ค. ์๊ฐ์ด ์ง๋์ ๋จผ๊ฐ ์ถ๊ฐํ ๋์๋ค์ด ์๊ฒผ๋ค. ์ด์ ๊ณ ์น๊ธฐ ์์ํ๋ค. Base ํด๋์ค ๋ถํฐ... ๊ณ ์น๊ณ ๋๋ ์ปดํ์ผ์ด ๋์ง ์๋๋ค. ์ฝ๋ ์์ ์ ์ฌํ๊ฐ ํ์ ํด๋์ค๋ค์๊ฒ ๊น์ง ๋ฏธ์น๋ค. ์ ๋ง ๋ฏธ์น๋ค. ์ด๋ฐ ์์์ ํตํ ๊ณ์ธต ๊ตฌ์กฐ๋ ์์ ํด๋์ค์ ํ์ ํด๋์ค์ ๊ฒฐํฉ๋๋ฅผ ๋์ฌ์ค๋ค. ์ง๋ ์น๊ฒ ํฌ๊ฒ..! ๋๊ฐํ์ง ์๋๊ฐ? ํ๋๋ฅผ ๊ณ ์ณค๋๋ฐ ์์ ํ ๊บผ๋ฆฌ๊ฐ ๋ง๊ตฌ ์์์ง๋ ์ํฉ์...
์์์ ์ฌ์ฉํ๋ ์ํฉ์ ๊ตญํ ์์ผ์ผ ํ ๊ฒ๊ฐ๋ค. ์์ ํด๋์ค์ ๊ธฐ๋ฅ์ 100%๋ก ์ฌ์ฉํ๋ฉด์ ์ถ๊ฐ์ ์ธ ๊ธฐ๋ฅ์ ํ์๋ก ํ๋ ๊ฐ์ฒด๊ฐ ํ์ํ ๋! .. ์ด๋ฐ ์ํฉ์ผ ๋๋ ์์์ ์ฌ์ฉํด๋ ํํ์ด ๋๋ ต์ง ์์ ๊ฒ ๊ฐ๋ค. GoF์ ์ฑ ์ด๋ ๋ค๋ฅธ DP์ ์ฑ ๋ค์ ํญ์ ๋งํ๋ค. ์์ ๋ณด๋ค๋ ์ธํฐํ์ด์ค๋ฅผ ํตํด ๋คํ์ฑ์ ์ฌ์ฉํ๋ผ๊ณ ... ๊ทธ ์ด์ ๋ฅผ ์ด์ ์ผ ์ ๊ฒ๊ฐ๋ค. ๋๊ฐํ์ง ์๋๊ฐ? Base ํด๋์ค๋ฅผ ์์ ํ ๋๋ง๋ค ํ์ ํด๋์ค๋ฅผ ์์ ํด์ผ ํ๋ ์ํฉ์ด ๋ฐ์ํ๋ค๋ฉด ๊ทธ๊ฑด ์ธํฐํ์ด์ค๋ฅผ ํตํด ๋คํ์ฑ์ ์ง์ํ๋๊ฒ ๋ ๋ซ๋ค๋ ์ ํธ์ด๋ค. ๊ฐ์ฒด๋ ์ธ์ ๋ SRP (Single Responsiblity Principle)์ ์ง์ผ์ผ ํ๋ค๊ณ ์๊ฐํ๋ค.
์ธ์ ๋ ๊ฐ๋ฐ์ ํ ๋ '์ด๋ผ~ ๊ฐ์ ์ผ ํ๋๋ฐ? ์ด๊ฑฐ Base ํด๋์ค ๋ง๋ค์ด์ ์๋ก ์ฌ๋ ค์ผ ๊ฒ ๋๋ฐ?' ์ผ๋ง์ ํ๋ ์ฃผ์ง ์๊ณ ์คํํ๋ค. ๋คํ์ฑ์ ์ฌ์ฉํ๋ ์ฝ๋๋ฅผ ์์ฑํ๋ค. '์์ฐ~! ํ๊ฒฐ ๊น๋ํด ์ก๋๊ฑธ?' ํ์ง๋ง ์ค์ฐ์ด์๋ค. ์๊ฐ์ด ์ง๋์ ๋จผ๊ฐ ์ถ๊ฐํ ๋์๋ค์ด ์๊ฒผ๋ค. ์ด์ ๊ณ ์น๊ธฐ ์์ํ๋ค. Base ํด๋์ค ๋ถํฐ... ๊ณ ์น๊ณ ๋๋ ์ปดํ์ผ์ด ๋์ง ์๋๋ค. ์ฝ๋ ์์ ์ ์ฌํ๊ฐ ํ์ ํด๋์ค๋ค์๊ฒ ๊น์ง ๋ฏธ์น๋ค. ์ ๋ง ๋ฏธ์น๋ค. ์ด๋ฐ ์์์ ํตํ ๊ณ์ธต ๊ตฌ์กฐ๋ ์์ ํด๋์ค์ ํ์ ํด๋์ค์ ๊ฒฐํฉ๋๋ฅผ ๋์ฌ์ค๋ค. ์ง๋ ์น๊ฒ ํฌ๊ฒ..! ๋๊ฐํ์ง ์๋๊ฐ? ํ๋๋ฅผ ๊ณ ์ณค๋๋ฐ ์์ ํ ๊บผ๋ฆฌ๊ฐ ๋ง๊ตฌ ์์์ง๋ ์ํฉ์...
์์์ ์ฌ์ฉํ๋ ์ํฉ์ ๊ตญํ ์์ผ์ผ ํ ๊ฒ๊ฐ๋ค. ์์ ํด๋์ค์ ๊ธฐ๋ฅ์ 100%๋ก ์ฌ์ฉํ๋ฉด์ ์ถ๊ฐ์ ์ธ ๊ธฐ๋ฅ์ ํ์๋ก ํ๋ ๊ฐ์ฒด๊ฐ ํ์ํ ๋! .. ์ด๋ฐ ์ํฉ์ผ ๋๋ ์์์ ์ฌ์ฉํด๋ ํํ์ด ๋๋ ต์ง ์์ ๊ฒ ๊ฐ๋ค. GoF์ ์ฑ ์ด๋ ๋ค๋ฅธ DP์ ์ฑ ๋ค์ ํญ์ ๋งํ๋ค. ์์ ๋ณด๋ค๋ ์ธํฐํ์ด์ค๋ฅผ ํตํด ๋คํ์ฑ์ ์ฌ์ฉํ๋ผ๊ณ ... ๊ทธ ์ด์ ๋ฅผ ์ด์ ์ผ ์ ๊ฒ๊ฐ๋ค. ๋๊ฐํ์ง ์๋๊ฐ? Base ํด๋์ค๋ฅผ ์์ ํ ๋๋ง๋ค ํ์ ํด๋์ค๋ฅผ ์์ ํด์ผ ํ๋ ์ํฉ์ด ๋ฐ์ํ๋ค๋ฉด ๊ทธ๊ฑด ์ธํฐํ์ด์ค๋ฅผ ํตํด ๋คํ์ฑ์ ์ง์ํ๋๊ฒ ๋ ๋ซ๋ค๋ ์ ํธ์ด๋ค. ๊ฐ์ฒด๋ ์ธ์ ๋ SRP (Single Responsiblity Principle)์ ์ง์ผ์ผ ํ๋ค๊ณ ์๊ฐํ๋ค.
Holub์ด ์ฌ์ฉํ๋ ์์ ๋ฅผ ๋ณด์. ์์์ ์ฌ์ฉํด Stack์ ๊ตฌํํ๋ค.
class Stack extends ArrayList { private int topOfStack = 0; public void push(Object article) { add(topOfStack++, article); } public Object pop() { return remove(--topOfStack); } public void pushMany(Object[] articles) { for(int i=0; i<articles.length; ++i) push(articles[i]); } }
์๋ฒฝํ Stack์ด๋ค. ํ์ง๋ง ๋ค์ ์ฝ๋๋ฅผ ์ฌ์ฉํ๋ฉด ์ฐ๋ฆฌ๋ Stack ๊ฐ์ฒด๊ฐ ์ด๋ป๊ฒ ๋์๊ฐ์ง ์์ธก ํ ์ ์๋ค.
Stack aStack = new Stack(); stack.push("1"); stack.push("2"); stack.clear(); // ??? ArrayList์ ๋ฉ์๋์ด๋ค...;;
์ ๋ชจ๋ ๊ฐ์ clear ๋ฅผ ์ฌ์ฉํด ์ญ์ ํ๋๋ฐ topOfStack์ ๊ฐ์ ์ฌ์ ํ 3์ผ ๊ฒ์ด๋ค. ์ ์์์ ํตํ ๋ฌธ์ ๋ฅผ ํ๋ ์๊ฒ ๋์๋ค. ์์์ ์ฌ์ฉํ๋ฉด ์์น ์๋ ์์ ํด๋์ค์ ๋ฉ์๋๊น์ง ์์ํ ์ ์๋ค ๋ ๊ฒ์ด๋ค.
์์ ํด๋์ค๊ฐ ๊ฐ์ง๋ ๋ฉ์๋๊ฐ ์ ๋ค๋ฉด ๋ชจ๋ ์ค๋ฒ๋ผ์ด๋ฉํ๋ ๋ฐฉ๋ฒ์ด ์์ง๋ง ๋ง์ฝ ๊ท์ฐฎ์ ์ ๋๋ก ๋ง์ ๋ฉ์๋๊ฐ ์๋ค๋ฉด ์ค๋ ์๊ฐ์ด ๊ฑธ๋ฆด ๊ฒ์ด๋ค. ๊ทธ๋ฆฌ๊ณ ๋ง์ฝ ์์ ํด๋์ค๊ฐ ์์ ๋๋ค๋ฉด ๋ค์ ๊ทธ ์ฌํ๊ฐ ํ์ ํด๋์ค์๊ฒ ์ ๋ฌ๋๋ค. ๋ ๋ค๋ฅธ ๋ฐฉ๋ฒ์ผ๋ก ํจ์๋ฅผ ์ค๋ฒ๋ผ์ด๋ฉํ์ฌ ์์ธ๋ฅผ ๋์ง๋๋ก ๋ง๋ค์ด ์์น์๋ ํธ์ถ์ ๋ง์ ์ ์์ง๋ค. ํ์ง๋ง ์ด๋ ์ปดํ์ผ ํ์ ์๋ฌ๋ฅผ ๋ฐํ์ ์๋ฌ๋ก ๋ฐ๊พธ๋ ๊ฒ์ด๋ค. ๊ทธ๋ฆฌ๊ณ LSP (Liskov Sustitution Principle : "๊ธฐ๋ฐ ํด๋์ค๋ ํ์ํด๋์ค๋ก ๋์ฒด ๊ฐ๋ฅํด์ผ ํ๋ค") ์์น์ ์ด๊ธฐ๊ฒ ๋๋ค. ๋น์ฐํ ArrayList๋ฅผ ์์๋ฐ์ Stack์ clear ๋ฉ์๋๋ฅผ ์ฌ์ฉํ ์ ์์ด์ผ ํ๋ค. ๊ทธ๋ฐ๋ฐ ์์ธ๋ฅผ ๋์ง๋ค๋ ๋ง์ด ๋๋๊ฐ?
Stack์ ๊ตฌํํ๋ ๋ค๋ฅธ ๋ฐฉ๋ฒ์ ์์ ๋์ ์บก์ํ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด๋ค.
class Stack { private int topOfStack = 0; private ArrayList theData = new ArrayList(); public void push(Object article){ theData.add(topOfStack++, article); } public Object pop() { return theData.remove(--topOfStack); } public void pushMany(Object [] articles) { for(int i=0; i<articles.length; ++i) push(articles[i]); } public int size() { return return theData.size(); } }์.. Stack๊ณผ ArrayList๊ฐ์ ๊ฒฐํฉ๋๊ฐ ๋ง์ด ๋ฎ์ ์ก๋ค. ๊ตฌํํ์ง ์์ clear ๋ฐ์ ํธ์ถ ๋์ง๋ ์๋๋ค. ์ ์ง ํฉ์ฑ์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ด ๋ ๋์ ๊ฒ ๊ฐ๋ค. ์ด๋ฐ ๋ง๋ ์๋ค. ์์ ๋ณด๋ค๋ ํฉ์ฑ์ ์ฌ์ฉํ๋ผ๊ณ ... ์ ๋ค์ ๋ณธ๋ก ์ผ๋ก ๋ค์ด์ ์ Stack์ ์์ํ๋ ํด๋์ค๋ฅผ ๋ง๋ค์ด ๋ณด์. MonitorableStack์ Stack์ ์ต์, ์ต๋ ํฌ๊ธฐ๋ฅผ ๊ธฐ์ตํ๋ Stack์ด๋ค.
class MonitorableStack extends Stack { private int maxHeight = 0; private int minHeight = 0; public void push(Object o) { push(0); if(size() > maxHeight) maxHeight = size(); } public Object pop() { Object poppedItem = pop(); if(size() < minHeight) minHeight = size(); return poppedItem; } public int maximumSize() { return maxHeight; } public int minimumSize() { return minHeight; } }๊น๋ํ ์ฝ๋๊ฐ ๋์๋ค. ํ์ง๋ง MonitorableStack์ pushMany ํจ์๋ฅผ ์์ํ๋ค. MonitorableStack์ ์ฌ์ฉํด pushMany ํจ์๋ฅผ ํธ์ถํ๋ฉด MonitorableStack์ ์ ๋ ฅ ๋ฐ์ articles์ articles.length ๋งํผ push๊ฐ ํธ์ถ๋๋ค. ํ์ง๋ง ์ง๊ธ ํธ์ถ๋ push ๋ฉ์๋๋ MonitorableStack์ ๊ฒ์ด๋ผ๋ ์ ! ๋งค๋ฒ size() ํจ์๋ฅผ ํธ์ถํด ์ต๋ ํฌ๊ธฐ๋ฅผ ๊ฐฑ์ ํ๋ค. ์๋๊ฐ ๋๋ ค์ง ์๋ ์๋ค. ๊ทธ๋ฆฌ๊ณ ๋ง์ฝ ๋๊ตฐ๊ฐ Stack์ ์ฝ๋๋ฅผ ๋ณด๊ณ pushMany ํจ์์ ๋น ํจ์จ์ฑ ๋๋ฌธ์ Stack์ ๋ฐ์ ์ฝ๋์ ๊ฐ์ด ์์ ํ๋ค๋ฉด ์ด๋ป๊ฒ ๋ ๊ฒ์ธ๊ฐ???
class Stack { private int topOfStack = -1; private Object[] theData = new Object[1000]; public void push(Object article) { theData[++topOfStack] = article; } public Object pop() { Object popped = theData[topOfStack--]; theData[topOfStack] = null; return popped; } public void pushMany(Object [] articles) { assert((topOfStack + articles.length) < theData.length); System.arraycopy(articles, 0, theData, topOfStack + 1, articles.length); topOfStack += articles.length; } public int size() { return topOfStack + 1; } }์!~ ์์ ์ Stack๋ณด๋ค ์ฑ๋ฅ์ ํ์คํ ์ข์ ์ก์ ๊ฒ์ด๋ค. ๊ทธ๋ฐ๋ฐ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค. ๋์ด์ pushMany ๋ฉ์๋์์ push ๋ฉ์๋๋ฅผ ํธ์ถํ์ง ์๋๋ค. ์ด๋ ๊ฒ ๋๋ฉด MonitorableStack์ ๋์ด์ Stack์ ์ต๋ ํฌ๊ธฐ๋ฅผ ์ถ์ ํ์ง ๋ชปํ๊ฒ ๋๋ค. ์๊ธฐ์น ์์ ๊ฒฐ๊ณผ์ด๋ค. ์์์ ์ฌ์ฉํ ๊ตฌํ์ผ๋ก ๋ฐ์ํ ๋ฌธ์ ์ด๋ค. ์ฌ๊ธฐ๊น์ง ๊ธ์ (์ฑ ์ ๋ด์ฉ) ์ฝ์๋ค๋ฉด, ์๋ง '์์์ ์ฌ์ฉํ๊ธฐ ์ ์ ํ๋ฒ ๋ ์๊ฐํ๋๊ฒ ์ข๊ฒ ๋ค' ๋ผ๋ ์๊ฐ์ ๊ฐ์ด ๊น์ด ๋๊ผ์ ๊ฒ์ด๋ค. ์๋๋ฉด ๋ณ์ ์๋ ์ผ์ด๋ค...

์ ๊ธธ์๋ ์ฌํ์ ์ข
์ฐฉ์ ์ด๋ค. ์ต์ข
์ฝ๋๋ฅผ ๋ณด์. ์์์ ๋งํ๋ ์์๋ณด๋ค๋ ํฉ์ฑ์... ์์๋ณด๋ค๋ ์ธํฐํ์ด๋ฅผ... ์ด๋ ๋ง์ ์ข
ํฉ ํ๋ฉด ...
"์์(ํฉ์ฑ)์ ํตํด ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ์." ๋ผ๋ ๊ฒฐ๋ก ์ด ๋์จ๋ค.
interface Stack { void push(Object article); Object pop(); void pushMany(Object [] articles); int size(); } class SimpleStack implements Stack { private int topOfStack = 0; private ArrayList theData = new ArrayList(); public void push(Object article){ theData.add(topOfStack++, article); } public Object pop() { return theData.remove(--topOfStack); } public void pushMany(Object [] articles) { for(int i=0; i<articles.length; ++i) push(articles[i]); } public int size() { return return theData.size(); } } class MonitorableStack implements Stack { private int maxHeight = 0; private int minHeight = 0; private SimpleStack stack = new SimpleStack(); public void push(Object o) { stack.push(0); if(stack.size() > maxHeight) maxHeight = size(); } public Object pop() { Object poppedItem = stack.pop(); if(size() < minHeight) minHeight = stack.size(); return poppedItem; } public void pushMany(Object [] articles) { for(int i=0; i<articles.length; ++i) push(articles[i]); if(stack.size() > maxHeight) maxHeight = stack.size(); } public int maximumSize() { return maxHeight; } public int minimumSize() { return minHeight; } public int size() { stack.size(); } }์์ฑ๋ ์ฝ๋์์๋ ์์์ผ๋ก ์ธํ ๋ฌธ์ ๋ค์ด ๋ฐ์ํ์ง ์๋๋ค.