본문 바로가기

ALL

(127)
4-4) 상속보다는 컴포지션을 사용하라 상속 상속은 코드의 재사용성을 올려주고 유연한 개발을 가능하게 해주는 강력한 수단이다. 하지만 상속은 캡슐화를 깨뜨린다는 문제가 있다. 캡슐화는 클래스의 필드와 메소드의 동작방식을 외부에 숨기는 것을 말한다. 이를 통해 클래스와 외부 사용자는 서로에 대한 독립성을 가지고 서로에 대한 영향을 최소화할 수 있다. 하지만 상위 클래스를 상속받은 하위클래스는 상위클래스의 구현방식이 바뀌면 그에 따라 영향을 받는다. public class InstrumentedHashSet extends HashSet { private int addCount = 0; public InstrumentedHashSet() { } public InstrumentedHashSet(int initCap, float loadFactor) ..
4-3) 변경 가능성을 최소화하라 불변 클래스 불변 클래스란 인스턴스 내부 값을 수정할 수 없는 클래스를 말한다. Java에서는 String, BigInteger, BigDecimal 등 여러 클래스가 불변 클래스로 설계되었다. 불변 클래스 방식은 설계 및 구현이 쉽고 사용하기에도 쉽다. 또 오류가 생길 여지도 적어서 훨씬 안전하다. 이러한 장점을 가진 불변 클래스를 만들기 위해서는 다음 다섯가지 규칙을 따르면 된다. 1. 객체의 상태를 변경하는 메소드를 제공하지 않는다. 2. 클래스를 확장할 수 없도록 한다. 3. 모든 필드를 final로 선언한다. 4. 모든 필드를 private로 선언한다. 5. 자신 외에는 내부의 가변 컴포넌트에 접근할 수 없도록 한다. 불변 복소수 클래스 public final class Complex { pri..
4-2) public 클래스에서는 public 필드가 아닌 접근자 메소드를 사용하라 접근자 메소드 // Point 클래스의 x, y 값은 아무곳에서나 접근 가능하고 // 그 결과 x, y에 대한 관리 책임은 Point가 가지고있지 않는다. class Point { public double x; public double y; } 위 Point 클래스는 x, y 좌표를 나타내는 실수형 필드를 가지고 있다. 정말 그뿐이다. 연관된 정보를 모아둔 것 이외의 역할을 하지않는 C언어 시절의 클래스이다. OOP의 철학을 따르는 Java의 개발자라면 이런 퇴보한 클래스는 만들면 안된다. Point 클래스를 만들었다면 x, y 값에 대한 책임도 함께 가지고 있는 클래스를 만들어야 한다. // x, y의 값은 Point 클래스 내부에서만 수정하기때문에 // x, y에 대한 관리책임은 Point 클래스에 ..
4-1) 클래스와 멤버의 접근 권한을 최소화하라 캡슐화(정보 은닉) 잘 만들어진 컴포넌트의 특징 중 하나는 내부 구현 내용을 외부에 잘 숨긴다는 것이다. 외부의 사용자는 내부의 구현에 신경쓰지 않고 컴포넌트를 사용한다. 이는 OOP와 소프트웨어 설계의 기본이 캡슐화(정보 은닉)와 같은 뜻이다. 캡슐화는 내부의 상태나 정보를 외부에서 접근할수 없도록 하여 감추는 것인데, 이렇게 하면 여러 장점을 얻을 수 있다. 1. 시스템 개발 속도를 높인다 > 여러 컴포넌트를 병렬로 개발 할 수 있다. 2. 시스템 관리 비용을 낮춘다 > 디버깅 하기 쉽고, 컴포넌트 교체 부담도 적다. 3. 성능 최적화에 좋다 > 내부 컴포넌트의 코드 수정이 외부 컴포넌트에 주는 영향이 적기 때문에 성능최적화 하기에 좋다. 4. 소프트웨어 재사용성을 높인다 > 캡슐화(정보은닉)을 지킨..
3-5) Comparable을 구현할지 고려하라 Comparable 인터페이스, compareTo 메소드 public class Customer implements Comparable { private int customerId; ... @Override public int compareTo(Customer o) { return Integer.compare(this.customerId, o.customerId); } ... } // customerList가 customerId 기준으로 정렬된다. Arrays.sort(customerList); Comparable 인터페이스는 compareTo 메소드를 가지고있다. compareTo 메소드는 두 객체를 비교하고 그 결과를 반환한다. 얼핏 equals 메소드와 비슷해 보일 수 있지만, equals는 두 객..
3-4) clone 재정의는 주의해서 진행하라 clone 메소드와 Cloneable 인터페이스 public class MyClass{ ... } // 컴파일 에러, clone 메소드를 찾을 수 없다. myClass.clone(); public class MyClass{ ... public MyClass clone() throws CloneNotSupportedException { return super.clone(); } ... } // 런타임 에러, CloneNotSupportedException 발생 myClass.clone(); public class MyClass implements Cloneable{ ... public MyClass clone() throws CloneNotSupportedException { return super.clo..
3-3) toString을 재정의 하라 toString log.info("고객 객체 반환: {}", customer); toString은 무시하기 쉬우나 생각보다 자주 사용되는 공통 메소드이다. API를 호출할 때 사용자에게 보여주는 반환값을 toString을 사용하여 보여줄 수 있고, 로그를 출력할 때 toString이 사용될 수 있다. 하지만 기본 Object의 toString은 "클래스명@해시코드값"으로 되어있어서 일반적인 기대값과는 다르다. 따라서 클래스를 만들 때 기대에 맞는 toString을 재정의하고 그 포맷을 사용자가 알 수 있도록 알려주어야 한다. toString을 잘 정의한 사례 일자를 표현하는 클래스인 LocalDate가 toString을 잘 재정의한 사례이다. 먼저 일자의 핵심정보인 년/월/일이 모두 출력된다. 그리고 메..
3-2) equals를 재정의 하려면 hashCode도 재정의하라 hashCode equals 처럼 hashCode도 Object 클래스부터 정의되어있는 공통 메소드이다. hashCode는 일반적으로 객체를 비교하기 전에 간략히 비교할 대상을 걸러내는 용도로 사용된다. hashCode역시 공통 메소드로서 지켜야하는 공통 규약이 존재한다. 1. equals에 사용되는 정보가 변경되지 않는다면, hashCode의 값도 일정해야 한다. 2. equals 비교 결과 true인 두 객체는 hashCode의 값도 같아야 한다. 3. equals 비교 결과 false인 두 객체의 hashCode 값이 꼭 달라야 하는건 아니다. hashCode의 역할 public class Customer { private String customerId; public Customer(String ..