Skip to content

Latest commit

ย 

History

History
720 lines (611 loc) ยท 31.5 KB

ch3_method.md

File metadata and controls

720 lines (611 loc) ยท 31.5 KB

3์žฅ. ํ•จ์ˆ˜

์ดํ•ดํ•˜๊ธฐ ์–ด๋ ค์šด ํ•จ์ˆ˜

HtmlUtil.java
HtmlUtil.java
public static String testableHtml (
    PageData pageData,
    boolean includeSuiteSetup
) throws Exception {
    WikiPage wikiPage = pageData.getWikiPage();
    StringBuffer buffer = new StringBuffer();
    if (pageData.hasAttribute("Test")) {
        if (includeSuiteSetup) {
            WikiPage suiteSetup =
                PageCrawlerImpl.getInheritedPage(
                        SuiteResponder.SUITE_SETUP_NAME, wikiPage
                );
            if (suiteSetup != null) {
                WikiPagePath pagePath  = suiteSetup.getPageCrawler().getFullPath(suiteSetup);
                String pagePathName = PathParser.render(pagePath);
                buffer.append("!include -setup .")
                    .append(pagePathName)
                    .append("\n");
            }
        }
        WikiPage setup = PageCrawlerImpl.getInheritedPage("SetUp", wikiPage);
        if (setup != null) {
            WikiPagePath setupPath = wikiPage.getPageCrawler().getFullPath(setup);
            String setupPathName = PathParser.render(setupPath);
            buffer.append("!include -setup .")
                .append(setupPathName)
                .append("\n");
        }
    }
    buffer.append(pageData.getContent());
    if (pageData.hasAttribute("Test")) {
        WikiPage teardown = PageCrawlerImpl.getInheritedPage("TearDown", wikiPage);
        if (teardown != null) {
            WikiPagePath tearDownPath = wikiPage.getPageCrawler().getFullPath(teardown);
            String tearDownPathName = PathParser.render(tearDownPath);
            buffer.append("\n")
                .append("!include -teardown .")
                .append(tearDownPathName)
                .append("\n");
        }
        if (includeSuiteSetup) {
            WikiPage suiteTeardown = PageCrawlerImpl.getInheritedPage(SuiteResponder.SUITE_TEARDOWN_NAME, wikiPage);
            if (suiteTeardown != null) {
                WikiPagePath pagePath = suiteTeardown.getPageCrawler().getFullPath (suiteTeardown);
                String pagePathName = PathParser.render(pagePath);
                buffer.append("!include -teardown .")
                    .append(pagePathName)
                    .append("\n");
            }
        }
    }
    pageData.setContent(buffer.toString());
    return pageData.getHtml();
}

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

๋„ˆ๋ฌด ์ดํ•ดํ•˜๊ธฐ ์–ด๋ ต๋‹ค!!

์œ„ ์ฝ”๋“œ๋ฅผ ๋ฆฌํŒฉํ„ฐ๋งํ•ด๋ณด์ž.

HtmlUtil.java (๋ฆฌํŒฉํ„ฐ๋งํ•œ ๋ฒ„์ „)
public static String renderPageWithSetupsAndTeardowns (
    PageData pageData, boolean isSuite
) throws Exception {
    boolean isTestPage = pageData.hasAttribute("Test");
    if (isTestPage) {
        WikiPage testPage = pageData.getWikiPage();
        StringBuffer newPageContent = new StringBuffer();
        includeSetupPages(testPage, newPageContent, isSuite);
        pageData.setContent(newPageContent.toString());
    }
    return pageData.getHtml();
}

์œ„ ์ฝ”๋“œ๋ฅผ ๋ณด๊ณ  100% ์ดํ•ดํ•˜์ง€๋Š” ๋ชปํ•ด๋„, ํ•จ์ˆ˜๊ฐ€ ์„ค์ •(setup) ํŽ˜์ด์ง€์™€ ํ•ด์ œ(teardown) ํŽ˜์ด์ง€๋ฅผ ํ…Œ์ŠคํŠธ ํŽ˜์ด์ง€์— ๋„ฃ์€ ํ›„ ํ•ด๋‹น ํ…Œ์ŠคํŠธ ํŽ˜์ด์ง€๋ฅผ HTML๋กœ ๋ Œ๋”๋งํ•œ๋‹ค๋Š” ์‚ฌ์‹ค์€ ์ง์ž‘ํ•  ์ˆ˜ ์žˆ๋‹ค.

์œ„ ํ•จ์ˆ˜๊ฐ€ ์ดํ•ดํ•˜๊ธฐ ์‰ฌ์šด ์ด์œ ๊ฐ€ ๋ฌด์—‡์ผ๊นŒ? ์˜๋„๋ฅผ ๋ถ„๋ช…ํžˆ ํ‘œํ˜„ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์„๊นŒ? ํ•จ์ˆ˜์— ์–ด๋–ค ์†์„ฑ์„ ๋ถ€์—ฌํ•ด์•ผ ์ฒ˜์Œ ์ฝ๋Š” ์‚ฌ๋žŒ์ด ํ”„๋กœ๊ทธ๋žจ ๋‚ด๋ถ€๋ฅผ ์ง๊ด€์ ์œผ๋กœ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ์„๊นŒ?

ํ•จ์ˆ˜๋ฅผ ์ฝ๊ธฐ ์‰ฝ๊ณ  ์ดํ•ดํ•˜๊ธฐ ์‰ฝ๊ฒŒ ์งœ๋Š” ๋ฐฉ๋ฒ•

1. ์ž‘๊ฒŒ ๋งŒ๋“ค์–ด๋ผ

  • ํ•จ์ˆ˜๋Š” ์งง๊ณ  ๊ฐ„๊ฒฐํ• ์ˆ˜๋ก ์ข‹๋‹ค.
HtmlUtil.java re-refactoring
public static String renderPageWithSetupsAndTeardowns(
    PageData pageData, boolean isSuite) throws Exception {
        if (isTestPage(pageData))
            includeSetupAndTeardownPages(pageData, isSuite);
        return pageData.getHtml();
    }
)

HtmlUtils๋ฅผ ๋ฆฌํŒฉํ† ๋งํ•œ ์ฝ”๋“œ๋„ ์œ„์™€ ๊ฐ™์ด ๋” ๊ฐ„๊ฒฐํ•˜๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค!

1.1 ํ•จ์ˆ˜์˜ ๊ธธ์ด์™€ ๊ฐ€๋กœ ๊ธ€์ž ์ˆ˜

  • ์š”์ฆ˜์€ ํ•œ ํ™”๋ฉด์— ๊ธ€๊ผด์„ ์กฐ์ ˆํ•˜๋ฉด ๊ฐ€๋กœ 150์ž ์„ธ๋กœ 100์ค„๋„ ๋“ค์–ด๊ฐ„๋‹ค. ๋”ฐ๋ผ์„œ ๊ฐ€๋กœ 150์ž๋ฅผ ๋„˜์–ด์„œ๋Š” ์•ˆ๋œ๋‹ค. (๋ฐ˜๋“œ์‹œ ํ•œ ํ™”๋ฉด์— ๋“ค์–ด๊ฐ€์•ผ ํ•œ๋‹ค.)
  • ํ•จ์ˆ˜๋Š” 100์ค„์„ ๋„˜์–ด์„œ๋Š” ์•ˆ๋œ๋‹ค. ์•„๋‹ˆ, 20์ค„๋„ ๊ธธ๋‹ค!

