ํ‹ฐ์Šคํ† ๋ฆฌ ๋ทฐ

ํ•จ์ˆ˜๋ฅผ cleanํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•

 

1. ์ž‘๊ฒŒ๋งŒ๋“ค๊ธฐ

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

3. ํ•œ๊ฐ€์ง€ ๊ธฐ๋Šฅ๋งŒ

4. ์„œ์ˆ ์ ์ธ ๋„ค์ด๋ฐ

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

6. ๋ถ€์ˆ˜ํšจ๊ณผx

7. ๋ช…๋ น์กฐํšŒ

8. ์˜ค๋ฅ˜์˜ˆ์™ธ

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

 

 

์šฐ์„  bad ํ•จ์ˆ˜ ์˜ˆ์‹œ

 

๋‘๊ฒน์œผ๋กœ ์ค‘์ฒฉ๋œ if๋ฌธ์œผ๋กœ 

๋ช…ํ™•ํ•˜์ง€ ์•Š์€ ํ”Œ๋ž˜๊ทธ, ๋ฌธ์ž์—ด,ํ•จ์ˆ˜ ํ˜ธ์ถœ์„ ํ•˜๊ณ  ์žˆ์Œ.

 

-> ํ•จ์ˆ˜๊ฐ€ ๋„ˆ๋ฌด ๊ธธ๊ณ , ์–ด๋–ค ์ผ์„ ํ•˜๋Š”์ง€ ์•Œ๊ธฐ ํž˜๋“ฆ.

 

 

 

1. ์ž‘๊ฒŒ ๋งŒ๋“ค๊ธฐ

ํ•œ ํ•จ์ˆ˜์˜ ๊ธธ์ด๋Š” 20์ค„๋„ ๊ธธ๋‹ค๊ณ  ํ•œ๋‹ค.

 

 

 

2. ๋ธ”๋ก๊ณผ ๋“ค์—ฌ์“ฐ๊ธฐ๋Š” ํ•œ์ค„์ด์–ด์•ผ ํ•œ๋‹ค.

if, else, while ๋ฌธ์— ๋“ค์–ด๊ฐ€๋Š” ์ฝ”๋“œ๋Š” ํ•œ์ค„์ด์–ด์•ผํ•˜๊ณ  ๋Œ€๊ฐœ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ด์•ผํ•œ๋‹ค.

func rednerPageWithSetupAndTeardowns(pageData: PageData, isSuite: Bool) -> String {
	if (isTestPage(pageData)) {
		inlcudeSetupAndTeardownPages(pageData, isSuite)
	}
    return pageData.getHtml()
}

 

 

 

3. ํ•œ๊ฐ€์ง€ ํ•จ์ˆ˜์— ํ•œ๊ฐ€์ง€ ๊ธฐ๋Šฅ๋งŒ.

ํ•จ์ˆ˜๋‹น ์ถ”์ƒํ™” ์ˆ˜์ค€๋„ ํ•œ๊ฐ€์ง€๋กœ, ํ•จ์ˆ˜๋‚ด ๋ชจ๋“  ์ถ”์ƒํ™” ์ˆ˜์ค€์ด ๋™์ผํ•ด์•ผํ•œ๋‹ค.

 

getHtml() - ์ถ”์ƒํ™” ์ˆ˜์ค€ ๋†’์€ํŽธ

.append("\n") - ์ถ”์ƒํ™” ์ˆ˜์ค€ ๋‚ฎ์€ ํŽธ

 

๊ตฌํ˜„ํ•˜๋‹ค๋ณด๋ฉด ํ•œ ํ•จ์ˆ˜์— ๊ฐ™์€ ์ถ”์ƒํ™” ์ˆ˜์ค€๋งŒ์„ ๊ฐ€์ง€๊ธด ์‰ฝ์ง€ ์•Š์€๋ฐ,

'ํ•œ ๊ฐ€์ง€'๋งŒ ํ•˜๋Š” ํ•จ์ˆ˜๋กœ ๊ตฌํ˜„ํ•˜๋ฉด ์‰ฝ๊ฒŒ ๊ตฌํ˜„ ๊ฐ€๋Šฅํ•˜๋‹ค.

 

switch๋ฌธ์„ ์‚ฌ์šฉํ•˜๊ฒŒ๋  ๊ฒฝ์šฐ 'ํ•œ ๊ฐ€์ง€'๋งŒ ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๊ตฌํ˜„ํ•˜๊ธฐ ์–ด๋ ค์›€. (N๊ฐ€์ง€ ์ผ์„ ์ฒ˜๋ฆฌ)

 

