동시성은 결합을 없애는 전략. 무엇(what)과 언제(when)을 분리하는 전략인데, 이렇게 하면 애플리케이션 구조와 효율이 극적으로 나아짐. 예를 들어, 웹 애플리케이션이 표준으로 사용하는 서블릿 모델에서는 원칙적으로 각 서블릿 스레드는 다른 서블릿 스레드와 무관하게 자신만의 세상에서 돌아감. 동시성을 정확히 구현하도록 해야하는 이유는 구조적 이점, 응답 시간과 작업 처리량 개선 등의 요구사항이 있기 때문. 1. 동시성은 때로 성능을 높여준다. 2. 단일 스레드 시스템과 다중 스레드 시스템은 설계가 판이하게 다르다. 3. 동시성은 다소 부하를 유발한다.(코드도 더 짜야 한다.) 4. 동시성은 복잡하다. 5. 일반적으로 동시성 버그는 재현하기 어렵다. 6. 동시성을 구현하려면 흔히 근본적인 설계 전략을 ..
켄트 벡이 제시한 단순한 설계 규칙 네 가지(중요도 순) 1. 모든 테스트를 실행한다. 의도한 대로 돌아가는 시스템을 내놓아야 하는데, 의도한 대로 돌아가는지 검증하기 위해 테스트를 시행하고, 이 테스트를 항상 통과하는 시스템을 만들어야 한다. 2. 중복을 없앤다., 3. 프로그래머 의도를 표현한다., 4. 클래스와 메서드 수를 최소로 줄인다. 위의 2~4를 한마디로 표현하자면 리팩터링이다. 코드를 몇 줄 추가할 때마다 잠시 멈추고 새로 추가하려는 코드가 설계 품질을 낮추는지 테스트 케이스로 확인한다. 소프트웨어 프로젝트 비용 중 대다수가 장기적인 유지보수에 들어가는데, 유지보수 개발자가 시스템을 이해하기 쉽도록 하려면 아래의 규칙을 지키면 된다. 1. 좋은 이름을 선택할 것 2. 함수와 클래스 크기를 ..
소프트웨어 시스템은 준비 과정과 런타임 로직을 분리해야 한다. public Service getService() { if (service == null) service = new ServiceImpl(...); return service; } 초기화 지연(Lazy Initialization) 혹은 계산 지연(Lazy Evaluation) 기법 실제로 필요해지기 전까진 객체를 생성하지 않으므로 불필요한 부하가 없고, 어떤 경우에도 null 포인터를 반환하지 않는다. 하지만 의존성의 문제도 있고, 일반 런타임 로직에다 객체 생성 로직을 섞어놓은 탓에 모든 실행 경로도 테스트해야 함. -> 단일 책임원칙을 깬다! 의존성 주입(Dependency Injection) : 사용과 제작을 분리하는 강력한 매커니즘 중..
클래스를 정의할 때, 1. static public 변수 2. static private 변수 3. private 인스턴스 변수 4. public 인스턴스 변수 -> 필요한 경우가 거의 없다. 5. public 함수 6. private 함수는 자신을 호출하는 공개 함수 직후에. 이런 순으로 체계를 잡는다. 추상화 단계가 순차적으로 내려간다. 클래스를 만들 때 유념해야 할 점은 클래스의 크기는 작아야 한다는 것이다. 그렇다면 얼마나 작게 만들어야 할까? SRP에 따라 한 클래스에 하나의 책임이 있도록 해야 한다. 자잘하게 책임을 나누면 큰 그림을 이해하기 어렵다고 걱정하지만, 작은 클래스가 많건 큰 클래스가 몇 개뿐이건 돌아가는 부품의 수는 결국 비슷해진다. 그러므로 큰 상자에 여러 도구를 넣고 쓸 것인지..
TDD 법칙 세 가지 1. 실패하는 단위 테스트를 작성할 때까지 실제 코드를 작성하지 않는다. 2. 컴파일은 실패하지 않으면서 실행이 실패하는 정도로만 단위 테스트를 작성한다. 3. 현재 실패하는 테스트를 통과할 정도로만 실제 코드를 작성한다. 주의할 점은 실제 코드와 맞먹을 정도로 많은 테스트 코드는 관리가 힘들다는 것이다. 실제 코드가 변경됨에 따라 테스트 코드도 변화해야 하는데 테스크 코드가 복잡하고, 지저분할수록 테스트 코드에 대한 부담이 늘어난다. 그렇다면 깨끗한 테스트 코드를 유지하려면 어떻게 해야 할까? 명료성, 단순성, 풍부한 표현력으로 가독성을 높이면 된다. 그리고 또 주의할 점은 실제 환경에서는 절대로 안 되지만 테스트 환경에서는 전혀 문제없는 방식이 있을 수도 있다는 것이다. 실제 환..
하나의 프로덕트를 만든다고 할 때, 처음부터 끝까지 전부 직접 개발하는 경우는 드물다. 외부 패키지, API, 프레임워크 등 외부 코드를 사용하게 된다. 이때, 제공자들은 범용적으로 사용되도록 하지만 사용자는 자신의 요구에 집중하는 인터페이스를 원한다. 경계 인터페이스를 이용할 때는 이를 이용하는 클래스나 클래스 계열 밖으로 노출되지 않도록 주의한다. 외부 코드를 사용할 때는 먼저 간단한 테스트 케이스를 작성해서 외부 코드를 익히자. 이렇게 테스트를 통하게 되면 필요한 지식만 확보하게 되고, 이해도도 높여준다. 그리고 새 버전이 나왔을 때 우리 코드와 호환되는지, 호환되지 않는지 테스트를 통해 바로 알 수 있게 된다. 외부 패키지를 호출하는 코드를 가능한 줄여 경계를 관리하자. 새로운 클래스로 경계를 감..
오류 처리 코드 때문에 프로그램 논리를 이해하기 어렵다면 그건 깨끗한 코드라고 할 수 없다. 예외가 발생할 수 있는 코드를 짤 때는 try-catch-finally문으로 시작해라. 외부 API를 사용할 때는 감싸기 기법을 사용해라. 특수 사례 패턴의 경우, 코드상에서 처리하지 말고 클래스를 만들거나 객체를 조작해 처리한다. null을 반환 혹은 전달하는 방식으로 에러 처리를 하지 마라. 필요에 따라 자바에서 지원하는 Collections.emptyList()를 사용해서 미리 정의된 읽기 전용 리스트를 반환해라.
요약에 들어가기 전에 에서 말하는 객체와 자료구조의 의미 차이를 정리해둘 필요가 있어서 여기에 정리해둔다. 실제 현업에서도 이런 것을 자료구조라고 하는지는 모르겠지만. 객체란 자료를 숨기고 공개한 함수로 그 숨긴 자료를 다루는 것이고, 자료구조는 자료는 공개하되 아무 메서드도 제공하지 않는 것을 말한다. 객체라고 하면 private으로 인스턴스 변수를 선언하고 그 외의 메서드들을 가지고 있지만, 자료구조는 멤버변수만을 가지고 있는 것이다. 이 책에서는 객체가 포함하는 자료를 표현할 가장 좋은 방법을 심각하게 고민해야 한다고, 아무 생각없이 getter/setter 메서드를 만드는 것이 제일 나쁘다고 한다. 객체(객체지향기법)와 자료구조(절차적인 코드)는 요약 전에 설명한 것 같은 차이가 있기 때문에 서로..