CleanCode TIL (2022.02.06)

Henry Choยท2022๋…„ 2์›” 6์ผ
0

๋…ธ๊ฐœ๋ถ

๋ชฉ๋ก ๋ณด๊ธฐ
15/31

DAY 17

๐Ÿ”–ย ์˜ค๋Š˜ ์ฝ์€ ๋ฒ”์œ„ : 9. ๋‹จ์œ„ํ…Œ์ŠคํŠธ(154~164p)


๐Ÿค“ย ์ฑ…์—์„œ ๊ธฐ์–ตํ•˜๊ณ  ์‹ถ์€ ๋‚ด์šฉ

TDD ๋ฒ•์น™ ์„ธ ๊ฐ€์ง€

  1. ์‹คํŒจํ•˜๋Š” ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ๊นŒ์ง€ ์‹ค์ œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์ง€ ์•Š๋Š”๋‹ค.
  2. ์ปดํŒŒ์ผ์€ ์‹คํŒจํ•˜์ง€ ์•Š์œผ๋ฉด์„œ ์‹คํ–‰์ด ์‹คํŒจํ•˜๋Š” ์ •๋„๋กœ๋งŒ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ฅผ ์ž‘์„ฑํ•œ๋‹ค.
  3. ํ˜„์žฌ ์‹คํŒจํ•˜๋Š” ํ…Œ์ŠคํŠธ๋ฅผ ํ†ต๊ณผํ•  ์ •๋„๋กœ๋งŒ ์‹ค์ œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•œ๋‹ค.
  • ๋ฒ•์น™์„ ๋”ฐ๋ฅด๋ฉด ๊ฐœ๋ฐœ๊ณผ ํ…Œ์ŠคํŠธ๊ฐ€ 30์ดˆ ์ฃผ๊ธฐ๋กœ ๋ฌถ์ด๊ณ  ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๊ฐ€ ์‹ค์ œ ์ฝ”๋“œ๋ณด๋‹ค ๋ถˆ๊ณผ ๋ช‡ ์ดˆ ์ „์— ๋‚˜์˜จ๋‹ค

๊นจ๋—ํ•œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์œ ์ง€ํ•˜๊ธฐ

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

ํ…Œ์ŠคํŠธ๋Š” ์œ ์—ฐ์„ฑ, ์œ ์ง€๋ณด์ˆ˜์„ฑ, ์žฌ์‚ฌ์šฉ์„ฑ์„ ์ œ๊ณตํ•œ๋‹ค

  • ๋‹จ์œ„ ํ…Œ์ŠคํŠธ: ์ฝ”๋“œ์— ์œ ์—ฐ์„ฑ, ์œ ์ง€๋ณด์ˆ˜์„ฑ, ์žฌ์‚ฌ์šฉ์„ฑ์„ ์ œ๊ณตํ•˜๋Š” ๋ฒ„ํŒ€๋ชฉ
  • ์ž๋™ํ™”๋œ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ์ŠˆํŠธ๋Š” ๋ณ€๊ฒฝ์ด ์‰ฌ์›Œ์ง€๊ฒŒ ํ•จ โ†’ ์„ค๊ณ„์™€ ์•„ํ‚คํ…์ฒ˜๋ฅผ ์ตœ๋Œ€ํ•œ ๊นจ๋—ํ•˜๊ฒŒ ๋ณด์กด ๊ฐ€๋Šฅ

๊นจ๋—ํ•œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ

  • ๊ฐ€๋…์„ฑ์€ ์‹ค์ œ ์ฝ”๋“œ๋ณด๋‹ค ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์—์„œ ๋” ์ค‘์š”ํ•˜๋‹ค
public void testGetPageHieratchyAsXml() throws Exception {
    crawler.addPage(root, PathParser.parse("PageOne"));
    crawler.addPage(root, PathParser.parse("PageOne.ChildOne"));
    crawler.addPage(root, PathParser.parse("PageTwo"));
    request.setResource("root");
    request.addInput("type", "pages");
    Responder responder = new SerializedPageResponder();
    SimpleResponse response =
        (SimpleResponse) responder.makeResponse(new FitNesseContext(root), request);
    String xml = response.getContent();
    assertEquals("text/xml", response.getContentType());
    assertSubString("<name>PageOne</name>", xml);
    assertSubString("<name>PageTwo</name>", xml);
    assertSubString("<name>ChildOne</name>", xml);
}