public Money calculatePay(Employee e) throws InvalidEmployeeType {
  switch (e.type) {
    case COMMISIONED :
      return calculateCommissionedPay(e);
    case HOURLY :
      return calculateHourlyPay(e);
    case SALARIED :
      return calculateSalariedPay(e);
    default :
      throw new InvalidEmployeeType(e.type);
  }
}

 

  1. ์ƒˆ ์ง์›์˜ ์œ ํ˜• (case)๊ฐ€ ์ถ”๊ฐ€ ๋  ๊ฒฝ์šฐ ๋” ๊ธธ์–ด์ง„๋‹ค.
  2. e.type ์— ๋”ฐ๋ผ ๊ธฐ๋Šฅ์ด ๋‹ฌ๋ผ์ ธ์„œ ํ•œ ๊ฐ€์ง€ ๊ธฐ๋Šฅ๋งŒ์„ ํ•˜์ง€ ์•Š์Œ. SRP ์›์น™ (ํ•˜๋‚˜์˜ ๊ฐ์ฒด๋Š” ํ•˜๋‚˜์˜ ์ฑ…์ž„๋งŒ) ์œ„๋ฐ˜
  3. OCP ์›์น™(ํ™•์žฅ open, ์ˆ˜์ • close) ์œ„๋ฐ˜ - ์ƒˆ ์ง์› ์œ ํ˜•์„ ์ถ”๊ฐ€ํ•  ๋•Œ ๋งˆ๋‹ค ์ฝ”๋“œ๋ฅผ ๋ณ€๊ฒฝํ•ด์•ผํ•จ.
  4. ์œ„ ๊ตฌ์กฐ์™€ ๋™์ผํ•œ ํ•จ์ˆ˜๊ฐ€ ๋ฌดํ•œ์ • ์กด์žฌํ•จ.
isPayday(Employee e, Date date);

deliverPay(Employee e, Money pay);

 

 

ํ•ด๊ฒฐ๋ฐฉ์•ˆ

- Employee์˜ ์ธํ„ฐํŽ˜์ด์Šค Abstract Factory๋ฅผ ๋งŒ๋“ค์–ด switch๋ฌธ์„ ์ˆจ๊น€.

- ์–ป์„ ์ˆ˜ ์žˆ๋Š” ํšจ๊ณผ: ์œ„ 4๋ฅผ ํ•ด๊ฒฐํ•˜๊ณ  1,2,3์„ ์ตœ์†Œํ™” ํ•  ์ˆ˜ ์žˆ์Œ.

 

// ์ถ”์ƒ ํด๋ž˜์Šค Employee
public abstract class Employee {
  public abstract boolean isPayday();
  public abstract boolean calculatePay();
  public abstract boolean deliverPay(Money pay);
}

// Employee๋ฅผ ์ƒ์† ๋ฐ›๋Š” ๊ฐ์ฒด๋“ค์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋Š” EmployeeFactory ์ธํ„ฐํŽ˜์ด์Šค
public interface EmployeeFactory {
  public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType;
}

// EmployeeFactory๋ฅผ ๊ตฌํ˜„ํ•œ ํด๋ž˜์Šค
public class EmployeeFactoryImpl implements EmployeeFactory {
  public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType {
    switch (r.type) {
      case COMMISIONED :
        return new CommissionedEmployee(r);
      case HOURLY :
        return new HourlyEmployee(r);
      case SALARIED :
        return new SalariedEmployee(r);
      default :
        throw new InvalidEmployeeType(r.type);
    }
  }
}

 

์ด๋ ‡๊ฒŒ switch๋ฌธ์„ employee์˜ impl๋กœ ๋”ฐ๋กœ ๊ตฌํ˜„ํ•ด์ฃผ๊ฒŒ๋œ๋‹ค๋ฉด

ํ•˜์œ„ employee ๊ฐ์ฒด์—์„œ switch๋ฌธ์„ ์—ฌ๋Ÿฌ๋ฒˆ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ฒŒ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

 

Abstract Factory ๋Š” ๊ฐ์ฒด ์ƒ์„ฑ์— ๋Œ€ํ•œ ์ถ”์ƒํ™”๋œ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ œ๊ณตํ•˜๋Š” ๋””์ž์ธ ํŒจํ„ด

๊ฐ์ฒด ์ƒ์„ฑ์„ ๋‹ด๋‹นํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ถ”์ƒํ™”ํ•˜์—ฌ ๊ฐ์ฒด ์ƒ์„ฑ ๊ณผ์ •์˜ ๋ณ€ํ™”์— ์œ ์—ฐํ•˜๊ฒŒ ๋Œ€์ฒ˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

