์ด์ „ ์žฅ๋“ค์—์„œ๋Š” Code, Code Block, ํ•จ์ˆ˜ ๊ตฌํ˜„ ๋ฐฉ๋ฒ•, ํ•จ์ˆ˜๋“ค์˜ ์—ฐ๊ด€ ๋ฐฉ์‹์„ ์•Œ์•„๋ณด์•˜์ง€๋งŒ, ์ด๋ฒˆ์—๋Š” ์กฐ๊ธˆ๋” ๊ณ ์ฐจ์›์˜ ์–˜๊ธฐ๋ฅผ ํ• ๊นŒ ํ•œ๋‹ค.

ํด๋ž˜์Šค ์ฒด๊ณ„

Java Convention์— ๋”ฐ๋ฅด๋ฉด ๊ฐ€์žฅ ๋จผ์ € ๋ณ€์ˆ˜ ๋ชฉ๋ก์ด ๋‚˜์˜จ๋‹ค. static public, static private, private instance, (public instance), public function, private function ์ˆœ์œผ๋กœ ์ „๊ฐœ๋œ๋‹ค. ์ฆ‰, ์ถ”์ƒํ™” ๋‹จ๊ณ„๊ฐ€ ์ˆœ์ฐจ์ ์œผ๋กœ ๋‚ด๋ ค๊ฐ„๋‹ค.

์บก์Šํ™”

๋ณ€์ˆ˜์™€ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํ•จ์ˆ˜๋Š” ๊ฐ€๋Šฅํ•œ ๊ณต๊ฐœํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ์ข‹๋‹ค. ํ•˜์ง€๋งŒ ๊ทธ๋ ‡๋‹ค๊ณ  ๋ฐ˜๋“œ์‹œ ์ˆจ๊ฒจ์•ผ ํ•˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด protected๋กœ ์ ‘๊ทผ์„ ํ—ˆ์šฉํ•˜๊ธฐ๋„ ํ•œ๋‹ค. ํ•˜์ง€๋งŒ ๋ช…์‹ฌํ•ด์•ผ ํ•˜๋Š” ๊ฒƒ์€ ๋น„๊ณต๊ฐœ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•  ์˜จ๊ฐ– ๋ฐฉ๋ฒ•์„ ๊ฐ•๊ตฌํ•˜๊ณ , ์บก์Šํ™”๋ฅผ ํ’€์–ด์ฃผ๋Š” ๊ฒฐ์ •์€ ์ตœํ›„์˜ ์ˆ˜๋‹จ์ด๋ผ๋Š” ๊ฒƒ์ด๋‹ค.

ํด๋ž˜์Šค๋Š” ์ž‘์•„์•ผ ํ•œ๋‹ค!

์—ญ์‹œ ํ•จ์ˆ˜์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์ž‘์•„์•ผ ํ•œ๋‹ค. ํ•จ์ˆ˜์˜ ๊ฒฝ์šฐ์—๋Š” ๋ฌผ๋ฆฌ์ ์ธ ํ–‰์ˆ˜๋กœ ์ด๋ฅผ ๊ฐ€๋Š ํ–ˆ๋‹ค. ํด๋ž˜์Šค์˜ ๊ฒฝ์šฐ๋Š” ๋งก์€ ์ฑ…์ž„์„ ์ธก์ •ํ•œ๋‹ค.

// ์–ด๋งˆ์–ด๋งˆํ•˜๊ฒŒ ํฐ ์Šˆํผ ๋งŒ๋Šฅ ํด๋ž˜์Šค
 
public class SuperDashboard extends JFrame implements MetaDataUser {
	public String getCustomizerLanguagePath()
	public void setSystemConfigPath(String systemConfigPath) 
	public String getSystemConfigDocument()
	public void setSystemConfigDocument(String systemConfigDocument) 
	public boolean getGuruState()
	public boolean getNoviceState()
	public boolean getOpenSourceState()
	public void showObject(MetaObject object) 
	public void showProgress(String s)
	public boolean isMetadataDirty()
	public void setIsMetadataDirty(boolean isMetadataDirty)
	public Component getLastFocusedComponent()
	public void setLastFocused(Component lastFocused)
	public void setMouseSelectState(boolean isMouseSelected) 
	public boolean isMouseSelected()
	public LanguageManager getLanguageManager()
	public Project getProject()
	public Project getFirstProject()
	public Project getLastProject()
	public String getNewProjectName()
	public void setComponentSizes(Dimension dim)
	public String getCurrentDir()
	public void setCurrentDir(String newDir)
	public void updateStatus(int dotPos, int markPos)
	public Class[] getDataBaseClasses()
	public MetadataFeeder getMetadataFeeder()
	public void addProject(Project project)
	public boolean setCurrentProject(Project project)
	public boolean removeProject(Project project)
	public MetaProjectHeader getProgramMetadata()
	public void resetDashboard()
	public Project loadProject(String fileName, String projectName)
	public void setCanSaveMetadata(boolean canSave)
	public MetaObject getSelectedObject()
	public void deselectObjects()
	public void setProject(Project project)
	public void editorAction(String actionName, ActionEvent event) 
	public void setMode(int mode)
	public FileManager getFileManager()
	public void setFileManager(FileManager fileManager)
	public ConfigManager getConfigManager()
	public void setConfigManager(ConfigManager configManager) 
	public ClassLoader getClassLoader()
	public void setClassLoader(ClassLoader classLoader)
	public Properties getProps()
	public String getUserHome()
	public String getBaseDir()
	public int getMajorVersionNumber()
	public int getMinorVersionNumber()
	public int getBuildNumber()
	public MetaObject pasting(MetaObject target, MetaObject pasted, MetaProject project)
	public void processMenuItems(MetaObject metaObject)
	public void processMenuSeparators(MetaObject metaObject) 
	public void processTabPages(MetaObject metaObject)
	public void processPlacement(MetaObject object)
	public void processCreateLayout(MetaObject object)
	public void updateDisplayLayer(MetaObject object, int layerIndex) 
	public void propertyEditedRepaint(MetaObject object)
	public void processDeleteObject(MetaObject object)
	public boolean getAttachedToDesigner()
	public void processProjectChangedState(boolean hasProjectChanged) 
	public void processObjectNameChanged(MetaObject object)
	public void runProject()
	public void setAรงowDragging(boolean allowDragging) 
	public boolean allowDragging()
	public boolean isCustomizing()
	public void setTitle(String title)
	public IdeMenuBar getIdeMenuBar()
	public void showHelper(MetaObject metaObject, String propertyName) 
	
