CH3
항상 인터페이스를 사용하자!
객체는 살아가는 유기체입니다. 이 객체는 다른 유기체들과 의사소통 하면서 그들의 작업을 지원하고, 다른 유기체들 역시 이 객체에게 도움을 줍니다. 객체들의 세계는 매우 사회적이면서 유대감이 높은 환경입니다.
결합도를 낮추자!
- 설계를 시작하는 단계에서 각각의 객체가 어떤 일을 수행하고 다른 객체에게 어떤 서비스를 제공할 지 정확하게 알고있는 편이 낮기 때문에 결합이 유용합니다.
- 하지만 애플리케이션의 서비스가 커지면서 수십개의 객체가 생겨나고 객체 사이의 강한 결합도가 심각한 문제로 발생됩니다.
- 결합도 문제는 유지보수에 많은 영향을 미치게 됩니다.
유지보수를 가능하도록 만들기 위해서는 최선을 다해 객체를 분리해야 합니다. 기술적인 관점에서 객체분리란 상호작용하는 다른 객체를 수정하지 않고 해당 객체를 수정할 수 있도록 만들어야 합니다. 이를 가능하게 하는 가장 훌륭한 도구가 바로 interface 입니다!
public interface Cash { Cash multiply(float factor); }
- Cash는 인터페이스 입니다. 우리의 객체가 다른 객체와 의사소통 하기위해 따라야 하는 계약 입니다.
public class DefaultCash implements Cash { private int dollars; public DefaultCash(int dollars) { this.dollars = dollars; } @Override public Cash multiply(float factor) { return new DefaultCash((int) (this.dollars * factor)); } }
- 이렇게 구현할 수 있으며 실제 구현 대신 계약에 의존시키도록 하면 됩니다.
public class Employee { private Cash salary; }
- Employee 클래스는 Cash 인터페이스의 구현 방법에 아무런 관심이 없습니다.
- multiply() 메서드가 어떻게 동작하는지도 관심이 없습니다. 즉 동작 방식을 알지 못합니다.
- 그대로 Cash인터페이스를 이용하면 Employee 클래스와 DefaultCash 클래스를 느슨하게 분리할 수 있다는 뜻을 가지고 있습니다.
- 이제 DefaultCash 내부의 구현을 변경하거나 심지어 Cash 인터페이스의 구현체를 다른것으로 교체하더라도 아무런 영향을 받지 않습니다.
CH4
CH6
불변 객체로 만들자 !
모든 클래스를 상태 변경이 불가능한 불변 클래스(immutable class)로 구현하면 유지보수성을 크게 향상시킬 수 있다.
불변성
- 크기가 작고 응집력이 높으며 느슨하게 결합되고 유지보수하기 쉬운 클래스를 만들 수 있도록 해준다.
- 가변 클래스보다는 불변 클래스를 이해하는 쪽이 훨씬 수월하다.
- 불변 객체를 기반으로 사고하면 더 깔끔하고, 더 작고, 더 쉽게 이해할 수 있는 코드를 만들 수 있다.
불변객체란
- 인스턴스를 생성한 후에 상태를 변경할 수 없는 객체를 불변객체라고 부른다.
그럼 가변 객체는 뭘까?
public class Cash { private int dollars; public void setDollars(int dollars) { this.dollars = dollars; } }
- 위의 Cash클래스를 이용해서 생성한 객체는 상태 변경이 가능하기 때문에 가변 객체로 부른다.
불변 객체는 어떻게 생겼을까?
public class Cash { private final int dollars; Cash(int dollars) { this.dollars = dollars; } }
- 가변 객체와 비슷하게 생겼지만 상태를 변경할 수 없는 불변 객체를 생성합니다.
- 가변객체와 비교해서 달라진 부분은 final 키워드를 추가했다는 점 입니다.
- final 키워드는 생성자 외부에서 프로퍼티 값을 수정할 경우 컴파일 타임 에러가 발생됩니다.
- 불변객체는 필요한 모든 것을 내부에 캡슐화 하고 변경할 수 없도록 해야합니다. 불변객체를 수정해야 한다면 프로퍼티를 수정하는 대신 새로운 객체를 생성해야 합니다.
금액을 곱하는 예제 - 가변객체
public class Cash { private int dollars; public void mul(int factor) { this.dollars *= factor; } }
금액을 곱하는 예제 - 불변객
public class Cash { private int dollars; public Cash(int dollars) { this.dollars = dollars; } public Cash mul(int factor) { return new Cash(this.dollars * factor); } }
둘 사이의 명확한 차이점은 불변 객체는 어떤 방식으로든 자기 자신을 수정할 수 없으며 항상 원하는 상태를 가지는 새로운 객체를 생성해서 반환해야 합니다.
명확한 이해를 돕는 예제
// 가변객체를 사용한 경우 Cash five = new Cssh(5); five.mul(10); System.out.println(five) // 50 // 불변 객체를 사용할 경우 Cash five = new Cash(5); Cash fifty = five.mul(10); System.out.println(fifty) // 50
- five객체를 생성하고 나면 five는 fifty가 될 수 없습니다. 5는 5일뿐입니다. 5는 생명이 다하는 순간까지 5입니다 항상!