외부 코드 사용하기 외부 코드를 내 코드에서 호출하는 부분을 경계라고 한다. (아는 코드와 모르는 코드를 분리하는 것도 경계라고 한다.) 예를 들어 Map 인터페이스가 변할 경우 수정할 코드가 상당히 많아진다. Map과 같은 경계 인터페이스를 이용할 때는 이를 이용하는 클래스나 클래스 계열 밖으로 노출되지 않도록 주의한다. Map 인스턴스를 공개 APi의 인수로 넘기거나 반환값으로 사용하지 않는다. 학습 테스트는 공짜 이상이다. 학습 테스트란 먼저 간단한 테스트 케이스를 작성해 외부 코드를 익히는 것을 말한다. 학습 테스트는 비용이 들지 않으며, 필요한 지식만 확보할 수 있다. 깨끗한 경계 경계에 위치하는 코드는 깔끔히 분리한다. 기대치를 정의하는 테스트 케이스도 작성한다. 통제 불가능한 외부 패키지보다..
오류 코드보다 예외를 사용하라 오류 코드를 사용하면 호출자 코드가 복잡해진다. 반면 예외를 던지게 되면 논리와 오류 처리 코드가 뒤섞이지 않아 깔끔해진다. Try-Catch-Finally 문부터 작성하라 try 블록은 트랜잭션과 비슷하다. try 블록에서 무슨 일이 생기든 catch 블록은 프로그램 상태를 일관성 있게 유지해야 한다. try 블록에서 무슨 일이 생기든지 호출자가 기대하는 상태를 정의하기 쉬워진다. 미확인 예외를 사용하라 확인된 예외는 OCP를 위반한다. → 대규모 시스템의 경우 최하위 단계에서 최상위 단계까지 연쇄적인 수정 발생 확인된 예외는 모든 함수가 최하위 함수에서 던지는 예외를 알아야 하므로 캡슐화가 깨진다. 확인된 예외와 미확인된 예외의 차이는 아래 글에서 확인할 수 있다. ht..
자료 추상화 구현을 감추려면 추상화가 필요하다. 추상 인터페이스를 제공해 사용자가 구현을 모른 채 자료의 핵심을 조작할 수 있어야 한다. public interface Vehicle { double getFuelTankCapacityInGallons(); double getGallonsOfGasoline(); } 위 인터페이스는 자동차 연료 상태를 구체적인 숫자 값으로 알려준다. 두 함수가 변숫값을 읽어 반환할 뿐이라는 사실이 거의 확실하다. public interface Vehicle { double getPercentFuelRemaining(); } 반면에 위 인터페이스는 자동차 연료 상태를 백분율이라는 추상적인 개념으로 알려준다. 정보가 어디서 오는지 전혀 드러나지 않는다. 자료를 세세하게 공개하기..
형식을 맞추는 목적 지금 구현한 기능이 다음 버전에서 바뀔 확률은 아주 높다. 지금 구현한 코드의 가독성은 앞으로 바뀔 코드의 품질에 지대한 영향을 미친다. 처음 잡아놓은 구현 스타일과 가독성 수준은 유지보수 용이성과 확장성에 계속 영향을 미친다. 적절한 행 길이를 유지하라 500줄을 넘지 않고 대부분 200줄 정도인 파일로도 커다란 시스템을 구축할 수 있다. 일반적으로 큰 파일보다 작은 파일이 이해하기 쉽다. 여러 프로젝트를 조사해 보면 평균 파일 크기는 약 65줄이다. 신문 기사처럼 작성하라 첫 문단은 전체 기사 내용을 요약한다. 쭉 읽으며 내려가면 세세한 사실이 조금씩 드러난다. 소스 파일 이름은 간단하면서도 설명이 가능하게 짓는다. 소스 파일 첫 부분은 고차원 개념과 알고리즘을 설명한다. 마지막에..
나쁜 코드에는 주석을 달지 마라. 새로 짜라 - 브라이언 W. 커니핸, P.J. 플라우거 주석이 안 좋은 이유 주석은 오래될수록 코드에서 멀어진다. 주석을 유지하고 보수하기란 현실적으로 불가능하다. 처음부터 주석이 없는 방향으로 코드를 구현하는 것이 좋다. 부정확한 주석은 아예 없는 주석보다 훨씬 더 나쁘다. 그러므로 우리는 주석을 가능한 줄이도록 노력해야 한다. 주석은 나쁜 코드를 보완하지 못한다 코드에 주석을 추가하는 이유는 코드의 품질이 나쁘기 때문이다. 난장판으로 어질러 놓은 코드를 주석으로 설명할 시간에 난장판을 깨끗이 치우자 코드로 의도를 표현하자 // 직원에게 복지 혜택을 받을 자격이 있는지 검사한다. if ((employee.flags & HOURLY_FLAG) && (employee.ag..
어떤 프로그램이든 가장 기본적인 단위는 함수이다. 때문에 함수를 잘 만드는 것은 매우 중요하다! 작게 만들어라 일반적으로 5줄보다 짧아야 한다. 하지만 5줄로 함수를 구현하는 것은 쉽지 않기 때문에 연습해야 한다. 중첩 구조가 생길 만큼 함수가 커져서는 안 된다. 한 가지만 해라 함수는 한 가지를 해야 하며, 그 한 가지를 잘해야 한다. 지정된 함수 이름 아래에서 추상화 수준이 하나인 단계만 수행해야 한다. 의미 있는 이름으로 다른 함수를 추출할 수 있다면 그 함수는 여러 작업을 하고 있는 것이다. 추상화 수준은 하나로 함수가 한 가지 작업만 하려면 함수 내 모든 문장의 추상화 수준이 동일해야 한다. 근본 개념과 세부사항이 한 번 뒤섞이면 함수에 세부사항이 끝없이 추가된다. 하나의 함수를 구현하고 난 뒤..
의도를 분명히 밝혀라 좋은 이름을 지으려면 시간이 걸리지만 좋은 이름으로 절약하는 시간이 훨씬 더 많다. 변수나 함수 그리고 클래스 이름은 아래의 질문에 모두 답할 수 있어야 한다. 변수(혹은 함수나 클래스)의 존재 이유는? 수행 기능은? 사용 방법은? int d; int elapsedTimeInDays; int daysSinceCreation; int daysSinceModification; int fileAgeInDays; 위 변수 대신 아래 변수처럼 의도가 드러나는 이름을 사용하면 코드 이해와 변경이 쉬워진다. public List getThem(){ List list1 = new ArrayList(); for(int []x : theList){ if(x[0] == 4){ list1.add(x); ..
어째서 나쁜 코드를 짜는가 제대로 짤 시간이 없어서? 서두르느라? 우리는 프로그램이 돌아간다는 사실에 안도한다. 돌아가는 쓰레기에 스스로 위로를 하며 나중에 돌아와 고치겠다는 다짐을 한다. 하지만 나중은 결코 오지 않는다. - 르블랑의 법칙 中 나쁜 코드로 치르는 대가 나쁜 코드가 쌓일수록 팀 생산성은 떨어진다. 그때가 되면 관리팀에서 복구를 시도한다. 하지만 복구를 위해 추가된 인력과 무관하게 변경과 설계 의도를 구분하지 못해 나쁜 코드를 더 많이 양산하게 된다. 코드가 엉망이면 몇 시간으로 예상한 업무가 몇 주가 되기도 한다. 순식간에 나쁜 코드로 전락되는 것의 책임은 상사의 문제가 아니다. 프로그래머의 문제이다. 때문에 기한을 맞추며 빨리 가는 유일한 방법은 최대한 깨끗한 코드를 유지하는 것이다. ..