1.2 ๋ธ”๋ก๊ณผ ๋“ค์—ฌ์“ฐ๊ธฐ

  • If ๋ฌธ / else ๋ฌธ / while๋ฌธ์— ๋“ค์–ด๊ฐ€๋Š” ๋ธ”๋ก์€ ํ•œ ์ค„์ด์–ด์•ผ ํ•œ๋‹ค.
  • ๋ธ”๋ก ์•ˆ์—์„œ ํ˜ธ์ถœํ•˜๋Š” ํ•จ์ˆ˜ ์ด๋ฆ„์„ ์ ์ ˆํžˆ ์ง“๋Š”๋‹ค๋ฉด, ์ฝ”๋“œ๋ฅผ ์ดํ•ดํ•˜๊ธฐ๋„ ์‰ฌ์›Œ์ง„๋‹ค.
  • ์ค‘์ฒฉ ๊ตฌ์กฐ๊ฐ€ ์ƒ๊ธธ๋งŒํผ ํ•จ์ˆ˜๊ฐ€ ์ปค์ ธ์„œ๋Š” ์•ˆ๋œ๋‹ค. ๊ทธ๋Ÿฌ๋ฏ€๋กœ ํ•จ์ˆ˜์—์„œ ๋“ค์—ฌ์“ฐ๊ธฐ ์ˆ˜์ค€์€ 1๋‹จ์ด๋‚˜ 2๋‹จ์„ ๋„˜์–ด์„œ๋ฉด ์•ˆ๋œ๋‹ค. ๊ทธ๋ž˜์•ผ ํ•จ์ˆ˜๋Š” ์ฝ๊ณ  ์ดํ•ดํ•˜๊ธฐ ์‰ฌ์›Œ์ง„๋‹ค.

2. ํ•œ ๊ฐ€์ง€๋งŒ ํ•ด๋ผ

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

3. ํ•จ์ˆ˜ ๋‹น ์ถ”์ƒํ™” ์ˆ˜์ค€์€ ํ•˜๋‚˜๋กœ

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

์œ„์—์„œ ์•„๋ž˜๋กœ ์ฝ”๋“œ ์ฝ๊ธฐ: ๋‚ด๋ ค๊ฐ€๊ธฐ ๊ทœ์น™

  • ์ฝ”๋“œ๋Š” ์œ„์—์„œ ์•„๋ž˜๋กœ ์ด์•ผ๊ธฐ์ฒ˜๋Ÿผ ์ฝํ˜€์•ผ ์ข‹๋‹ค. ํ•œ ํ•จ์ˆ˜ ๋‹ค์Œ์—๋Š” ์ถ”์ƒํ™” ์ˆ˜์ค€์ด ํ•œ ๋‹จ๊ณ„ ๋‚ฎ์€ ํ•จ์ˆ˜๊ฐ€ ์˜จ๋‹ค. ์ฆ‰, ์œ„์—์„œ ์•„๋ž˜๋กœ ํ”„๋กœ๊ทธ๋žจ์„ ์ฝ์œผ๋ฉด ํ•จ์ˆ˜ ์ถ”์ƒํ™” ์ˆ˜์ค€์ด ํ•œ ๋ฒˆ์— ํ•œ ๋‹จ๊ณ„์”ฉ ๋‚ฎ์•„์ง„๋‹ค.

4. Switch ๋ฌธ

  • Switch ๋ฌธ์€ ์ž‘๊ฒŒ ๋งŒ๋“ค๊ธฐ ์–ด๋ ต๋‹ค.
    • case ๋ถ„๊ธฐ๊ฐ€ ๋‹จ ๋‘ ๊ฐœ์ธ switch ๋ฌธ๋„ ๊ธธ๊ณ , ๋‹จ์ผ ๋ธ”๋ก์ด๋‚˜ ํ•จ์ˆ˜๊ฐ€ ์ข‹๋‹ค.(์ž‘๊ฐ€์˜ ์ทจํ–ฅ)

    • ๋˜ํ•œ 'ํ•œ ๊ฐ€์ง€' ์ž‘์—…๋งŒ ํ•˜๋Š” switch ๋ฌธ๋„ ๋งŒ๋“ค๊ธฐ ์–ด๋ ต๋‹ค. ๋ณธ์งˆ์ ์œผ๋กœ switch ๋ฌธ์€ N๊ฐ€์ง€๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

    • ๊ฐ switch ๋ฌธ์„ ์ˆจ๊ธฐ๊ณ  ์ ˆ๋Œ€๋กœ ๋ฐ˜๋ณตํ•˜์ง€ ์•Š๋Š” ๋ฐฉ๋ฒ•์€ ์žˆ๋‹ค. ๋ฌผ๋ก  ๋‹คํ˜•์„ฑ์„ ์ด์šฉํ•œ๋‹ค.

      ์ง์› ์œ ํ˜•์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ๊ฐ’์„ ๊ณ„์‚ฐํ•ด ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜
          public Money calculatePay(Employee e)
          throws InvalidEmployeeType {
              switch (e.type) {
                  case COMMISSIONED:
                      return calculateCommissionedPay(e);
                  case HOURLY:
                      return calculateHourlyPay(e);
                  case SALARIED:
                      return calculateSalariedPay(e);
                  default:
                      throw new InvalidEmployeeType(e.type);
              }
          }

      ์œ„ ํ•จ์ˆ˜๋Š” ํ•จ์ˆ˜๊ฐ€ ๊ธธ๊ณ , ํ•œ ๊ฐ€์ง€ ์ž‘์—…๋งŒ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š๊ณ , SRP(Single Responsibility Principle)๋ฅผ ์œ„๋ฐ˜ํ•˜๊ณ , OCP(Open Closeed Principle)๋ฅผ ์œ„๋ฐ˜ํ•œ๋‹ค๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค. ์ฆ‰, ์ƒˆ๋กœ์šด ์ง์› ์œ ํ˜•์„ ์ถ”๊ฐ€ํ•  ๋•Œ๋งˆ๋‹ค ์ฝ”๋“œ๋ฅผ ๋ณ€๊ฒฝํ•ด์•ผ ํ•˜๊ณ  ๊ตฌ์กฐ๊ฐ€ ๋™์ผํ•œ ํ•จ์ˆ˜๊ฐ€ ๋ฌดํ•œ์ • ์กด์žฌํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค. (ex. isPayday(Employee e, Date date), deliverPay(Employee e, Mone pay) ๋“ฑ) ์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” switch ๋ฌธ์„ ์ถ”์ƒ ํŒฉํ† ๋ฆฌ์— ๊ฝ๊ฝ ์ˆจ๊ธด๋‹ค. ํŒฉํ† ๋ฆฌ๋Š” switch ๋ฌธ์„ ์‚ฌ์šฉํ•ด ์ ์ ˆํ•œ Employee ํŒŒ์ƒ ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•œ๋‹ค. calculatePay, isPayday, deliverPay ๋“ฑ๊ณผ ๊ฐ™์€ ํ•จ์ˆ˜๋Š” Employee ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ฑฐ์ณ ํ˜ธ์ถœ๋œ๋‹ค.

      Employee and Factory with switch ๋ฌธ
      public abstract class Employee {
          public abstract boolean isPayday();
          public abstract Money calculatePay();
          public abstract void deliverPay(Money pay);
      }
      public interface EmployeeFactory {
          public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType;
      }
      public class EmployeeFactoryImpl {
          public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType {
              switch (r.type) {
                  case COMMISSIONED:
                      return new CommissionedEmployee(r);
                  case HOURLY:
                      return new HourlyEmployee(r);
                  case SALARIED:
                      return new SalariedEmployee(r);
                  default:
                      throw new InvalidEmployeeType(r.type);
              }
          }
      }

