< 작게 만들자 > 

함수를 만들 때 가장 중요한 규칙은 크기다. “작게 만들어야 한다”는 것이다. 더 작다면 더 좋다. 요리 초보자가, 특별한 재료를 사용하는 50단계 요리보다, 간단한 재료로 만드는 5단계 요리를 만들어 보고 싶을 것이다. 마찬가지로, 함수도 단계가 적을수록 이해하기 쉽다.

public static String prepareDeliciousDish(Ingredients ingredients, boolean isVegan) {
    if (isGoodCombination(ingredients)) {
        mixIngredients(ingredients, isVegan);
    }
    return serveDish(ingredients);
}

< 간격을 일정하게 유지하자 >

요리할 때도 조리도구와 재료를 가까운 곳에 두는 것이 편리하듯이 코드도 마찬가지다. 만약 함수 안에 if, else 또는 while 같은 구문이 있다면, 그 안에 넣을 내용은 간단하게 한 줄 정도로 만들어보자. 그 한 줄에는 다른 함수를 호출하는 내용이 들어갈 수 있다. 이렇게 하면 더 읽기 쉬운 코드가 되고, 동시에 더 가까운 간격으로 구성된다.

< 함수 당 추상화 수준은 하나로 >

함수를 만들 때, 내부의 내용은 동일한 수준의 ‘추상화’를 가져야 한다. 한 그림에는 한 스타일로 그리는 것과 같다. 높은 수준의 개념과 세부적인 요소를 함께 섞으면, 누군가 그 그림을 볼 때 혼란스러울 것이다. 함수도 마찬가지로, 동일한 수준의 추상화를 유지하면 더 이해하기 쉽다.

< 위에서 아래로 읽혀야 한다 >

코드를 읽는 것은 마치 이야기를 읽는 것과 같아야 한다. 이야기를 읽을 때는 위에서 아래로 읽으며, 내용이 점점 풀어진다. 마찬가지로 코드도 그렇게 작성되면 좋다. 함수의 추상화 수준이 한 단계씩 점점 낮아지는 방향으로 코드를 작성하면 더 이해하기 쉽다.

쉽게 말해서, 먼저 개요를 읽고, 그다음에 세부사항을 읽는 방식이다. 이를 '내려가기 규칙'이라고 부른다.

< 스위치문 > 

스위치문은 여러 가지 경우의 수를 한 번에 처리할 수 있어 매우 유용하나, 잘못 사용하면 너무 복잡해질 수 있다. 예를 들어, 회사에서 직원들의 급여를 계산하는데 스위치문을 사용한다고 생각해 보자.

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);
    }
}


이 코드는 직원의 유형에 따라 다른 급여 계산 방식을 적용하는데, 새로운 직원 유형이 추가될 때마다 이 스위치문도 업데이트해야 하는 단점이 있다.  이 Switch문을 사용하면 복잡도가 급격히 늘어나는 문제가 있다.

[다형성]

이 문제를 해결할 수 있는 해결책이 있다. 그것은 바로 '다형성'을 이용하는 것이다. 다형성을 사용하면 하나의 코드로 다양한 경우를 처리할 수 있다.

"다형성"을 이용하면, 각 직원 유형마다 다른 계산 방식을 적용하는 클래스를 만들 수 있다. 그리고 이 클래스들을 'Employee'라는 공통 인터페이스를 통해 관리할 수 있다.

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;
}


이렇게 하면, 새로운 직원 유형이 추가되어도 스위치문을 업데이트할 필요가 없게 된다. 단지 새로운 클래스를 추가하기만 하면 된다. 

 

여기서 다형성이란? ↘

더보기

다형성(polymorphism)은 그리스어에서 유래한 말로, '많은(many)'와 '형태(shape)'의 합성어로서 '많은 형태를 가짐'을 의미한다. 컴퓨터 과학에서의 다형성은 한 가지의 인터페이스를 가지고 다양한 동작을 수행할 수 있는 능력을 뜻한다. 다시 말해, 한 가지 이름 아래에서 여러 가지 동작을 하는 것이다.

