๋ณธ ๋ด์ฉ์ ํ๋น๋ฏธ๋์ด์์ ์ถ๊ฐํ ์ ์ฑ (๊ฐ๋ฐ์ ๊ธฐ์ ๋ฉด์ ๋ ธํธ)์ ์ผ๋ถ ๋ด์ฉ์ ๋ณด๊ฐํ๊ธฐ ์ํด์ ๋ง๋ ์ฝ๋ฉ ๊ฐ์ด๋ ์ ๋๋ค.
์ค๋ช ์์๋ ํ์ดํ์ ์ค์ด๊ธฐ ์ํด์ ์กด๋๋ฅผ ์๋ตํ์ผ๋ ์ํด ๋ฐ๋๋๋ค.
๋ง์ฝ ๋ด์ฉ์ ๋ฌธ์ ๊ฐ ์๊ฑฐ๋ ์ค/ํ์๊ฐ ์์ ๊ฒฝ์ฐ villainscode@gmail.com์ผ๋ก ๋ฉ์ผ ๋ถํ๋๋ฆฝ๋๋ค.
- Instagram - https://www.instagram.com/codevillains
- email - villainscode@gmail.com
- Yes24 - https://www.yes24.com/Product/Goods/125554439
- KyoboBooks - https://product.kyobobook.co.kr/detail/S000212738756
๊ฐ์ ์ด๋ ฅ
ํ์ฌ ๋ฒ์ v.1.0.0 (2024.05.09)
- ๋ ๋ค์ํ ์ฝ๋ ์ฉ๋ก๋ฅผ ์ถ๊ฐํ ๋ ๋ง๋ค ๊ฐ์ ์ด๋ ฅ์ ์ ๋ฐ์ดํธ ๋ ์์ ์ ๋๋ค.
- ๋ชฉ์ฐจ
- ์ฝ๋ฉ ๊ฐ์ด๋
- ์ฝ๋ฉ ๊ฐ์ด๋ ๊ฐ์
- ์ผ๋ฐ์ ์ธ ์๋ฐ ์ฝ๋ฉ ๊ท์น
- ์๋ฏธ ์๋ ์ด๋ฆ
- ํจ์ (๋ฉ์๋)
- ํ์ ๋ง์ถ๊ธฐ
- ๊ฐ์ฒด์ ์๋ฃ ๊ตฌ์กฐ
- ์ค๋ฅ ์ฒ๋ฆฌ
- ํด๋์ค
- ์ฝ๋ ๊ธฐ๋ณธ ์ฌ์ฉ ๊ท์น ๋ชจ์
- ๊ท์น 1. ํ์ผ์ UTF-8๋ก ์ธ์ฝ๋ฉ ํ๋ค.
- ๊ท์น 2. ํจํค์ง ์ด๋ฆ์ ์๋ฌธ์์ด๊ณ , ์ธ๋๋ผ์ธ์ ์ฐ์ง ์๋๋ค.
- ๊ท์น 3. ํด๋์ค ์ด๋ฆ์ UpperCamelCase (PascalCase) ๋ก ์์ฑํ๋ค.
- ๊ท์น 4 .๋ฉ์๋ ์ด๋ฆ์ lowerCamelCase๋ก ์์ฑํ๋ค.
- ๊ท์น 5. ๋๋ฌด ์งง์ ๋ณ์๋ช , ๋ฉ์๋๋ช ์ ์ง์ํ๊ณ ์๋ฏธ์๋ ์ด๋ฆ์ผ๋ก ์ง์ด์ผ ํ๋ค.
- ๊ท์น 6. ์์๋ CONST_CASE ์ด๋ค. (UPPER_SNAKE_CASE)
- ๊ท์น 7. ์์๊ฐ ์๋ ํ๋๋ lowerCamelCase๋ก ์์ฑํ๋ค.
- ๊ท์น 8. ๋ณ์๋ lowerCamelCase๋ก ์์ฑํ๋ค.
- Usage (์ฝ๋ ์์ ๋ชจ์)
- 1. ๊ดํธ๋ Egyptian brackets ์ ๋ฐ๋ฅธ๋ค.
- 2. ๋น ๋ธ๋ญ์ด๋ block-like construct
- 3. ๊ณต๋ฐฑ๊ณผ ๋น์ค
- 4. Static Factory Method ์ ๊ดํ์ ์ธ ๋ช ๋ช
- 5. List ํ์ ์์ ๋น ์ปฌ๋ ์ ์ ๋ฐํ
- 6. foreach ๋ฃจํ์์ ์ฝ๋ ์ ์ ์์๋ฅผ ์ถ๊ฐํ๊ฑฐ๋ ์ญ์ ํ์ง ๋ง๊ฒ
- 7. Key, Value ์ฝ๋ ์ ์์ null์ด ์ ์ฅ๋๋์ง ์ฒดํฌํด๋์ด์ผ ํ๋ค.
- 8. Thread
- 9. String ํ์ ์ ์ ์ ํ์ง ์์ ๋ฐ์ดํฐ๋ฅผ ํ๊ธฐํ์ง ๋ง์์ผ ํ๋ค.
- 10. String์ ์ด๊ธฐํ์๋ StringUtils.EMPTY์ ์ฌ์ฉํ๋ ๊ฒ์ ๊ถ์ฅํ๋ค.
- 11. ๋ถํ์ํ ์ฐ์ฐ์ ์ฌ์ฉํ์ง ๋ง์.
- 12. Optional ์ฌ์ฉ
- 13. Predicate๊ณผ Valdator ๊ตฌ๋ถํด์ ์ฌ์ฉ
- 14. ๋ณ์๊ฐ ๋๋ฌด ๋ง์ ํด๋์ค ๊ตฌ์กฐ๋ ๋ณ๋์ ๊ฐ์ฒด๋ก ๋ถ๋ฆฌํ์.
- 15. ๊ฐ๊ธ์ ์์ธ๋ฅผ ๊ตฌ์ฒดํ ํ๋ค.
- 16. ์งง์ try ~ catch ๋ธ๋ญ ๊ถ์ฅ
- 17. SpringFramework ์์ ์ ๊ณตํ๋ ์์ฒด Util ๋ค์ ์ฌ์ฉํ๋ฉด ์๋๋ค.
- 18. Spring Annotation์ ์ ์ ํ ์ฌ์ฉ
- DTO (Converter vs Mapper)
Table of contents generated with markdown-toc
์ด ๋ด์ฉ์ ์๋์ ์์ ๊ณผ ๋งํฌ๋ฅผ ์ฐธ๊ณ ํ์ฌ ์์ฑํ์๋ค. ๊ฐ์ข ์์ ์ฝ๋๋ค์ ์ฝ๋๋น๋ฐ ๋ณธ์ธ์ ๊ฐ์ธ์ฝ๋๋ฅผ ํฌํจํ์ฌ ์ฑGPT์ ๋์์ ๋ฐ์๋ค.GPT๋ง์!
์ฐธ๊ณ ์์ ๋ฐ ๋งํฌ
- Effective Java 3/E (Joshua Bloch)
- Clean Code (Robert C. Martin)
- ๊ฐ๋ฐ์ ๊ธฐ์ ๋ฉด์ ๋ ธํธ (Technical Interview Notes for Java Developers, Hanbit Media 2024.03.25, Code Villains)
- https://google.github.io/styleguide/javaguide.html
- https://www.oracle.com/java/technologies/javase/codeconventions-contents.html
- ์๋์ด๋ฉด์ ๊ด์ด ์๋ ค์ฃผ๋ ๊ฐ๋ฐ์ ์ทจ์ ๊ณผ ์ด์ง, ํ๋ฐฉ์ ํด๊ฒฐํ๊ธฐ ์ด๋ก ํธ
- ์๋์ด๋ฉด์ ๊ด์ด ์๋ ค์ฃผ๋ ๊ฐ๋ฐ์ ์ทจ์ ๊ณผ ์ด์ง, ํ๋ฐฉ์ ํด๊ฒฐํ๊ธฐ ์ค์ ํธ
ํ ๋ด ์ฝ๋๋ฆฌ๋ทฐ๋ฅผ ์ํ ๊ฐ์ด๋๋ฅผ ๋ง๋ค์ด์ ๋ค์ด๋ฐ, ์ฝ๋ ์ค์ ์ฌํญ์ ์์ฌ๊ฒฐ์ ํ์๋ฅผ ์ค์ด๊ณ ๋จ์ํ๊ณ ์ ์ฐํ ํต์ผ๊ฐ ์๋ ์ฝ๋๋ฅผ ์งํฅํจ์ผ๋ก์จ ๋ณด๋ค ํจ์จ์ ์ธ ํ ์ฝ๋ ๋ฌธ์๋ฅผ ์์ฐํ๋ค.
- ์๋ก ํฉ๋ฅํ ๋ฉค๋ฒ๋ ํ์ ๊ฐ์ด๋ ๋ผ์ธ์ ์ค์ํ ์ ์๋๋ก, ์ฝ๋ฉ ๊ฐ์ด๋ + ์ฝ๋ ๋ฆฌ๋ทฐ ๊ฐ์ด๋๋ฅผ ์์ฑํ๋ค.
- ์ฝ๋์ ๋ค์ด๋ฐ๋ง์ผ๋ก๋ ๊ธฐ๋ฅ์ ์์ ์๋๋ก ์์ฑํ๋ค.
- ์ฝ๋ฉ ๊ฐ์ด๋ + ์ฝ๋ ๋ฆฌ๋ทฐ ๊ฐ์ด๋๋ ํ๋ด ๊ฐ๋ฐ ๋ฌธํ๋ก์จ ์๊ฒฉํ ์ค์๋ฅผ ์งํฅํ๋ค.
- ํด์ ํต์ผ์ฑ, ๋จ์ถํค์ ๊ธฐ๋ณธ ์ค์ ํ๋ฅผ ์งํฅํ์ฌ ์ฝ๋ ๋ฆฌ๋ทฐ์ ๋จ์ถ๊ธฐ ์ฌ์ฉ๋ฑ์ ํ ๋ํ ๊ณต์ ๋์ด์ผ ํ๋ค.
- ๋จ์ํ ์ฝ๋์ ํ์ง๋ง ๋ ผ์ ํ๋๊ฒ ์๋๋ผ ๋ฒ๊ทธ๋ ๋ฐฉ์ด ์ฝ๋ ํ์ ์ฌ๋ถ, ๊ดํ์ ๋ช ๋ช ์ ์ค์ ํ์๋์ง (by ์ฝ๋ฉ ๊ฐ์ด๋), ์ฌ์ด๋ ์ดํํธ๋ ์๋์ง ๊ณ ๋ฏผํด์ ๋ฆฌ๋ทฐํ๊ณ ํญ์ ๋ณธ ๋ฌธ์ ์ด์ธ์ ๋ฐฉ์์์ ์ฃผ์ฅํ๋ ๋ฐ๋ ๊ทผ๊ฑฐ์๋ฃ๋ฅผ ์ฐพ์ ๋ณด์ถฉํ๋๋ก ํ๋ค.
- ๊ฐ์ ์ด ํ์ํ๋ค๋ฉด ์ ๊ฐ์ ํด์ผ ํ๋๊ฐ? ์ ๋ํด์ ์ถฉ๋ถํ ์ค๋ช ์ด ํ์ํ๋ค. ์ค๋ช ์ด ์๋ค๋ฉด ๊ฒฐ๊ตญ ์ฝ๋์ ํธ์ง์ ์ก๊ฒ ๋ค๋ ๊ฒ ๋ฐ์ ๋์ง ์์ผ๋ฏ๋ก ์ ์๊ณผ ์ด์ ์ ๋ํด์ ์์ธํ ์ค๋ช ํ๋ค.
- Value Object : ๋น์ฆ๋์ค ์ฉ์ด๋ฅผ ๋ํ๋ด๋ ๋ถ๋ณ ๊ฐ์ฒด
- Entity : ์์ฑ์ด ์๋ ์๋ณ์ฑ์ ๊ธฐ์ค์ผ๋ก ์ ์๋๋ ๋๋ฉ์ธ ๊ฐ์ฒด, ์ฌ๋ฌ Value Object๋ก ๊ตฌ์ฑ
- Service : ๋๋ฉ์ธ ๊ฐ์ฒด์ ํฌํจํ ์ ์๋ ๋์, ๋ก์ง์ ์ธ ์ฐ์ฐ์ ๊ฐ๋ ๊ฐ์ฒด
- Aggregate : ์ฐ๊ด๋ Value Object์ Entity์ ๋ฌถ์
- Factory : ๋ณต์กํ Entity, Aggregate๋ฅผ ์บก์ํํ์ฌ ์ฌ๋ฌ ๊ฐ์ฒด๋ฅผ ๋์์ ์์ฑ
- Repository : Aggregate์ ์์์ฑ ๋ฐ ๋ฑ๋กยท์์ ยท์ญ์ ยท์กฐํ ์ ์ผ๊ด์ฑ ๊ด๋ฆฌ
ํด๋์ค : ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ธฐ ์ํ ์ค๊ณ ์ฝ๋๋ก ์๋์ ๊ฐ์ ํํ๋ฅผ ๊ฐ์ง๊ณ ์๋ค.
- ์์ฑ์
- ์์ฑ
- ๋ฉ์๋
๊ตฌ์ฒด์ ์ธ ๊ฐ์ ๊ฐ์ง ์ํ๋ผ๊ธฐ ๋ณด๋ค๋ ์ถ์์ ์ธ ๊ฐ๋
๊ฐ์ฒด : ํด๋์ค๋ฅผ ํตํด ๋ง๋ค์ด์ง ๊ตฌ์ฒด์ ์ธ ๊ฐ์ฒด. ํด๋์ค์์ ์ ์๋ ์์ฑ๊ณผ ๋ฉ์๋๋ฅผ ๊ฐ์ง๊ณ ๋ฐ์ดํฐ๋ฅผ ์ง๋ ์ํ๋ก ๋ฉ์๋๋ฅผ ํธ์ถํ๊ณ ๋์์ ์ํํ๋ค. ํด๋์ค์ ์ ์๋ ๋ฉ์๋๋ฅผ ํตํด ๊ฐ์ฒด์ ์์ฑ๊ณผ ํ๋์ ์ด์ฉํ์ฌ ์ค์ ํ๋ก๊ทธ๋จ์์ ์ฌ์ฉ๋๋ค.
ํด๋์ค : ํด๋์ค๋ ๊ฑด์ถ๋ฌผ์ ์ค๊ณ๋์ ๊ฐ์ ๊ฐ๋
์ด๊ณ ๊ฐ์ฒด๋ ์ค๊ณ๋๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋ง๋ค์ด์ง ์ง๊ณผ ๊ฐ๋ค.
ํด๋์ค๋ ๋ฐ์ดํฐ์ ๊ทธ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๋ ๋ฉ์๋์ ์งํฉ์ผ๋ก ์ด๋ฃจ์ด์ ธ ์๋ค.
์ง์ด๋ผ๋ ํด๋์ค๋ฅผ ๋ณด๋ฉด ๋ฐฉ์ ์, ํฌ๊ธฐ, ํ์ฅ์ค์ ์, ์ธ๊ด์ ์๊น ๋ฑ์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ง๊ณ ์ง์ผ๋ก์์ ํ์์ ์ธ ๊ธฐ๋ฅ๋ค์ ์ํํ๋ ๋ฉ์๋๋ฅผ ๊ฐ์ง๊ณ ์๋ค.
์ธ์คํด์ค : ์ธ์คํด์ค๋ ํน์ ํด๋์ค๋ฅผ ํตํด ์ค์ ๋ก ๋ฉ๋ชจ๋ฆฌ์ ์์ฑ๋ ๊ฐ์ฒด๋ก ์ ๋ง๋ค ๊ณ ์ ํ ๊ฐ(์ฐธ์กฐ๊ฐ)์ ์ง๋๊ณ ์๋ค.
๊ฐ์ฒด๊ฐ [์ง]์ด๋ผ๊ณ ํ๋ ์ผ๋ฐ์ ์ธ ๊ฐ๋
์ด๋ผ๋ฉด ์ธ์คํด์ค๋ ์ง ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐํ๋ [์ฐ๋ฆฌ์ง, ์ํฌ๋ค ์ง, ์ฒ ์๋ค ์ง] ์ด๋ผ๊ณ ์๊ฐํ ์ ์๋ค.
๊ฐ๊ฐ์ ์ง์ ์๋ก ๋ค๋ฅธ ์์ฑ์ ๊ฐ์ง์ ์์ง๋ง ๋ชจ๋ ์ง ํด๋์ค์ ํน์ฑ๊ณผ ๊ธฐ๋ฅ์ ๊ณต์ ํ ์ ์๋ค.
๋ฐ๋ผ์ ํด๋์ค์ ๊ฐ์ฒด๋ 1:N์ ๊ด๊ณ๋ฅผ ๊ฐ์ง๊ณ , ๊ฐ์ฒด์ ์ธ์คํด์ค๋ 1:N์ ๊ด๊ณ๋ฅผ ๊ฐ์ง๋ค.
๊ฐ์ฒด๋ ๋ฐํ์(์คํ ์)์ ๋ฉ๋ชจ๋ฆฌ์ ํ ๋น๋๋ ๋ฐ๋ฉด, ํด๋์ค๋ ์ปดํ์ผ ์ ๋ฉ๋ชจ๋ฆฌ์ ๋ก๋๋๋ค.
๋ฉ์๋๋ ํน์ ํด๋์ค์ ์ํ๋ฉฐ, ๊ฐ์ฒด์ ์์ฑ์ด๋ ํ๋์ ์ ์ํ๋ ์ฝ๋๋ฅผ ์๋ฏธํ๋ค.
๊ฐ์ฒด๋ฅผ ํตํด์๋ง ํธ์ถํ ์ ์์ผ๋ฉฐ ๊ฐ์ฒด๊ฐ์ ์ํธ์์ฉ๊ณผ ๋ฐ์ดํฐ ์๋์ ๊ตฌํํ๋ค.
ํจ์๋ ์ข ๋ ๋ฒ์ฉ์ ์ธ ๊ฐ๋ ์ผ๋ก ๋ ๋ฆฝ์ ์ผ๋ก ์กด์ฌํ๋ฉฐ ํน์ ์์ ์ด๋ ๊ธฐ๋ฅ์ ์ํํ์ฌ ๊ฐ์ ๋ฐํํ๋, ์๋ฐ ์ด์ธ์ ๋ค๋ฅธ ํ๋ก๊ทธ๋๋ฐ์์๋ ์ฌ์ฉ๋๋ ๊ฐ๋ ์ด๋ค.
๊ฐ๋ ์ ์ผ๋ก ํจ์๋ ๋ฉ์๋๋ณด๋ค ์์์ ๊ฐ๋ ์ด์ง๋ง, ์ฃผ๋ก ์๋ฐ ์ง์์์๋ ์ด๊ธฐ๋ถํฐ ๋ฉ์๋๋ผ๋ ๋ช ๋ช ๋ฒ์ด ์ต์ํ๊ธฐ ๋๋ฌธ์ ํจ์ = ๋ฉ์๋ ๋ผ๊ณ ํํ์ ํ ๊ฒ์ด๋ค.
๋์ ์ฝ๋๊ฐ ์์ผ ์๋ก ํ ์์ฐ์ฑ์ ๋จ์ด์ง๋ค.
์ต๋ํ ๋น ๋ฅธ ๊ฐ๋ฐ์ ์ถ๊ตฌํ๋ ๊ฐ์ฅ ๊ฐ๋จํ ๊ธธ์ ์ต๋ํ ์ฝ๋๋ฅผ ํด๋ฆฐํ๊ฒ ์ ์งํ๋ ๊ฒ์ด๋ค.
๊ทธ๋ฌ๊ธฐ ์ํ ๋ช๊ฐ์ง ์์น์ ์๋์ ๊ฐ๋ค.
- ์ค๋ณต์ ํผํ๋ผ.
- ํ๊ธฐ๋ฅ๋ง ์ํํ๋ผ
- ์ ๋๋ก ํํํ๋ผ
- ์๊ฒ ์ถ์ํํ๋ผ.
- ๋ณด์ด์ค์นด์ฐํธ ๊ท์น : ์บ ํ์ฅ์ ์ฒ์ ์์ ๋๋ณด๋ค ๋ ๊นจ๋ํ๊ฒ ํด๋๊ณ ๋ ๋๋ผ.
- ๋ณ์ ์ด๋ฆ์ ๋ช ํํ ํ๊ณ
- ์กฐ๊ธ ๊ธด ๋ฉ์๋๋ ๋ถํ ํ๊ณ
- ์ฝ๊ฐ์ ์ค๋ณต์ ์ ๊ฑฐํ๊ณ
- ๋ณต์กํ if ๋ฌธ ํ๋๋ฅผ ์ ๋ฆฌํ๊ณ โฆ
- ๊ฐ์ฒด ์งํฅ ์ค๊ณ์ ๋ค์ฏ๊ฐ์ง ์์น
- SRP(Single Responsibility Principle) : ํด๋์ค์๋ ํ ๊ฐ์ง ๋ณ๊ฒฝ ์ด์ ๋ง ์กด์ฌํด์ผ ํ๋ค.
- OCP(Open Closed Principle) : ํด๋์ค๋ ํ์ฅ์ ์ด๋ ค ์์ด์ผ ํ๋ฉฐ ๋ณ๊ฒฝ์ ๋ซํ ์์ด์ผ ํ๋ค.
- LSP(Liskov Substitution Principle) : ์์๋ฐ์ ํด๋์ค๋ ๊ธฐ์ด ํด๋์ค๋ฅผ ๋์ฒดํ ์ ์์ด์ผ ํ๋ค.
- DIP(Dependency Inversion Principle) : ์ถ์ํ๋ ์์กดํด์ผ ํ๋ฉฐ, ๊ตฌ์ฒดํ๋ ์์กดํ๋ฉด ์๋๋ค.
- ISP(Interface Segregation Principle) : ํด๋ผ์ด์ธํธ๊ฐ ํ์๋ก ํ๋ ๊ธฐ๋ฅ๋ง์ ์ ๊ณตํ๋ค. (๋ถํ์ํ ๊ธฐ๋ฅ๋ค์ ๋ถ๋ฆฌํ์ฌ ์ธํฐํ์ด์ค๋ฅผ ์๊ฒ ์ ์งํด์ผ ํ๋ค)
์๋๋ฅผ ๋ถ๋ช ํ ๋ฐํ์ผ ํ๋ค.
// ์๋ฏธ ์๋ ํจ์ ์ด๋ฆ
public void processData(Object data) {
// ...
} // bad
// ์๋ฏธ ์๋ ํจ์ ์ด๋ฆ
public void calculateStudentGrade(List<Student> students) {
// ...
} // good
์กฐ๊ฑด์ ๊ฒฝ์ฐ ๋ค์๊ณผ ๊ฐ์ ์ฝ๋๊ฐ ๋ ๋ช ํํ ์ฝํ๋ค.
์กฐ๊ฑด๋ฌธ ๋ถ๋ฆฌ ์ฝ๋
// ์ค์ฒฉ if ๋ฌธ
if (user.isLogIn()) {
if (user.isAdmin()) {
// ๊ด๋ฆฌ์ ํ์ด์ง ํ์
} else {
// ์ผ๋ฐ ์ฌ์ฉ์ ํ์ด์ง ํ์
}
} else {
// ๋ก๊ทธ์ธ ํ์ด์ง ํ์
} // bad
// ์กฐ๊ฑด๋ฌธ ๋ถ๋ฆฌ ๋ฐ ํจ์ ์ฌ์ฉ
if (user.isLogIn()) {
renderUserPage(user);
} else {
renderLoginPage();
}
private void renderUserPage(User user) {
if (user.isAdmin()) {
renderAdminPage(user);
} else {
renderUserPage(user);
}
}
private void renderLoginPage() {
// ๋ก๊ทธ์ธ ํ์ด์ง ํ์
} // good
์กฐ๊ฑด๋ถ ํํ์ ์ฌ์ฉ
// if ๋ฌธ ์ฌ์ฉ
if (user.getAge() >= 19) {
// ์ฑ์ธ์์ ์๋ฆฌ๋ ๋ฉ์์ง ์ถ๋ ฅ
} else {
// ๋ฏธ์ฑ๋
์์ ์๋ฆฌ๋ ๋ฉ์์ง ์ถ๋ ฅ
} // bad
boolean isAdult = user.getAge() >= 19;
if (isAdult) {
// ์ฑ์ธ์์ ์๋ฆฌ๋ ๋ฉ์์ง ์ถ๋ ฅ
} else {
// ๋ฏธ์ฑ๋
์์ ์๋ฆฌ๋ ๋ฉ์์ง ์ถ๋ ฅ
} // good
์๋ฌธ์ O์ ์ซ์ 0 ๊ณผ ๊ฐ์ด ๊ตฌ๋ถ์ด ์ด๋ ค์ด ์กฐํฉ์ ์ ๋ณด๋ก ์ ๊ณตํ์ง ์์์ผ ํ๋ค.
์ ์ฌํ ๊ฐ๋ ์ ์ ์ฌํ ํ๊ธฐ๋ฒ์ ์ฌ์ฉํด์ผ ํ์ง๋ง(์ผ๊ด์ฑ) ๋ค๋ฅธ ๊ฐ๋ ๊ณผ ํผ์ฉ๋ ์ ์๋ ์ ๋ณด๋ฅผ ์ ๊ณตํด์๋ ์๋๋ค.
๋ฌธ์์ด์ ์นดํผํ ๋ ํ๋ผ๋ฏธํฐ๋ก (char a1[], char a2[]) ๋ณด๋ค๋ (char source[], char destination[]) ์ด ํจ์ฌ ์ฝ๊ธฐ ํธํ๋ค.
ํด๋์ค ์ด๋ฆ๊ณผ ๊ฐ์ฒด ์ด๋ฆ์ ๋ช ์ฌ๋ ๋ช ์ฌ๊ตฌ๊ฐ ์ ํฉํ๋ค.
ex)Customer, Account ๋ฑ (info, data, manager ๋ฑ์ ํผํ๋ค)
๋์ฌ๋ ๋์ฌ๊ตฌ๊ฐ ์ ํฉํ๋ค.
ex) registerUser, deletePage
ex) fetch, get, retrieve๋ฑ๊ณผ ๊ฐ์ด ํ๊ฐ์ง ๊ธฐ๋ฅ์ ํ๋ ์ฌ๋ฌ ํ๊ธฐ๋ ์ฌ๋ฌ ๋จ์ด๋ ํผํ๋ผ.
- ํจ์๋ ์๊ฒ ๋ง๋ค์ด์ผ ํ๋ค.
- ํ๊ฐ์ง๋ง ์ํํด์ผ ํ๋ค.
- ํจ์๋น ํ๋์ ์ถ์ํ ์์ค์ ์ ์งํ๋ค.
- ์์์ ์๋๋ก ์ฝ๋ ์ฝ๊ธฐ : ๋ด๋ ค๊ฐ๊ธฐ ๊ท์น, ์์์ ๋ถํฐ ์๋๋ก ์ฝ๋๊ฐ ์ด๋๋์ด์ผ ํ๋ค.
- ๊ฐ์ธ์ ์ธ ์๊ฒฌ์ด์ง๋ง ํ ๋ฉ์๋์ ๋ค๋ฅธ ๋ฉ์๋๋ค์ด ์กด์ฌํ๋ค๋ฉด ๋จผ์ ์ฝํ๋ ๋ฉ์๋๊ฐ ๋ฐ๋ก ๋ฐ์ ์กด์ฌํ๋๊ฒ ์ฝ๊ธฐ ํธํ๋ค.
switch ๋ฌธ์ ๊ธฐ๋ณธ์ ์ธ ๋ถ๊ธฐ ๋ฌธ๋ฒ์ด ๊ธธ์ด ์๊ฒ ๋ง๋ค๊ธฐ ์ด๋ ต๋ค.
๋ํ ๊ธฐ๋ฅ์์ผ๋ก ์ฌ๋ฌ๊ฐ์ ์กฐ๊ฑด์ ๋ง๋ ์ฒ๋ฆฌ๋ฅผ ํ๊ธฐ ๋๋ฌธ์
ํ๊ฐ์ง ๊ธฐ๋ฅ์ ์ํํ์ง ๋ชปํ๋ฏ๋ก, ์ด๋ฅผ ์ ์ฐจ์ ํด๋์ค๋ก ์จ๊ธฐ๊ณ ๋ณ๊ฒฝ๋๋ ์์ธ์ ๋ฐ๋ฅธ ์ํฅ๋๋ฅผ ์ต์ํ ํด์ผ ํ๋ค.
์๋์ ์์ ๋ฅผ ์ดํด๋ณด์. ๊ฐ ๊ทผ๋ฌดํํ์ ๋ฐ๋ฅธ ๊ธ์ฌ๋ฅผ ๊ณ์ฐํ๋ ๋ก์ง์ด๋ค.
/**
* @author CodeVillains
* @description ์ง๊ด์ ์ผ๋ก switch ์กฐ๊ฑด์์ ํตํด ์์ฑํ ๊ธ์ฌ ๊ณ์ฐ ๋ก์ง.
* ์ฅ์ : switch ๊ตฌ๋ฌธ์ ์ด์ฉํด์ ๋ก์ง์ ์ง๊ด์ ์ผ๋ก ์ดํดํ ์ ์๋ค. ๋จ์ํ ๋ก์ง์ ๊ฒฝ์ฐ ์ง์ ๊ตฌํํ๋ ํํ๋ก ์ด๋ณด์์ฉ ์ฝ๋๋ก ์ ํฉํ๋ค.
* ๋จ์ : ์ฝ๋ ์ค๋ณต - switch ๋ฌธ ๋ด์ ๊ฐ ์ฌ๋ก๊ฐ ์๋ก ๋ค๋ฅธ ๊ณ์ฐ์ ์ฒ๋ฆฌํ์ฌ ์ค๋ณต์ด ๋ฐ์ํ๋ฏ๋ก ์ผ์ ์์ค์ ์ฝ๋ ์ค๋ณต์ด ๋ฐ์ํ๋ค.
* OCP ์๋ฐ - ์ง์ ์ ํ์ด ์ถ๊ฐ๋๋ค๊ฑฐ๋ ๊ธ์ฌ ๊ณ์ฐ์์ด ๋ฐ๋ ๊ฒฝ์ฐ ์ค์์น ๊ตฌ๋ฌธ๋ด๋ถ๊ฐ ๊ธ๊ฒฉํ๊ฒ ๋์ด๋๊ณ ๊ฐ๊ฐ์ ์ฝ๋ ๋ก์ง์ด ๋ณต์กํด์ ธ ์ ์ง๊ด๋ฆฌ๊ฐ ์ด๋ ต๋ค.(๊ฐ๋ฐฉํ์ ์์น ์๋ฐ)
*/
public class EmployeePaySwitch {
public static void main(String[] args) {
String employeeType = "Contract"; // ์ง์ ์ ํ ์ค์
double hoursWorked = 40; // ๊ทผ๋ฌด ์๊ฐ
double hourlyRate = 15000; // ์๊ฐ๋น ๊ธ์ฌ
double pay = calculatePay(employeeType, hoursWorked, hourlyRate);
System.out.println("์ง์ ์ ํ: " + employeeType);
System.out.println("๊ทผ๋ฌด ์๊ฐ: " + hoursWorked + "์๊ฐ");
System.out.println("์๊ฐ๋น ๊ธ์ฌ: " + hourlyRate + "์");
System.out.println("์ด ๊ธ์ฌ: " + pay + "์");
}
public static double calculatePay(String employeeType, double hoursWorked, double hourlyRate) {
double pay = 0;
switch (employeeType) {
case "Regular":
pay = hoursWorked * hourlyRate + 50000; // ์ ๊ท์ง ์ถ๊ฐ ๊ธ์ฌ
break;
case "Contract":
pay = hoursWorked * hourlyRate;
break;
case "Temporary":
pay = hoursWorked * hourlyRate * 0.8; // ์์์ง ๊ธ์ฌ์จ
break;
case "Asist":
pay = hoursWorked * hourlyRate * 0.7; // ์ด์์คํธ ๊ธ์ฌ์จ
break;
default:
System.out.println("์ ํจํ์ง ์์ ์ง์ ์ ํ์
๋๋ค.");
}
return pay;
}
}
๋๋ถ๋ถ์ ๊ฒฝ์ฐ ์ด ์ฝ๋์์ ์ข ๋ ๊ฐ์ ํด๋ณด์๋ฉด ์๋์ ๊ฐ์ ํํ๋ก ์์ฑํ ๊ฒ์ด๋ค.
public class EmployeePaySwitchRefactor {
public static void main(String[] args) {
String employeeType = "Contract"; // ์ง์ ์ ํ ์ค์
double hoursWorked = 40; // ๊ทผ๋ฌด ์๊ฐ
double hourlyRate = 15000; // ์๊ฐ๋น ๊ธ์ฌ
double pay = calculatePay(employeeType, hoursWorked, hourlyRate);
System.out.println("์ง์ ์ ํ: " + employeeType);
System.out.println("๊ทผ๋ฌด ์๊ฐ: " + hoursWorked + "์๊ฐ");
System.out.println("์๊ฐ๋น ๊ธ์ฌ: " + hourlyRate + "์");
System.out.println("์ด ๊ธ์ฌ: " + pay + "์");
}
public static double calculatePay(String employeeType, double hoursWorked, double hourlyRate) {
double pay = 0;
switch (employeeType) {
case "Regular":
pay = calculatePayWithAdjustment(hoursWorked, hourlyRate, 1.0); // ์ ๊ท์ง ์ถ๊ฐ ๊ธ์ฌ
break;
case "Contract":
pay = calculatePayWithAdjustment(hoursWorked, hourlyRate, 1.0);
break;
case "Temporary":
pay = calculatePayWithAdjustment(hoursWorked, hourlyRate, 0.8); // ์์์ง ๊ธ์ฌ์จ
break;
case "Assist":
pay = calculatePayWithAdjustment(hoursWorked, hourlyRate, 0.7); // ์ด์์คํธ ๊ธ์ฌ์จ
break;
default:
System.out.println("์ ํจํ์ง ์์ ์ง์ ์ ํ์
๋๋ค.");
}
return pay;
}
private static double calculatePayWithAdjustment(double hoursWorked, double hourlyRate, double adjustment) {
return hoursWorked * hourlyRate * adjustment;
}
}
switch ๊ตฌ๋ฌธ ๋ด์์ ๋ก์ง์ด ๋ค์ด๊ฐ๋ ๋ถ๋ถ์ ๊ณตํต ๋ฉ์๋๋ก ๋ถ๋ฆฌํ์๋ค.
์ฅ์ ์ผ๋ก๋ ๊ณตํต ๊ธ์ฌ ๊ณ์ฐ ๋ก์ง์ ๋ณ๋์ ๋ฉ์๋๋ก ์ถ์ถํ๋ฉด ์ค๋ณต์ด ์ค์ด๋ค๊ณ ์ฝ๋ ์ฌ์ฌ์ฉ์ฑ์ด ์ข์์ก๋ค๋ ๊ฒ๊ณผ
๊ฐ ๊ณ์ฐ ๋ก์ง์ด ๋ฉ์๋ ํธ์ถ ํํ๋ก ๋ณ๊ฒฝ ๋์ด ๊ฐ๋
์ฑ ๋ฐ ๋ณ๊ฒฝ ์ํฅ๋๊ฐ ์ค์ด๋ค์ด ์ ์ง๋ณด์๊ฐ ๋ ์ฝ๋ค๋ ๊ฒ์ ๋ค ์ ์๋ค.
๊ทธ๋ฌ๋ ์์ง switch ๋ฌธ์ ๋ก์ง์ด ์กด์ฌํ์ฌ ๊ทผ๋ฌด ํํ์ ๋ฐ๋ฅธ ์ ์ฑ
์ด ๋ณ๊ฒฝ๋๋ฉด ๋ด๋ถ ๋ก์ง์ ์์ ์ด ํ์ํ ์ ์๊ณ (OCP) switch ๋ฌธ์ด ์ฃผ์ ํ๋ฆ์ด๋ฏ๋ก ๋ฉ์๋๊ฐ ์ถ๊ฐ ๋๋ค๊ฑฐ๋ ํ๋ ๋ฑ์ ํ์ฅ์ฑ์๋ ์์ ํ ์ ์ฉํด๋ณด์ด์ง ์๋๋ค.
๋ฐ๋ผ์ ์ด๋ฌํ switch ๊ตฌ๋ฌธ์ ์ด๋๊ฐ๋ก ์์ํ๊ฑฐ๋ ์์ ๊ด๊ณ๋ก ์จ๊ฒจ ์ค์ ๋ก์ง์ ํ์ ํด๋์ค์ ๋ฉ์๋๋ฅผ ํธ์ถํ๋ ๋ฐฉ์์ผ๋ก ๋ณ๊ฒฝํ๋๊ฒ ๋ OOP์ ์ ํฉํ๋ค.
์ฌ๊ธฐ์ ๊ณตํต ๊ธฐ๋ฅ์ ์ํํ๋ ๊ธ์ฌ ๊ณ์ฐ์ ๊ตฌํํ๋ ๋ชจ๋ ๊ทผ๋ฌดํํ ํด๋์ค์์ ๊ฐ๊ฐ์ ๋ก์ง์ ๊ฐ์ ธ์ผ ํ๋ฏ๋ก ์ธํฐํ์ด์ค๋ก ์ ์ํ๋ค.
/**
* ๊ธ์ฌ ๊ณ์ฐ์ ๋ํ ๊ณตํต ์ธํฐํ์ด์ค ์ ์
*/
public interface PayCalculator {
double calculatePay(double hoursWorked, double hourlyRate);
}
์ ๊ท์ง์ธ ๊ฒฝ์ฐ ๊ธ์ฌ ๊ณ์ฐ์์์ + 50000์ด ์ถ๊ฐ๋๋ฏ๋ก ์๋์ ๊ฐ์ ๊ณ์ฐ์์ด ๋๋ค.
public class RegularPayCalculator implements PayCalculator {
@Override
public double calculatePay(double hoursWorked, double hourlyRate) {
return hoursWorked * hourlyRate + 50000;
}
}
๊ณ์ฝ์ง๊ณผ ์์์ง, ์ด์์คํธ ๊ณ์ฐ์๋ ๊ฐ๊ฐ ๊ตฌํํด์ค๋ค.
// ๊ณ์ฝ์ง
public class ContractPayCalculator implements PayCalculator {
@Override
public double calculatePay(double hoursWorked, double hourlyRate) {
return hoursWorked * hourlyRate;
}
}
// ์์์ง
public class TemporaryPayCalculator implements PayCalculator {
@Override
public double calculatePay(double hoursWorked, double hourlyRate) {
return hoursWorked * hourlyRate * 0.8; // Temporary employe
}
}
// ์ด์์คํธ
class AssistPayCalculator implements PayCalculator {
@Override
public double calculatePay(double hoursWorked, double hourlyRate) {
return hoursWorked * hourlyRate * 0.7; // Assist employee
}
}
์ฌ๊ธฐ๊น์ง๊ฐ ๊ตฌ์ฒด์ ์ธ ๊ธ์ฌ ๊ณ์ฐ ํด๋์ค๋ค์ด๋ค. ์ด์ ์ด๋ฅผ ํธ์ถํ๋ ํด๋ผ์ด์ธํธ(๋ฉ์ธ ์ฝ๋)์ ์ธ์คํด์ค๋ฅผ ์์ฑํ๋ ์ฝ๋๋ฅผ ์ดํด๋ณด์.
public class PayCalculatorFactory {
static PayCalculator createPayCalculator(String employeeType) {
switch (employeeType) {
case "Regular":
return new RegularPayCalculator();
case "Contract":
return new ContractPayCalculator();
case "Temporary":
return new TemporaryPayCalculator();
case "Assist":
return new AssistPayCalculator();
default:
return null;
}
}
}
createPayCalculator ๋ฉ์๋๋ฅผ ํตํด ์ ์ ํ ๊ธ์ฌ ๊ณ์ฐ ์ธ์คํด์ค๋ฅผ ์์ฑํด์จ๋ค. ๊ทธ ๊ฒฐ๊ณผ์ ๋ฐ๋ผ switch ๊ตฌ๋ฌธ์ ๋ฐ๋ฅธ ์ง์ ์ ํ์ ๊ณ์ฐ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๋ค.
public class EmployeePayAbstractFatoryMain {
public static void main(String[] args) {
String employeeType = "Contract"; // Employee type setting
double hoursWorked = 40; // Hours worked
double hourlyRate = 15000; // Hourly rate
PayCalculator payCalculator = PayCalculatorFactory.createPayCalculator(employeeType);
if (payCalculator != null) {
double pay = payCalculator.calculatePay(hoursWorked, hourlyRate);
System.out.println("Employee type: " + employeeType);
System.out.println("Hours worked: " + hoursWorked + " hours");
System.out.println("Hourly rate: " + hourlyRate + " won");
System.out.println("Total pay: " + pay + " won");
} else {
System.out.println("Invalid employee type.");
}
}
}
์ค์ ํธ์ถ ๋ถ๋ถ์ switch ๊ตฌ๋ฌธ์ด ๋ณด์ด์ง ์๊ณ ํฉํ ๋ฆฌ๋ฅผ ํตํด ์์ฑํ ์ง์ ์ ํ์ ๋ฐ๋ฅธ ๊ณ์ฐ์์ ํธ์ถ ํ ๋ค ๊ฒฐ๊ณผ๊ฐ๋ง์ ์ถ๋ ฅ(return) ํด์ฃผ๋ ์ญํ ์ด ๋์ด๋ค.
์ด ์ฝ๋๋ ๊ฐ ์ง์ ์ ํ์ ๋ํ ๊ธ์ฌ ๊ณ์ฐ์ ๋ชจ๋ํํ๊ณ , ์ถ๊ฐ์ ์ธ ์ง์ ์ ํ์ด ์ถ๊ฐ๋๊ฑฐ๋ ๋ณ๊ฒฝ๋๋๋ผ๋ ์์ ์ ์ต์ํํ๋ค. ๋ฌผ๋ก ์๋ก์ด ์ง์ ์ ํ์ ์ถ๊ฐํด์ผ ํ๋ ๊ฒฝ์ฐ ์ฌ์ ํ ์ค์์น ๋ฌธ ๋ด์์ ์๋ก์ด ์ผ์ด์ค๋ฅผ ์ฒ๋ฆฌํด์ฃผ์ด์ผ ํ๊ธฐ ๋๋ฌธ์ OCP๋ฅผ ์์ ํ ๊ตฌํํ ๊ฒ์ ์๋๋ค.
๊ทธ๋ฌ๋ ์๋ณธ ์ฝ๋์ ๋น๊ตํ๋ค๋ฉด ๋ ์ ์ง๋ณด์ ํ๊ธฐ ์ฝ๊ณ ๊ธฐ๋ฅ๋ณ ๋ชจ๋ํ๋ฅผ ํตํด ๊ฐ์ฒด ์งํฅ์ ์ธ ์ฝ๋์ ๋ฐฉํฅ์ ๋ง๊ฒ ์ค๊ณ๊ฐ ๊ฐ๋ฅํ๋ค๋ ๊ฒ์ ์์ ์๋ค.
๋ง์ฝ ํด๋น ์ฝ๋์ switch ๊ตฌ๋ฌธ์ ์ ๊ฑฐํ๊ณ ์ ๋ต ํจํด์ผ๋ก ์ข ๋ ์๋ฒฝํ๊ฒ ๋ถ๋ฆฌํ๋ ๊ฒ์ ์ถ๊ฐ ํ๊ณ ์ถ๋ค๋ฉด ์๋์ ์์ ์ฝ๋๋ค์ ์ดํด๋ณด๋ฉด ๋์์ด ๋ ๊ฒ์ด๋ค.
๋น์ทํ ๊ตฌ์กฐ๋ก ๋ฆฌํฉํ ๋ง ๋์ด์์ผ๋ฏ๋ก ์ดํดํ๊ธฐ ์ด๋ ต์ง ์์ ๊ฒ์ด๋ค.
๋ฉ์๋๊ฐ ํ๋ ์ผ์ ์ข ๋ ์ ํํํ ์ ์๋๋ก ์์ ํ ์ด๋ฆ์ ์ง์ด์ผ ํ๋ค.
๋ชจ๋ ๋ด์์ ํจ์ ์ด๋ฆ์ ์ผ๊ด์ฑ(๊ฐ์ ๋ฌธ๊ตฌ๋ ๋ช ์ฌ, ๋์ฌ๋ฑ์ ์ฌ์ฉ ๊ท์น)์ ์ ์งํด์ผ ํ๋ค.
์ด์์ ์ธ ์ธ์์ ๊ฐฏ์๋ '์์ผ๋ฉด ์์์๋ก ์ข๋ค' ์ด๋ค. ์ธ์๋ฅผ ์ฃผ๋๋ผ๋ 3๊ฐ ์ด์์ ์ง์ํ๊ณ , 4๊ฐ ๋ถํฐ๋ ํน๋ณํ ๊ฒฝ์ฐ๋ฅผ ์ ์ธํ๊ณ ๋ ๊ฐ์ฒด๋ฅผ ๋๊ฒจ์ ์ฒ๋ฆฌํ๋ ๊ฒ์ด ๋ซ๋ค. ๋ํ ์ธ์๊ฐ์ผ๋ก flag๋ฅผ ๋๊ธฐ๋ค๋ ๊ฒ์ ๋ฉ์๋์์ ์ฌ๋ฌ๊ฐ์ ์ฒ๋ฆฌ๋ฅผ ํ๊ฒ ๋ค๋ ์๋ฏธ์ด๋ฏ๋ก boolean ์ ํตํด ๋ด๋ถ์์ ๊ฐ์ ์กฐ์ํ๋ ํ์๋ ์ง์ํด์ผ ํ๋ค.
์ฆ, ์ฃผ์์ ๋์ ์ฝ๋๋ฅผ ๋ณด์ํ์ง ๋ชปํ๋ค.
Code as Docuementation by Martin fowler
๋จ์ ์ ๋ณด๋ง ์ฃผ๋ ์ฃผ์์ ์๋ฌด๋ฐ ๊ฐ์น๊ฐ ์๊ณ ์คํ๋ ค ์ฝ๋์ ์๋๋ง ํด์น๊ฒ ๋๊ณ ์๋ชป๋ ์ ๋ณด๋ฅผ ์ ๊ณตํ ๊ฐ๋ฅ์ฑ์ด ๋์ผ๋ฏ๋ก ํ์์ ์ธ ๋ด์ฉ๋ง์ ๊ธฐ๋กํ๋๊ฒ ์ข๋ค.
๋ค์ฌ์ฐ๊ธฐ, ๋น ํ์ผ๋ก ๊ฐ์์ฑ์ ํ๋ณดํ๊ฑฐ๋ ์ ์ ํ ์์ค์ ํ ๊ธธ์ด๋ฅผ ๋ง์ถ๋ ๋ฑ์ ๋
ธ๋ ฅ์ ๊ฒฐ๊ตญ ์ฝ๋์ ํ์ง๊ณผ ๊ฐ๋
์ฑ์ ์ง๊ฒฐ๋๋ค.
20์~60๋ฐ ์ ๋์ ๊ฐ๋กํ์ด ์ ์ ํ๊ณ ๊ทธ๋ณด๋ค ๋ ์งง์ ์๋ก ์ธ์ง์จ์ด ์ฌ๋ผ๊ฐ๋ค.
๋ํ ํ์ด๋ผ๋ฉด ํ ๋ด๋ถ์์ ๋ช
๋ช
๋ฒ์ด๋ ์ฝ๋ฉ ๊ท์น์ ๋ง๋ จํด์ ์ด ํ์์ ๋ฐ๋ผ ๊ฐ๋ฐ์ ์งํํด์ผ ํ๋ค.
๋๋ฏธํฐ์ ๋ฒ์น : ์ค์ง ํ๋์ ์ (.)์ ์ํด ํธ์ถ๋๋ ๋ฉ์๋๋ง ํธ์ถํด์ผ ํ๋ค.
public class Car {
private Engine engine;
public Car(Engine engine) {
this.engine = engine;
}
public Engine getEngine() {
return engine;
}
}
public class Engine {
public void start() {
// ์์ง์ ์์ํ๋ ๋ก์ง
}
}
public class Driver {
public void drive(Car car) {
// ๋๋ฏธํฐ์ ๋ฒ์น ์๋ฐ: Car ๊ฐ์ฒด์์ ์์ง์ ์ง์ ์ ๊ทผํ์ฌ ๋ฉ์๋๋ฅผ ํธ์ถํจ
car.getEngine().start(); // ์์ง์ ์์ํ๋ ๋ฉ์๋ ํธ์ถ
}
}
Driver ํด๋์ค์ drive ๋ฉ์๋๋ Car ํด๋์ค์ getEngine์ ํตํด start ๋ฉ์๋๋ฅผ ํธ์ถํ๋ค. ์ด๋ ๊ฒ ํ๋ ๋์ ์ Car ํด๋์ค ๋ด์์ ์์ง์ ์์ํ๋ ๋ฉ์๋๋ฅผ ํธ์ถํ๋๋ก ๋ณ๊ฒฝํด์ผ ํ๋ค.
public class Car {
private Engine engine;
public Car(Engine engine) {
this.engine = engine;
}
public void startEngine() {
engine.start();
}
}
public class Engine {
public void start() {
// ์์ง์ ์์ํ๋ ๋ก์ง
}
}
public class Driver {
public void drive(Car car) {
car.startEngine(); // ๋๋ฏธํฐ์ ๋ฒ์น ์ค์: Car ํด๋์ค์์ ์์ง ์์ ๋ฉ์๋๋ฅผ ํธ์ถํจ
}
}
Driver ํด๋์ค์์๋ Car ๊ฐ์ฒด์ startEngine ๋ฉ์๋๋ง ํธ์ถํ์ฌ ์์ง์ ์์ํ ์ ์์ผ๋ฉฐ, ์ด๋ก์จ Car ํด๋์ค๊ฐ ์์ง์ ๋ํ ๋ด๋ถ ๊ตฌํ์ ์บก์ํํ๊ณ ์ธ๋ถ๋ก ๋ ธ์ถํ์ง ์๊ฒ ๋๋ค.
DTO(Data Transfer Object)๋ฅผ ํตํด ์ ์กํด์ผ ํ๋ ๋ฐ์ดํฐ๋ฅผ ๋ช
ํํ๊ฒ ์ ์ํ๊ณ ๋ชจ๋๊ฐ์ ๊ฒฐํฉ๋๋ฅผ ๋ฎ์ถฐ์ผ ํ๋ค.
๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ๋ ๊ณผ์ ์์ ๊ฐ์ฒด๋ฅผ ์ง์ ์ฌ์ฉํ๋ฉด ๊ฐ์ฒด์ ๋ด๋ถ ๊ตฌ์กฐ์ ์์กดํ๊ฒ ๋์ด ๊ฒฐํฉ๋๊ฐ ๋์์ง๊ณ , ์ ์ฐ์ฑ๊ณผ ์ฌ์ฌ์ฉ์ฑ์ด ์ ํ๋๋ค.
๋ํ ๋ค๋ฅธ ์๋น์ค๋ฅผ ํธ์ถํ์ฌ ๋ฐ์ดํฐ๋ฅผ ์ ์กํ ๋ ๋ถํ์ํ๊ฒ ๋ง์ ๋ฐ์ดํฐ๊ฐ ์ ์ก๋ ๊ฐ๋ฅ์ฑ์ด ์์ผ๋ฏ๋ก ์ ์ ํ ์ ๋ฌ ๊ฐ์ฒด๋ฅผ ํตํด ๋ฐ์ดํฐ ์ ์ก์ ๋ช
ํ์ฑ๊ณผ ์์ ์ฑ์ ํ๋ณดํ๊ณ , ์์คํ
์ ๊ฒฐํฉ๋๋ฅผ ๋ฎ์ถ์ด ์ ์ง ๋ณด์์ฑ๊ณผ ํ์ฅ์ฑ์ ํฅ์์์ผ์ผ ํ๋ค.
์ค๋ฅ ์ฝ๋ ๋์ ์ ์์ธ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๊ถ์ฅํ๋ค. ์์ธ๋ฅผ ์ฌ์ฉํ๋ฉด ์ฝ๋์ ๊ฐ๋ ์ฑ์ด ํฅ์๋๊ณ ์ค๋ฅ ์ฒ๋ฆฌ๊ฐ ๋ช ํํด์ง๋ฉฐ, ์ฝ๋์ ์ ์ฐ์ฑ๊ณผ ์์ ์ฑ์ด ํฅ์๋๋ค.
public class Calculator {
public int divide(int dividend, int divisor) {
if (divisor == 0) {
return -1; // ์ค๋ฅ ์ฝ๋ ๋ฐํ
}
return dividend / divisor;
}
}
์ด๋ฐ ์ฝ๋๋ ์์ธ๊ฐ ๋ฐ์ํ๊ฑด์ง ๋ก์ง์์ ๋ ์ด์ ์ํํ ์ ์๋ ๊ฒ์ธ์ง ํ๋จํ๊ธฐ ์ด๋ ต๋ค. ์๋์ ๊ฐ์ด ๋ฐ๊ฟ์ผ ํ๋ค.
public class Calculator {
public int divide(int dividend, int divisor) {
if (divisor == 0) {
throw new IllegalArgumentException("Divisor cannot be zero"); // ์์ธ ๋ฐ์
}
return dividend / divisor;
}
}
null์ ๋ฐํํ๋ ๊ฒ์ ํธ์ถ์๊ฐ ํด๋น ๊ฐ์ด null์ธ์ง ํ์ธํ๊ณ ์ฒ๋ฆฌํด์ผ ํ๋ฏ๋ก ์ฝ๋์ ๊ฐ๋ ์ฑ๊ณผ ์์ ์ฑ์ด ์ ํ๋ ์ ์๋ค. ๋ฐ๋ผ์ return null ๊ณผ ๊ฐ์ ์ฝ๋๋ฅผ ๋๊ฒจ์ฃผ์ง ๋ง๊ณ ๋ช ์์ ์ผ๋ก ์์ธ๋ฅผ ๋ฐ์์์ผ์ผ ํ๋ค.
if (user == null) {
throw new IllegalArgumentException("User not found with username: " + username);
}
ํด๋์ค ์ฒด๊ณ์ ํด๋์ค๋ฅผ ์ ์ํ๋ ํ์ค ์๋ฐ ๊ท์น์ ์๋์ ๊ฐ๋ค.
- ์์(Constant): ํด๋์ค ์์ค์์ ์ฌ์ฉ๋๋ ์์๋ฅผ ์ ์. ์ ์ ๊ณต๊ฐ ์์๊ฐ ๋งจ ์ฒ์ ๋์ค๊ณ ๋ค์์ผ๋ก ์ ์ ๋น๊ณต๊ฐ๊ฐ ๋์จ๋ค.
- ๋ฉค๋ฒ ๋ณ์(Fields): ํด๋์ค์ ์ํ๋ฅผ ํํํ๋ ๋ฉค๋ฒ ๋ณ์๋ฅผ ์ ์.
- ์์ฑ์(Constructor): ํด๋์ค์ ๊ฐ์ฒด๋ฅผ ์์ฑํ ๋ ํธ์ถ๋๋ ์์ฑ์๋ฅผ ์ ์.
- ๋ฉ์๋(Methods): public ๋ฉ์๋๊ฐ ๋์ค๊ณ ๊ทธ ๋ค์ private ํธ์ถ ๋ฉ์๋๋ฅผ ์์ฐจ์ ์ผ๋ก ์์ฑํด์ค๋ค.
์๋์ ์์ ๋ฅผ ๋ณด์.
public class ExampleClass {
// ์์
private static final int MAX_SIZE = 100;
private static final String DEFAULT_NAME = "Default";
// ๋ฉค๋ฒ ๋ณ์
private int size;
private String name;
// ์์ฑ์
public ExampleClass() {
this.size = 0;
this.name = DEFAULT_NAME;
}
public ExampleClass(int size, String name) {
this.size = size;
this.name = name;
}
// ๋ฉ์๋
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
// ์ค์ฒฉ ํด๋์ค
private static class NestedClass {
// ์ค์ฒฉ ํด๋์ค์ ๋ฉค๋ฒ ๋ณ์, ์์ฑ์, ๋ฉ์๋ ๋ฑ
}
}
SRP(Single Responsibility Principle, ๋จ์ผ ์ฑ ์ ์์น)๋ ์ํํธ์จ์ด ๊ฐ๋ฐ์์ ํด๋์ค๋ ๋ชจ๋์ ํ๋์ ์ฑ ์๋ง ๊ฐ์ ธ์ผ ํ๋ค๋ ์๋ฏธ๋ก, ์ด๋ ํด๋์ค๊ฐ ๋ณ๊ฒฝ๋์ด์ผ ํ๋ ์ด์ ๊ฐ ๋จ ํ๋๋ฟ์ด์ด์ผ ํจ์ ์๋ฏธํ๋ค.
public class ReportGenerator {
public void generateReport() {
// ๋ณด๊ณ ์ ์์ฑ ๋ก์ง
String data = fetchDataFromDatabase();
formatData(data);
saveReportToFile(data);
}
private String fetchDataFromDatabase() {
// ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ๋ก์ง
return "Data from database";
}
private void formatData(String data) {
// ๋ฐ์ดํฐ ํฌ๋งทํ
๋ก์ง
System.out.println("Formatting data: " + data);
}
private void saveReportToFile(String data) {
// ํ์ผ์ ๋ณด๊ณ ์๋ฅผ ์ ์ฅํ๋ ๋ก์ง
System.out.println("Saving report to file: " + data);
}
}
์ด ์ฝ๋๋ ๋ณด๊ณ ์๋ฅผ ์์ฑํ ๋ ๊ฐ์ข ํ์ ์ ๋ฐ๋ผ ์ฌ๋ฌ ์ญํ ์ ์ํํ๋๋ก ๊ตฌํ๋์ด ์๋ค. ์ด๋ SRP ์๋ฐ์ด๊ณ ํด๋์ค๊ฐ ๋ณ๊ฒฝ ๋์ด์ผ ํ๋ ์ด์ ๊ฐ ๋ค์ํ๊ธฐ ๋๋ฌธ์ ์ ์ง๋ณด์๊ฐ ์ด๋ ค์์ง๋ค. ๋ฐ๋ผ์ ์๋์ ๋ฐฉ์์ผ๋ก ๋ณ๊ฒฝํด์ผ ํ๋ค.
public class ReportGenerator {
private DatabaseFetcher databaseFetcher;
private DataFormatter dataFormatter;
private FileSaver fileSaver;
public ReportGenerator(DatabaseFetcher databaseFetcher, DataFormatter dataFormatter, FileSaver fileSaver) {
this.databaseFetcher = databaseFetcher;
this.dataFormatter = dataFormatter;
this.fileSaver = fileSaver;
}
public void generateReport() {
String data = databaseFetcher.fetchData();
String formattedData = dataFormatter.formatData(data);
fileSaver.saveToFile(formattedData);
}
}
public class DatabaseFetcher {
public String fetchData() {
// ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ๋ก์ง
return "Data from database";
}
}
public class DataFormatter {
public String formatData(String data) {
// ๋ฐ์ดํฐ ํฌ๋งทํ
๋ก์ง
return "Formatted data: " + data;
}
}
public class FileSaver {
public void saveToFile(String data) {
// ํ์ผ์ ๋ณด๊ณ ์๋ฅผ ์ ์ฅํ๋ ๋ก์ง
System.out.println("Saving report to file: " + data);
}
}
ReportGenerator ํด๋์ค๊ฐ ๋ณด๊ณ ์ ์์ฑ๋ง ๋ด๋นํ๋๋ก ๋ณ๊ฒฝ๋์๊ณ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ(DatabaseFetcher), ๋ฐ์ดํฐ ํฌ๋งทํ
(DataFormatter), ํ์ผ ์ ์ฅ(FileSaver) ๋ฑ์ ์ญํ ์ ๊ฐ๊ฐ์ ํด๋์ค๋ก ๋ถ๋ฆฌ๋์๋ค.
์ด๋ ๊ฒ ํ๋ฉด ๊ฐ ํด๋์ค๋ ๋จ์ผ ์ฑ
์์ ๊ฐ๊ฒ ๋๊ณ , ๋ณ๊ฒฝ์ด ๋ฐ์ํ ๊ฒฝ์ฐ ํด๋น ํด๋์ค๋ง ์์ ํ๋ฉด ๋๋ฏ๋ก ์ ์ง ๋ณด์๊ฐ ์ฉ์ดํด์ง๋ค.
์์ง๋๊ฐ ๋์ ์ฝ๋๋ ๊ฐ๊ธ์ ์๊ฒ ๋๋ ์ ์์ง๋๋ฅผ ๋ฎ์ถฐ์ผ ํ๋ค. ๋จ, ํ๊ฐ์ง ๊ธฐ๋ฅ๋ง์ ์ํํ๊ธฐ ์ํ ๋ชฉ์ ์ด๋ผ๋ฉด ์๊ด์๋ค.
์ฉ๋ก๋ฅผ ์ดํด๋ณด๊ธฐ ์ ์ ์์๋ฌ์ผ ํ ๊ฒ
ํ๋์ ์ฝ๋ ๋ธ๋ญ์ผ๋ก ํํํ ๋ถ๋ถ์ ์๋จ ์ค๋ช ์ ๋ํ ์ฝ๋ ์ํ์ด๋ค.
์๋จ ์ค๋ช ์ Bold๋ก ํ๊ธฐ ํ์๋ค.
์ฝ๋๋ฅผ ๋น๊ตํด์ผ ํ๋ ๋ถ๋ถ์ ์์ ์๋๋ก ๋๋ ์๋ BadCase, ์๋๋ GoodCase๋ก ๊ธฐ์ ํ์๋ค.
import ๊ตฌ๋ฌธ์ ์ค ๋ฐ๊ฟ ํ์ง ์๋๋ค. (ํ ์ค์ ์ ์ฒด๋ฅผ ๊ธฐ์ ํ๋ค.)
ํจํค์ง ๊ตฌ์กฐ๋ ์ต์์ ๋๋ฉ์ธ๋ถํฐ ์ญ์์ผ๋ก ํ๊ธฐํ๋๊ฒ์ด ์ปจ๋ฒค์ ์ด๋ค.
com.company.project
๊ท์น 3. ํด๋์ค ์ด๋ฆ์ UpperCamelCase (PascalCase) ๋ก ์์ฑํ๋ค. ๋จ์ด์ ๋จ์ด ์ฌ์ด์๋ ๋๋ฌธ์๋ก ํ๊ธฐํ๊ณ ๋ช ์ฌ๋ก ๋ช ๋ช ํ๋ค.
์ฝ์ด๋ฅผ ์ฌ์ฉํ์ง ์๋๋ค. ์ธํฐํ์ด์ค๋ ๋์ผํ๋ค.
CustomerController # ํด๋์ค
CustomerService # ํด๋์ค
Runnable # ์ธํฐํ์ด์ค
๊ท์น 4 .๋ฉ์๋ ์ด๋ฆ์ lowerCamelCase ๋ก ์์ฑํ๋ค. ๋ฉ์๋๋ช ์ ๋์ฌ ๋๋ ๋์ฌ๊ตฌ์ด๋ค.
๋ค์ด๋ฐ์ ๋์ฌ๋ ๋์ฌ๊ตฌ๋ก ์ง๊ณ , boolean ๊ฐ์ ๋ฐํํ๋ ๊ฒฝ์ฐ๋ is๋ has๋ก ์์ํ๊ณ ํ์ฉ์ฌ๋ก ๊ธฐ๋ฅํ๋ ๋จ์ด๋ก ๋๋๋ค.
๊ฐ์ฒด์ ํ์ ์ ๋ฐ๊ฟ์, ๋ค๋ฅธ ํ์ ์ ๋ ๋ค๋ฅธ ๊ฐ์ฒด๋ฅผ ๋ฐํํ๋ ์ธ์คํด์ค ๋ฉ์๋์ ์ด๋ฆ์ ๋ณดํต toType ํํ๋ก ์ง๋๋ค.(toString, toArray)
getName()
isEmpty()
hasLevel()
toString()
๊ท์น 5. ๋๋ฌด ์งง์ ๋ณ์๋ช , ๋ฉ์๋๋ช ์ ์ง์ํ๊ณ ์๋ฏธ์๋ ์ด๋ฆ์ผ๋ก ์ง์ด์ผ ํ๋ค. ๋ฉ์๋ ๋ช ์ด๋ ๋ณ์๋ช ์ ์๋ฏธ๋ฅผ ์ ๋ฌํ๋ ์ฉ๋๋ก ์์ฑํด์ผ ํ๋ค.
- ์์๋ ๋ด์ฉ์ด ๋ณ๊ฒฝ ๋ถ๊ฐ๋ฅ ํด์ผ ํ๋ค.
- static final ํ๋์ด์ด์ผ ํ๋ค.
๋งค๊ฐ๋ณ์์ ์ด๋ฆ์ lowerCamelCase๋ก ์์ฑํ๋ค. ๋งค๊ฐ๋ณ์๊ฐ 4๊ฐ๋ฅผ ์ด๊ณผํ ๊ฒฝ์ฐ Object ํ์ ์ผ๋ก ์ ์ํ๋๊ฒ OCP(Open-Closed Principle) ์ค๊ณ์ ๋ง๋ ๋์ ๋ฐฉ์์ด๋ค.
์ง์ญ๋ณ์ ์ด๋ฆ์ lowerCamelCase๋ก ์์ฑํ๋ค. ๋ณ์ ์ด๋ฆ์ ์งง์ง๋ง ์๋ฏธ๊ฐ ์์ด์ผ ํ๊ณ , ์ธ๋์ค์ฝ์ด๋ ํน์๋ฌธ์๋ฅผ ์ฐ๋ฉด ์๋๋ค.
์์๋ static final ํค์๋๋ก ์ ์๋ ํ๋์ด๋ค. ์ด ํ๋์ ๋ด์ฉ์ ๋ถ๋ณํ๋ฉฐ, ๋ฉ์๋๋ ๋ถ์์ฉ์ด ์์ผ๋ฉด ์๋๋ค. ์ด๋ ์์ ์๋ฃํ, String, ๋ถ๋ณ ํ์ , ๋ถ๋ณ ํ์ ์ ๋ถ๋ณ collection์ ํฌํจํ๋ค. ๋ง์ฝ ์ด๋ค ์ธ์คํด์ค์ ์ํ๊ฐ ๋ฐ๋ ์ ์๋ค๋ฉด ์ด๋ ์์๊ฐ ์๋๋ค.
// ์์
static final int NUMBER = 5;
static final ImmutableList<String> NAMES = ImmutableList.of("Ed", "Ann");
static final ImmutableMap<String, Integer> AGES = ImmutableMap.of("Ed", 35, "Ann", 32);
static final Joiner COMMA_JOINER = Joiner.on(','); // Joiner๋ ๋ถ๋ณ์ด๊ธฐ ๋๋ฌธ์.
static final SomeMutableType[] EMPTY_ARRAY = {};
enum SomeEnum { ENUM_CONSTANT }
// ์์ ์๋
static String nonFinal = "non-final"; // final ์์
final String nonStatic = "non-static"; // static ์๋
static final Set<String> mutableCollection = new HashSet<String>(); // ๊ฐ๋ณํ์
String ์ฌ์ฉ
static final ImmutableSet<SomeMutableType> mutableElements = ImmutableSet.of(mutable); // ๊ฐ๋ณํ์
์ ๋ณ์๋ก ์ด๊ธฐํ ํจ
static final ImmutableMap<String, SomeMutableType> mutableValues =
ImmutableMap.of("Ed", mutableInstance, "Ann", mutableInstance2); // ๊ฐ๋ณํ์
์ ๋ณ์๋ก ์ด๊ธฐํ ํจ
static final Logger logger = Logger.getLogger(MyClass.getName());
static final String[] nonEmptyArray = {"these", "can", "change"};
๋จ, ๋ณ์์์ boolean ํ์ ์ ์ ์ ํ ๊ฒฝ์ฐ ๋ฉ์๋์๋ ๋ค๋ฅด๊ฒ isXXX ์ ๊ฐ์ ์ด๋ฆ์ ํผํ๋๋ก ํ๋ค. ์ผ๋ถ ์ง๋ ฌํ ์์ธ๊ฐ ๋ฐ์ํ ์ ์๋ค.
์์ ๋ณ์ (๋ฐ๋ณต๋ฌธ์ ์ํํ๋ ๊ฒฝ์ฐ) ๋ผ๋ ๊ฐ๊ธ์ ํ๊ธ์์ ๋ณ์๋ช ๋ณด๋ค๋ ์๋ฏธ๊ฐ ์๋ ์ด๋ฆ์ผ๋ก ์ง๋๊ฒ์ด ๋ซ๋ค.
return () -> {
while (condition()) {
method();
}
};
return new MyClass() {
@Override public void method() {
if (condition()) {
try {
something();
} catch (ProblemException e) {
recover();
}
} else if (otherCondition()) {
somethingElse();
} else {
lastThing();
}
}
};
2. ๋น ๋ธ๋ญ์ด๋ block-like construct ๋ ์๋์ ๊ฐ์ด ์ฌ์ฉ ๊ฐ๋ฅํ์ง๋ง ๋ฉํฐ ๋ธ๋ญ์ ์์ด์ ์ฌ์ฉํ ์ ์๋ค.
// ๊ฐ๋ฅ
void doNothing() {}
// ๊ฐ๋ฅ
void doNothingElse() {
}
// ๋ถ๊ฐ๋ฅ : multi-block ์์ ๊ดํธ์ ์ค๋ฐ๊ฟ๊ณผ ๋น ๋ธ๋ญ์ ๋์์ ์ฌ์ฉํ์ง ๋ง๊ฒ
try {
doSomething();
} catch (Exception e) {}
- ๋ค์ฌ์ฐ๊ธฐ (Indentation)์ 4์นธ์ ๊ณต๋ฐฑ์ด์ด์ผ ํ๋ค. (4 spaces key)
- โifโ, โwhileโ, โreturnโ, โcatchโ, โswitchโ, โforโ ๋ฑ์ ํค์๋์ ์ด์ด์ง๋ ๊ดํธ์๋ ๊ณต๋ฐฑ์ด ์์ด์ผ ํ๋ค.
- ์ธ๋ฏธ์ฝ๋ก , ์ผํ ๋ค์๋ ๊ณต๋ฐฑ์ด ์์ด์ผ ํ๋ค.
- ์ฝ๋์ ์ฃผ์ ๋ถ๋ถ์ ์๋ณํ๊ธฐ ์ํด ๋น ์ค์ ๋ฃ์ด์ค๋ค. ์๋ฅผ ๋ค์ด, ๋ณ์ ์ ์ธ ๋ถ๋ถ๊ณผ ๋ณ์๋ฅผ ์ด์ฉํ ๋ก์ง์ด ๋ค์ด๊ฐ๋ค๋ฉด ์ด ๋ถ๋ถ์ ๊ตฌ๋ถ์ ํด์ฃผ๊ธฐ ์ํด ๋น ์ค์ ์ฝ์ ํด์ฃผ๋๊ฒ์ด ์ข๋ค.
- from: ํ๋์ ๋งค๊ฐ๋ณ์๋ฅผ ๋ฐ์์ ํด๋น ํ์ ์ ์ธ์คํด์ค๋ฅผ ์์ฑ (ํ๋ณํ)
- valueOf : ๋งค๊ฐ๋ณ์์ ๋์ผํ ๊ฐ์ ๊ฐ๋ ์ธ์คํด์ค๋ฅผ ๋ฐํ
- of: ์ฌ๋ฌ๊ฐ์ ๋งค๊ฐ๋ณ์๋ฅผ ๋ฐ์์ ์ธ์คํด์ค๋ฅผ ์์ฑ (aggregate)
- instance or getInstance: (๋งค๊ฐ๋ณ์๋ฅผ ๋ฐ๋๋ค๋ฉด ๋งค๊ฐ๋ณ์๋ก ๋ช
์ํ ) ์ธ์คํด์ค๋ฅผ ๋ฐํํ์ง๋ง ๋์ผํ ์ธ์คํด์ค์์ ๋ณด์ฅํ์ง ์๋๋ค.
- ๋ณดํต singleton์ ๊ตฌํํ ๋ ๋ง์ด ์ฌ์ฉํ๋ ๋ค์ด๋ฐ์ด์ง๋ง ์ฑ๊ธํค์ ๊ฒฝ์ฐ getInstance๋ ๋งค๊ฐ๋ณ์๋ฅผ ์ฌ์ฉํ์ง ์๊ณ ์ ์ผํ ์ธ์คํด์ค๋ฅผ ๋ฐํํ๋ค.
- create or newInstance: getInstance ์ ์ ์ฌํ์ง๋ง ๋งค๋ฒ ์๋ก์ด ์ธ์คํด์ค๋ฅผ ๋ฐํํ๋ค.
- getType: getInstance์ ๊ฐ์ผ๋ ํธ์ถํ๋ ํด๋์ค๊ฐ ์๋ ๋ค๋ฅธ ํ์ ์ ์ธ์คํด์ค๋ฅผ ๋ฐํํ ๋ ์ฌ์ฉ
- newType โ newInstance์ ๋น์ทํ์ง๋ง ํธ์ถํ๋ ํด๋์ค๊ฐ ์๋ ๋ค๋ฅธ ํ์ ์ ์ธ์คํด์ฌ ๋ฐํํ ๋ ์ฌ์ฉ. Type์ ํฉํ ๋ฆฌ ๋ฉ์๋์์ ๋ฐํ๋ ๊ฐ์ฒด์ ์ ํ์ ๋ํ๋ธ๋ค.
5. List ํ์ ์์ ๋น ์ปฌ๋ ์ ์ ๋ฐํํ ๋๋ null์ด๋ new ์์ฑ ๋ฆฌ์คํธ ํ์ ๋ณด๋ค๋ Empty Collection์ ์ฌ์ฉํ๋๊ฒ์ด GC์ ์ ๋ฆฌํ๋ค.
List<Integer> list = new ArrayList();
Set<Integer> set = new HashSet();
Map<Integer,Integer> map = new HashMap();
List<Integer> list = Collections.emptyList();
Set<Integer> set = Collections.emptySet();
Map<Integer,Integer> map = Collections.emptyMap()
- Iterator ๋ฅผ ํตํด ์์๋ฅผ ์กฐ์ํ๋๋ก ํ๋ค.
List<String> a = new ArrayList<String>();
a.add("1");
a.add("2");
for (String temp : a) {
if ("2".equals(temp)){
a.remove(temp);
}
} // bad
Iterator<String> it = a.iterator();
while (it.hasNext()) {
String temp = it.next();
if (delete condition) {
it.remove();
}
} // good
Collection | Key | Value | Super | Note |
---|---|---|---|---|
Hashtable | Null is not allowed | Null is not allowed | Dictionary | Thread-safe |
ConcurrentHashMap | Null is not allowed | Null is not allowed | AbstractMap | Segment lock |
TreeMap | Null is not allowed | Null is allowed | AbstractMap | Thread-unsafe |
HashMap | Null is allowed | Null is allowed | AbstractMap | Thread-unsafe |
์๋ฏธ ์๋ ์ค๋ ๋ ์ด๋ฆ์ ์ค๋ฅ ์ ๋ณด๋ฅผ ์ถ์ ํ๋ ๋ฐ ๋์์ด ๋๋ฏ๋ก ์ค๋ ๋ ๋๋ ์ค๋ ๋ ํ์ ์์ฑํ ๋ ์ด๋ฆ์ ์ง์ ํ๊ธฐ๋ฅผ ์ถ์ฒํ๋ค.
public class TimerTaskThread extends Thread {
public TimerTaskThread() {
super.setName("TimerTaskThread");
...
}
}
์ค๋ ๋๋ ์ค๋ ๋ ํ์์ ์ ๊ณต๋์ด์ผ ํ๋ค. ๋ช ์์ ์ผ๋ก ์ค๋ ๋๋ฅผ ์์ฑํ๋๊ฒ์ ํ์ฉ๋์ง ์๋๋ค.
- ์ค๋ ๋ ํ์ ์ฌ์ฉํ๋ฉด ์ค๋ ๋ ์์ฑ ๋ฐ ์๋ฉธ ์๊ฐ์ ์ค์ด๊ณ ์์คํ ๋ฆฌ์์ค๋ฅผ ์ ์ฝํ ์ ์๋ค.
- ์ค๋ ๋ ํ์ ์ฌ์ฉํ์ง ์์ผ๋ฉด ์ ์ฌํ ์ค๋ ๋๊ฐ ๋ง์ด ์์ฑ๋์ด "๋ฉ๋ชจ๋ฆฌ ๋ถ์กฑ" ๋๋ ์ค๋ฒ ์ค์์นญ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค.
String value = โTrueโ; // bad
boolean value = true; // good
String value = โโ; // bad
String value = StringUtils.EMPTY; // good
public class BadExample {
public void foo() {
boolean flag = false;
if (flag == true) { // bad
// some code
} else {
// some code
}
}
}
public class GoodExample {
public void foo() {
boolean flag = false;
if (flag) { // good
// some code
} else {
// some code
}
}
}
Exception ํน์ null์ ๋ฐํํ๋ method์๋ Optional์ ์ฌ์ฉํ๋ค.
- Optional ๊ฐ์ postfix๋ก Optional์ ์ฌ์ฉํ๋ค.
- Exception์ ์ ๋ง Exception์ด ํ์ํ ๋ถ๋ถ์์ ์ฌ์ฉํ๋ค. ์ ์์ ์ธ ํ๋ฆ์ ํต์ ํ๊ธฐ ์ํด์๋ Exception ๋ณด๋ค๋ Optional์ ์ฌ์ฉํ๋ค.
- Optional์ null์ ๋ฐํ๋ ๊ฐ๋ฅ์ฑ์ด ์๋ค๋ ๊ฒ์ ๋ช ์์ ์ผ๋ก ์ ์ํ๋ค.
- Collection์ ๋ฐํํ๋ ๊ฒฝ์ฐ์๋ Optional์ ์ฌ์ฉํ์ง ์๋๋ค.
public ExportService getExportRequest() {
...
ExportService exportInfo = getExportRequest();
if(exportInfo == null){ // bad
...
}
}
public Optional<ExportService> getExportRequest(){
...
Optional<ExportService> exportInfoOptional = getExportRequest();
if(exportInfoOptional.isEmpty()){
// handling the null
}
ExportService exportInfo = exportInfoOptional.get();
}
Predicate ์ Validator ์ ์ฉ๋๊ฐ ๋ค๋ฅด๋ค.
Predicate
- https://docs.oracle.com/javase/8/docs/api/java/util/function/Predicate.html
- Represents a predicate (boolean-valued function) of one argument.
Validator
- https://en.wikiversity.org/wiki/Object-Oriented_Programming/Validation
- Validate data Generate and handle exceptions Use assertions to validate parameter assumptions
๊ทธ๋ฌ๋ฏ๋ก ์ ๋ฆฌํ๋ฉด
Predicate๋ 1๊ฐ์ arg ์ ๋ฐ์์ true/false ๋ฆฌํดํ๋ ์ฉ๋์ผ๋ก ์ฌ์ฉํ๋ค.
Validator๋ ๋ฐ์ดํ ์ ํจ์ฑ์ ํ๋จํ๋ ์ฉ๋์ผ๋ก ๊ฒฝ์ฐ์ ๋ฐ๋ผ์ exception ๋ฐ์์ํจ๋ค.
public class BadExample { //bad
private int x;
private int y;
private int z;
private int w;
public void foo() {
// some code
}
}
public class GoodExample { // good
private Point position;
public void move(int x, int y) {
// some code
}
}
public class BadExample {
public void foo() {
try {
// some code
} catch (Exception e) {
// some code
}
}
}
public class GoodExample {
public void foo() throws SpecificException {
// some code
}
}
๊ฐ๋
์ฑ๋ฉด์์ try catch ๋ธ๋ก์ ์งง์์ผ ํ๋ฉฐ, ๊ฐ๊ธ์ exception
๋ธ๋ญ๋ง ์ฒ๋ฆฌํ์ฌ ๊ฐ๋
์ฑ์ ํ๋ณดํด์ผ ํ๋ค.
void tryCatchBlock() { // bad
try {
statement1;
statement2;
statement3; // can throw
statement4;
} catch (...) {
โฆ.
}
}
void tryCatchBlock() { // good
statement1;
statement2;
boolean success = false;
try {
statement3; // can throw
success = true;
} catch (...) {
โฆ
}
if (success) {
statement4;
}
}
์ด์ ๋ํ ์ค๋ช ์ผ๋ก๋ ์๋ ๋งํฌ๋ฅผ ์ฐธ๊ณ ํ๋ค.
tight try catch block (https://stackoverflow.com/questions/2633834/should-java-try-blocks-be-scoped-as-tightly-as-possible)
the size of a try block makes no difference in performance. Performance is affected by actually raising exceptions at runtime, and that's independent of the size of the try block.
However, keeping try blocks small can lead to better programs.
You might catch exceptions to recover and proceed, or you might catch them simply to report them to the caller (or to a human, via some UI).
In the first case, failures from which you can recover are often very specific, and this leads to smaller try blocks.
In the second case, where an exception is caught so that it can be wrapped by another exception and re-thrown, or displayed to the user, small try blocks mean that you know more precisely which operation failed, and the higher-level context in which that call was made. This allows you to create more specific error reports.
- ์ด๋ SpringFramework ๋ด๋ถ์์ ์ฐ๊ณ ์ ๋ง๋ ๊ฒ์ผ๋ก, ๋ง์ฝ Utils์ฑ๊ฒฉ์ ํด๋์ค๋ฅผ ์ฐ๊ณ ์ถ๋ค๋ฉด Apache Commons StringUtils๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค.
- @Component๋ ์ผ๋ฐ์ ์ธ ์คํ๋ง ๋น์ผ๋ก ๋ฑ๋ก๋(๊ด๋ฆฌํ๋) ์ปดํฌ๋ํธ๋ฅผ ์๋ฏธํ๋ค.
- @Service @Controller, @Repository ๋ฑ์ Component ์๊ณผ ๋์์ ์ธ๋ถ์ ์ธ ๊ธฐ๋ฅ์ ๊ตฌ๋ถํ๋ ๋ช ์๋ฅผ ์ํ ์ด๋ ธํ ์ด์ ์ด๋ค.
- @Controller๋ Presentation Layer๋ฅผ ๋ด๋นํ๊ณ , @Service๋ ์๋น์ค์ ๋ก์ง, @Repository ์ด๋ ธํ ์ด์ ์ DB์์ ๋์ํ๋ Persistence ๋ ์ด์ด๋ฅผ ํ์ํ๋ค.
๊ธฐ๋ฅ ์ญํ ์ ๋ช ํํ๊ฒ ํ๋ ๋ ์ด์ด๋ผ๋ฉด Component ๋ณด๋ค๋ ๊ตฌ์ฒด์ ์ธ ์คํ ๋ ์ค ํ์ ์ธ ํ์ ์ด๋ ธํ ์ด์ ์ ๊ถ์ฅํ๋ค.
์ค์ ์ดํ๋ฆฌ์ผ์ด์ ์์๋ Entity ๋ก ์กฐํํ DB์ ๊ฐ์ฒด๋ฅผ ๋น์ฆ๋์ค ๋ ผ๋ฆฌ ๊ณ์ธต์์ ์ฌ์ฉํ๊ธฐ ์ํด DTO์ ๋งคํ๋์ด์ผ ํ๋๋ฐ ์ด๋ฅผ ์นํํ๊ธฐ ์ํด ์๋ง์ DTO์ Setter, Getter ์ค๋ณต์ด ๋ฐ์ํ๊ฒ ๋๋ค. ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ํ ์ด๋ธ์ธ Entity๋ค์ ๋ฐ๋ก API์ ๊ฒฐ๊ณผ๋ก ๋ ธ์ถํ๋ฉด ์๋๋ค.
์ด๋ฅผ ํด๊ฒฐ ํ๊ธฐ ์ํด DTO ๋ ์ด์ด๋ฅผ ๋์ด์ผ ํ๋ฉฐ, ์ด DTO๋ค์ Converter ๋ฅผ ์์ฑํ์ฌ ๋ฆฌํดํด์ฃผ๋ ํจํด์ผ๋ก ๊ฐ๋ฐ์ ํด์ผ ํ๋ค.
์๋ฐฉํฅ์ด ์๋ ๋จ๋ฐฉํฅ ๋ณํ์ ๊ฒฝ์ฐ์๋ Mapper (ํ ๊ฐ์ฒด์ ์์ฑ์ ๋ค๋ฅธ ๊ฐ์ฒด๋ก ๋ณต์ฌํ ๊ฒฝ์ฐ) ๋ผ๊ณ ๋ช ๋ช ํ์ฌ ์ฌ์ฉํ๋ค.
Converter ํจํด์ ๋ชฉ์ ์ ํด๋น ํ์ ๊ฐ์ ์ผ๋ฐ์ ์ธ ์๋ฐฉํฅ ๋ณํ ๋ฐฉ๋ฒ์ ์ ๊ณตํ์ฌ ๊น๋ํ ๊ตฌํ์ ํ์ฉํ๋ ๊ฒ์ด๋ค. Converter ํจํด์ ํตํด ์๋ฐฉํฅ ๋งคํ์ ๋ณํํจ์ผ๋ก์จ ๊ธฐ์กด ์ฝ๋๋ค์ ์ธ๋ฐ์๋ Setter/Getter ๋ฐ๋ณต ์์ ์ ํ์ ์ ์ผ๋ก ์ค์ผ ์ ์๋ค.
์๋์ ์์ ๋ฅผ ์ฐธ๊ณ ํ์.
public class Converter<T, U> {
private final Function<T, U> fromDto;
private final Function<U, T> fromEntity;
public Converter(final Function<T, U> fromDto, final Function<U, T> fromEntity) {
this.fromDto = fromDto;
this.fromEntity = fromEntity;
}
public final U convertFromDto(final T dto) {
return fromDto.apply(dto);
}
public final T convertFromEntity(final U entity) {
return fromEntity.apply(entity);
}
public final List<U> createFromDtos(final Collection<T> dtos) {
return dtos.stream().map(this::convertFromDto).collect(Collectors.toList());
}
public final List<T> createFromEntities(final Collection<U> entities) {
return entities.stream().map(this::convertFromEntity).collect(Collectors.toList());
}
}
๋ณํ ํ๊ณ ์ ํ๋ ๋์ ๊ฐ์ฒด๋ ์์ ๊ธฐ๋ณธ Converter ์ ๋ค๋ฆญ์ ์์ํ์ฌ ๊ตฌํํ๋ค.
public class UserConverter extends Converter<UserDto, User> {
public UserConverter() {
super(UserConverter::convertToEntity, UserConverter::convertToDto);
}
private static UserDto convertToDto(User user) {
return new UserDto(user.getFirstName(), user.getLastName(), user.isActive(), user.getUserId());
}
private static User convertToEntity(UserDto dto) {
return new User(dto.getFirstName(), dto.getLastName(), dto.isActive(), dto.getEmail());
}
}
UserDto ์ User Entity ์ ๋ณํ์ ์๋์ ๊ฐ์ด ๊ฐ๋จํ๊ฒ ์ฒ๋ฆฌ ํ ์ ์๋ค.
var userConverter = new UserConverter();
var dtoUser = new UserDto("John", "Doe", true, "whatever[at]wherever.com");
var user = userConverter.convertFromDto(dtoUser);
๊ทธ๋ฆผ 2
UserConvert ๋ฅผ ํตํด dto์ entity ์ฌ์ด์ ์๋ฐฉํฅ ๋ณํ์ ์ข ๋ ๋ช ํํ๊ณ ์ฌํํ๊ฒ ์ฒ๋ฆฌํ ์ ์๋ค.
์ ๋๋ฆญ ์ฝ๋์ ํ์ฅ์ฑ์ ๋ํด์ ๊ต์ฅํ ์ ์ฐํ ์ฝ๋๋ค์ ์์ฑํ ์ ์๊ฒ ๋๋ฏ๋ก์จ Converter ์ฝ๋๋ค๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฑ์ ํตํด ์ฝ๋๋ฅผ ์๋์ผ๋ก ๋ณํํ๊ฒ ๋ง๋ค์๋ ์๋ค.
๋ํ์ ์ผ๋ก ModelMapper (์ฐธ๊ณ ๋งํฌ : https://modelmapper.org/getting-started/)๊ฐ ์ ์ฉํ๋ฐ, ์๋์ ๊ฐ์ด ์ ๋๋ฆญ์ผ๋ก ํ์ฅํด์ ํด๋์ค๊ฐ์ ๋ณํ์ ๊ฐํธํ๊ฒ ์ ์ฉํ ์ ์๋ค.
/**
* @author CodeVillains
*/
@Component
public class EntityConverter {
private final ModelMapper modelMapper;
public EntityConverter(ModelMapper modelMapper) {
this.modelMapper = modelMapper;
}
public <S, D> D convertToDto(S entity, Class<D> dto) {
return modelMapper.map(entity, dto);
}
public <S, D> D convertToEntity(S dto, Class<D> entity) {
return modelMapper.map(dto, entity);
}
}
์ด ์ฝ๋๋ฅผ ํธ์ถํ๋ ๋ถ๋ถ์์ ์๋์ ๊ฐ์ด ์ฌ์ฉํ ์ ์๋ค.
private final GoalService goalService;
private final EntityConverter entityConverter;
...
public GoalResponse findById(Long id) {
Goal goal = goalService.findById(id).orElseThrow(() -> new GoalNotFoundException(id));
GoalResponse response = entityConverter.convertToDto(goal, GoalResponse.class); // entity to dto
return response;
}
Converter ํด๋์ค๋ณด๋ค ModelMapper ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ด์ฉํ EntityConverter ๊ฐ ์ฝ๋์์ผ๋ก ํจ์ฌ ์ดํดํ๊ธฐ ํธํ ๊ฒ์ด๋ค.
๋ง์ฝ List ํ์ ์ ์ค๋ธ์ ํธ ๋งคํ์ ํ๊ณ ์ ํ๋ค๋ฉด ์๋์ ๊ฐ์ด ๋ณํ ์ฝ๋๋ฅผ ๋ง๋ค์ด์ ์ฌ์ฉํ ์ ์๋ค.
public <S, D> List<D> convertListToDto(List<S> sourceList, Class<D> destinationType) {
return sourceList.stream()
.map(source -> modelMapper.map(source, destinationType))
.collect(Collectors.toList());
}
public <S, D> List<D> convertListToEntity(List<S> sourceList, Class<D> destinationType) {
return sourceList.stream()
.map(source -> modelMapper.map(source, destinationType))
.collect(Collectors.toList());
}
์ค์ ํธ์ถํ๋ ๋ถ๋ถ์ ์ฝ๋๋ ์๋์ ๊ฐ๋ค.
List<DtoClass> dtoList = converter.convertListToDto(entityList, DtoClass.class);
for (DtoClass dto : dtoList) {
System.out.println(dto.getId());
System.out.println(dto.getName());
}
List<EntityClass> convertedEntityList = converter.convertListToEntity(dtoList, EntityClass.class);
for (EntityClass entity : convertedEntityList) {
System.out.println(entity.getId());
System.out.println(entity.getName());
}