Servlet ์์, ๋๋ Switch - Case ๋ฑ ๋ง์ ๋ถ๊ธฐ๋ฅผ ํ๋ ๋ฐฉ๋ฒ์ ๋ํ ๋์์ธ & ํผํฌ๋จผ์ค ๊ด์ ์์์ ๊ตฌ๊ฒฝ.
1. if-else ¶
๋๊ฐ ๋ค์์ ์ฝ๋ ์คํ์ผ์ผ๊ฒ์ด๋ค.
WhySwitchStatementsAreBadSmell์ ๊ฑธ๋ฆฌ์ง ์์๊น? ๊ทผ๋ฐ.. ๊ทธ์ ๋ํ ๋ฐ๋ก ์ผ๋ก ๋ค์ด์ค๋๊ฒ์ด '์ด๊ฑด mode ๋ถ๊ธฐ์ด๋ฏ๋ก ์์์์ Switch-Statement ์์์ ์์๋ ๋ค๋ฅธ ์ํฉ ์๋๋. ์ด์ฐจํผ ๋ถ๊ธฐ ์ดํ์ ๊ทธ๋ฅ ํด๋น ๋ถ๋ถ์ด ์คํ๋๊ณ ๋์ด๋ค.' ๋ผ๊ณ ๋งํ ๊ฒ์ด๋ค. ๊ธ์. ๋ชจ๋ฅด๊ฒ ๋ค.
ํํธ์ผ๋ก ๋๊ปด์ง๋ ๊ฒ์ผ๋ก๋, switch ๋ก ๋ถ๊ธฐ๋ฅผ ๋๋ mode string ๊ณผ ์น parameter ์์ ์ค๋ณต์ด ์์ ๊ฒ์ด๋ผ๋ ์ ์ด ๋ณด์ธ๋ค. ๊ทธ๋ฆฌ๊ณ ํ๋์ mode ๊ฐ ๋์ด๋ ๋๋ง๋ค ํด๋น method ๊ฐ ๋์ด๋๊ณ , mode string ์ด ๋์ด๋๊ณ , if-else ๊ตฌ๋ฌธ์ด ์ฃผ์ฑ ๊ธธ์ด์ง๋ค๋ ์ ์ด ์๋ค. ์ง๊ธ์ ๋ฉ์๋๋ก ์ถ์ถ์ ํด๋์ ์ํฉ์ด์ง๋ง, ๋ง์ผ ์ ๋ถ๋ถ์ด ๋ฉ์๋๋ก ์ถ์ถ์ด ์๋์ด์๋ค๋ฉด? ๊ทธ๊ฑด ๋จ ํ๋ง๋ ๋ฐ์ ํ ๋ง์ด ์๋ค. (๋จ, ์ ๋ ผ๋ฌธ์ ์๋ ์ฌ๋์ ํํด์) GotoStatementConsideredHarmful.
~cpp public class IfElse { public void printPerformance(String[] modeExecute) { long start; long end; start = System.currentTimeMillis(); for (int i = 0; i < ModeChoicePerformanceTest.LOOPING_COUNT; i++) { executeIfElse(modeExecute); } end = System.currentTimeMillis(); System.out.println("if - else elapsed time :" + (end - start) + "ms "); } private void executeIfElse(String[] modeExecute) { for (int i = 0; i < modeExecute.length; i++) { executeWithIfElse(modeExecute[i]); } } public void executeWithIfElse(String mode) { if (mode.equals("One")) { doOne(1); } else if (mode.equals("Two")) { doTwo(1); } else if (mode.equals("Three")) { doThree(1); } else if (mode.equals("Four")) { doFour(1); } else if (mode.equals("Five")) { doFive(1); } else { doDefault(1); } } public void doOne(int i) { i = 10; } public void doTwo(int i) { i = 10; } public void doThree(int i) { i = 10; } public void doFour(int i) { i = 10; } public void doFive(int i) { i = 10; } public void doDefault(int i) { i = 100; } }์ด ๋ฐฉ๋ฒ์ ์ผ๋จ ์๋์์ผ๋ก ๊ฐ์ฅ ๋น ๋ฅด๋ค. ํ์ง๋ง, ํํธ์ผ๋ก