5. ์„œ์ˆ ์ ์ธ ์ด๋ฆ„์„ ์‚ฌ์šฉํ•˜๋ผ

  • ex) testableHtml -> SetupTeardownIncluder.render
    • ํ•จ์ˆ˜๊ฐ€ ํ•˜๋Š” ์ผ์„ ๋” ์ž˜ ํ‘œํ˜„ํ•˜๋Š” ๊ฒƒ์ด ํ›จ์”ฌ ์ข‹์€ ์ด๋ฆ„์ด๋‹ค.
  • ์ฝ”๋“œ๋ฅผ ์ฝ์œผ๋ฉด์„œ ์ง์ž‘ํ–ˆ๋˜ ๊ธฐ๋Šฅ์„ ๊ฐ ๋ฃจํ‹ด์ด ๊ทธ๋Œ€๋กœ ์ˆ˜ํ–‰ํ•œ๋‹ค๋ฉด ๊นจ๋—ํ•œ ์ฝ”๋“œ๋ผ ๋ถˆ๋Ÿฌ๋„ ๋˜๊ฒ ๋‹ค.
  • ๊ธธ๊ณ  ์„œ์ˆ ์ ์ธ ์ด๋ฆ„์ด ์งง๊ณ  ์–ด๋ ค์šด ์ด๋ฆ„๋ณด๋‹ค ์ข‹๋‹ค.
  • ๊ธธ๊ณ  ์„œ์ˆ ์ ์ธ ์ด๋ฆ„์ด ๊ธธ๊ณ  ์„œ์ˆ ์ ์ธ ์ฃผ์„๋ณด๋‹ค ์ข‹๋‹ค.
  • ์ด๋ฆ„์„ ๋ถ™์ผ ๋•Œ๋Š” ์ผ๊ด€์„ฑ์ด ์žˆ์–ด์•ผ ํ•œ๋‹ค. ๋ชจ๋“ˆ ๋‚ด์—์„œ ํ•จ์ˆ˜ ์ด๋ฆ„์€ ๊ฐ™์€ ๋ฌธ๊ตฌ, ๋ช…์‚ฌ, ๋™์‚ฌ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
    • ์˜ˆ) includeSetupAndTeardownPages, includeSetupPages, includeSuiteSetupPage, includeSetupPage ๋“ฑ

6. ํ•จ์ˆ˜ ์ธ์ˆ˜

  • ํ•จ์ˆ˜์—์„œ ์ด์ƒ์ ์ธ ์ธ์ˆ˜ ๊ฐœ์ˆ˜๋Š” 0๊ฐœ (๋ฌดํ•ญ)์ด๋‹ค. ๋‹ค์Œ์€ 1๊ฐœ(๋‹จํ•ญ), ๋‹ค์Œ์€ 2๊ฐœ(์ดํ•ญ)๋‹ค. 3๊ฐœ(์‚ผํ•ญ)์€ ๊ฐ€๋Šฅํ•œ ํ”ผํ•˜๋Š” ํŽธ์ด ์ข‹๋‹ค. 4๊ฐœ ์ด์ƒ(๋‹คํ•ญ)์€ ํŠน๋ณ„ํ•œ ์ด์œ ๊ฐ€ ํ•„์š”ํ•˜๋‹ค. ํŠน๋ณ„ํ•œ ์ด์œ ๊ฐ€ ์žˆ์–ด๋„ ์‚ฌ์šฉํ•˜๋ฉด ์•ˆ๋œ๋‹ค.
  • ํ…Œ์ŠคํŠธ ๊ด€์ ์—์„œ ๋ณด๋ฉด ์ธ์ˆ˜๋Š” ๋” ์–ด๋ ต๋‹ค. ๊ฐ–๊ฐ€์ง€ ์ธ์ˆ˜ ์กฐํ•ฉ์œผ๋กœ ํ•จ์ˆ˜๋ฅผ ๊ฒ€์ฆํ•˜๋Š” ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ์ž‘์„ฑํ•œ๋‹ค๊ณ  ํ–ˆ์„ ๋•Œ, ์ธ์ˆ˜๊ฐ€ ์—†์œผ๋ฉด ๊ฐ„๋‹จํ•˜์ง€๋งŒ ์ธ์ˆ˜๊ฐ€ 2๊ฐœ, 3๊ฐœ ๋„˜์–ด๊ฐ€๋ฉด ์ธ์ˆ˜๋งˆ๋‹ค ์œ ํšจํ•œ ๊ฐ’์œผ๋กœ ๋ชจ๋“  ์กฐํ•ฉ์„ ์ž‘์„ฑํ•ด์•ผํ•˜๋ฏ€๋กœ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์ƒ๋‹นํžˆ ๋ถ€๋‹ด์Šค๋Ÿฌ์›Œ ์ง„๋‹ค.

6.1 ๋งŽ์ด ์“ฐ๋Š” ๋‹จํ•ญ ํ˜•์‹

  • ํ•จ์ˆ˜์— ์ธ์ˆ˜ 1๊ฐœ๋ฅผ ๋„˜๊ธฐ๋Š” ์ด์œ ๋กœ ๊ฐ€์žฅ ํ”ํ•œ ๊ฒฝ์šฐ๋Š” ๋‘ ๊ฐ€์ง€๋‹ค.
      1. ์ธ์ˆ˜์— ์งˆ๋ฌธ์„ ๋˜์ง€๋Š” ๊ฒฝ์šฐ
      • ์˜ˆ) boolean fileExists("MyFile")
      1. ์ธ์ˆ˜๋ฅผ ๋ญ”๊ฐ€๋กœ ๋ณ€ํ™˜ํ•ด ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒฝ์šฐ
      • ์˜ˆ) InputStream fileOpen("Myfile")
  • ํ•จ์ˆ˜์— ์ธ์ˆ˜ 1๊ฐœ๋ฅผ ๋„˜๊ธฐ์ง€๋งŒ ์ถœ๋ ฅ ์ธ์ˆ˜๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ (์ด๋ฒคํŠธ)
    • passwordAttemptFailedNtimes(int attempts)
    • ์ด๋Ÿฐ ์ด๋ฒคํŠธ ์„ฑ์ด ์•„๋‹Œ ๊ฒฝ์šฐ ๋‹จํ•ญ ํ•จ์ˆ˜์ธ๋ฐ ๋ฐ˜ํ™˜ ๊ฐ’์ด ์—†๋Š” ๊ฒฝ์šฐ๋Š” ๊ฐ€๊ธ‰์  ํ”ผํ•œ๋‹ค.

6.2 ํ”Œ๋ž˜๊ทธ ์ธ์ˆ˜

  • ํ”Œ๋ž˜๊ทธ ์ธ์ˆ˜๋ฅผ ๋„˜๊ธฐ๋Š” ๊ด€๋ก€๋Š” ํ•จ์ˆ˜๊ฐ€ ํ•œ๊บผ๋ฒˆ์— ์—ฌ๋Ÿฌ ๊ฐ€์ง€๋ฅผ ์ฒ˜๋ฆฌํ•œ๋‹ค๊ณ  ๋Œ€๋†“๊ณ  ์–˜๊ธฐํ•˜๋Š” ๊ฑฐ๋ผ ์ข‹์ง€ ์•Š๋‹ค.

6.3 ์ดํ•ญ ํ•จ์ˆ˜

  • ์ธ์ˆ˜๊ฐ€ 2๊ฐœ์ธ ํ•จ์ˆ˜๋Š” ์ธ์ˆ˜๊ฐ€ 1๊ฐœ์ธ ํ•จ์ˆ˜๋ณด๋‹ค ์ดํ•ดํ•˜๊ธฐ ์–ด๋ ต๋‹ค.
  • ๋ฌผ๋ก  ์ดํ•ญ ํ•จ์ˆ˜๊ฐ€ ์ ์ ˆํ•œ ๊ฒฝ์šฐ๋„ ์žˆ๋‹ค. ์˜ˆ) Point p = new Point(0, 0)
    • ์ฆ‰, ์ธ์ˆ˜ 2๊ฐœ๊ฐ€ ํ•œ ๊ฐ’์„ ํ‘œํ˜„ํ•˜๋Š” ๋‘ ์š”์†Œ์ผ ๊ฒฝ์šฐ ์ ํ•ฉํ•˜๋‹ค.
  • ์ดํ•ญ ํ•จ์ˆ˜๊ฐ€ ๋ฌด์กฐ๊ฑด ๋‚˜์˜๋‹ค๋Š” ์†Œ๋ฆฌ๋Š” ์•„๋‹ˆ๋‹ค. ๋ถˆ๊ฐ€ํ”ผํ•œ ๊ฒฝ์šฐ๋„ ์ƒ๊ธฐ์ง€๋งŒ ๊ทธ๋งŒํผ ์œ„ํ—˜์ด ๋”ฐ๋ฅธ๋‹ค๋Š” ์‚ฌ์‹ค์„ ์ดํ•ดํ•˜๊ณ  ๊ฐ€๋Šฅํ•˜๋ฉด ๋‹จํ•ญํ•จ์ˆ˜๋กœ ๋ฐ”๊พธ๋„๋ก ์• ์จ์•ผ ํ•œ๋‹ค.