์ถ”์ƒ ํŒฉํ† ๋ฆฌ ํŒจํ„ด์€ ์ผ๋ฐ˜์ ์œผ๋กœ ๋น„์Šทํ•œ ๊ธฐ๋Šฅ์„ ๊ฐ€์ง„ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฒฝ์šฐ ์‚ฌ์šฉ๋œ๋‹ค.

 

Abstract Factory ์˜ˆ์ œ

๋งŒ์•ฝ ์—ฌ๋Ÿฌ ์ข…๋ฅ˜์˜ GUI ์ปดํฌ๋„ŒํŠธ(๋ฒ„ํŠผ, ํ…์ŠคํŠธ ์ƒ์ž ๋“ฑ)๋ฅผ ๋งŒ๋“ค์–ด์•ผ ํ•œ๋‹ค๋ฉด, 

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

 

// button, textfield๋ฅผ ์ƒ์„ฑํ•˜๋Š” Abstract Factory Protocol
protocol GUIFactory {
    func createButton() -> Button
    func createTextField() -> TextField
}

// button, textfield๊ฐ€ ๊ฐ€์ ธ์•ผ ํ•  ์†์„ฑ๊ณผ ๋ฉ”์„œ๋“œ๋ฅผ ์ •์˜ Abstract Product Protocol
protocol Widget {
    var backgroundColor: UIColor { get set }
    func display()
}

 

๋ฒ„ํŠผ๊ณผ ํ…์ŠคํŠธํ•„๋“œ์˜ ์ƒ์„ฑ์„ ์ถ”์ƒํ™” ํ•˜์˜€์œผ๋ฉฐ,

 

// iOS์šฉ ๊ตฌ์ฒด์ ์ธ ํŒฉํ† ๋ฆฌ ํด๋ž˜์Šค
class IOSFactory: GUIFactory {
    func createButton() -> Button {
        return IOSButton()
    }
    
    func createTextField() -> TextField {
        return IOSTextField()
    }
}

// iOS์šฉ ๊ตฌ์ฒด์ ์ธ ๋ฒ„ํŠผ ํด๋ž˜์Šค
class IOSButton: UIButton, Widget {
    var backgroundColor: UIColor = .white
    
    func display() {
        // ๋ฒ„ํŠผ์„ ํ™”๋ฉด์— ๋ณด์—ฌ์ฃผ๋Š” ์ฝ”๋“œ
    }
}

// iOS์šฉ ๊ตฌ์ฒด์ ์ธ ํ…์ŠคํŠธ ํ•„๋“œ ํด๋ž˜์Šค
class IOSTextField: UITextField, Widget {
    var backgroundColor: UIColor = .white
    
    func display() {
        // ํ…์ŠคํŠธ ํ•„๋“œ๋ฅผ ํ™”๋ฉด์— ๋ณด์—ฌ์ฃผ๋Š” ์ฝ”๋“œ
    }
}

 

์ด๋Ÿฐ์‹์œผ๋กœ ์œ„ ํ”„๋กœํ† ์ฝœ์„ ๊ตฌํ˜„ํ•˜๋Š” ๊ตฌ์ฒด์ ์ธ ํŒฉํ† ๋ฆฌ์™€ ํ”„๋กœ๋•ํŠธ class๋ฅผ ๊ตฌํ˜„ํ•ด์ค€ ๋’ค,

 

 

// ํด๋ผ์ด์–ธํŠธ ์ฝ”๋“œ
let factory: GUIFactory = IOSFactory()

let button = factory.createButton()
button.backgroundColor = .blue
button.display()

let textField = factory.createTextField()
textField.backgroundColor = .gray
textField.display()


ํด๋ผ์ด์–ธํŠธ์—์„œ๋Š” IOSFactory๋ฅผ ์ƒ์„ฑํ•˜๊ณ ,

createButton(), createTextField() ๋ฉ”์„œ๋“œ๋กœ Button, TextField ์ƒ์„ฑ.

 

๊ณตํ†ต๋œ ์†์„ฑ backgroundColor์™€ display() ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ™”๋ฉด์— ์ถœ๋ ฅํ•ด์คŒ

 

 

 

4. ์„œ์ˆ ์ ์ธ ์ด๋ฆ„ ์‚ฌ์šฉ

์ €์ž๋Š” ํ•จ์ˆ˜ ์ด๋ฆ„์„ tesatableHtml -> SetupTeardownIncluder.render๋กœ ๋ณ€๊ฒฝํ•ด์คŒ

 