예를 들어, 동물원에 간다고 상상해 보자. 거기에는 사자, 원숭이, 펭귄 등 다양한 동물들이 있다. 이 동물들 모두 '동물'이라는 공통의 이름 아래에서 그들 각각의 동작(울음소리, 움직임 등)을 한다. 다형성이라는 마법은 바로 이것과 같다. 

[다형성을 이용한 코드]
앞에서 살펴본 직원 급여 계산 문제로 보자면. 여기서 각 직원 유형(COMMISSIONED, HOURLY, SALARIED)마다 다른 계산 방식을 적용해야 했다.

public abstract class Employee {
    public abstract boolean isPayday();
    public abstract Money calculatePay();
    public abstract void deliverPay(Money Pay);
}


먼저 'Employee'라는 공통 인터페이스를 만들고, 이 인터페이스에는 모든 직원이 가질 수 있는 메서드들을 정의한다. 이렇게 되면 각 직원 유형의 클래스는 이 'Employee' 인터페이스를 통해 서로 다른 급여 계산 방식을 구현할 수 있게 된다.

따라서 새로운 직원 유형이 추가되더라도 기존의 스위치문을 수정할 필요가 없게 된다. 단지 새로운 직원 유형 클래스를 만들어 'Employee' 인터페이스를 구현하기만 하면 되는 것이다.

이렇게 '다형성'이라는 것을 이용하면, 각 직원 유형마다 다른 계산 방식을 적용하는 동시에 코드의 복잡성을 크게 줄일 수 있다. 

< 숨겨야 가장 효과적 >

효과적으로 사용하려면, 스위치문을 숨겨야 한다. 이를 '추상 팩토리'라는 기법을 사용해서 가능하게 할 수 있다.

public class EmployeeFactoryImpl implements EmployeeFactory {
    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);
        }
    }
}


이렇게 스위치문을 팩토리 클래스 안에 숨겨두면, 다른 부분의 코드에서는 이 스위치문을 신경 쓸 필요가 없어진다. 

 

출처 : 출판사 인사이트북 / 클린 코드 / https://product.kyobobook.co.kr/detail/S000001032980

※저의 블로그에 있는 모든 참고 서적, 강의 내용은 출판사, 저자(혹은 편집자)에게 허락을 직접 맡고, 게시하는 글입니다.

 

Clean Code(클린 코드) | 로버트 C. 마틴 - 교보문고

Clean Code(클린 코드) | 프로그래머, 소프트웨어 공학도, 프로젝트 관리자, 팀 리더, 시스템 분석가에게 추천하는더 나은 코드를 만드는 책『Clean Code(클린 코드)』은 오브젝트 멘토(Object Mentor)의 동

product.kyobobook.co.kr

 

프로그래밍에서 가장 중요하면서도 자주 무시되는 요소 중 하나는 코드 내의 변수, 함수, 클래스의 이름이다. 적절한 이름은 코드의 가독성을 높이고 이해를 돕는다.

< 검색 가능한 이름 사용하기 >

코드에서 이름을 검색하는 것은 개발자의 일상 중 하나다. 그러나 너무 일반적인 이름이나, 너무 짧은 이름은 검색이 어렵게 만든다.

예를 들어, 7이라는 숫자를 상수로 사용했다고 가정해보자. 이 숫자를 찾으려면 수많은 파일과 수식에서 7이 사용된 모든 곳을 검색해야 한다. 동일한 이유로 'e'와 같은 일반적인 문자도 변수 이름으로 적합하지 않다.

그렇다면 어떤 이름이 검색하기 쉬울까? 바로 "길고 구체적인 이름"이다. 이런 이름을 사용하면 코드의 의미가 더욱 명확해지며, 해당 이름을 찾기도 훨씬 쉬워진다.

int realDaysPerIdealDay = 4;
const int WORK_DAYS_PER_WEEK = 5;
int sum = 0;

for (int j=0; j < NUMBER_OF_TASKS; j++) {
    int realTaskDays = taskEstimate[j] * realDaysPerIdealDay;
    int realTaskWeeks = (realTaskDays / WORK_DAYS_PER_WEEK);
    sum += realTaskWeeks;
}

 

