제육's 휘발성 코딩
Published 2021. 9. 6. 17:20
[Clean Code] Chap3. 함수 Book/Clean Code
반응형

SOLID

  • 객체지향 설계의 5가지 원칙
  • SRP : 단일 책임 원칙
    • 한 클래스는 하나의 책임만 가져야 한다.
    • 클래스는 하나의 기능만 가지며, 어떤 변화에 의해 클래스를 변경 해야 하는 이유는 오직 하나뿐이어야 한다.
    • SRP로 책임이 분명해지기 때문에, 변경에 의한 연쇄작용에서 자유로워 질 수 있다.
    • 가독성 향상과 유지보수가 용이해진다.
    • 실전에서는 쉽지 않지만 늘 상기해야 한다.

함수1

  • OCP : 개방-폐쇄 원칙
    • 소프트웨어 요소는 확장에는 열려 있으나 변경에는 닫혀있어야 한다.
    • 변경을 위한 비용은 가능한 줄이고, 확장을 위한 비용은 극대화 한다.
    • 요구사항의 변경이나 추가사항이 발생하더라도, 기존 구성요소에는 수정이 일어나지 않고, 기존 구성 요소를 쉽게 확장해서 재사용한다.
    • 객체지향의 추상화와 다형성을 활용한다.

함수2

  • LSP : 리스코프 치환 원칙
    • 서브 타입은 언제나 기반 타입으로 교체할 수 있어야 한다.
    • 서브 타입은 기반 타입이 약속한 규약 (접근제어자, 예외 포함)을 지켜야 한다.
    • 클래스 상속, 인터페이스 상속을 이용해 확장성을 획득
    • 다형성과 확장성을 극대화하기 위해 인터페이스를 사용하는 것이 더 좋다.
    • 합성(composition)을 이용할 수도 있다.

함수3

  • ISP : 인터페이스 분리 원칙

함수4

  • 자신이 사용하지 않는 인터페이스는 구현하지 말아야 한다.
  • 가능한 최소한의 인터페이스만 구현
  • SRP가 클래스의 단일 책임이라면, ISP는 인터페이스의 단일 책임
  • DIP : 의존성 역전 원칙
    • 상위 모델은 하위 모델에 의존하면 안된다. 둘 다 추상화에 의존해야 한다.
    • 실제 사용관계는 그대로지만, 추상화를 매개로 메시지를 주고 받으면서 관계를 느슨하게 한다.

imageimage

*카드 결제 시스템을 구현한다고 가정했을 때 카드사별 조립을 위한 추상화가 필요하다. *

간결한 함수

public static String renderPageWithSetupsAndTeardowns(PageData pageData, boolean isSuite) {
    boolean isTestPage = pageData.hasAttribute("Test");
    if (isTestPage) {
        WikiPage testPage = pageData.getWikiPage();
        StringBuffer newPageContent = new StringBuffer();
        includesSetupPages(testPage, newPageContent, isSuite);
        newPageContent.append(pageData.getContent());
        includeTeardownPages(testPage, newPageContent, isSuite);
        pageData.setContent(newPageContent.toString());
    }
    return pageData.getHtml();
}

해당 코드는 함수가 길고, 여러가지 기능이 섞여있다.

public static String renderPageWithSetupsAndTeardowns(PageData pageData, boolean isSuite) {
    if (isTestPage(pageData))
        includeSetupAndTearDownPages(pageData, isSuite);
    return pageData.getHtml();
}

작게 쪼갠다. 함수 내 추상화 수준을 동일하게 맞춘다.

public Money calculatePay(Employee e) throws InvalidEmployeeType {
    switch (e.type) {
        case COMMISSIONED:
            return calculateCommissionPay(e);
        case HOURLY :
            return calculateHourlyPay(e);
        default :
            throws new InvalidEmployeeType(e.type);
    }
}

한 메서드에서 계산도 하고, Money도 생성한다. 두가지 기능을 모두 하고 있는 상황은 적절하지 않다.

public abstract class Employee {
    public abstract boolean isPayday();
    public abstract Money calculatePay();
    public abstract void deliveryPay(Money pay);
}

public interface EmployeeFactory {
    public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType;
}

public class EmployeeFactoryImpl implements EmployeeFactory {
    public Employee makeEmployee(EmployeeRecord r) thorws InvalidEmployeeType {
        switch(r.type) {
                // ... 이하동일
        }
    }
}

한 가지만 하기(SRP), 변경에 닫게 만들기 (OCP)를 적용하여 계산과, 타입관리를 분리하였고

타입에 대한 처리는 최대한 Factory 내에서만 처리할 수 있도록 적용

함수 인수

  • 인수의 갯수는 0~2개가 적당하다.
  • 3개 이상일 경우?

image

안전한 함수

  • 안전한 함수란 부수 효과가 없는 함수를 의미한다.
  • 부수효과 : 값을 반환하는 함수가 외부 상태를 변경하는 경우

image

Session.initialize()는 함수와 관계없는 외부 상태를 변경시킨다.

함수 리팩터링

image

테스트 과정을 거치면서 리팩터링 하자.

 


본 포스팅은 Clean Code (Robert C. Martin), 제로베이스-한달한권(클린코드)를 참고하여 작성하였습니다.

반응형
profile

제육's 휘발성 코딩

@sasca37

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요! 맞구독은 언제나 환영입니다^^