๐Ÿ“‚ ๊ฐœ๋ฐœ ์„œ์ /ํด๋ฆฐ ์ฝ”๋“œ

ํด๋ฆฐ ์ฝ”๋“œ(Clean Code) - 7์žฅ

Amenable 2023. 1. 10. 22:12

7์žฅ. ์˜ค๋ฅ˜ ์ฒ˜๋ฆฌ ๐Ÿฐ

  ๋ฌด์–ธ๊ฐ€ ์ž˜๋ชป๋  ๊ฐ€๋Šฅ์„ฑ์€ ๋Š˜ ์กด์žฌํ•œ๋‹ค. ๋ฌด์–ธ๊ฐ€ ์ž˜๋ชป๋˜๋ฉด ๋ฐ”๋กœ ์žก์„ ์ฑ…์ž„์€ ๋ฐ”๋กœ ์šฐ๋ฆฌ ํ”„๋กœ๊ทธ๋ž˜๋จธ์—๊ฒŒ ์žˆ๋‹ค. 

 

  • ํ˜ธ์ถœ์ž๋ฅผ ๊ณ ๋ คํ•ด ์˜ˆ์™ธ ํด๋ž˜์Šค๋ฅผ ์ •์˜ํ•˜๋ผ.
// ์ค‘๋ณต์ด ์‹ฌํ•˜์—ฌ ๋‚˜์œ ์ฝ”๋“œ
ACMEPort port = new ACMEPort(12);

try {
    port.open();
} catch (DeviceResponseException e) {
    reportPortError(e);
    logger.log("Device response exception", e);
} catch (ATM1212UnlockedException e) {
    reportPortError(e);
    logger.log("Unlock exception", e);
} catch (GMXError e) {
    reportPortError(e);
    logger.log("Device response exception");
} finally {
    ...
}
// ๊ฐ์‹ธ๋Š” ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•œ ์ข‹์€ ์ฝ”๋“œ
LocalPort port = new LocalPort(12);
try {
    port.open();
} catch (PortDeviceFailure e) {
    reportError(e);
    logger.log(e.getMessage(), e);
} finally {
    ...
}

----------------------------------

public class LocalPort {
    private ACMEPort innerPort;
    
    public LocalPort(int portNumber) {
        innerPort = new ACMEPort(portNumber);
    }
    