๊ธธ๊ณ  ์„œ์ˆ ์ ์ธ ์ด๋ฆ„์ด ์งง๊ณ  ํ•จ์ถ•์ ์ธ ์ด๋ฆ„๋ณด๋‹ค ์ข‹์Œ.

-> ํ•จ์ˆ˜๋ช…๋งŒ ์ฝ๊ณ  ํ•จ์ˆ˜ ๊ธฐ๋Šฅ์„ ํŒŒ์•…ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์คŒ(ํ•œ๊ฐ€์ง€ ๊ธฐ๋Šฅ ์ž˜ ์ˆ˜ํ–‰ํ•œ๋‹ค๋ฉด)

 

 

 

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

 

5.1 ํ•จ์ˆ˜ ์ธ์ˆ˜์˜ ๊ฐœ์ˆ˜๋Š” 3๊ฐœ ์ดํ•˜๋กœ ํ•ด์•ผํ•จ.

 

ํ•จ์ˆ˜ ์ธ์ˆ˜๋Š” ์ ์„์ˆ˜๋ก ์ข‹๋‹ค.

includeSetupPageInto(newPageContent) ๋ณด๋‹ค includeSetupPage()๊ฐ€ ์ดํ•ดํ•˜๊ธฐ ์‰ฌ์›€

 

private void inlcudeSetupAndTeardownPages() throws Exception {
	
    includeSetupPages();
    includePageContent();
    includeTeardownPages();
    updatePageContent();
    
}

 

์ €์ž๋Š” ํ•จ์ˆ˜ ์ธ์ˆ˜ 4๊ฐœ๋ถ€ํ„ฐ๋Š” ํŠน๋ณ„ํ•œ ์ด์œ ๊ฐ€ ์žˆ์–ด๋„ ์‚ฌ์šฉํ•˜์ง€ ๋ง๋ผ๊ณ ํ•จ.

 

 

5.2 ํ”Œ๋ž˜๊ทธ ์ธ์ˆ˜๋Š” ์ง€์–‘ํ•ด์•ผํ•œ๋‹ค.

ํ•จ์ˆ˜๋กœ ๋ถ€์šธ๊ฐ’์„ ๋„˜๊ธฐ๋Š” ๊ด€๋ก€๋Š” ๋”์ฐํ•˜๋‹ค๊ณ  ๋งํ•˜๊ณ  ์žˆ์Œ.

 

func rednerPageWithSetupAndTeardowns(pageData: PageData, isSuite: Bool) -> String {
	if (isTestPage(pageData)) {
		inlcudeSetupAndTeardownPages(pageData, isSuite)
	}
    return pageData.getHtml()
}

 

isSuite๋กœ Bool ๊ฐ’์„ ๋„ฃ์–ด์ฃผ๋Š” ๋Œ€์‹  

rederForSuite()์™€ renderForSingleTest()๋ผ๋Š” ํ•จ์ˆ˜๋กœ ๋‚˜๋ˆ ์„œ ์‚ฌ์šฉํ•˜๋ผ๊ณ  ํ•จ.

 

 

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

write(name) ๋Œ€์‹  ๋™์‚ฌ์™€ ๋ช…์‚ฌ๋ฅผ ์Œ์œผ๋กœ ์‚ฌ์šฉํ•˜์—ฌ

wirteField(name) ์ด๋ ‡๊ฒŒ ์ž‘์„ฑํ•˜๋ฉด ์ข€ ๋” ์•Œ๊ธฐ ์‰ฝ๋‹ค.

 

ํ•จ์ˆ˜ ์ด๋ฆ„์— ํ‚ค์›Œ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ๋ฉด ์ธ์ˆ˜ ์ˆœ์„œ๋ฅผ ๊ธฐ์–ตํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค๊ณ  ํ•จ.

assertExpected() ๋Œ€์‹  assertExpectedEqualsActual(expected, actual)

 

 

 

6. ๋ถ€์ˆ˜ ํšจ๊ณผ ์ผ์œผํ‚ค๊ธฐx

userName๊ณผ password๋ฅผ ์ž…๋ ฅ๋ฐ›์•„ true of false๋ฅผ return ํ•˜๋Š”

์•”ํ˜ธ ํ™•์ธํ•˜๋Š” ํ•จ์ˆ˜์— Session.initialize() ํ˜ธ์ถœ ํ•   ๊ฒฝ์šฐ

