์ด ํ๋ก์ ํธ๋ Robert C. Martin์ Clean Coders Screen Cast ์ค Episode 10. OCP(Open but Closed Principle)์์ ์ฌ์ฉ๋ ์์ ๋ฅผ ๊ฐ์ง๊ณ ์ด๋ค ๋ฌธ์ ๊ฐ ์๋์ง, ์ด๋ป๊ฒ ํด๊ฒฐํ ์ ์๋์ง๋ฅผ ์งง์ ๋ณ๊ฒฝ ๋จ์๋ก ์ปค๋ฐํ์ฌ ๋ณด์ฌ์ค๋ค. ์ด ์์ ์์๋
- Function Should Do One Thing
- SRP(Single Responsibility Principle)
- Feature Envy
- OCP(Open but Closed Principle)
๋ฑ์ ์ค์ํ๋ ๋ฐฉ๋ฒ์ ์๋์ ๊ฐ์ ๊ธฐ๋ฒ์ ํตํด ๋ณด์ฌ์ค๋ค.
- Extract Variable / Field / Method / Class / Interface
- Inline Variable
- Rename
- Change Signature
- Create a Field for Parameter
- Move Method
- Safely Delete
- Replace Type Code with Subclasses
- push members down
์์ ์์๋ IDE๋ก intelliJ๋ฅผ ์ฌ์ฉํ๋ค.
- ํจ์๊ฐ ๋๋ฌด ํฌ๊ณ ,
- ์ค๋ณต๋ ์กด์ฌ(xx / 100)
- ์์ ๋ฉ์๋๋ค๋ก ์ถ์ถ(extract)ํ ํ์๊ฐ ์๋ค.
- Composed Method Pattern, Extract till drop ๊ท์น์ ๋ฐ๋ผ extract method๋ฅผ ์ํ
- ๋น์ฆ๋์ค ๋ก์ง(๋น์ฉ ๊ณ์ฐ ๋ก์ง)๊ณผ ๋ฉ์์ง/ํฌ๋งทํ ๋ก์ง์ด ์์ฌ ์๋ค.
- ํ๊ฐ์ง๊ฐ ์๋๋ผ ์ฌ๋ฌ๊ฐ์ง ์ด์ ๋ก ๋ณ๊ฒฝ์ด ํ์ํ ๊ฒฝ์ฐ๊ฐ ๋ฐ์ํ๋ค.
- ๋น์ง๋์ค ๊ท์น์ ํ์ฅํ๊ณ ์ ํ๋ค๋ฉด ์ด ๋ชจ๋์ ์์ ํด์ผ ํ๋ค.
- ๋ฉ์์ง์ ํฌ๋งทํ ์ ๋ณ๊ฒฝํ๊ณ ์ ํ๋ค๋ฉด ์ด ๋ชจ๋์ ์์ ํด์ผํ๋ค(์ ๊ทธ๋ฆผ์์ ๋ถ์ ๋ฐ์ค์ฒ๋ผ ํ์ ์ ์์กดํ ์ฝ๋๋ก ์ธํด)
๋ง์ผ ์๋ก์ด ์์ฌ ํ์ ์ผ๋ก LUNCH๋ฅผ ์ถ๊ฐํ๋ ๊ฒฝ์ฐ ํ์ ์ ์์กดํ๋ ์ฝ๋๋ก ์ธํด ์ด๋ ค์์ ๊ฒช๊ฒ๋๋ค. ์์คํ ๋ด์ ๋ชจ๋ ์์ค์์ expense type์ ์์กดํ๋ ๋ชจ๋ switch-case๋ if-else ๋ฌธ์ฅ์ ์์ ํด์ผ ํ ๊ฒ์ด๋ค.
- Expense์ type, amount ๋ฑ์ ์ด์ฉํ์ฌ ๊ฒฐ์ ์ ๋ด๋ฆฌ๊ณ ์๋ค.
- ์ด๋ฐ ๋ก์ง์ Expense์ ์์ด์ผ ํจ.
๋คํํ ํ ์คํธ(๋ช ํํ ๋ฌธ์๋ก์ ์คํ ๊ฐ๋ฅํ ์๊ตฌ์ฌํญ ์ญํ )๋ ์กด์ฌํ์ฌ ๊ณต๊ฒฉ์ ์ผ๋ก ๋ฆฌํฉํ ๋ง์ ํ ์ ์๋ค.
Function Should Do One Thing์ ์ค์ํ๊ธฐ ์ํด extract method๋ฅผ ์ํํ๋ค.
์ ์ฐจ
- Extract Variable
- Extract Method
- ์ฒ์ ์ค๋ณต์ฝ๋(amount/100.0)์ ์ ํํ๊ณ "Extract Method"๋ฅผ ์ํํ๋ค.
- IDE๊ฐ ๋ค๋ฅธ 2๊ณณ์ ์ฝ๋์๋ extract method๋ฅผ ์ ์ฉํ ์ง ๋ฌผ์ผ๋ฉด "Yes"๋ฅผ ์ ํํ๋ค.
- Process Duplicate ๋ํ์์์์ All์ ์ ํํ์ฌ ๋ชจ๋ ์ค๋ณต ์ฝ๋์ extract method๋ฅผ ์ํํ๋ค.