public void testGetPageHieratchyAsXmlDoesntContainSymbolicLinks() throws Exception {
    WikiPage pageOne = crawler.addPage(root, PathParser.parse("PageOne"));
    crawler.addPage(root, PathParser.parse("PageOne.ChildOne"));
    crawler.addPage(root, PathParser.parse("PageTwo"));
    PageData data = pageOne.getData();
    WikiPageProperties properties = data.getProperties();
    WikiPageProperty symLinks = properties.set(SymbolicPage.PROPERTY_NAME);
    symLinks.set("SymPage", "PageTwo");
    pageOne.commit(data);
    request.setResource("root");
    request.addInput("type", "pages");
    Responder responder = new SerializedPageResponder();
    SimpleResponse response =
        (SimpleResponse) responder.makeResponse(new FitNesseContext(root), request);
    String xml = response.getContent();
    assertEquals("text/xml", response.getContentType());
    assertSubString("<name>PageOne</name>", xml);
    assertSubString("<name>PageTwo</name>", xml);
    assertSubString("<name>ChildOne</name>", xml);
    assertNotSubString("SymPage", xml);
}

public void testGetDataAsHtml() throws Exception {
    crawler.addPage(root, PathParser.parse("TestPageOne"), "test page");
    request.setResource("TestPageOne");
    request.addInput("type", "data");
    Responder responder = new SerializedPageResponder();
    SimpleResponse response =
        (SimpleResponse) responder.makeResponse(new FitNesseContext(root), request);
    String xml = response.getContent();
    assertEquals("text/xml", response.getContentType());
    assertSubString("test page", xml);
    assertSubString("<Test", xml);
}
  • addPage์™€ assertSubString์„ ๋ถ€๋ฅด๋Š๋ผ ์ค‘๋ณต๋˜๋Š” ์ฝ”๋“œ๊ฐ€ ๋งค์šฐ ๋งŽ๋‹ค
  • PathParser ๋Š” ๋ฌธ์ž์—ด์„ pagePath ์ธ์Šคํ„ด์Šค๋กœ ๋ณ€ํ™˜ โ†’ web crawler ๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ๊ฐ์ฒด์ด๋ฏ€๋กœ ํ…Œ์ŠคํŠธ์™€ ๋ฌด๊ด€
  • responder, response๋ฅผ ์ˆ˜์ง‘ํ•ด ๋ณ€ํ™˜ํ•˜๋Š” ์ฝ”๋“œ ์—ญ์‹œ ์žก์Œ
  • ์•„๋ž˜์™€ ๊ฐ™์ด refactoring
public void testGetPageHierarchyAsXml() throws Exception {
    makePages("PageOne", "PageOne.ChildOne", "PageTwo");
    submitRequest("root", "type:pages");
    assertResponseIsXML();
    assertResponseContains(
        "<name>PageOne</name>", "<name>PageTwo</name>", "<name>ChildOne</name>");
}

public void testSymbolicLinksAreNotInXmlPageHierarchy() throws Exception {
    WikiPage page = makePage("PageOne");
    makePages("PageOne.ChildOne", "PageTwo");
    addLinkTo(page, "PageTwo", "SymPage");
    submitRequest("root", "type:pages");
    assertResponseIsXML();
    assertResponseContains(
        "<name>PageOne</name>", "<name>PageTwo</name>", "<name>ChildOne</name>");
    assertResponseDoesNotContain("SymPage");
}

public void testGetDataAsXml() throws Exception {
    makePageWithContent("TestPageOne", "test page");
    submitRequest("TestPageOne", "type:data");
    assertResponseIsXML();
    assertResponseContains("test page", "<Test");
}
  • Build-Operate-Check ํŒจํ„ด
    1. ํ…Œ์ŠคํŠธ ์ž๋ฃŒ๋ฅผ ๋งŒ๋“ฌ
    2. ํ…Œ์ŠคํŠธ ์ž๋ฃŒ๋ฅผ ์กฐ์ž‘
    3. ๊ฒฐ๊ณผ๊ฐ€ ์˜ฌ๋ฐ”๋ฅธ์ง€ ํ™•์ธ

๋„๋ฉ”์ธ์— ํŠนํ™”๋œ ํ…Œ์ŠคํŠธ ์–ธ์–ด

  • DSL: ํ…Œ์ŠคํŠธ๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋‹น์‚ฌ์ž์™€ ๋‚˜์ค‘์— ํ…Œ์ŠคํŠธ๋ฅผ ์ฝ์–ด๋ณผ ๋…์ž๋ฅผ ๋„์™€์ฃผ๋Š” ํ…Œ์ŠคํŠธ์–ธ์–ด
  • ์‹œ์Šคํ…œ ์กฐ์ž‘ API ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋Œ€์‹  API ์œ„์—๋‹ค ํ•จ์ˆ˜์™€ ์œ ํ‹ธ๋ฆฌํ‹ฐ๋ฅผ ๊ตฌํ˜„ํ•œ ํ›„ ๊ทธ ํ•จ์ˆ˜์™€ ์œ ํ‹ธ๋ฆฌํ‹ฐ๋ฅผ ์‚ฌ์šฉ