์‚ฌ์šฉ์ž๋ฅผ ์ธ์ฆํ•˜๋ฉด์„œ ๊ธฐ์กด ์„ธ์…˜ ์ •๋ณด๋ฅผ ์ง€์›Œ๋ฒ„๋ฆฌ๋Š” ์ƒํ™ฉ์ด ์ƒ๊ธธ ์ˆ˜ ๋„ ์žˆ๋‹ค.

 

 

 

7. ๋ช…๋ น๊ณผ ์กฐํšŒ ๋ถ„๋ฆฌ

์†์„ฑ๊ฐ’์„ ์ฐพ์•„ value๋กœ ์„ค์ •ํ•˜๊ณ  ์„ฑ๊ณตํ•˜๋ฉด true๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜๋กœ

public boolean set(String attribute, String value); ๊ฐ€ ์žˆ์„ ๋•Œ

 

if (set("username", "unclebob")).. ์ด๋Ÿฐ์‹์œผ๋กœ๋„ ์‚ฌ์šฉ ๋  ์ˆ˜ ์žˆ๋‹ค.

 

1. username์ด unclebob์œผ๋กœ ์„ค์ •๋˜์–ด ์žˆ๋Š” ๊ฒฝ์šฐ์ธ์ง€

2. username์ด๋ผ๋Š” ์†์„ฑ๊ฐ’์— unclebob๊ฐ’์„ ์„ฑ๊ณต์ ์œผ๋กœ ๋„ฃ์—ˆ์„ ๊ฒฝ์šฐ์ธ์ง€

 

if ๋ฌธ์žฅ๋งŒ ๋ด์„œ๋Š” ์ง๊ด€์ ์œผ๋กœ ์•Œ๊ธฐ ์–ด๋ ต๋‹ค.

 

if (attributeExist("username")) {

  setAttribute("username", "unclebob");

}

 

์ด๋Ÿฐ์‹์œผ๋กœ ๋ช…๋ น๊ณผ ์กฐํšŒ๋ฅผ ๋ถ„๋ฆฌํ•ด์„œ ์ž‘์„ฑํ•˜๋ฉด ์ข‹์Œ.

 

 

 

8.์˜ค๋ฅ˜ ์ฝ”๋“œ๋ณด๋‹ค๋Š” ์˜ˆ์™ธ๋ฅผ ์‚ฌ์šฉ

์˜ค๋ฅ˜์ฝ”๋“œ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ˜•ํƒœ๋Š” ์œ„ 7๋ฒˆ์˜ ๋ช…๋ น์กฐํšŒ ๋ถ„๋ฆฌ ๊ทœ์น™์— ์–ด๊ธ‹๋‚จ.

 

if (deletePage(page) == E_OK) {

  if (registry.deleteRefrence(page.name) == E_OK) {

~~

  }

}

 

์ด๋ ‡๊ฒŒ ์ž‘์„ฑํ•˜๊ธฐ ๋ณด๋‹ค๋Š”

try {

  deletePage(page);

  registry.deleteReference(page.name);

}  catch {

}

 

์ด๋Ÿฐ์‹์œผ๋กœ Try/Catch๋กœ ์ถ”์ถœํ•ด์„œ ์‚ฌ์šฉํ•˜๋ฉด ์ข‹์Œ

 

 

public enum Error {

  OK,

  INVAILD,

  NO_SUCH,

  LOCKED

}

์ด๋Ÿฐ์‹์œผ๋กœ error enum์„ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ

์ˆ˜์ •์‚ฌํ•ญ์ด ์ƒ๊ธฐ๋ฉด Error ํด๋ž˜์Šค ์ „๋ถ€ ๋‹ค์‹œ ์ปดํŒŒ์ผํ•˜๊ณ  ๋ฐฐ์น˜ํ•ด์•ผํ•จ.

 

์˜ค๋ฅ˜ enum ๋Œ€์‹  ์˜ˆ์™ธ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ข‹์Œ

์ƒˆ ์˜ˆ์™ธ๋Š” Exception ํด๋ž˜์Šค์—์„œ ํŒŒ์ƒ๋จ

 

 

 

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

return์€ ํ•˜๋‚˜์—ฌ์•ผํ•˜๋ฉฐ ๋ฃจํ”„์•ˆ์—์„œ break, continue ์‚ฌ์šฉx

goto ๊ธˆ์ง€

 

 

์—ฌ๊ธฐ๊นŒ์ง€๊ฐ€ clean code ์ œ 3์žฅ

ํ•จ์ˆ˜๋ฅผ ์ž˜ ๋งŒ๋“œ๋Š” ๊ธฐ๊ต

์˜€์Šต๋‹ˆ๋‹ค.

 

๋Œ“๊ธ€