- ํ๋ผ๋ฏธํฐ ์ฒ๋ฆฌ๋ฅผ ์ํด extractํ๋ ๋ณ์๋ฅผ inlineํ๋ค.

- ๊ฐ๋
์ฑ์ ๋์ด๊ณ Composed Method Pattern์ ์ค์ํ๊ธฐ ์ํด printHeader ๋ฉ์๋๋ฅผ ์ถ์ถํ๋ค.

- ๊ฐ์ ์ด์ ๋ก printTotals ๋ฉ์๋๋ฅผ ์ถ์ถํ๋ค.

- printExpenses ๋ฉ์๋๋ฅผ ์ถ์ถํ๊ณ ์ถ๋ค. ํ์ง๋ง ์ด ๋ก์ง์ ๋น์ฆ๋์ค ๋ก์ง๊ณผ ์์ฌ์๋ค.
- ๋น์ฆ๋์ค ๋ก์ง์ totalUpExpenses๋ผ๋ ๋ฉ์๋๋ฅผ ์ถ์ถํ๊ณ ์ถ์ผ๋ 2๊ฐ์ ๋ณ์๊ฐ ๋ณ๊ฒฝ๋์ด extractํ ์ ์๋ค.
*** ๋ณดํต ์ฝ๋์ ์ฌ๋ฌ๊ณณ์ ์ฌ์ฉ๋๋ ๋ณ์๋ค์ ๋ฆฌํฉํ ๋ง ์ ์ field๋ก ์ถ์ถํ๋ ์์ ์ ์ ํํ๋ค. ๊ทธ๋ผ ๋ ํธํ๊ฒ ๋ฆฌํฉํ ๋ง์ด ๊ฐ๋ฅํ๋ค.***
printReport์ ์ ๋ฌ๋ printer๊ฐ ์ฌ๋ฌ ํจ์์ ํ๋ผ๋ฏธํฐ๋ก ์ ๋ฌ๋๊ณ ์๋ค. ์ด๋ฅผ field๋ก ์์ฑํ์ฌ ํ๋ผ๋ฏธํฐ ๊ฐ์๋ฅผ ์ค์ธ๋ค.
- Create a Field for Parameter

- field๋ก ๋ณํํ printer๋ฅผ ํ๋ผ๋ฏธํฐ๋ก ๊ฐ๋ ๋ฉ์๋๋ค์ ๋ํด change signature๋ฅผ ์ํํ์ฌ ํ๋ผ๋ฏธํฐ๋ฅผ ์ ๊ฑฐํ๋ค.

- ์ด ๋ฉ์๋๋ฅผ ์ถ์ถํ๊ธฐ ์ํ ์ฌ์ ์์
์ผ๋ก move line up์ ์ํํ๋ค.

- printExpensesAndTotals ๋ฉ์๋๋ฅผ ์ถ์ถํ๋ค.

- {}๋ฅผ ์์ ๊ธฐ ์ํด ๋ฉ์๋ ์ถ์ถ
- {}๋ function should do one thing์ ์๋ฐํ๊ณ ์๋ค๋ ์ฆํ์ด๋ค.

- ๋ถํ์ํ ๋ณ์ ์ ์ธ์ ์ ๊ฑฐํ๊ธฐ ์ํด name์ inlineํ๋ค.