위 코드에서는 sum이 유용하진 않지만 최소한 검색이 가능하다. 이름을 의미있게 지으면 함수가 길어지지만, realDaysPerIdealDay, WORK_DAYS_PER_WEEK와 같이 상세한 이름을 사용함으로 검색하기 쉽고, 코드가 무엇을 하는지 이해하기도 쉽다.

< 불필요한 인코딩 피하기 >

다음으로, 이름에 불필요한 인코딩을 포함시키지 않는 것이 중요하다. 이름에 인코딩을 넣으면 그만큼 이름을 이해하기 어려워지며, 이름을 바꾸는 것도 복잡해진다.

예를 들어, 'm_' 접두사를 붙이는 관습은 이제는 필요 없어졌다. 변수명 앞에 이런 접두사를 붙이면 코드를 읽는 사람이 불필요한 정보에 신경 쓰게 되고, 결국 코드의 가독성을 저해하게 된다.

public class Part {
    String description;

    void setDescription(String description) {
        this.description = description;
    }
}

 

여기에서는 description이라는 명확한 이름만을 사용하여 Part의 설명을 표현하였다.

또한, 클래스가 인터페이스인지 아닌지를 나타내는 'I'와 같은 접두사도 피하는 것이 좋다. 이는 클래스를 사용하는 사람에게 불필요한 정보를 제공하며, 가독성을 해칠 수 있다. 만약 인터페이스와 구현 클래스를 명확히 구분해야 한다면, 인터페이스는 단순한 이름을 사용하고, 구현 클래스 이름에 인코딩을 하는 것이 더 좋다.

public interface ShapeFactory { ... }

public class ShapeFactoryImp implements ShapeFactory { ... }


이렇게 하면 사용자는 단순히 ShapeFactory라는 이름으로 클래스를 사용하며, 인터페이스와 구현 클래스를 쉽게 구분할 수 있다.

< 단일 문자 변수명: 좋은 선택일까? >

한 번씩 우리는 코드에서 단일 문자로 이루어진 변수명을 발견하곤 한다. 예를 들어, 루프에서 반복 횟수를 세는 변수로 'i', 'j', 'k'를 사용하는 것이 대표적인 경우다. 하지만 이런 단일 문자 변수명은 국한된 상황에서만 적절하다고 할 수 있다. 즉, 루프 범위가 작고, 다른 이름과의 충돌이 없을 때에 적용할 수 있다.

그럼에도 불구하고, 단일 문자 변수명의 남발은 피해야 한다. 이유는 단순하다. 코드를 읽는 사람이 'c'라는 변수가 무엇을 의미하는지 기억하고 있어야 한다면, 이는 그 사람에게 부담을 주는 것이기 때문이다.

['똑똑함'의 함정에서 벗어나기]
프로그래머들은 대체로 똑똑하다. 하지만 이 똑똑함이 가끔 함정이 될 수 있다.  'r'이라는 변수가 호스트와 프로토콜을 제외한 소문자 URL을 나타내는 것을 항상 기억할 수 있다고 자신할지라도, 이는 명확하지 않은 코드를 만들어낼 수 있다.

전문적인 프로그래머는 자신의 능력을 잘 활용하여 다른 사람들이 쉽게 이해할 수 있는 코드를 작성한다. 이들은 '명료함'이 가장 중요하다는 사실을 잘 이해하고 있다.

[클래스 이름과 메서드 이름 잘 짓기]
클래스 이름은 명사나 명사구를 사용한다. 이는 클래스가 어떤 '것(thing)'을 나타내기 때문이다. 예를 들면, 'Customer', 'WikiPage', 'Account', 'AddressParser' 등이 있다. 'Manager', 'Processor', 'Data', 'Info' 같은 모호한 단어는 피하도록 하자.

반면에 메서드 이름은 동사나 동사구를 사용한다. 메서드는 어떤 행동을 나타내므로, 이름도 그러한 행동을 잘 설명해야 한다. 'postPayment', 'deletePage', 'save' 등이 좋은 예이다.