6.4 ์‚ผํ•ญ ํ•จ์ˆ˜

  • ์ธ์ˆ˜๊ฐ€ 3๊ฐœ์ธ ํ•จ์ˆ˜๋Š” ์ธ์ˆ˜๊ฐ€ 2๊ฐœ์ธ ํ•จ์ˆ˜๋ณด๋‹ค ํ›จ์”ฌ ๋” ์ดํ•ดํ•˜๊ธฐ ์–ด๋ ต๋‹ค.
  • ์ˆœ์„œ, ์ฃผ์ถคํ•˜๋Š” ๊ฒฝ์šฐ, ๋ฌด์‹œ๋กœ ์•ผ๊ธฐ๋˜๋Š” ๋ฌธ์ œ๊ฐ€ ๋‘ ๋ฐฐ ์ด์ƒ ๋Š˜์–ด๋‚œ๋‹ค.

6.5 ์ธ์ˆ˜ ๊ฐ์ฒด

  • ์ธ์ˆ˜๊ฐ€ 2-3๊ฐœ ํ•„์š”ํ•˜๋‹ค๋ฉด ์ผ๋ถ€๋ฅผ ๋…์ž์ ์ธ ํด๋ž˜์Šค ๋ณ€์ˆ˜๋กœ ์„ ์–ธํ•  ๊ฐ€๋Šฅ์„ฑ์„ ์งš์–ด๋ณธ๋‹ค.
  • ์˜ˆ
    • Circle makeCircle(double x, double y, double radius);
    • Circle makeCircle(Point center, double radius);

6.6. ์ธ์ˆ˜ ๋ชฉ๋ก

  • ๋•Œ๋กœ๋Š” ์ธ์ˆ˜ ๊ฐœ์ˆ˜๊ฐ€ ๊ฐ€๋ณ€์ ์ธ ํ•จ์ˆ˜๋„ ํ•„์š”ํ•˜๋‹ค. String.format ๋ฉ”์„œ๋“œ๊ฐ€ ์ข‹์€ ์˜ˆ๋‹ค.
    • String.format("%s worked %.2f hours.", name, hours);

6.7 ๋™์‚ฌ์™€ ํ‚ค์›Œ๋“œ

  • ํ•จ์ˆ˜์˜ ์˜๋„๋‚˜ ์ธ์ˆ˜์˜ ์ˆœ์„œ์™€ ์˜๋„๋ฅผ ์ œ๋Œ€๋กœ ํ‘œํ˜„ํ•˜๋ ค๋ฉด ์ข‹์€ ํ•จ์ˆ˜ ์ด๋ฆ„์ด ํ•„์ˆ˜๋‹ค.
  • ๋‹จํ•ญ ํ•จ์ˆ˜๋Š” ํ•จ์ˆ˜์™€ ์ธ์ˆ˜๊ฐ€ ๋™์‚ฌ/๋ช…์‚ฌ ์Œ์„ ์ด๋ค„์•ผ ํ•œ๋‹ค.
    • ์˜ˆ) write(name), writeField(name)
  • ํ•จ์ˆ˜ ์ด๋ฆ„์— ํ‚ค์›Œ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ํ˜•์‹ (ํ•จ์ˆ˜ ์ด๋ฆ„์— ์ธ์ˆ˜ ์ด๋ฆ„์„ ๋„ฃ๋Š”๋‹ค.)
    • ์˜ˆ) assertEquals ๋ณด๋‹ค assertExpectedEqualsActual(expected, actual)์ด ๋” ์ข‹๋‹ค. (์ธ์ˆ˜ ์ˆœ์„œ๋ฅผ ๊ธฐ์–ตํ•  ํ•„์š”๊ฐ€ ์—†์–ด์ง„๋‹ค.)

7. ๋ถ€์ˆ˜ ํšจ๊ณผ๋ฅผ ์ผ์œผํ‚ค์ง€ ๋งˆ๋ผ

  • ๋ถ€์ˆ˜ ํšจ๊ณผ๋Š” ๊ฑฐ์ง“๋ง์ด๋‹ค.
  • ํ•จ์ˆ˜์—์„œ ํ•œ ๊ฐ€์ง€๋ฅผ ํ•˜๊ฒ ๋‹ค๊ณ  ์•ฝ์†ํ•˜๊ณ ์„  ๋‚จ๋ชฐ๋ž˜ ๋‹ค๋ฅธ ์ง“์„ ํ•˜๋Š” ๊ฒƒ์€ ํ•ด๋กœ์šด ๊ฑฐ์ง“๋ง์ด๋‹ค.
    • ํ•จ์ˆ˜๋กœ ๋„˜์–ด์˜จ ์ธ์ˆ˜๋‚˜ ์‹œ์Šคํ…œ ์ „์—ญ ๋ณ€์ˆ˜๋ฅผ ์ˆ˜์ •ํ•˜๋Š” ๋“ฑ
    • ๋งŽ์€ ๊ฒฝ์šฐ ์‹œ๊ฐ„์ ์ธ ๊ฒฐํ•ฉ(temporal coupling)์ด๋‚˜ ์ˆœ์„œ ์ข…์†์„ฑ(order dependency)๋ฅผ ์ดˆ๋ž˜ํ•œ๋‹ค.
UserValidator.java
public class UserValidator {
    private Cryptographer cryptographer;

    public boolean checkPassword(String userName, String password) {
        User user = UserGateway.findByName(userName);
        if (user != User.NULL) {
            String codedPhrase = user.getPhraseEncodedByPassword();
            String phrase = cryptographer.decrypt(codedPhrase, password);
            if ("Valid Password".equals(phrase)) {
                Session.initialize();
                return true;
            }
        }
        return false;
    }
}

์—ฌ๊ธฐ์„œ ํ•จ์ˆ˜๊ฐ€ ์ผ์œผํ‚ค๋Š” ๋ถ€์ˆ˜ ํšจ๊ณผ๋Š” Session.initialize() ํ˜ธ์ถœ์ด๋‹ค. ์ด ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ์‚ฌ์šฉ์ž๋Š” ์‚ฌ์šฉ์ž๋ฅผ ์ธ์ฆํ•˜๋ฉด์„œ ๊ธฐ์กด ์„ธ์…˜ ์ •๋ณด๋ฅผ ์ง€์›Œ๋ฒ„๋ฆด ์œ„ํ—˜์— ์ฒ˜ํ•œ๋‹ค. ์ด๋Ÿฐ ๋ถ€์ˆ˜ํšจ๊ณผ๊ฐ€ ์‹œ๊ฐ„์ ์ธ ๊ฒฐํ•ฉ์„ ์ดˆ๋ž˜ํ•œ๋‹ค. (์ฆ‰, ์„ธ์…˜์„ ์ดˆ๊ธฐํ™”ํ•ด๋„ ๊ดœ์ฐฎ์€ ๊ฒฝ์šฐ์—๋งŒ ํ˜ธ์ถœ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.) ๋”ฐ๋ผ์„œ checkPassword ๋ฉ”์„œ๋“œ๋Š” checkPasswordAndInitializeSession์ด๋ผ๋Š” ์ด๋ฆ„์ด ํ›จ์”ฌ ์ข‹๋‹ค.