{}๋ฅผ ์ ๊ฑฐํ๊ธฐ ์ํด addTotals ๋ฉ์๋๋ฅผ ์ถ์ถํ๋ค.

๊ฐ๋
์ฑ์ ์ํด isOverage๋ฅผ ์ถ์ถํ๋ค.

ExpenseReport์์ ๋น์ฆ๋์ค ๋ก์ง์ ์ถ์ถํ์ฌ ์ด๋ฅผ ExpeseReport๋ผ๊ณ ํ๊ธฐ์ํด ์ง๊ธ์ ExpeseReport๋ ExpeseReporter๋ก renameํ๋ค.
- extract classํ ๋๋ ๊ด๋ จ๋ ๋ฉ์๋/ํ๋๋ฅผ ํจ๊ป ์ถ์ถํด์ผ ํ๋ค.
- ์ฌ์ฉํ๊ฑฐ๋ ํธ์ถํ๋ ๋ฉ์๋/ํ๋๋ฅผ "Members to Extract"์์ ์ ์ ํํ๋ค.
- extract class ํ์ ์ฌ์ฉ๋์ง ์๋ ๋ฉ์๋๋ค์ด ๋ฐ์ํ๋ค. ์ด๋ฅผ ์ ๊ฑฐํ๋ค.
- (intellJ์์๋ ์ปค๋ฐ๋ ์ด๋ฐ warning์ด ์์ผ๋ฉด ์๋ ค์ฃผ๋โฆ ์ปค๋ฐ ์ ์ ์์
ํ๋ ๊ฒ์ด ์ข๋ค.)

Feature Envy์ธ ๋ฉ์๋ isOverage, isMeal๋ฅผ Expense ํด๋์ค๋ก Move Methodํ๋ค.

Type์ ์ข ์์ ์ด์ฌ์ OCP๋ฅผ ์๋ฐํ๊ณ ์๋ค. Replace Type Code with Subclasses๋ฅผ ์ ์ฉํ์.
- Test๋ฅผ ์์ ํ์ฌ Expense๊ฐ ์๋๋ผ ํ์ํ ํ์
์ ์๋ธ ํด๋์ค๋ฅผ ์์ฑํ๋๋ก ํ๋ค.

- IDE์ quick fix ๊ธฐ๋ฅ์ ์ด์ฉํ์ฌ 3๊ฐ์ ์๋ธ ํด๋์ค๋ฅผ ์ถ๊ฐํ๊ณ ํ ์คํธ๋ฅผ ์ํํ์ฌ ์ฑ๊ณต์ ํ์ธ
- ๊ฐ์ฒด ์์ฑ์ ํ๋ผ๋ฏธํฐ๋ก ์ ๋ฌํ๋ type(์. DINNER)๊ณผ ํด๋์ค๋ช (์. DinnerExpense)๋ ์ค๋ณต์ด๋ค. ํ๋ผ๋ฏธํฐ๋ฅผ ์ ๊ฑฐํ๋ค.
-
Expense ํด๋์ค์์ isMeal, isOverage 2๊ฐ์ ๋ฉ์๋๋ฅผ ์ ํํ์ฌ
-
keep abstract๋ฅผ ์ ํํ์ฌ base ํด๋์ค์๋ abstract๋ก ์ ์ง๋๋๋ก
- isMeal, isOverage ๋ฉ์๋์ ๋ํด์ ํ์ ๋ณ๋ก ์์
- ์ด๋ ์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง๋ฅผ ์ฐธ์กฐํ๋ฉด์ ์ํํ ์๋ ์๋ค.

- ๋ ์ด์ ์ฌ์ฉ๋์ง ์๋ Expense.Type enum์ ์ ๊ฑฐํ๋ค.
- getName ๋ฉ์๋์์ Type์ ์ฌ์ฉํ์ง ์๋๋ก ์์ ํ๋ค.