    public void open() {
        try {
            innerPort.open();
        } catch (DeviceResponseException e) {
            throw new PortDeviceFailure(e);
        } catch (ATM1212UnlockedException e) {
            throw new PortDeviceFailure(e);
        } catch (GMXError e) {
            throw new PortDeviceFailure(e);
        }
    }
    ...
}

  LocalPort ํด๋ž˜์Šค์ฒ˜๋Ÿผ ACMEPort๋ฅผ ๊ฐ์‹ธ๋Š” ํด๋ž˜์Šค๋Š” ๋งค์šฐ ์œ ์šฉํ•˜๋‹ค. ์‹ค์ œ๋กœ ์™ธ๋ถ€ API๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ๊ฐ์‹ธ๊ธฐ ๊ธฐ๋ฒ•์ด ์ตœ์„ ์ด๋‹ค. ์™ธ๋ถ€ API๋ฅผ ๊ฐ์‹ธ๋ฉด ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ํ”„๋กœ๊ทธ๋žจ ์‚ฌ์ด์—์„œ ์˜์กด์„ฑ์ด ํฌ๊ฒŒ ์ค„์–ด๋“ ๋‹ค. ๋‚˜์ค‘์— ๋‹ค๋ฅธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ๊ฐˆ์•„ํƒ€๋„ ๋น„์šฉ์ด ์ ๋‹ค. ๋˜ํ•œ ๊ฐ์‹ธ๊ธฐ ํด๋ž˜์Šค์—์„œ ์™ธ๋ถ€ API๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๋Œ€์‹  ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ๋„ฃ์–ด์ฃผ๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ ํ”„๋กœ๊ทธ๋žจ์„ ํ…Œ์ŠคํŠธํ•˜๊ธฐ๋„ ์‰ฌ์›Œ์ง„๋‹ค.

  ๊ทธ๋ฆฌ๊ณ   ๊ฐ์‹ธ๊ธฐ ๊ธฐ๋ฒ•์„ ์‚ฌ์šฉํ•˜๋ฉด ํŠน์ • ์—…์ฒด๊ฐ€ API๋ฅผ ์„ค๊ณ„ํ•œ ๋ฐฉ์‹์— ๋ฐœ๋ชฉ ์žกํžˆ์ง€ ์•Š๋Š”๋‹ค. ํ”„๋กœ๊ทธ๋žจ์ด ์‚ฌ์šฉํ•˜๊ธฐ ํŽธ๋ฆฌํ•œ API๋ฅผ ์ •์˜ํ•˜๋ฉด ๊ทธ๋งŒ์ด๋‹ค.

  ํ”ํžˆ ์˜ˆ์™ธ ํด๋ž˜์Šค๊ฐ€ ํ•˜๋‚˜๋งŒ ์žˆ์–ด๋„ ์ถฉ๋ถ„ํ•œ ์ฝ”๋“œ๊ฐ€ ๋งŽ๋‹ค. ์˜ˆ์™ธ ํด๋ž˜์Šค์— ํฌํ•จ๋œ ์ •๋ณด๋กœ ์˜ค๋ฅ˜๋ฅผ ๊ตฌ๋ถ„ํ•ด๋„ ๊ดœ์ฐฎ์€ ๊ฒฝ์šฐ๊ฐ€ ๊ทธ๋ ‡๋‹ค. ํ•œ ์˜ˆ์™ธ๋Š” ์žก์•„๋‚ด๊ณ  ๋‹ค๋ฅธ ์˜ˆ์™ธ๋Š” ๋ฌด์‹œํ•ด๋„ ๊ดœ์ฐฎ์€ ๊ฒฝ์šฐ๋ผ๋ฉด ์—ฌ๋Ÿฌ ์˜ˆ์™ธ ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

 

  • ์ •์ƒ ํ๋ฆ„์„ ์ •์˜ํ•˜๋ผ
      ์œ„์—์„œ ์‚ดํŽด๋ณธ ๊ฒƒ์ฒ˜๋Ÿผ ์™ธ๋ถ€ API๋ฅผ ๊ฐ์‹ธ ๋…์ž์ ์ธ ์˜ˆ์™ธ๋ฅผ ๋˜์ง€๊ณ , ์ฝ”๋“œ ์œ„์— ์ฒ˜๋ฆฌ๊ธฐ๋ฅผ ์ •์˜ํ•ด ์ค‘๋‹จ๋œ ๊ณ„์‚ฐ์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค. ๋Œ€๊ฐœ๋Š” ๋ฉ‹์ง„ ์ฒ˜๋ฆฌ ๋ฐฉ์‹์ด์ง€๋งŒ, ๋•Œ๋กœ๋Š” ์ค‘๋‹จ์ด ์ ํ•ฉํ•˜์ง€ ์•Š์€ ๋•Œ๋„ ์žˆ๋‹ค. ์ด๋Ÿฐ ๊ฒฝ์šฐ ํŠน์ˆ˜ ์‚ฌ๋ก€ ํŒจํ„ด(special case pattern)์„ ์‚ฌ์šฉํ•˜์ž. ์ด๋Š” ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค๊ฑฐ๋‚˜ ๊ฐ์ฒด๋ฅผ ์กฐ์ž‘ํ•ด ํŠน์ˆ˜ ์‚ฌ๋ก€๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ํด๋ž˜์Šค๋‚˜ ๊ฐ์ฒด๊ฐ€ ์˜ˆ์™ธ์ ์ธ ์ƒํ™ฉ์„ ์บก์Аํ™”ํ•ด์„œ ์ฒ˜๋ฆฌํ•˜๋ฏ€๋กœ ํด๋ผ์ด์–ธํŠธ ์ฝ”๋“œ๊ฐ€ ์˜ˆ์™ธ์ ์ธ ์ƒํ™ฉ์„ ์ฒ˜๋ฆฌํ•  ํ•„์š”๊ฐ€ ์—†์–ด์ง„๋‹ค.
      ์˜ˆ๋ฅผ ์‚ดํŽด๋ณด์ž. ์ง์›์ด ์‹๋น„๋ฅผ ๋น„์šฉ์œผ๋กœ ์ฒญ๊ตฌํ–ˆ๋‹ค๋ฉด ์ง์›์ด ์ฒญ๊ตฌํ•œ ์‹๋น„๋ฅผ ์ด๊ณ„์— ๋”ํ•œ๋‹ค๊ณ  ํ•˜์ž. ๋งŒ์•ฝ ์‹๋น„๋ฅผ ๋น„์šฉ์œผ๋กœ ์ฒญ๊ตฌํ•˜์ง€ ์•Š์•˜๋‹ค๋ฉด ์ผ์ผ ๊ธฐ๋ณธ ์‹๋น„๋ฅผ ์ด๊ณ„์— ๋”ํ•œ๋‹ค.