	// ... many non-public methods follow ...
}

๋„ˆ๋ฌด ํฌ๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด ๋ช‡๊ฐœ์˜ ๋ฉ”์„œ๋“œ๋งŒ ์ถ”๋ฆฌ๋ฉด ๊ดœ์ฐฎ์„๊นŒ?

// ๋ฉ”์†Œ๋“œ๋ฅผ 5๊ฐœ๋กœ ์ค„์ธ๋‹ค๊ณ  ํ•˜๋”๋ผ๋„ ์—ฌ์ „ํžˆ ์ฑ…์ž„์ด ๋งŽ๋‹ค..
 
public class SuperDashboard extends JFrame implements MetaDataUser {
	public Component getLastFocusedComponent()
	public void setLastFocused(Component lastFocused)
	public int getMajorVersionNumber()
	public int getMinorVersionNumber()
	public int getBuildNumber() 
}

๊ฒ‰์œผ๋กœ๋Š” ์ž‘์•„๋ณด์—ฌ์„œ ์ข‹์ง€๋งŒ, ์„ธ์„ธํžˆ ๋ณด๋ฉด ์ด ํด๋ž˜์Šค๋Š” ๋‘๊ฐœ์˜ ์ฑ…์ž„์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค. ์ฒซ์งธ, ๋ฒ„์ „ ์ •๋ณด๋ฅผ ์ถ”์ ํ•œ๋‹ค. ๋‘˜์งธ, UI ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ด€๋ฆฌํ•œ๋‹ค.

์ด๋ ‡๊ฒŒ ์ฑ…์ž„์ด ์—ฌ๋Ÿฌ๊ฐœ์ธ ๊ฒฝ์šฐ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ„๋‹จํ•œ ๋ฐฉ๋ฒ•์€ ์ž‘๋ช…์ด๋‹ค. ์ด๋ฆ„ ์ง“๊ธฐ๊ฐ€ ๊ณค๋ž€ํ•˜๋‹ค๋ฉด, ํด๋ž˜์Šค ํฌ๊ธฐ๊ฐ€ ๋„ˆ๋ฌด ์ปค์„œ ๊ทธ๋ ‡๋‹ค. Processor, Manager, Super์™€ ๊ฐ™์ด ๋ชจํ˜ธํ•œ ๋‹จ์–ด๊ฐ€ ์žˆ๋‹ค๋ฉด ์—ฌ๋Ÿฌ ์ฑ…์ž„์„ ๋– ์•ˆ๊ฒผ๋‹ค๋Š” ์ฆ๊ฑฐ๋‹ค. ๋˜ if, and, or, but๊ณผ ๊ฐ™์€ ๋‹จ์–ด์—†์ด 25์ž ์•ˆ์— ํ‘œํ˜„๋˜์–ด์•ผ ํ•œ๋‹ค.

๋‹จ์ผ ์ฑ…์ž„ ์›์น™

๋‹จ์ผ ์ฑ…์ž„ ์›์น™์€ ํด๋ž˜์Šค๋‚˜ ๋ชจ๋“ˆ์„ ๋ณ€๊ฒฝํ•  ์ด์œ ๊ฐ€ ๋‹จ ํ•˜๋‚˜์—ฌ์•ผ ํ•œ๋‹ค๋Š” ์›์น™์ด๋‹ค. ํ•˜์ง€๋งŒ ์œ„์—์„œ ๋งํ–ˆ๋“ฏ SuperDashBoard์˜ ๊ฒฝ์šฐ ์ฑ…์ž„์ด ๋‘๊ฐœ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋ณ€๊ฒฝ์ด์œ ๋„ ๋‘๊ฐ€์ง€๋‹ค.

// ์ด ์ฝ”๋“œ๋Š” ์ž‘์•„๋ณด์ด์ง€๋งŒ, ๋ณ€๊ฒฝํ•  ์ด์œ ๊ฐ€ 2๊ฐ€์ง€์ด๋‹ค.
 
public class SuperDashboard extends JFrame implements MetaDataUser {
	public Component getLastFocusedComponent()
	public void setLastFocused(Component lastFocused)
	public int getMajorVersionNumber()
	public int getMinorVersionNumber()
	public int getBuildNumber() 
}

์ด๋Ÿฐ ๊ฒฝ์šฐ ๋ถ„๋ฆฌ๋ฅผ ํ†ตํ•œ ์ถ”์ƒํ™”๋ฅผ ์ ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋” ์ข‹๋‹ค.