์ด์ ์๋ก์ด ํ์ ์ด ์ถ๊ฐ๋๋ฉด ์์ ์ ์๋ค. ๊ทธ์ Expense๋ฅผ ์์๋ฐ๋ ์๋ก์ด ํด๋์ค๋ฅผ ์ถ๊ฐํ๋ฉด ๋๋ค.
ํ์ง๋ง ExpenseRepoter#getName๋ ์๋ก์ด ํ์ ์ด ์ถ๊ฐ๋ ๋๋ง๋ค ์์ ๋์ด์ผ ํ๋ค. OCP ์๋ฐ์ด๋ค.
์ด๋ฅผ ์๋ก์ด ํด๋์ค๋ก ์ถ์ถํ๊ณ , ์ธํฐํ์ด์ค๋ฅผ ์ถ์ถํ๊ณ , ExpenseRepoter๊ฐ ์ธํฐํ์ด์ค์ ์์กด์ฑ์ ๊ฐ๊ฒ ํ์ฌ ๋น์ฆ๋์ค ๋ก์ง(app part)์ ๋ณ๊ฒฝ ์์ด ์๋ก์ด ํ์ ์ ์ถ๊ฐํ ์ ์๋๋ก ํ๋ค.
- Extract Class - ExpenseNamer

- ๋ ์ด์ ์ฌ์ฉ๋์ง ์๋ ExpenseReport#getName ๋ฉ์๋๋ฅผ safely deleteํ๋ค.
- change scope of getName to public
- extract interface๋ฅผ ์ํด ๋ณ๊ฒฝํ๋ค.
- extract interface

์ด์์ ๊ณผ์ ์์ ์ด์ ๊ฐ์ ์ํคํ ์ณ๋ฅผ ์ป์๋ค.
- ๋ถ์์์ผ๋ก ๋๋ ์ง ์ ๋ถ๋ถ์ ๋ณ๊ฒฝ์ ์ํฅ์ ๋ฐ์ง ์๊ณ ์ฌ์ฌ์ฉ๋ ์ ์๋ ๋ถ๋ถ์ด๊ณ
- ์๋ ๋ถ๋ถ์ ๋ณ๊ฒฝ์ด ์๊ตฌ๋ ๋ ์ถ๊ฐ๋์ด์ผํ๋ ์๋ธ ํด๋์ค๋ค๊ณผ ๋ณ๊ฒฝ๋์ด์ผ ํ ๊ตฌํ์ฒด์ด๋ค.
๊ฐ์ธ์ ์ผ๋ก๋ getName์ Expese ํด๋์ค๋ก ์ฎ๊ธฐ๋ฉด ๋ ์ข์ ๊ฒ ๊ฐ๋ค๋ ์๊ฐ์ด ๋ ๋ค.
- ํด๋์ค ๋ค์ด์ด๊ทธ๋จ์ ๋ณด๋ฉด ๋ณต์กํด ์ง ๊ฒ ๊ฐ์ผ๋, ๊ธฐ์กด ๋ค์ด์ด๊ทธ๋จ์์ Expense๊ฐ data holder๋ก์ ๊ฐ์ฒด์ ์ญํ ์ ์ํํ์ง ๋ชปํ๊ณ ์๋ ๊ฒ์ ๋ฐํด
- ๋ฆฌํฉํ ๋ง ํ์๋ ์ฑ ์(์ถ๋ ฅ, ๊ณ์ฐ)์ด ๋ถ๋ฆฌ๋์๊ณ , Expense์ ๊ด๋ จ๋ ํ์๊ฐ ์ฎ๊ฒจ์ก๋ค. ๋ ๋ค์ด์ด๊ทธ๋จ๋ง์ผ๋ก ์ด๋ค Expense๋ค์ด ์กด์ฌํ๋์ง ์ด๋ค ๊ธฐ๋ฅ๋ค์ด ์๋์ง๊ฐ ๋์ ์ ๋ค์ด์จ๋ค.
- ๋ ๋ฐ์ดํฐ์ ๊ด๋ จ๋ ๊ธฐ๋ฅ์ด ํ ๊ฐ์ฒด์ ๋ชจ์ด๋ฉด์ ๋ด๋ถ์ ๋ณ๊ฒฝ์ด ์ธ๋ถ์ ์ํฅ์ ์ฃผ์ง ์๊ฒ ๋์๋ค.
๋ฉ์ผ: msbaek71@gmail.com ์ด๋ facebook design by object ๊ทธ๋ฃน์ผ๋ก ์๊ฒฌ์ด๋ ์ง๋ฌธ ๋ฐ๊ฒ ์ต๋๋ค.





