ํํธ์ผ๋ก ๋๊ปด์ง๋ ๊ฒ์ผ๋ก๋, switch ๋ก ๋ถ๊ธฐ๋ฅผ ๋๋ mode string ๊ณผ ์น parameter ์์ ์ค๋ณต์ด ์์ ๊ฒ์ด๋ผ๋ ์ ์ด ๋ณด์ธ๋ค. ๊ทธ๋ฆฌ๊ณ ํ๋์ mode ๊ฐ ๋์ด๋ ๋๋ง๋ค ํด๋น method ๊ฐ ๋์ด๋๊ณ , mode string ์ด ๋์ด๋๊ณ , if-else ๊ตฌ๋ฌธ์ด ์ฃผ์ฑ ๊ธธ์ด์ง๋ค๋ ์ ์ด ์๋ค. ์ง๊ธ์ ๋ฉ์๋๋ก ์ถ์ถ์ ํด๋์ ์ํฉ์ด์ง๋ง, ๋ง์ผ ์ ๋ถ๋ถ์ด ๋ฉ์๋๋ก ์ถ์ถ์ด ์๋์ด์๋ค๋ฉด? ๊ทธ๊ฑด ๋จ ํ๋ง๋ ๋ฐ์ ํ ๋ง์ด ์๋ค. (๋จ, ์ ๋ ผ๋ฌธ์ ์๋ ์ฌ๋์ ํํด์) GotoStatementConsideredHarmful.
๋๋ฒ์งธ - Method reflection ¶
~cpp import java.lang.reflect.Method; import java.lang.reflect.InvocationTargetException; public class MethodFullReflection { public void printPerformance(String[] modeExecute) throws InvocationTargetException, IllegalAccessException { long start; long end; start = System.currentTimeMillis(); for (int i = 0; i < ModeChoicePerformanceTest.LOOPING_COUNT; i++) { executeReflection(modeExecute); } end = System.currentTimeMillis(); System.out.println("elapsed time :" + (end - start) + "ms "); } private void executeReflection(String[] modeExecute) throws InvocationTargetException, IllegalAccessException { for (int i = 0; i < modeExecute.length; i++) { Method method = null; try { method = this.getClass().getMethod("do" + modeExecute[i], new Class[]{int.class}); method.invoke(this, new Object[]{new Integer(1)}); } catch (NoSuchMethodException e) { this.doDefault(1); } catch (SecurityException e) { e.printStackTrace(); //To change body of catch statement use Options | File Templates. } } } private void doDefault(int i) { i = 100; } public void doOne(int i) { i = 10; } public void doTwo(int i) { i = 10; } public void doThree(int i) { i = 10; } public void doFour(int i) { i = 10; } public void doFive(int i) { i = 10; } }
์ฅ์ : MODE ๊ฐ ์ถ๊ฐ๋ ๋๋ง๋ค doXXX ์์ผ๋ก ์ด๋ฆ์ ์ ํด์ฃผ๊ณ ์ด๋ฅผ ์คํํ๋ฉด ๋๋ค. ์กฐ๊ฑด ๋ถ๊ธฐ ๋ถ๋ถ์ ์ฝ๋๊ฐ ์ฆ๊ฐ๋์ง ์๊ณ , ํด๋น Mode ๊ฐ ์ถ๊ฐ๋ ๋๋ง๋ค ๋ฉ์๋ ํ๋๋ง ์ถ๊ฐํด์ฃผ๋ฉด ๋๋ค.
๋จ์ : ์๋ฐ์์๋ Method Reflection & Invoke ๊ฐ ์์ฒญ ๋๋ฆฌ๋ค.; ์๋๋ ๋ฐ์๊บผ ์ฐธ์กฐ.
๋จ์ : ์๋ฐ์์๋ Method Reflection & Invoke ๊ฐ ์์ฒญ ๋๋ฆฌ๋ค.; ์๋๋ ๋ฐ์๊บผ ์ฐธ์กฐ.
์ธ๋ฒ์งธ. ์์ ๋ฐฉ๋ฒ์ ๋ณด์ํ ๋ฐฉ๋ฒ์ด๋ค. ๋ฐ๋ก ์ผ์ข ์ Table Lookup. ¶
~cpp import java.util.HashMap; import java.util.Map; import java.lang.reflect.Method; import java.lang.reflect.InvocationTargetException; /** * User: Administrator Date: 2003. 7. 12. Time: ์ค์ 12:48:38 */ public class MethodTableLookupReflection { public void printPerformance(String[] modeExecute) throws InvocationTargetException, IllegalAccessException { long start; long end; start = System.currentTimeMillis(); initReflectionMap(modeExecute); end = System.currentTimeMillis(); System.out.println("reflection with method initialize table elapsed time :" + (end - start) + "ms "); start = System.currentTimeMillis(); for (int i = 0; i < ModeChoicePerformanceTest.LOOPING_COUNT; i++) { executeReflectionWithMapping(modeExecute); } end = System.currentTimeMillis(); System.out.println("reflection with method elapsed time :" + (end - start) + "ms "); } private static Map methodMap; private void executeReflectionWithMapping(String[] modeExecute) throws InvocationTargetException, IllegalAccessException { for (int i = 0; i < modeExecute.length; i++) { Method method = (Method) methodMap.get(modeExecute[i]); if (method != null) method.invoke(this, new Object[]{new Integer(1)}); else doDefault(1); } } private void initReflectionMap(String[] methodNames) { methodMap = new HashMap(); for (int i = 0; i < methodNames.length; i++) { try { methodMap.put(methodNames[i], this.getClass().getMethod("do" + methodNames[i], new Class[]{int.class})); } catch (NoSuchMethodException e) { } catch (SecurityException e) { } } } private void doDefault(int i) { i = 100; } public void doOne(int i) { i = 10; } public void doTwo(int i) { i = 10; } public void doThree(int i) { i = 10; } public void doFour(int i) { i = 10; } public void doFive(int i) { i = 10; } }
์ด ๋ฐฉ๋ฒ์ ์์ ๋ฐฉ๋ฒ๊ณผ ๊ฐ์ ์ฅ์ ์ ์ง๋๋ฉด์ ํผํฌ๋จผ์ค๋ฅผ ๊ฑฐ์ 10๋ฐฐ๊ฐ๋ ํฅ์์ํจ๋ค.
๋ค๋ฒ์งธ. Inner Class ์ ๋ํด Command Pattern ์ ์ฌ์ฉ. ¶
~cpp import java.util.Map; import java.util.HashMap; /** * User: Administrator Date: 2003. 7. 12. Time: ์ค์ 12:57:7 */ public class InterfaceTableLookup { protected Map modeMap = new HashMap(); public void printPerformance(String[] modeExecute) { long start; long end; start = System.currentTimeMillis(); initModeMap(); end = System.currentTimeMillis(); System.out.println("interface table lookup init table elapsed time :" + (end - start) + "ms "); start = System.currentTimeMillis(); for (int i = 0; i < ModeChoicePerformanceTest.LOOPING_COUNT; i++) { executeInnerclassMapping(modeExecute); } end = System.currentTimeMillis(); System.out.println("interface table lookup elapsed time :" + (end - start) + "ms "); } private void executeInnerclassMapping(String[] modeExecute) { for (int i = 0; i < modeExecute.length; i++) { executeMode(modeExecute[i]); } } private void executeMode(String s) { IMode mode = (IMode) modeMap.get(s); if (mode == null) doDefault(1); else mode.execute(1); } public class ExOne implements IMode { public void execute(int i) { i = 10; } } public class ExTwo implements IMode { public void execute(int i) { i = 10; } } public class ExThree implements IMode { public void execute(int i) { i = 10; } } public class ExFour implements IMode { public void execute(int i) { i = 10; } } public class ExFive implements IMode { public void execute(int i) { i = 10; } } private void initModeMap() { modeMap.put("One", new ExOne()); modeMap.put("Two", new ExTwo()); modeMap.put("Three", new ExThree()); modeMap.put("Four", new ExFour()); modeMap.put("Five", new ExFive()); } }
์ด ๋ฐฉ๋ฒ์ initModeMap ์์ ๋งค๋ฒ Mode์ ๋ํ ๋ฑ๋ก์ ํด์ค์ผ ํ๋ค. ํผํฌ๋จผ์ค๋ Method Reflection ๋ณด๋ค ํจ์ฌ ๋น ๋ฅด๋ค.
๋ง์ง๋ง ๋ฐฉ๋ฒ - interface & reflection ¶
์์ ๋ฐฉ๋ฒ์ initModeMap ์ reflection ์ผ๋ก ์ฒ๋ฆฌํ ๊ฒ์ด๋ค.
~cpp import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.HashMap; import java.util.Map; /** * User: Administrator Date: 2003. 7. 12. Time: ์ค์ 1:2:16 */ public class InterfaceTableLookupReflection { public void printPerformance(String[] modeExecute) throws NoSuchMethodException, InstantiationException, ClassNotFoundException, InvocationTargetException, IllegalAccessException { long start; long end; start = System.currentTimeMillis(); initModeMapWithReflection(modeExecute); end = System.currentTimeMillis(); System.out.println("interface reflection & table lookup init able elapsed time :" + (end - start) + "ms "); start = System.currentTimeMillis(); for (int i = 0; i < ModeChoicePerformanceTest.LOOPING_COUNT; i++) { executeInnerclassMapping(modeExecute); } end = System.currentTimeMillis(); System.out.println("interface reflection & table lookup elapsed time :" + (end - start) + "ms "); } private void executeInnerclassMapping(String[] modeExecute) { for (int i = 0; i < modeExecute.length; i++) { executeMode(modeExecute[i]); } } private void executeMode(String s) { IMode mode = (IMode) modeMap.get(s); if (mode == null) doDefault(1); else mode.execute(1); } protected Map modeMap = new HashMap(); public final static String modeClassHeader = "Ex"; String expectedClassNameHeader = this.getClass().getName() + "$" + modeClassHeader; private void initModeMapWithReflection(String[] modeExecute) throws InstantiationException, InvocationTargetException, IllegalAccessException, NoSuchMethodException { Class[] consParamClasses = new Class[]{this.getClass()}; Object[] consParams = new Object[]{this}; Class[] inners = this.getClass().getClasses(); for (int i=0;i<inners.length;i++) { if (inners[i].getName().startsWith(expectedClassNameHeader)) { Constructor innerCons = inners[i].getDeclaredConstructor(consParamClasses); modeMap.put(modeExecute[i], innerCons.newInstance(consParams)); } } } public void doDefault(int i) { i = 100; } public class ExOne implements IMode { public void execute(int i) { i = 10; } } public class ExTwo implements IMode { public void execute(int i) { i = 10; } } public class ExThree implements IMode { public void execute(int i) { i = 10; } } public class ExFour implements IMode { public void execute(int i) { i = 10; } } public class ExFive implements IMode { public void execute(int i) { i = 10; } } }์ด ๋ฐฉ๋ฒ์ Mode ์ถ๊ฐ์ ๊ทธ๋ฅ ExModeName ์์ผ๋ก ์ถ๊ฐํด์ฃผ๋ฉด ๋๋ค. ๊ทธ๋ฌ๋ฉด์ Mode ์กฐ๊ฑด ๋ถ๊ธฐ ๋ถ๋ถ์ด ๋ณํ์ง ์๋๋ค. Reflection์ผ๋ก table lookup ์ฑ์ฐ๋ ๋ถ๋ถ์ด๋ Mode ์กฐ๊ฑด ๋ถ๊ธฐ ๋ถ๋ถ์ ์์ ์์ ํด๋์ค๋ก ์ถ์ถํ ์ ์๋ค. ํผํฌ๋จผ์ค๋ฉด์์๋ ์์ธ๋ก ์์์ ์๋์ผ๋ก map ์ ์ฑ์ฐ๋ ๋ฐฉ๋ฒ๊ณผ ๊ฐ๋ค. ์ ์ฐ์ฑ๊ณผ ํผํฌ๋จผ์ค ๋๊ฐ์ง๊ฐ ์ ์ ํ ์ด์ธ์ด์ง๋ ๋ฐฉ๋ฒ์ด๋ค.
์ด๊ฑด ์์ ํ
์คํธ๋ค์ ํ๋ฒ์ ์คํ์ํค๊ธฐ ์ํ runner class.
~cpp import java.lang.reflect.InvocationTargetException; /* ํ๊ฐํ๋ ค๋ ์กฐ๊ฑด๋ค : 1. ํด๋น method ์ naming ์ผ๋ก reflection call. 2. switch - case ๋ก mode ๊ตฌ๋ถ. 3. interface & class - command pattern ์ ๊ตฌํ. ํ๊ฐ์์๋ค 1. ํด๋น ์ฝ๋ ์์ฑ๋๋น ๋์ด๋๋ ๋ฐ๋ณต ์์ ๋ค 2. performance 3. maintance */ public class ModeChoicePerformanceTest { public static final int LOOPING_COUNT = 1000000; public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, ClassNotFoundException, InstantiationException { String[] modeExecute = new String[]{"One", "Two", "Three", "Four", "Five", "hugu"}; new IfElse().printPerformance(modeExecute); new MethodFullReflection().printPerformance(modeExecute); new MethodTableLookupReflection().printPerformance(modeExecute); new InterfaceTableLookup().printPerformance(modeExecute); new InterfaceTableLookupReflection().printPerformance(modeExecute); } }
๊ฒฐ๊ณผ ¶
~cpp if - else elapsed time :611ms elapsed time :61889ms reflection with method initialize table elapsed time :0ms reflection with method elapsed time :6459ms interface table lookup init table elapsed time :10ms interface table lookup elapsed time :741ms interface reflection & table lookup init able elapsed time :10ms interface reflection & table lookup elapsed time :731ms