// ์œ„ ์ฝ”๋“œ์—์„œ ๋ฒ„์ „ ์ •๋ณด๋ฅผ ๋‹ค๋ฃจ๋Š” ๋ฉ”์„œ๋“œ 3๊ฐœ๋ฅผ ๋”ฐ๋กœ ๋นผ์„œ
// Version์ด๋ผ๋Š” ๋…์ž์ ์ธ ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด ๋‹ค๋ฅธ ๊ณณ์—์„œ ์žฌ์‚ฌ์šฉํ•˜๊ธฐ ์‰ฌ์›Œ์กŒ๋‹ค.
 
public class Version {
	public int getMajorVersionNumber() 
	public int getMinorVersionNumber() 
	public int getBuildNumber()
}

SRP๋Š” ๊ฐ์ฒด์ง€ํ–ฅ์„ค๊ณ„์—์„œ ๋”์šฑ ์ค‘์š”ํ•œ ๊ฐœ๋…์ด๊ณ , ์ง€ํ‚ค๊ธฐ ์ˆ˜์›”ํ•œ ๊ฐœ๋…์ธ๋ฐ, ๊ฐœ๋ฐœ์ž๊ฐ€ ๊ฐ€์žฅ ๋ฌด์‹œํ•˜๋Š” ๊ทœ์น™ ์ค‘ ํ•˜๋‚˜์ด๋‹ค. ์ด๋Š” ๋Œ€๋ถ€๋ถ„์˜ ํ”„๋กœ๊ทธ๋ž˜๋จธ๋“ค์ด ๋Œ์•„๊ฐ€๋Š” ์†Œํ”„ํŠธ์›จ์–ด์— ์ดˆ์ ์„ ๋งž์ถ”๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ „์ ์œผ๋กœ ์˜ฌ๋ฐ”๋ฅธ ํƒœ๋„์ด๊ธฐ๋Š” ํ•˜์ง€๋งŒ, ๋Œ์•„๊ฐ€๋Š” ์†Œํ”„ํŠธ์›จ์–ด๊ฐ€ ์ž‘์„ฑ๋˜๋ฉด ๊นจ๋—ํ•˜๊ณ  ์ฒด๊ณ„์ ์ธ ์†Œํ”„ํŠธ์›จ์–ด๋ผ๋Š” ๋‹ค์Œ ๊ด€์‹ฌ์‚ฌ๋กœ ์ „ํ™˜์„ ํ•ด์•ผํ•œ๋‹ค.

์‹œ์Šคํ…œ์„ ๊ตฌ์„ฑํ•˜๋Š”๋ฐ ์žˆ์–ด ๊ฒฐ๊ตญ ๋Œ์•„๊ฐ€๋Š” ๋ถ€ํ’ˆ์˜ ๊ฐœ์ˆ˜๋Š” ๋น„์Šทํ•˜๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด ๊ทธ ๋ถ€ํ’ˆ(component)๋“ค์„ ์–ด๋–ป๊ฒŒ ๊ด€๋ฆฌํ•˜๊ณ  ์‹ถ์€๊ฐ€? ์ž‘์€ ์„œ๋ž์„ ์—ฌ๋Ÿฌ๊ฐœ ๋‘๊ณ  ์ด๋ฆ„ํ‘œ๋ฅผ ๋ถ™์—ฌ์„œ ๊ด€๋ฆฌํ•˜๊ณ  ์‹ถ์€๊ฐ€, ํฐ ์„œ๋žํ•˜๋‚˜์— ๋ชจ๋‘ ๋„ฃ์–ด๋ฒ„๋ฆฌ๊ณ  ์‹ถ์€๊ฐ€? ๋‹น์—ฐํžˆ ์ž‘์€ ํด๋ž˜์Šค ์—ฌ๋Ÿฟ์œผ๋กœ ์ด๋ค„์ง„ ์‹œ์Šคํ…œ์ด ๋” ๋ฐ”๋žŒ์งํ•˜๋‹ค.

์‘์ง‘๋„(Cohesion)

ํด๋ž˜์Šค๋Š” ์ธ์Šคํ„ด์Šค ๋ณ€์ˆ˜ ์ˆ˜๊ฐ€ ์ž‘์•„์•ผ ํ•œ๋‹ค. ๋ชจ๋“  ์ธ์Šคํ„ด์Šค ๋ณ€์ˆ˜๋ฅผ ๋ฉ”์„œ๋“œ๋งˆ๋‹ค ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ๊ฐ€์žฅ ์‘์ง‘๋„๊ฐ€ ๋†’์„ ๊ฒƒ์ด๋‹ค. ์ด๋ฅผ ์„ ํ˜ธํ•˜๋Š” ์ด์œ ๋Š”, ํด๋ž˜์Šค ๋‚ด๋ถ€์— ์žˆ๋Š” ๋ฉ”์„œ๋“œ์™€ ๋ณ€์ˆ˜๊ฐ€ ์„œ๋กœ ์˜์กดํ•˜๋ฉฐ ๋…ผ๋ฆฌ์ ์ธ ๋‹จ์œ„๋กœ ๋ฌถ์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

// Stack์„ ๊ตฌํ˜„ํ•œ ์ฝ”๋“œ, ์‘์ง‘๋„๊ฐ€ ๋†’์€ ํŽธ์ด๋‹ค.
 
public class Stack {
	private int topOfStack = 0;
	List<Integer> elements = new LinkedList<Integer>();
 
	public int size() { 
		return topOfStack;
	}
 
	public void push(int element) { 
		topOfStack++; 
		elements.add(element);
	}
	
	public int pop() throws PoppedWhenEmpty { 
		if (topOfStack == 0)
			throw new PoppedWhenEmpty();
		int element = elements.get(--topOfStack); 
		elements.remove(topOfStack);
		return element;
	}
}