์ด์ค‘ ํ‘œ์ค€

  • ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋Š” ์‹ค์ œ์ฝ”๋“œ๋งŒํผ ํšจ์œจ์ ์ผ ํ•„์š”๋Š” ์—†๋‹ค โ†’ ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์—์„œ ์ˆ˜ํ–‰๋˜๊ธฐ ๋•Œ๋ฌธ
@Test
public void turnOnLoTempAlarmAtThreashold() throws Exception {
    hw.setTemp(WAY_TOO_COLD);
    controller.tic();
    assertTrue(hw.heaterState());
    assertTrue(hw.blowerState());
    assertFalse(hw.coolerState());
    assertFalse(hw.hiTempAlarm());
    assertTrue(hw.loTempAlarm());
}
  • assertTrue, False ์ฝ๊ธฐ ๋„ˆ๋ฌด ์–ด๋ ต๋‹ค.
  • ์•„๋ž˜์™€ ๊ฐ™์ด refactoring
@Test
public void turnOnLoTempAlarmAtThreshold() throws Exception {
    wayTooCold();
    assertEquals("HBchL", hw.getState());
}
  • ๋Œ€๋ฌธ์ž๋Š” โ€˜์ผœ์งonโ€™์ด๊ณ  ์†Œ๋ฌธ์ž๋Š” โ€˜๊บผ์ง offโ€™์„ ๋œปํ•œ๋‹ค. ๋ฌธ์ž๋Š” ํ•ญ์ƒ {heater, blower, cooler, hi-temp-alarm, lo-temp- alarm} ์ˆœ์„œ
  • ๊ทธ๋ฆ‡๋œ ์ •๋ณด๋ฅผ ํ”ผํ•˜๋ผ ๊ทœ์น™์„ ์œ„๋ฐ˜ํ•˜์ง€๋งŒ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์—์„œ๋Š” ์ ์ ˆํ•˜๋‹ค
public String getState() {
    String state = "";
    state += heater ? "H" : "h";
    state += blower ? "B" : "b";
    state += cooler ? "C" : "c";
    state += hiTempAlarm ? "H" : "h";
    state += loTempAlarm ? "L" : "l";
    return state;
}
  • ํšจ์œจ์„ ์œ„ํ•ด์„œ๋Š” StringBuffer๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•˜์ง€๋งŒ ํ…Œ์ŠคํŠธ์ฝ”๋“œ์—์„œ๋Š” ๊ดœ์ฐฎ๋‹ค

๐Ÿค”ย ๋– ์˜ค๋ฅด๋Š” ์ƒ๊ฐ

  • ํ™•์‹คํžˆ ํ”„๋กœ์ ํŠธ ๋ฉ”์ธํ…Œ์ด๋„ˆ๊ฐ€ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ฅผ ์ž˜ ์ž‘์„ฑํ•ด๋†“์œผ๋ฉด ์ƒˆ๋กœ ํ•ฉ๋ฅ˜ํ•œ ์ธ์›์ด ์•„๋ฌด๋ ‡๊ฒŒ๋‚˜ ๋ณ€๊ฒฝํ•ด์„œ PR์„ ์˜ฌ๋ ค๋„ ๋‘๋ ค์›€์ด ์—†๊ณ  ์‹œ์Šคํ…œ์ด ์ž˜ ์œ ์ง€ ๋˜์—ˆ์—ˆ๋˜ ๊ฒฝํ—˜์ด ์žˆ๋‹ค

๐Ÿ”Žย ์งˆ๋ฌธ

๐Ÿ“ย ์†Œ๊ฐ 3์ค„ ์š”์•ฝ

  • ์ž๋™ํ™”๋œ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ์ŠˆํŠธ๋Š” ๋ณ€๊ฒฝ์ด ์‰ฌ์›Œ์ง€๊ฒŒ ํ•จ โ†’ ์„ค๊ณ„์™€ ์•„ํ‚คํ…์ฒ˜๋ฅผ ์ตœ๋Œ€ํ•œ ๊นจ๋—ํ•˜๊ฒŒ ๋ณด์กด ๊ฐ€๋Šฅ
  • ๊ฐ€๋…์„ฑ์€ ์‹ค์ œ ์ฝ”๋“œ๋ณด๋‹ค ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์—์„œ ๋” ์ค‘์š”ํ•˜๋‹ค โ†’ Build-Operate-Check ํŒจํ„ด
  • ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋Š” ์‹ค์ œ์ฝ”๋“œ๋งŒํผ ํšจ์œจ์ ์ผ ํ•„์š”๋Š” ์—†๋‹ค โ†’ ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์—์„œ ์ˆ˜ํ–‰๋˜๊ธฐ ๋•Œ๋ฌธ
profile
Full stack tech visionary

0๊ฐœ์˜ ๋Œ“๊ธ€