생성자의 이름을 지을 때는 조금 다른 접근 방식이 필요하다. 여러 생성자를 만들 때는, 각각의 차이점을 설명하는 이름을 가진 정적 팩토리 메서드를 사용하는 것이 좋다. 예를 들어, 'Complex.FromRealNumber(23.0)'처럼 사용하면, 생성자가 하는 일이 명확히 보인다.

 

 

 

 

 

 

출처 : 출판사 인사이트북 / 클린 코드 / https://product.kyobobook.co.kr/detail/S000001032980

※저의 블로그에 있는 모든 참고 서적,강의 내용은 출판사,저자(혹은 편집자)에게 허락을 직접 맡고, 게시하는 글 입니다.

 

Clean Code(클린 코드) | 로버트 C. 마틴 - 교보문고

Clean Code(클린 코드) | 프로그래머, 소프트웨어 공학도, 프로젝트 관리자, 팀 리더, 시스템 분석가에게 추천하는더 나은 코드를 만드는 책『Clean Code(클린 코드)』은 오브젝트 멘토(Object Mentor)의 동

product.kyobobook.co.kr

 

< 그릇된 정보를 피하자 >

코드에서 잘못된 정보를 제공하는 이름을 사용하면, 코드를 읽는 사람을 혼란스럽게 만들 수 있다. 예를 들어, 리스트가 아닌 것을 'accountList'라고 부르면, 이것은 프로그래머에게 잘못된 정보를 제공하는 것이다. 왜냐하면 프로그래머에게 'List'는 특별한 의미를 갖기 때문이다. 그래서 'accountGroup' 또는 'accounts'와 같이 명명하는 것이 더 좋다.

또한, 아주 비슷한 이름을 사용하면 혼란을 초래할 수 있다. 예를 들어, 'XYZControllerForEfficientHandlingOfStrings'와 'XYZControllerForEfficientStorageOfStrings'는 아주 비슷한 이름이다. 둘 사이의 차이를 구별하기 어렵다. 비슷한 이름을 사용하는 것보다는, 서로 다른 개념은 서로 다른 이름을 사용하는 것이 좋다.

< 의미있게 구분하자 >

코드 내에서 두 개념을 구분하기 위해 이름을 지을 때, 의미있게 구분하는 것이 중요하다. 연속된 숫자를 이름에 추가하거나 (예: a1, a2, ...), 불용어를 추가하는 것은 좋지 않다. 이름이 다르다면, 대표하는 의미도 달라져야 하기 때문이다.

이름에 'variable', 'table' 등의 단어를 추가하는 것 역시 피해야 한다. 이러한 단어는 불필요한 중복을 초래하고, 추가적인 정보를 제공하지 못한다. 예를 들어, 'NameString'이 'Name'보다 무엇이 더 나은가?  아무런 차이가 없다.

< 발음하기 쉬운 이름을 사용하자 >

마지막으로, 이름을 지을 때 발음하기 쉬운 이름을 선택하는 것이 좋다. 우리의 뇌는 단어를 처리하는 데 많은 부분을 사용하며, 그 단어들은 발음이 가능한 것이다. 그래서 발음하기 쉬운 이름을 사용하면, 우리는 코드에 대해 토론하기도 쉽고, 이해하기도 쉽다.

예를 들어, 다음의 두 코드 중 어느 쪽이 더 이해하기 쉬울까?

class DtaRcrd102 {
    private Date genydhms;
    private Date modymdhms;
    private final String pszqint = "102";
};


vs

class Customer {
    private Date generationTimestamp;
    private Date modificationTimestamp;
    private final String recordId = "102";
};



두 번째 코드가 더 이해하기 쉽고, 의미가 명확하다. 그리고 이야기하거나 토론하기에도 훨씬 쉽다.

결국, 이름을 지을 때는 잘못된 정보를 제공하지 않도록 주의하고, 서로 다른 개념에는 서로 다른 이름을 지어야 한다. 또한, 이름은 가능하면 발음하기 쉬워야 한다. 이렇게 하면, 우리의 코드는 읽기 쉽고 이해하기 쉬워질 것이다.

 

 

 