โ€ํด๋ž˜์Šค ์ธ์Šคํ„ด์Šค ๋ณ€์ˆ˜๋Š” ์ ๊ฒŒ, ํ•จ์ˆ˜๋Š” ์งง๊ฒŒโ€ ์ „๋žต์„ ๋”ฐ๋ฅด๋‹ค๋ณด๋ฉด ๋ช‡๋ช‡ ๋ฉ”์„œ๋“œ๋งŒ์ด ์‚ฌ์šฉํ•˜๋Š” ์ธ์Šคํ„ด์Šค ๋ณ€์ˆ˜๊ฐ€ ๋งŽ์•„์ง„๋‹ค. ์ด๋ ‡๊ฒŒ ๋˜๋ฉด ๊ทธ ๋ฉ”์„œ๋“œ์™€ ๋ณ€์ˆ˜๋“ค์„ ๋ฌถ์–ด์„œ ๋ถ„๋ฆฌํ•ด์•ผ ํ•œ๋‹ค๋Š” ์‹ ํ˜ธ๋‹ค. ์‘์ง‘๋„๊ฐ€ ๋†’๋‹ค๋Š” ๋ง์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

์‘์ง‘๋„๋ฅผ ์œ ์ง€ํ•˜๋ฉด ์ž‘์€ ํด๋ž˜์Šค ์—ฌ๋Ÿฟ์ด ๋‚˜์˜จ๋‹ค

ํฐ ํ•จ์ˆ˜๋ฅผ ์ž‘์€ ํ•จ์ˆ˜ ์—ฌ๋Ÿฟ์œผ๋กœ ๋‚˜๋ˆ„๊ธฐ๋งŒ ํ•ด๋„ ํด๋ž˜์Šค ์ˆ˜๊ฐ€ ๋งŽ์•„์ง„๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๋ณด์ž.

  • ๋ณ€์ˆ˜๊ฐ€ ์•„์ฃผ ๋งŽ์€ ํฐ ํ•จ์ˆ˜๊ฐ€ ํ•˜๋‚˜ ์žˆ๋‹ค.
  • ํฐ ํ•จ์ˆ˜ ์ผ๋ถ€๋ฅผ ์ž‘์€ ํ•จ์ˆ˜๋กœ ๋นผ๋‚ด๊ณ  ์‹ถ๋‹ค.
  • ๋นผ๋‚ด๋ ค๋Š” ์ฝ”๋“œ๊ฐ€ ํฐ ํ•จ์ˆ˜์— ์ •์˜ ๋œ ๋ณ€์ˆ˜๋ฅผ ๋งŽ์ด ์‚ฌ์šฉํ•œ๋‹ค.
  • ๋ณ€์ˆ˜๋“ค์„ ์ƒˆ ํ•จ์ˆ˜์— ์ธ์ˆ˜๋กœ ๋„˜๊ฒจ์•ผ ํ•˜๋‚˜? ์•„๋‹ˆ!
  • ๋ณ€์ˆ˜๋“ค์„ ํด๋ž˜์Šค ์ธ์Šคํ„ด์Šค ๋ณ€์ˆ˜๋กœ ์Šน๊ฒฉ ์‹œํ‚ค๋ฉด ์ธ์ˆ˜๊ฐ€ ํ•„์š”์—†๋‹ค. But! ์‘์ง‘๋ ฅ์ด ๋‚ฎ์•„์ง„๋‹ค. ์ฆ‰, ์ž‘์€ ํ•จ์ˆ˜์—์„œ๋งŒ ์‚ฌ์šฉํ•˜๋Š” ๋ณ€์ˆ˜๊ฐ€ ๋˜๋ฒ„๋ฆฐ๋‹ค.
  • ๋ช‡๋ช‡ ํ•จ์ˆ˜๊ฐ€ ๋ช‡๋ช‡ ์ธ์Šคํ„ด์Šค ๋ณ€์ˆ˜๋งŒ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ๋…์ž์ ์ธ ํด๋ž˜์Šค๋กœ ๋ถ„๋ฆฌํ•ด๋„ ๋œ๋‹ค!

์ด๋ ‡๊ฒŒ ํฐ ํ•จ์ˆ˜๋ฅผ ์ž‘์€ ํ•จ์ˆ˜๋กœ ๋ถ„๋ฆฌํ•˜๋‹ค ๋ณด๋ฉด ์ข…์ข… ์ž‘์€ ํด๋ž˜์Šค๋กœ ์ชผ๊ฐค ๊ธฐํšŒ๊ฐ€ ์ƒ๊ธด๋‹ค.

// ์ด ํ•˜๋‚˜์˜ ํฌ๊ณ  ๋”๋Ÿฌ์šด ํ•จ์ˆ˜๋ฅผ ์—ฌ๋Ÿฌ ํ•จ์ˆ˜์™€ ํด๋ž˜์Šค๋กœ ์ž˜๊ฒŒ ๋‚˜๋ˆ„๋ฉด์„œ ์ ์ ˆํ•œ ์ด๋ฆ„์„ ๋ถ€์—ฌํ•ด๋ณด์ž!
 
package literatePrimes;
 