์ถœ๋ ฅ ์ธ์ˆ˜

  • ์ธ์ˆ˜๋ฅผ ์ถœ๋ ฅ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ํ•จ์ˆ˜
    • ์˜ˆ) appendFooter(s); : ์ธ์ˆ˜ s์— ๋ฐ”๋‹ฅ๊ธ€์„ ์ฒจ๋ถ€ํ•œ๋‹ค. (return void)
  • ๊ฐ์ฒด ์ง€ํ–ฅ ์–ธ์–ด์—์„œ๋Š” ์ถœ๋ ฅ ์ธ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ํ•„์š”๊ฐ€ ๊ฑฐ์˜ ์—†๋‹ค. ์ถœ๋ ฅ ์ธ์ˆ˜๋กœ ์‚ฌ์šฉํ•˜๋ผ๊ณ  ์„ค๊ณ„๋œ ๋ณ€์ˆ˜๊ฐ€ this์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๋‹ค์‹œ ๋งํ•ด, ์œ„ ํ•จ์ˆ˜๋Š” report.appendFooter()๋กœ ํ˜ธ์ถœํ•˜๋Š” ๋ฐฉ์‹์ด ๋” ์ข‹๋‹ค.
  • ์ผ๋ฐ˜์ ์œผ๋กœ ์ถœ๋ ฅ ์ธ์ˆ˜๋ฅผ ํ”ผํ•ด์•ผ ํ•œ๋‹ค. ํ•จ์ˆ˜์—์„œ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•ด์•ผ ํ•œ๋‹ค๋ฉด ํ•จ์ˆ˜๊ฐ€ ์†ํ•œ ๊ฐ์ฒด ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๋ฐฉ์‹์„ ํƒํ•œ๋‹ค.

8. ๋ช…๋ น๊ณผ ์กฐํšŒ๋ฅผ ๋ถ„๋ฆฌํ•˜๋ผ

  • ํ•จ์ˆ˜๋Š” ๋ญ”๊ฐ€๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ฑฐ๋‚˜ ๋ญ”๊ฐ€์— ๋‹ตํ•˜๊ฑฐ๋‚˜ ๋‘˜ ์ค‘ ํ•˜๋‚˜๋งŒ ํ•ด์•ผ ํ•œ๋‹ค.
  • ๊ฐ์ฒด ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•˜๊ฑฐ๋‚˜ ์•„๋‹ˆ๋ฉด ๊ฐ์ฒด ์ •๋ณด๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ฑฐ๋‚˜ ๋‘˜ ์ค‘ ํ•˜๋‚˜๋‹ค. ๋‘˜ ๋‹ค ํ•˜๋ฉด ํ˜ผ๋ž€์„ ์ดˆ๋ž˜ํ•œ๋‹ค.
    • ์˜ˆ) public boolean set(String attribute, String value): ์ด๋ฆ„์ด attribute์ธ ์†์„ฑ์„ ์ฐพ์•„ ๊ฐ’์„ value๋กœ ์„ค์ •ํ•œ ํ›„ ์„ฑ๊ณตํ•˜๋ฉด true๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ  ์‹คํŒจํ•˜๋ฉด false๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
      • ์‹ค์ œ๋กœ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ if (set("username", "unclebob"))... ์™€ ๊ฐ™์€ ๊ดด์ƒํ•œ ํ˜•์‹์œผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค. ๋งˆ์น˜ if๋ฌธ๊ณผ ํ•จ๊ป˜ ์ฝํ˜€์„œ username ์†์„ฑ์ด unclebob์œผ๋กœ ์„ค์ •๋˜์–ด ์žˆ๋‹ค๋ฉด ์ด๋ผ๋Š” ๋‚ด์šฉ์œผ๋กœ ํ•ด์„๋œ๋‹ค.
      • ์•„์–˜ ๋ช…๋ น์™€ ์กฐํšŒ๋ฅผ ๋ถ„๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.
      if (attributeExists("username")) {
          setAttribute("username", "unclebob");
      }