출처 : 출판사 인사이트북 / 클린 코드 / https://product.kyobobook.co.kr/detail/S000001032980

※저의 블로그에 있는 모든 참고 서적,강의 내용은 출판사,저자(혹은 편집자)에게 허락을 직접 맡고, 게시하는 글 입니다.

 

Clean Code(클린 코드) | 로버트 C. 마틴 - 교보문고

Clean Code(클린 코드) | 프로그래머, 소프트웨어 공학도, 프로젝트 관리자, 팀 리더, 시스템 분석가에게 추천하는더 나은 코드를 만드는 책『Clean Code(클린 코드)』은 오브젝트 멘토(Object Mentor)의 동

product.kyobobook.co.kr

 

코딩을 하다 보면 변수, 함수, 클래스, 패키지 등 다양한 요소에 이름을 붙여야 할 상황이 빈번히 발생한다. 사실, 이름을 잘 지어주는 것만으로도 코드의 가독성과 유지 보수성이 크게 향상되곤 한다.

< 의도를 노출하는 이름 선택하기 >

이름을 지을 때 핵심은 '의도를 명확하게 표현'하는 것이다. 그럼 이게 무슨 말인지 예시를 통해 살펴보자.

int d; // 경과 시간(단위 : 날짜)

 

위의 코드에서 'd'라는 이름은 경과 시간을 의미한다는 것을 전혀 알려주지 않는다. 따라서 아래처럼 이름을 수정하면 더 좋을 것이다.

int elapsedTimeInDays;

 

이렇게 이름을 바꾸면 해당 변수가 어떤 정보를 담고 있는지 한 눈에 알 수 있게 된다.

< 예제로 보는 이름의 중요성 >

아래와 같은 함수가 있다고 가정해보자.

public List<int[]> getThem() {
    List<int[]> list1 = new ArrayList<int[]>();
    for(int[] x : theList)
        if (x[0] == 4)
            list1.add(x);
    return list1;
}

 

이 함수의 코드는 단순하지만 그 의도를 알기 어렵다. 이름이 그다지 도움이 되지 않기 때문이다.

만약 이 코드가 지뢰찾기 게임의 일부라고 하면, theList는 게임 판을 의미하고, x[0] == 4는 깃발이 표시된 셀을 찾는 것을 의미하게 된다. 이런 의도를 이름에 반영하면 아래처럼 코드를 개선할 수 있다.

public List<int[]> getFlaggedCells() {
    List<int[]> flaggedCells = new ArrayList<int[]>();
    for(int[] cell : gameBoard)
        if (cell[STATUS_VALUE] == FLAGGED)
            flaggedCells.add(cell);
    return flaggedCells;
}


이처럼 이름만 바꾸어도 코드가 무엇을 하는지 이해하기 훨씬 쉬워진다. 심지어, Cell이라는 클래스를 만들어 isFlagged()라는 메서드를 사용하면 더욱 명확해진다.

public List<Cell> getFlaggedCells() {
    List<Cell> flaggedCells = new ArrayList<Cell>();
    for (Cell cell : gameBoard)
        if (cell.isFlagged())
            flaggedCells.add(cell);
    return flaggedCells;
}

단순히 이름만 고쳤는데도 함수가 하는 일을 이해하기 쉬워졌다. 바로 이것이 좋은 이름이 주는 위력이라고 볼 수 있다.

 

 

출처 : 출판사 인사이트북 / 클린 코드 / https://product.kyobobook.co.kr/detail/S000001032980

※저의 블로그에 있는 모든 참고 서적,강의 내용은 출판사,저자(혹은 편집자)에게 허락을 직접 맡고, 게시하는 글 입니다.

 

Clean Code(클린 코드) | 로버트 C. 마틴 - 교보문고

Clean Code(클린 코드) | 프로그래머, 소프트웨어 공학도, 프로젝트 관리자, 팀 리더, 시스템 분석가에게 추천하는더 나은 코드를 만드는 책『Clean Code(클린 코드)』은 오브젝트 멘토(Object Mentor)의 동

product.kyobobook.co.kr

 

+ Recent posts