public class PrintPrimes {
	public static void main(String[] args) {
		final int M = 1000; 
		final int RR = 50;
		final int CC = 4;
		final int WW = 10;
		final int ORDMAX = 30; 
		int P[] = new int[M + 1]; 
		int PAGENUMBER;
		int PAGEOFFSET; 
		int ROWOFFSET; 
		int C;
		int J;
		int K;
		boolean JPRIME;
		int ORD;
		int SQUARE;
		int N;
		int MULT[] = new int[ORDMAX + 1];
		
		J = 1;
		K = 1; 
		P[1] = 2; 
		ORD = 2; 
		SQUARE = 9;
	
		while (K < M) { 
			do {
				J = J + 2;
				if (J == SQUARE) {
					ORD = ORD + 1;
					SQUARE = P[ORD] * P[ORD]; 
					MULT[ORD - 1] = J;
				}
				N = 2;
				JPRIME = true;
				while (N < ORD && JPRIME) {
					while (MULT[N] < J)
						MULT[N] = MULT[N] + P[N] + P[N];
					if (MULT[N] == J) 
						JPRIME = false;
					N = N + 1; 
				}
			} while (!JPRIME); 
			K = K + 1;
			P[K] = J;
		} 
		{
			PAGENUMBER = 1; 
			PAGEOFFSET = 1;
			while (PAGEOFFSET <= M) {
				System.out.println("The First " + M + " Prime Numbers --- Page " + PAGENUMBER);
				System.out.println("");
				for (ROWOFFSET = PAGEOFFSET; ROWOFFSET < PAGEOFFSET + RR; ROWOFFSET++) {
					for (C = 0; C < CC;C++)
						if (ROWOFFSET + C * RR <= M)
							System.out.format("%10d", P[ROWOFFSET + C * RR]); 
					System.out.println("");
				}
				System.out.println("\f"); PAGENUMBER = PAGENUMBER + 1; PAGEOFFSET = PAGEOFFSET + RR * CC;
			}
		}
	}
}

์ด ์ฝ”๋“œ๋ฅผ ๋‚˜๋ˆ ๋ณด์ž.

// ์‹คํ–‰ ํ™˜๊ฒฝ์„ ์ฑ…์ž„์ง„๋‹ค.
package literatePrimes;
 
public class PrimePrinter {
	public static void main(String[] args) {
		final int NUMBER_OF_PRIMES = 1000;
		int[] primes = PrimeGenerator.generate(NUMBER_OF_PRIMES);
		
		final int ROWS_PER_PAGE = 50; 
		final int COLUMNS_PER_PAGE = 4; 
		RowColumnPagePrinter tablePrinter = 
			new RowColumnPagePrinter(ROWS_PER_PAGE, 
						COLUMNS_PER_PAGE, 
						"The First " + NUMBER_OF_PRIMES + " Prime Numbers");
		tablePrinter.print(primes); 
	}
}
 
// ์ˆซ์ž ๋ชฉ๋ก์„ ์ฃผ์–ด์ง„ ํ–‰๊ณผ ์—ด์— ๋งž์ถฐ ํŽ˜์ด์ง€์— ์ถœ๋ ฅํ•œ๋‹ค.
 
package literatePrimes;
 
import java.io.PrintStream;
 
public class RowColumnPagePrinter { 
	private int rowsPerPage;
	private int columnsPerPage; 
	private int numbersPerPage; 
	private String pageHeader; 
	private PrintStream printStream;
	
	public RowColumnPagePrinter(int rowsPerPage, int columnsPerPage, String pageHeader) { 
		this.rowsPerPage = rowsPerPage;
		this.columnsPerPage = columnsPerPage; 
		this.pageHeader = pageHeader;
		numbersPerPage = rowsPerPage * columnsPerPage; 
		printStream = System.out;
	}
	
	public void print(int data[]) { 
		int pageNumber = 1;
		for (int firstIndexOnPage = 0 ; 
			firstIndexOnPage < data.length ; 
			firstIndexOnPage += numbersPerPage) { 
			int lastIndexOnPage =  Math.min(firstIndexOnPage + numbersPerPage - 1, data.length - 1);
			printPageHeader(pageHeader, pageNumber); 
			printPage(firstIndexOnPage, lastIndexOnPage, data); 
			printStream.println("\f");
			pageNumber++;
		} 
	}
	
	private void printPage(int firstIndexOnPage, int lastIndexOnPage, int[] data) { 
		int firstIndexOfLastRowOnPage =
		firstIndexOnPage + rowsPerPage - 1;
		for (int firstIndexInRow = firstIndexOnPage ; 
			firstIndexInRow <= firstIndexOfLastRowOnPage ;
			firstIndexInRow++) { 
			printRow(firstIndexInRow, lastIndexOnPage, data); 
			printStream.println("");
		} 
	}
	
	private void printRow(int firstIndexInRow, int lastIndexOnPage, int[] data) {
		for (int column = 0; column < columnsPerPage; column++) {
			int index = firstIndexInRow + column * rowsPerPage; 
			if (index <= lastIndexOnPage)
				printStream.format("%10d", data[index]); 
		}
	}
 
	private void printPageHeader(String pageHeader, int pageNumber) {
		printStream.println(pageHeader + " --- Page " + pageNumber);
		printStream.println(""); 
	}
		
	public void setOutput(PrintStream printStream) { 
		this.printStream = printStream;
	} 
}
// ์†Œ์ˆ˜ ๋ชฉ๋ก์„ ์ƒ์„ฑํ•œ๋‹ค.
package literatePrimes;
 
import java.util.ArrayList;
 
public class PrimeGenerator {
	private static int[] primes;
	private static ArrayList<Integer> multiplesOfPrimeFactors;
 
	protected static int[] generate(int n) {
		primes = new int[n];
		multiplesOfPrimeFactors = new ArrayList<Integer>(); 
		set2AsFirstPrime(); 
		checkOddNumbersForSubsequentPrimes();
		return primes; 
	}
 
	private static void set2AsFirstPrime() { 
		primes[0] = 2; 
		multiplesOfPrimeFactors.add(2);
	}
	