try {
    MealExpenses expenses = expenseReportDAO.getMeals(employee.getID());
    m_total += expense.getTotal();
} catch(MealExpenseNotFound e) {
    m_total += getMealPerDiem();
}

  ์œ„์˜ ์ฝ”๋“œ๋Š” ์˜ˆ์™ธ๊ฐ€ ๋…ผ๋ฆฌ๋ฅผ ๋”ฐ๋ผ๊ฐ€๊ธฐ ์–ด๋ ต๊ฒŒ ๋งŒ๋“ ๋‹ค. ํŠน์ˆ˜ ์ƒํ™ฉ์„ ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ๊ฐ„๊ฒฐํ•œ ์ฝ”๋“œ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

MealExpenses expenses = expenseReportDAO.getMeals(employee.getID());
m_total += expenses.getTotal();

----------------------------------------

public class PerDiemMealExpenses implements MealExpenses {
    public int getTotal() {
        // ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์ผ์ผ ๊ธฐ๋ณธ ์‹๋น„๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
    }
}

 

  • null์„ ์ „๋‹ฌํ•˜์ง€ ๋งˆ๋ผ
      ๋ฉ”์„œ๋“œ์—์„œ null์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ฐฉ์‹๋„ ๋‚˜์˜์ง€๋งŒ ๋ฉ”์„œ๋“œ๋กœ null์„ ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ์‹์€ ๋” ๋‚˜์˜๋‹ค. ์ •์ƒ์ ์ธ ์ธ์ˆ˜๋กœ null์„ ๊ธฐ๋Œ€ํ•˜๋Š” API๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด ๋ฉ”์„œ๋“œ๋กœ null์„ ์ „๋‹ฌํ•˜๋Š” ์ฝ”๋“œ๋Š” ์ตœ๋Œ€ํ•œ ํ”ผํ•œ๋‹ค.
public class MetricsCalculator {
    public double xProjection(Point p1, Point p2) {
        return (p2.x - p1.x) * 1.5;
    }
    ...
}

  ํ•ด๋‹น ์ฝ”๋“œ์—์„œ calculator.xProjection(null, new Point(12, 13)); ์„ ์‹คํ–‰ํ•˜๋ฉด ๋‹น์—ฐํžˆ NullPointerException์ด ๋ฐœ์ƒํ•œ๋‹ค.

public class MetricsCalculator {
    public double xProjection(Point p1, Point p2) {
        if (p1 == null || p2 == null) {
            throw InvalidArgumentException(
                    "Invalid argument for MetricsCalculator.xProjection");
        }
        return (p2.x - p1.x) * 1.5;
    }
}

  NullPointerException ๋ณด๋‹ค๋Š” ์กฐ๊ธˆ ๋‚˜์„์ง€๋„ ๋ชจ๋ฅด๊ฒ ์ง€๋งŒ ์œ„ ์ฝ”๋“œ๋Š” InvalidArgumentException์„ ์žก์•„๋‚ด๋Š” ์ฒ˜๋ฆฌ๊ธฐ๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

public class MetircsCalculator {
    public double xProjection(Point p1, Point p2) {
        assert p1 != null : "p1 should not be null";
        assert p1 != null : "p2 should not be null";
        return (p2.x - p1.x) * 1.5;
    }
}

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

 

7์žฅ ๊ฒฐ๋ก 

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