9. ์˜ค๋ฅ˜ ์ฝ”๋“œ๋ณด๋‹ค ์˜ˆ์™ธ๋ฅผ ์‚ฌ์šฉํ•˜๋ผ

  • ๋ช…๋ น ํ•จ์ˆ˜์—์„œ ์˜ค๋ฅ˜ ์ฝ”๋“œ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฐฉ์‹์€ ๋ช…๋ น/์กฐํšŒ ๋ถ„๋ฆฌ ๊ทœ์น™์„ ๋ฏธ๋ฌ˜ํ•˜๊ฒŒ ์œ„๋ฐ˜ํ•œ๋‹ค. ์ž์นซํ•˜๋ฉด if๋ฌธ์—์„œ ๋ช…๋ น์„ ํ‘œํ˜„์‹์œผ๋กœ ์‚ฌ์šฉํ•˜๊ธฐ ์‰ฌ์šด ํƒ“์ด๋‹ค.
  • ์˜ˆ) if (deletePage(page) == E_OK)`
    • ์œ„ ์ฝ”๋“œ๋Š” ๋™์‚ฌ/ํ˜•์šฉ์‚ฌ ํ˜ผ๋ž€์„ ์ผ์œผํ‚ค์ง€ ์•Š๋Š” ๋Œ€์‹  ์—ฌ๋Ÿฌ ๋‹จ๊ณ„๋กœ ์ค‘์ฒฉ๋˜๋Š” ์ฝ”๋“œ๋ฅผ ์•ผ๊ธฐํ•œ๋‹ค. ์˜ค๋ฅ˜์ฝ”๋“œ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด ํ˜ธ์ถœ์ž๋Š” ์˜ค๋ฅ˜ ์ฝ”๋“œ๋ฅผ ๊ณง๋ฐ”๋กœ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•œ๋‹ค๋Š” ๋ฌธ์ œ์— ๋ถ€๋”ชํžŒ๋‹ค.
    • ๋”ฐ๋ผ์„œ ์•„๋ž˜์™€ ๊ฐ™์ด ์˜ค๋ฅ˜ ์ฝ”๋“œ ๋Œ€์‹  ์˜ˆ์™ธ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ ์ฝ”๋“œ๊ฐ€ ์›๋ž˜ ์ฝ”๋“œ์—์„œ ๋ถ„๋ฆฌ๋˜๋ฏ€๋กœ ์ฝ”๋“œ๊ฐ€ ๊น”๋”ํ•ด์ง„๋‹ค.
    try {
        deletePage(page);
        registry.deleteReference(page.name);
        configKeys.deleteKey(page.name.makeKey());
    } catch (Exception e) {
        logger.log(e.getMessage());
    }

try/catch ๋ธ”๋ก ๋ฝ‘์•„๋‚ด๊ธฐ

  • try/catch ๋ธ”๋ก์€ ์ฝ”๋“œ ๊ตฌ์กฐ์— ํ˜ผ๋ž€์„ ์ผ์œผํ‚ค๋ฉฐ, ์ •์ƒ ๋™์ž‘๊ณผ ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ ๋™์ž‘์„ ๋’ค์„ž๋Š”๋‹ค. ๊ทธ๋Ÿฌ๋ฏ€๋กœ try/catch ๋ธ”๋ก์„ ๋ณ„๋„ ํ•จ์ˆ˜๋กœ ๋ฝ‘์•„๋‚ด๋Š” ํŽธ์ด ์ข‹๋‹ค.
public void delete(Page page) {
    try {
        deletePageAndAllReferences(page);
    } catch (Exception e) {
        logError(e);
    }
}

private void deletePageAndAllReferences(Page page) throws Exception {
    deletePage(page);
    registry.deleteReference(page.name);
    configKeys.deleteKey(page.name.makeKey());
}

private void logError(Exception e) {
    logger.log(e.getMessage());
}

์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ๋„ ํ•œ ๊ฐ€์ง€ ์ž‘์—…์ด๋‹ค.

  • ํ•จ์ˆ˜๋Š” 'ํ•œ ๊ฐ€์ง€' ์ž‘์—…๋งŒ ํ•ด์•ผ ํ•œ๋‹ค. ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ๋„ 'ํ•œ ๊ฐ€์ง€' ์ž‘์—…์— ์†ํ•œ๋‹ค. ๊ทธ๋Ÿฌ๋ฏ€๋กœ ์˜ค๋ฅ˜๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ํ•จ์ˆ˜๋Š” ์˜ค๋ฅ˜๋งŒ ์ฒ˜๋ฆฌํ•ด์•ผ ๋งˆ๋•…ํ•˜๋‹ค. (์œ„ ์˜ˆ์ œ์—์„œ ๋ณด์•˜๋“ฏ์ด)
  • ํ•จ์ˆ˜์— ํ‚ค์›Œ๋“œ try๊ฐ€ ์žˆ๋‹ค๋ฉด ํ•จ์ˆ˜๋Š” try ๋ฌธ์œผ๋กœ ์‹œ์ž‘ํ•ด catch/finally ๋ฌธ์œผ๋กœ ๋๋‚˜์•ผ ํ•œ๋‹ค.

Error.java ์˜์กด์„ฑ ์ž์„

  • ์˜ค๋ฅ˜ ์ฝ”๋“œ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค๋Š” ์ด์•ผ๊ธฐ๋Š”, ํด๋ž˜์Šค๋“  ์—ด๊ฑฐํ˜• ๋ณ€์ˆ˜๋“ , ์–ด๋””์„ ๊ฐ€ ์˜ค๋ฅ˜ ์ฝ”๋“œ๋ฅผ ์ •์˜ํ•œ๋‹ค๋Š” ๋œป์ด๋‹ค.
public enum Error {
    OK,
    INVALID,
    NO_SUCH,
    LOCKED,
    OUT_OF_RESOURCES,
    WAITING_FOR_EVENT;
}

๋”ฐ๋ผ์„œ, ์˜ค๋ฅ˜ ์ฝ”๋“œ๊ฐ€ ๋ฐ”๋€Œ๋ฉด ์ฐธ์กฐํ•˜๊ณ  ์žˆ๋Š” ๋‹ค๋ฅธ ํด๋ž˜์Šค ์ „๋ถ€๋ฅผ ๋‹ค์‹œ ์ปดํŒŒ์ผํ•˜๊ณ  ๋‹ค์‹œ ๋ฐฐ์น˜ํ•ด์•ผ ํ•œ๋‹ค. ๊ทธ๋ž˜์„œ ์ƒˆ ์˜ค๋ฅ˜ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๋Œ€์‹  ๊ธฐ์กด ์˜ค๋ฅ˜ ์ฝ”๋“œ๋ฅผ ์žฌ์‚ฌ์šฉํ•œ๋‹ค.

  • ์˜ค๋ฅ˜ ์ฝ”๋“œ ๋Œ€์‹  ์˜ˆ์™ธ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ƒˆ ์˜ˆ์™ธ๋Š” Exception ํด๋ž˜์Šค์—์„œ ํŒŒ์ƒ๋œ๋‹ค. ๋”ฐ๋ผ์„œ ์žฌ์ปดํŒŒ์ผ/์žฌ๋ฐฐ์น˜ ์—†์ด๋„ ์ƒˆ ์˜ˆ์™ธ ํด๋ž˜์Šค๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค.

10. ๋ฐ˜๋ณตํ•˜์ง€ ๋งˆ๋ผ

  • ์–ด์ฉŒ๋ฉด ์ค‘๋ณต์€ ์†Œํ”„ํŠธ์›จ์–ด์—์„œ ๋ชจ๋“  ์•…์˜ ๊ทผ์›์ด๋‹ค. ๋งŽ์€ ์›์น™๊ณผ ๊ธฐ๋ฒ•์ด ์ค‘๋ณต์„ ์—†์• ๊ฑฐ๋‚˜ ์ œ์–ดํ•  ๋ชฉ์ ์œผ๋กœ ๋‚˜์™”๋‹ค.
  • ์˜ˆ๋ฅผ ๋“ค์–ด, ์ค‘๋ณต์„ ์ œ๊ฑฐํ•  ๋ชฉ์ ์œผ๋กœ ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ •๊ทœ ํ˜•์‹๋„ ์žˆ๊ณ , ๊ฐ์ฒด ์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์€ ์ฝ”๋“œ๋ฅผ ๋ถ€๋ชจ ํด๋ž˜์Šค๋กœ ๋ชฐ์•„ ์ค‘๋ณต์„ ์—†์•ค๋‹ค. ๊ตฌ์กฐ์  ํ”„๋กœ๊ทธ๋ž˜๋ฐ, AOP(Aspect Oriented Programming), COP(Component Oriented Programming) ๋ชจ๋‘ ์–ด๋–ค ๋ฉด์—์„œ ์ค‘๋ณต ์ œ๊ฑฐ ์ „๋žต์ด๋‹ค.

11. ๊ตฌ์กฐ์  ํ”„๋กœ๊ทธ๋ž˜๋ฐ

  • ๊ตฌ์กฐ์  ํ”„๋กœ๊ทธ๋ž˜๋ฐ: ๋ชจ๋“  ํ•จ์ˆ˜์™€ ํ•จ์ˆ˜ ๋‚ด ๋ชจ๋“  ๋ธ”๋ก์— ์ž…๊ตฌ์™€ ์ถœ๊ตฌ๊ฐ€ ํ•˜๋‚˜๋งŒ ์กด์žฌํ•ด์•ผ ํ•œ๋‹ค. ์ฆ‰, ํ•จ์ˆ˜์˜ return ๋ฌธ์ด ํ•˜๋‚˜์—ฌ์•ผ ํ•œ๋‹ค. ๋ฃจํ”„ ์•ˆ์—์„œ break์ด๋‚˜ continue๋ฅผ ์‚ฌ์šฉํ•ด์„  ์•ˆ๋˜๋ฉฐ goto๋Š” ์ ˆ๋Œ€๋กœ, ์ ˆ๋Œ€๋กœ ์•ˆ๋œ๋‹ค.
  • ํ•จ์ˆ˜๊ฐ€ ์ž‘์„ ๋•Œ๋Š” ๊ฐ„ํ˜น return, break, continue๋ฅผ ์—ฌ๋Ÿฌ ์ฐจ๋ก€ ์‚ฌ์šฉํ•ด๋„ ๊ดœ์ฐฎ๋‹ค. ์˜คํžˆ๋ ค ๋•Œ๋กœ๋Š” ๋‹จ์ผ ์ž…/์ถœ๊ตฌ ๊ทœ์น™๋ณด๋‹ค ์˜๋„๋ฅผ ํ‘œํ˜„ํ•˜๊ธฐ ์‰ฌ์›Œ์ง„๋‹ค.

12. ํ•จ์ˆ˜๋ฅผ ์–ด๋–ป๊ฒŒ ์งœ์ฃ ?

  • ํ•จ์ˆ˜๋Š” ์ฒ˜์Œ์—๋Š” ๊ธธ๊ณ  ๋ณต์žกํ•˜๊ณ , ๋“ค์—ฌ์“ฐ๊ธฐ ๋‹จ๊ณ„๋„ ๋งŽ๊ณ  ์ค‘๋ณต๋œ ๋ฃจํ”„๋„ ๋งŽ๊ณ  ์ธ์ˆ˜ ๋ชฉ๋ก๋„ ์•„์ฃผ ๊ธธ ์ˆ˜ ์žˆ๋‹ค. ์ด๋ฆ„์€ ์ฆ‰ํฅ์ ์ด๊ณ  ์ฝ”๋“œ๋Š” ์ค‘๋ณต๋œ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด ์„œํˆฌ๋ฅธ ์ฝ”๋“œ๋ฅผ ๋น ์ง์—†์ด ํ…Œ์ŠคํŠธํ•˜๋Š” ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ๋งŒ๋“ ๋‹ค.
  • ๊ทธ๋Ÿฌ๋ฉด ์ฝ”๋“œ๋ฅผ ๋‹ค๋“ฌ๊ณ , ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค๊ณ , ์ด๋ฆ„์„ ๋ฐ”๊พธ๊ณ , ์ค‘๋ณต์„ ์ œ๊ฑฐํ•ด๋„ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ฅผ ํ†ต๊ณผํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ถ€๋‹ด์ด ์—†๋‹ค.
  • ์ตœ์ข…์ ์œผ๋กœ ์ด ์žฅ์—์„œ ์„ค๋ช…ํ•œ ๊ทœ์น™์„ ๋”ฐ๋ฅด๋Š” ํ•จ์ˆ˜๊ฐ€ ์–ป์–ด์ง„๋‹ค. ์ฒ˜์Œ๋ถ€ํ„ฐ ๋”ฑ ๋งŒ๋“œ๋Š” ์‚ฌ๋žŒ์€ ์—†์œผ๋ฆฌ๋ผ.

๊ฒฐ๋ก 

์˜ˆ์‹œ ์ฝ”๋“œ

SetupTeardownIncluder.java
import fitnesse.responders.run.SuiteResponder;
import fitnesse.wiki.*;

public class SetupTeardownIncluder {
    private PageData pageData;
    private boolean isSuite;
    private WikiPage testPage;
    private StringBuffer newPageContent;
    private PageCrawler pageCrawler;

    public static String render(PageData pageData) throws Exception {
        return render(pageData, false);
    }

    public static String render(PageData pageData, boolean isSuite) throws Exception {
        return new SetupTeardownIncluder(pageData).render(isSuite);
    }

    private SetupTeardownIncluder(PageData pageData) {
        this.pageData = pageData;
        testPage = pageData.getWikiPage();
        pageCrawler = testPage.getPageCrawler();
        newPageContent = new StringBuffer();
    }

    private String render(boolean isSuite) throws Exception {
        this.isSuite = isSuite;
        if (isTestPage())
        includeSetupAndTeardownPage();
        return pageData.getHtml();
    }

    private boolean isTestPage() throws Exception {
        return pageData.hasAttribute("Test");
    }
    
    private void includeSetupAndTeardownPage() throws Exception {
        includeSetupPage();
        includePageContent();
        includeTeardownPages();
        updatePageContent();
    }

    private void includeSetupPage() throws exception {
        if (isSuite)
            includeSuiteSetupPage();
        includeSetupPage();
    }

    private void includeSuiteSetupPage() throws Exception {
        include(SuiteResponder.SUITE_SETUP_NAME, "-setup");
    }

    private void includeSetupPage() throws Exception {
        include("SetUp", "-setup");
    }

    private void includePageContent() throws Exception {
        newPageContent.append(pageData.getContent());
    }

    private void includeTeardownPages() throws Exception {
        includeTeardownPage();
        if (isSuite)
            includeSuiteTeardownPage();
    }

    private void includeTeardownPage() throws Exception {
        include("TearDown", "-teardown");
    }

    private void includeSuiteTeardownPage() throws Exception {
        include(SuiteREsponder.SUITE_TEARDOWN_NAME, "-teardown");
    }

    private void updatePageContent() throws Exception {
        pageData.setContent(newPageContent.toString());
    }

    private void include(String pageName, String arg) throws Exception {
        WikiPage inheritedPage = findInheritedPage(pageName);
        if (inheritedPage != null) {
            String pagePathName = gePathNameForPage(inheritedPage);
            buildIncludeDirective(pagePathName, arg);
        }
    }

    private WikiPage findInheritedPage(String pageName) throws Exception {
        WikiPagePath pagePath = pageCrawler.getFullPath(page);
        return PathParser.render(pagePath);
    }

    private void buildIncludeDirective(String pagePathName, String arg) {
        newPageContent
            .append("\n!include ")
            .append(arg)
            .append(" .")
            .append(pagePathName)
            .append("\n");
    }
}

๋ฆฌํŒฉํ† ๋ง ์ „ upsertChannel

/**
* ์ฑ„๋„ ์ˆ˜์ • - 2021.02.10 ํ˜„์žฌ ์ฑ„๋„ ์ƒ์„ฑ ๋กœ์ง ๋ถ„๋ฆฌ๋กœ ์ธํ•ด update ๋งŒ์„ ์ฒ˜๋ฆฌํ•จ
*
* @param updateChannelParam
* @return
* @throws Exception
*/
public boolean upsertChannel(ContentsUpdateChannelParam updateChannelParam) throws Exception {

    ContentsSearchTrackParam searchTrackParam = new ContentsSearchTrackParam();
    searchTrackParam.setPaging(false);
    searchTrackParam.setTrackIdList(updateChannelParam.getTrackIdList());

    updateChannelParam.setChnlPlayTm(searchTrackParam.getTrackIdList().size() > 0 ? this.getPlayTm(channelMapper.selectTrackList(searchTrackParam)) : "0");

    if (null == updateChannelParam.getChnlId()) {
        // ์ฑ„๋„ ๋“ฑ๋ก
        LocalDateTime renewDate = LocalDateTime.now();

        updateChannelParam.setRenewDtime(renewDate);
        updateChannelParam.setRenewTrackCnt(0);

        channelMapper.insertChannel(updateChannelParam);
        this.insertChannelMultiLink(updateChannelParam);

        if (ChnlType.AFLO == updateChannelParam.getChnlType()) {
            channelMapper.insertAfloChnl(updateChannelParam);
        }

        if (ChnlType.AFLO != updateChannelParam.getChnlType() && !ObjectUtils.isEmpty(updateChannelParam.getOrgGenreId())) {
            // ์ฑ„๋„ ์žฅ๋ฅด ๋งตํ•‘ ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€
            channelMapper.deleteMapSvcGenreChannel(updateChannelParam);
            // ์™„๋ฃŒ
            channelMapper.insertMapSvcGenreChannel(updateChannelParam);
        }

        // ์ฑ„๋„ ํŠธ๋ž™ ๋“ฑ๋ก
        insertChannelTrack(updateChannelParam);
    } else {
        updateChannelParam.setRenewTrackCnt(0);

        //์ฑ„๋„ ์ˆ˜์ •
        channelMapper.updateChannel(updateChannelParam);
        this.updateChannelMultiLink(updateChannelParam);

        if (ChnlType.AFLO != updateChannelParam.getChnlType() && !ObjectUtils.isEmpty(updateChannelParam.getOrgGenreId())) {
            // ์ฑ„๋„ ์žฅ๋ฅด ๋งตํ•‘ ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€
            channelMapper.deleteMapSvcGenreChannel(updateChannelParam);
            channelMapper.insertMapSvcGenreChannel(updateChannelParam);
        } else {
            channelMapper.deleteMapSvcGenreChannel(updateChannelParam);
        }

        //์ฑ„๋„ ํŠธ๋ž™ ์ˆ˜์ •
        updateChannelTrack(updateChannelParam);
    }

    // ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ
    updateChannelImg(updateChannelParam);

    // ์œ ํšจํ•œ ํŠธ๋ž™ ๊ฐฏ์ˆ˜๋กœ ์—…๋ฐ์ดํŠธ
    channelMapper.updateChannelTrackCnt(updateChannelParam.getChnlId());

    //์ฑ„๋„ ํƒ€์ดํ‹€๊ณก ์—…๋ฐ์ดํŠธ
    Long titleTrackId = updateChannelParam.getTitleTrackId();
    if (updateChannelParam.getTrackIdList().size() > 0) {
        if (titleTrackId == null) {
            //์ฒซ๋ฒˆ์งธ trackId ๋กœ
            titleTrackId = updateChannelParam.getTrackIdList().get(0);
        }
        channelMapper.updateInitChannelTitleYn(updateChannelParam.getChnlId());
        channelMapper.updateChannelTitleYn(updateChannelParam.getChnlId(), titleTrackId);
    }

    // ์ด๋ ฅ ์ƒ์„ฑ
    insertChannelHistory(updateChannelParam);

    // ๋ ˆ๋””์Šค ์บ์‹œ ์‚ญ์ œ
    String channelRedisKey = String.format(godmusicChanneRedislKeyFormat, updateChannelParam.getChnlId());
    redisService.delWithPrefix(channelRedisKey); // ์ฒด๋„ ์บ์‹œ ์‚ญ์ œ

    for (String key : godmusicRecommendRedislKey) { // ์ถ”์ฒœ ์บ์‹œ ์‚ญ์ œ
        redisService.delWithPrefix(key);
    }

    return true;
}

๋ฆฌํŒฉํ† ๋ง

/**
* ์ฑ„๋„ ์ˆ˜์ • ์„œ๋น„์Šค
*
* @param updateChannelParam
* @return
* @throws Exception
*/
public boolean updateChannel(ContentsUpdateChannelParam updateChannelParam) throws Exception {
    beforeUpdate(updateChannelParam);
    channelMapper.updateChannel(updateChannelParam);
    afterUpdate(updateChannelParam);
    return true;
}

public void beforeUpdate(ContentsUpdateChannelParam updateChannelParam) {
    ContentsSearchTrackParam searchTrackParam = new ContentsSearchTrackParam();
    searchTrackParam.setPaging(false);
    searchTrackParam.setTrackIdList(updateChannelParam.getTrackIdList());

    updateChannelParam.setChnlPlayTm(searchTrackParam.getTrackIdList().size() > 0 ? this.getPlayTm(channelMapper.selectTrackList(searchTrackParam)) : "0");

    if (Objects.isNull(updateChannelParam.getChnlId())) throw new FloAdminException(CommonErrorDomain.BAD_REQUEST);
    updateChannelParam.setRenewTrackCnt(0);

}

public void afterUpdate(ContentsUpdateChannelParam updateChannelParam) throws ParseException {
    updateChannelMultiLink(updateChannelParam);
    updateMapSvcGenreChannel(updateChannelParam);
    updateChannelTrack(updateChannelParam);
    updateChannelImg(updateChannelParam);
    updateValidChannelTrackCnt(updateChannelParam);
    updateChannelTitle(updateChannelParam);
    insertChannelHistory(updateChannelParam);
    deleteRedisCache(updateChannelParam);
}

/**
* ์ฑ„๋„ ์žฅ๋ฅด ๋งคํ•‘ ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€
* AFLO ํƒ€์ž…์ผ ๊ฒฝ์šฐ ์žฅ๋ฅด ๊ฐ’ ์‚ญ์ œ
*
* @param updateChannelParam
*/
private void updateMapSvcGenreChannel(ContentsUpdateChannelParam updateChannelParam) {
    if (ChnlType.AFLO != updateChannelParam.getChnlType() && !ObjectUtils.isEmpty(updateChannelParam.getOrgGenreId())) {
        // ์ฑ„๋„ ์žฅ๋ฅด ๋งตํ•‘ ๋ฐ์ดํ„ฐ ์ถ”๊ฐ€
        channelMapper.deleteMapSvcGenreChannel(updateChannelParam);
        channelMapper.insertMapSvcGenreChannel(updateChannelParam);
    } else {
        channelMapper.deleteMapSvcGenreChannel(updateChannelParam);
    }
}

/**
* ์œ ํšจํ•œ ์ฑ„๋„ ํŠธ๋ž™ ๊ฐฏ์ˆ˜๋กœ ์—…๋ฐ์ดํŠธ
* @param updateChannelParam
*/
private void updateValidChannelTrackCnt(ContentsUpdateChannelParam updateChannelParam) {
    channelMapper.updateChannelTrackCnt(updateChannelParam.getChnlId());
}

/**
* ์—…๋ฐ์ดํŠธ ํ›„ ๋ ˆ๋””์Šค ์บ์‹œ ์‚ญ์ œ
* @param updateChannelParam
*/
private void deleteRedisCache(ContentsUpdateChannelParam updateChannelParam){
    // ๋ ˆ๋””์Šค ์บ์‹œ ์‚ญ์ œ
    String channelRedisKey = String.format(godmusicChanneRedislKeyFormat, updateChannelParam.getChnlId());
    redisService.delWithPrefix(channelRedisKey); // ์ฒด๋„ ์บ์‹œ ์‚ญ์ œ

    for (String key : godmusicRecommendRedislKey) { // ์ถ”์ฒœ ์บ์‹œ ์‚ญ์ œ
        redisService.delWithPrefix(key);
    }
}

/**
* ์ฑ„๋„ ํƒ€์ดํ‹€๊ณก ์—…๋ฐ์ดํŠธ
* titleTrackId๊ฐ€ ์—†์„ ๊ฒฝ์šฐ ์ฒซ ๋ฒˆ์งธ ํŠธ๋ž™ ์•„์ด๋””๋กœ ์„ค์ •
*
* @param updateChannelParam
*/
private void updateChannelTitle(ContentsUpdateChannelParam updateChannelParam){
    Long titleTrackId = updateChannelParam.getTitleTrackId();
    if (updateChannelParam.getTrackIdList().size() > 0) {
        if (titleTrackId == null) {
            titleTrackId = updateChannelParam.getTrackIdList().get(0);
        }
        channelMapper.updateInitChannelTitleYn(updateChannelParam.getChnlId());
        channelMapper.updateChannelTitleYn(updateChannelParam.getChnlId(), titleTrackId);
    }
}

/**
* ์ฑ„๋„ ์ด๋ฏธ์ง€ ์—…๋ฐ์ดํŠธ
* @param updateChannelParam
*/
private void updateChannelImg(ContentsUpdateChannelParam updateChannelParam) {
    if (ChnlType.AFLO.getCode().equals(updateChannelParam.getChnlType().getCode())) {
        Long chnlId = updateChannelParam.getChnlId();
        if (!CollectionUtils.isEmpty(updateChannelParam.getChnlImages())) {
            channelMapper.deleteChnlImage(updateChannelParam.getChnlId());
            for (ContentsChnlImage chnlImage : updateChannelParam.getChnlImages()) {
                chnlImage.setChnlId(updateChannelParam.getChnlId());
                chnlImage.setCreateUserNo(updateChannelParam.getCreateUserNo());
                channelMapper.insertChnlImage(chnlImage);
            }
        } else {
            channelMapper.deleteChnlImage(updateChannelParam.getChnlId());
        }
        channelMapper.updateAfloChnl(chnlId, updateChannelParam.getAfloArtistId());
    } else {
        if (null != updateChannelParam.getImgUrl() && !("").equals(updateChannelParam.getImgUrl())) {
            channelMapper.deleteChnlImage(updateChannelParam.getChnlId());
            channelMapper.insertChnlImage(ContentsChnlImage.builder()
                    .chnlId(updateChannelParam.getChnlId()).imgUrl(updateChannelParam.getImgUrl()).chnlImgType(ChnlImgType.PANNEL)
                    .createUserNo(updateChannelParam.getCreateUserNo()).build());
        } else {
            channelMapper.deleteChnlImage(updateChannelParam.getChnlId());
        }
    }
}

/**
* ์ฑ„๋„ ํžˆ์Šคํ† ๋ฆฌ ์ด๋ ฅ ์ƒ์„ฑ
* @param updateChannelParam
*/
private void insertChannelHistory(ContentsUpdateChannelParam updateChannelParam) {

    ContentsChannelHistory channelHistory = new ContentsChannelHistory();
    channelHistory.setChnlChgType("TRACK");
    channelHistory.setChnlId(updateChannelParam.getChnlId());
    channelHistory.setCreateUserNo(updateChannelParam.getUpdateUserNo());
    channelHistory.setCreateDtime(updateChannelParam.getCreateDtime());

    channelMapper.insertChannelHistory(channelHistory);
    channelMapper.insertChannelTrackHistory(channelHistory);

}

์ฐธ๊ณ  ์ž๋ฃŒ

  • Clean Code