	private static void checkOddNumbersForSubsequentPrimes() { 
		int primeIndex = 1;
		for (int candidate = 3 ; primeIndex < primes.length ; candidate += 2) { 
			if (isPrime(candidate))
				primes[primeIndex++] = candidate; 
		}
	}
 
	private static boolean isPrime(int candidate) {
		if (isLeastRelevantMultipleOfNextLargerPrimeFactor(candidate)) {
			multiplesOfPrimeFactors.add(candidate);
			return false; 
		}
		return isNotMultipleOfAnyPreviousPrimeFactor(candidate); 
	}
 
	private static boolean isLeastRelevantMultipleOfNextLargerPrimeFactor(int candidate) {
		int nextLargerPrimeFactor = primes[multiplesOfPrimeFactors.size()];
		int leastRelevantMultiple = nextLargerPrimeFactor * nextLargerPrimeFactor; 
		return candidate == leastRelevantMultiple;
	}
	
	private static boolean isNotMultipleOfAnyPreviousPrimeFactor(int candidate) {
		for (int n = 1; n < multiplesOfPrimeFactors.size(); n++) {
			if (isMultipleOfNthPrimeFactor(candidate, n)) 
				return false;
		}
		return true; 
	}
	
	private static boolean isMultipleOfNthPrimeFactor(int candidate, int n) {
		return candidate == smallestOddNthMultipleNotLessThanCandidate(candidate, n);
	}
	
	private static int smallestOddNthMultipleNotLessThanCandidate(int candidate, int n) {
		int multiple = multiplesOfPrimeFactors.get(n); 
		while (multiple < candidate)
			multiple += 2 * primes[n]; 
		multiplesOfPrimeFactors.set(n, multiple); 
		return multiple;
	} 
}

์ผ๋‹จ ๋‚˜๋ˆ„๊ฒŒ ๋˜๋‹ˆ ํ”„๋กœ๊ทธ๋žจ์ด ๊ธธ์–ด์กŒ๋‹ค. ์ด๋Š” ์„œ์ˆ ์ ์ธ ๋ณ€์ˆ˜ ์ด๋ฆ„ ์‚ฌ์šฉ, ํ•จ์ˆ˜ ์„ ์–ธ, ํด๋ž˜์Šค ์„ ์–ธ, ๊ณต๋ฐฑ ์ถ”๊ฐ€, ํ˜•์‹ ๋ณ€๊ฒฝ ๋“ฑ์˜ ์ด์œ  ๋•Œ๋ฌธ์ด๋‹ค. ์ด๋ ‡๊ฒŒ ๋ฐ”๋กœ ์ฒ˜์Œ๋ถ€ํ„ฐ ์žฌ๊ตฌํ˜„์„ ํ•œ ๊ฒƒ์ด ์•„๋‹ˆ๋‹ค. ์•ž์žฅ์—์„œ ๋งํ•œ TDD๋ฅผ ์ ์šฉํ•˜์—ฌ, ํ•œ๋ฒˆ์— ํ•˜๋‚˜์”ฉ ์ˆ˜์ฐจ๋ก€์— ๊ฑธ์ณ ๋ณ€๊ฒฝํ•œ ๊ฒฐ๊ณผ์ด๋‹ค.

๋ณ€๊ฒฝํ•˜๊ธฐ ์‰ฌ์šด ํด๋ž˜์Šค

์‹œ์Šคํ…œ์€ ๋ณ€๊ฒฝ์ด ๋ถˆ๊ฐ€ํ”ผํ•˜๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋ณ€๊ฒฝ๋งˆ๋‹ค ์˜๋„๋Œ€๋กœ ๋™์ž‘ํ•˜์ง€ ์•Š์„ ์œ„ํ—˜์ด ์žˆ๋‹ค. ๊นจ๋—ํ•œ ์‹œ์Šคํ…œ์€ ํด๋ž˜์Šค๋ฅผ ์ฒด๊ณ„์ ์œผ๋กœ ๊ด€๋ฆฌํ•ด ๋ณ€๊ฒฝ์— ๋”ฐ๋ฅด๋Š” ์œ„ํ—˜์„ ์ตœ๋Œ€ํ•œ ๋‚ฎ์ถ˜๋‹ค.

// ํ•ด๋‹น ์ฝ”๋“œ๋Š” ์ƒˆ๋กœ์šด SQL๋ฌธ์„ ์ง€์›ํ•  ๋•Œ ์†๋Œ€์•ผ ํ•˜๊ณ , ๊ธฐ์กด SQL๋ฌธ์„ ์ˆ˜์ •ํ•  ๋•Œ๋„ ์†๋Œ€์•ผ ํ•˜๋ฏ€๋กœ SRP์œ„๋ฐ˜
 
public class Sql {
	public Sql(String table, Column[] columns)
	public String create()
	public String insert(Object[] fields)
	public String selectAll()
	public String findByKey(String keyColumn, String keyValue)
	public String select(Column column, String pattern)
	public String select(Criteria criteria)
	public String preparedInsert()
	private String columnList(Column[] columns)
	private String valuesList(Object[] fields, final Column[] columns) private String selectWithCriteria(String criteria)
	private String placeholderList(Column[] columns)
}

ํด๋ž˜์Šค ์ผ๋ถ€์—์„œ๋งŒ ์‚ฌ์šฉ๋˜๋Š” ๋น„๊ณต๊ฐœ ๋ฉ”์„œ๋“œ๋Š” ์ฝ”๋“œ ๊ฐœ์„ ์˜ ์ž ์žฌ์ ์ธ ์—ฌ์ง€๊ฐ€ ์žˆ์Œ์„ ๋ณด์—ฌ์ค€๋‹ค. ๋ถ„๋ฆฌํ•˜์ž.

// ๊ณต๊ฐœ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ „๋ถ€ SQL ํด๋ž˜์Šค์—์„œ ํŒŒ์ƒํ•˜๋Š” ํด๋ž˜์Šค๋กœ ๋งŒ๋“ค๊ณ , ๋น„๊ณต๊ฐœ ๋ฉ”์„œ๋“œ๋Š” ํ•ด๋‹น ํด๋ž˜์Šค๋กœ ์˜ฎ๊ธฐ๊ณ ,
// ๊ณตํ†ต๋œ ์ธํ„ฐํŽ˜์ด์Šค๋Š” ๋”ฐ๋กœ ํด๋ž˜์Šค๋กœ ๋บ๋‹ค.
// ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด update๋ฌธ ์ถ”๊ฐ€ ์‹œ์— ๊ธฐ์กด์˜ ํด๋ž˜์Šค๋ฅผ ๊ฑด๋“œ๋ฆด ์ด์œ ๊ฐ€ ์—†์–ด์ง„๋‹ค.
 
	abstract public class Sql {
		public Sql(String table, Column[] columns) 
		abstract public String generate();
	}
	public class CreateSql extends Sql {
		public CreateSql(String table, Column[] columns) 
		@Override public String generate()
	}
	
	public class SelectSql extends Sql {
		public SelectSql(String table, Column[] columns) 
		@Override public String generate()
	}
	
	public class InsertSql extends Sql {
		public InsertSql(String table, Column[] columns, Object[] fields) 
		@Override public String generate()
		private String valuesList(Object[] fields, final Column[] columns)
	}
	
	public class SelectWithCriteriaSql extends Sql { 
		public SelectWithCriteriaSql(
		String table, Column[] columns, Criteria criteria) 
		@Override public String generate()
	}
	
	public class SelectWithMatchSql extends Sql { 
		public SelectWithMatchSql(String table, Column[] columns, Column column, String pattern) 
		@Override public String generate()
	}
	
	public class FindByKeySql extends Sql {
        public FindByKeySql(String table, Column[] columns, String keyColumn, String keyValue) 
		@Override public String generate()
	}
	
	public class PreparedInsertSql extends Sql {
		public PreparedInsertSql(String table, Column[] columns) 
		@Override public String generate()
		private String placeholderList(Column[] columns)
	}
	
	public class Where {
		public Where(String criteria) public String generate()
	}
	
	public class ColumnList {
		public ColumnList(Column[] columns) public String generate()
	}

์ž˜ ์งœ์—ฌ์ง„ ์‹œ์Šคํ…œ์€ ์ถ”๊ฐ€์™€ ์ˆ˜์ •์— ์žˆ์–ด์„œ ๊ฑด๋“œ๋ฆด ์ฝ”๋“œ๊ฐ€ ์ตœ์†Œ์ด๋‹ค. ํ™•์žฅํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” interface๋ฅผ ์ฑ„ํƒํ•˜์—ฌ ์ž‘์—…ํ•˜๋ฉด ๋˜๊ณ , ์ˆ˜์ •์€ ํŠน์ • ํด๋ž˜์Šค๋งŒ ์†๋ณด๋ฉด ๋œ๋‹ค.

๋ณ€๊ฒฝ์œผ๋กœ๋ถ€ํ„ฐ ๊ฒฉ๋ฆฌ

OOP์ž…๋ฌธ์—์„œ concrete ํด๋ž˜์Šค์™€ abstract ํด๋ž˜์Šค๊ฐ€ ์žˆ๋Š”๋ฐ, concrete ํด๋ž˜์Šค์— ์˜์กด(์ƒ์„ธํ•œ ๊ตฌํ˜„์— ์˜์กด)ํ•˜๋Š” ํด๋ผ์ด์–ธํŠธ ํด๋ž˜์Šค๋Š” ๊ตฌํ˜„์ด ๋ฐ”๋€Œ๋ฉด ์œ„ํ—˜์— ๋น ์ง„๋‹ค. ๊ทธ๋ž˜์„œ ์ธํ„ฐํŽ˜์ด์Šค์™€ abstract ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•ด ๊ตฌํ˜„์ด ๋ฏธ์น˜๋Š” ์˜ํ–ฅ์„ ๊ฒฉ๋ฆฌ์‹œ์ผœ์•ผ ํ•œ๋‹ค.

์ƒ์„ธํ•œ ๊ตฌํ˜„์— ์˜์กดํ•˜๋Š” ์ฝ”๋“œ๋Š” ํ…Œ์ŠคํŠธ๊ฐ€ ์–ด๋ ต๋‹ค. ์ถ”์ƒํ™”๋ฅผ ํ†ตํ•ด ํ…Œ์ŠคํŠธ๊ฐ€ ๊ฐ€๋Šฅํ•  ์ •๋„๋กœ ์‹œ์Šคํ…œ์˜ ๊ฒฐํ•ฉ๋„๋ฅผ ๋‚ฎ์ถ”๊ฒŒ ๋˜๋ฉด ์œ ์—ฐ์„ฑ๊ณผ ์žฌ์‚ฌ์šฉ์„ฑ๋„ ๋†’์•„์ง„๋‹ค. ๊ฒฐํ•ฉ๋„๊ฐ€ ๋‚ฎ๋‹ค๋Š” ๋ง์€ ์‹œ์Šคํ…œ์˜ ๊ฐ ์š”์†Œ๊ฐ€ ๋‹ค๋ฅธ ์š”์†Œ๋กœ๋ถ€ํ„ฐ, ๊ทธ๋ฆฌ๊ณ  ๋ณ€๊ฒฝ์œผ๋กœ๋ถ€ํ„ฐ ์ž˜ ๊ฒฉ๋ฆฌ๋˜์–ด ์žˆ๋‹ค๋Š” ๋œป์ด๋‹ค.

// Portfolio ํด๋ž˜์Šค๋ฅผ ๊ตฌํ˜„ํ•˜์ž, ๊ทธ๋Ÿฐ๋ฐ ์ด ํด๋ž˜์Šค๋Š” ์™ธ๋ถ€ TokyoStockExchange API๋ฅผ ์‚ฌ์šฉํ•ด ํฌํŠธํด๋ฆฌ์˜ค ๊ฐ’์„ ๊ณ„์‚ฐํ•œ๋‹ค.
// ๋”ฐ๋ผ์„œ API ํŠน์„ฑ ์ƒ ์‹œ์„ธ ๋ณ€ํ™”์— ์˜ํ–ฅ์„ ๋งŽ์ด ๋ฐ›์•„ 5๋ถ„๋งˆ๋‹ค ๊ฐ’์ด ๋‹ฌ๋ผ์ง€๋Š”๋ฐ, ์ด๋•Œ๋ฌธ์— ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์งœ๊ธฐ ์‰ฝ์ง€ ์•Š๋‹ค.
// ๊ทธ๋Ÿฌ๋ฏ€๋กœ Portfolio์—์„œ ์™ธ๋ถ€ API๋ฅผ ์ง์ ‘ ํ˜ธ์ถœํ•˜๋Š” ๋Œ€์‹  StockExchange๋ผ๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ƒ์„ฑํ•œ ํ›„ ๋ฉ”์„œ๋“œ๋ฅผ ์„ ์–ธํ•˜๋‹ค.
 
public interface StockExchange { 
	Money currentPrice(String symbol);
}
// ์ดํ›„ StockExchange ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” TokyoStockExchange ํด๋ž˜์Šค๋ฅผ ๊ตฌํ˜„ํ•œ๋‹ค.
// ๊ทธ๋ฆฌ๊ณ  Portfolio ์ƒ์„ฑ์ž๋ฅผ ์ˆ˜์ •ํ•ด StockExchange ์ฐธ์กฐ์ž๋ฅผ ์ธ์ˆ˜๋กœ ๋ฐ›๋Š”๋‹ค.
 
public Portfolio {
	private StockExchange exchange;
	public Portfolio(StockExchange exchange) {
		this.exchange = exchange; 
	}
	// ... 
}
// ์ด์ œ TokyoStockExchange ํด๋ž˜์Šค๋ฅผ ํ‰๋‚ด๋‚ด๋Š” ํ…Œ์ŠคํŠธ์šฉ ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.(FixedStockExchangeStub)
// ํ…Œ์ŠคํŠธ์šฉ ํด๋ž˜์Šค๋Š” StockExchange ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋ฉฐ ๊ณ ์ •๋œ ์ฃผ๊ฐ€๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
// ๊ทธ๋Ÿผ์œผ๋กœ์จ ๋ฌด๋‚œํžˆ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑ ํ•  ์ˆ˜ ์žˆ๋‹ค.
 
public class PortfolioTest {
	private FixedStockExchangeStub exchange;
	private Portfolio portfolio;
	
	@Before
	protected void setUp() throws Exception {
		exchange = new FixedStockExchangeStub(); 
		exchange.fix("MSFT", 100);
		portfolio = new Portfolio(exchange);
	}
 
	@Test
	public void GivenFiveMSFTTotalShouldBe500() throws Exception {
		portfolio.add(5, "MSFT");
		Assert.assertEquals(500, portfolio.value()); 
	}
}
 

์™ธ๋ถ€์—์„œ ๊ตฌํ˜„์ฒด๋ฅผ ๋„ฃ์–ด์ฃผ๋Š” ๋ฐฉ์‹์„ ํ†ตํ•ด, ๊ตฌํ˜„์ฒด๋ฅผ ๋ณ€๊ฒฝํ•ด์„œ ๋„ฃ์–ด์ค„ ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค. ์ƒ์„ธ ๊ตฌํ˜„ ํด๋ž˜์Šค๊ฐ€ ์•„๋‹Œ interface์— ์˜์กดํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„์ด ๋˜์–ด์žˆ๋Š”์ง€ ์ˆจ๊ธด ์ƒํƒœ๋กœ ๋™์ž‘์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

์š”์•ฝ

  • ํด๋ž˜์Šค๋Š” ํ•˜๋‚˜์˜ ์ฑ…์ž„์„ ๊ฐ€์ ธ์•ผ ํ•œ๋‹ค.
  • ์‘์ง‘๋„๋ฅผ ๋†’๊ฒŒ ์œ ์ง€ํ•˜๋ฉด ์ž‘์€ ํด๋ž˜์Šค ์—ฌ๋Ÿฟ์ด ๋‚˜์˜จ๋‹ค.
  • ์ž‘์€ ํด๋ž˜์Šค ์—ฌ๋Ÿฟ์ด ํฐ ํด๋ž˜์Šค ํ•˜๋‚˜๋ณด๋‹ค ๋‚ซ๋‹ค.
  • ๊ฒฐํ•ฉ๋„๋ฅผ ๋‚ฎ๊ฒŒ ์œ ์ง€ํ•˜๋ฉด ๋ณ€๊ฒฝ์ด ์‰ฝ๊ณ  ๊ฒฉ๋ฆฌ๋œ ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

Reference