4. 모듈화
결합도는 낮게
모듈 사이 의존 관계는 최소로!
응집도는 높게
한 모듈은 한가지 책임만!
들어가며
4
5. 모듈화
변경은 최소로
최대한 숨기기!
최대한 변하지 않기!
계약은 철저히
계약 준수
• 계약만 지킨다면 답을 내놓는 방법은 중요하지 않다.
• 답은 내놓은 방법이 변한다고 계약이 변하진 않는다.
계약 불변
• 한 번 맺은 계약은 최대한 변하지 않아야 한다.
• 계약이 변하면 계약 맺은 고객은 번거로울 수 밖에 없다.
들어가며
5
9. 객체 지향
객체는 기능 집합
객체는 메소드라는 함수가 있는 자료구조로 메소드가 자료를
조작한다라는 개소리! (Holub)
위임
필요한 정보를 요청해서 직접 가져와 처리하려 하지 말고 정보
를 가진 객체를 찾아 계약에 따라 일을 시켜라. 만약 정보가 부
족하면 제공해라.
구현 은닉
최대한 구현을 감춰 계약 크기를 최소화하라.
들어가며
9
10. SOLID
모두 지킬 수는 없다.
깰 수도 있다.
하지만, 원칙을 이해하라.
무슨 원칙을 깨는지, 왜 원칙을 깨야 하는지 이해하라.
Working is better than perfect!
들어가며
10
12. OCP
Open Closed Principle
확장엔 열려있고 변경엔 닫혀있다.
새로운 요구사항이 발생
• 감춰진 구현은 자유롭게 재구성할 수 있어야 한다. (Open)
• 공개된 인터페이스는 변하지 않아야 한다. (Close)
Open은 지키기 쉽지만 Closed는 지키기 어렵다.
들어가며
12
13. OCP
왜 나쁜 설계인가?
다른 도형을 추가하면?
들어가며
public void drawAll(Shape[] shapes)
{
for (int i = 0; i < shapes.length; i++)
{
Shape shape = shapes[i];
if (shape.shapeType() == Shape.CIRCLE)
{
((Circle)shape).drawCircle();
}
else if (shape.shapeType() == Shape.SQUARE)
{
((Square)shape).drawSquare();
}
}
}
13
14. OCP
왜 나쁜 설계인가?
다른 제약 사항을 추가하면?
들어가며
public void drawAll(Shape[] shapes)
{
for (int i = 0; i < shapes.length; i++)
{
Shape shape = shapes[i];
if (shape.shapeType() ==
Shape.CIRCLE)
{
if (((Circle)shape).radius() > 10.0)
{
shape.draw();
}
}
else
{
shape.draw();
}
}
}
14
15. LSP
Liscov Substitution Principle
서브 클래스는 슈퍼 클래스를 대체할 수 있어야 한다.
완벽한 is-a 관계가 되도록 상속 구조를 정의해야 한다.
• 슈퍼 클래스의 행위를 서브 클래스가 거부하면 안 된다.
• 서브 클래스가 슈퍼 클래스를 대신하기 위해 뭔가 더 필요하면 안 된다.
예외
• Arrays.asList(new String[] {“0”, “1”, “2”}).add(“3”) Exception
• UnmodifiableList를 반환하기 때문
• Collection API 크기를 줄이기 위한 선택
들어가며
15
16. ISP
Interface Segregation Principle
특화된 인터페이스가 통합된 범용 인터페이스보다 낫다.
클라이언트에게 용도가 명확한 인터페이스를 제공해야 한다.
들어가며
16
17. DIP
Dependency Inversion Principle
클라이언트는 구체 클래스가 아닌 인터페이스에 의존한다.
바람직한 방향으로 의존성을 재설정
들어가며
Framework User
login
Framework
ILogin
Authorizer UserProfile
Authorizer
login User
UserProfile
17
20. 이펙티브 자바 구조
객체 생성과 소멸
생성
소멸
계약
공통 계약
클래스/인터페이스
메소드
들어가며
프로그래밍 기본
일반
예외
동시성
심화
제너릭
열거형
주석
직렬화
20
21. I. 객체 생성과 소멸
객체는 시스템 구성 기본 단위
객체 == 메모리
객체 생성 방법
객체 소멸 방법
객체 생성과 소멸
21
22. 1. Static Factory Method
왜?
적절한 시그니처
객체 재사용
다양한 서브타입 제공
간결한 코드
요약
무심코 생성자를 만들기 전에 static factory method를 먼저
고려하라.
객체 생성과 소멸
22
23. 1. Static Factory Method
어떻게?
인스턴스를 생성하는 static 메소드
GoF Factory Method와 비교
• 의도 : . 인스턴스 생성을 하위 클래스에게 미룬다.
객체 생성과 소멸
Client
Creator c = new ConcreteCreator();
String result = c.anOperation();
23
24. 1. Static Factory Method
적절한 시그니처
new BigInteger(int, int, Random) vs
BigInteger.probablePrime(int, Random)
Foo(int, String) Foo(String, int) vs
Foo.createFoo1(int, String) Foo.createFoo2(int, String)
Human(String name) Human(String location) vs
Human.find(String name) Human.from(String location)
객체 생성과 소멸
24
25. 1. Static Factory Method
객체 재사용
Boolean.valueOf(boolean)
• 미리 만들어 놓은 Boolean.TRUE, Boolean.FALSE 재사용
• 복잡한 객체라면?
인스턴스 제어
• 인스턴스 개수 제한 : Singleton
• 인스턴스 생성 방지 : Utility class
• 동일한(== && equals) 인스턴스 생성 방지 : 불변 클래스, Enum
객체 생성과 소멸
25
26. 1. Static Factory Method
서브타입 생성
사악한 new
• 항상 구체 클래스가 나와야 한다.
인터페이스 기반 프레임워크
• public이 아닌 class의 인스턴스 제공
• List<T> Collections.unmodifiableList(List<? Extends T>)
• List<T> Collections.synchronizedList(List<T>)
• 작은 규모 API 가능
• 다양한 구현체 제공
객체 생성과 소멸
Collections
List
UnmodifableList
SynchronizedList
<<생성한다>>
Client
unmodifableList()
synchronizedList()
26
27. 1. Static Factory Method
서브타입 생성
서비스 제공자 프레임워크
객체 생성과 소멸
Client
JNDI API
Naming Manager
LDAP
Active
Directory
NDSDNS
COSNami
ng
JNDI SPI
업체가
구현하는
부분
Client
JDBC API
Driver Manager
Oracle MySQLSybaseDB2MSSQL
JDBC Dirver API
업체가
구현하는
부분
27
28. 1. Static Factory Method
서브타입 생성
서비스 제공자 프레임워크
• 인터페이스만 존재해도 클라이언트를 구현할 수 있다.
• JDBC, JNDI, JTA 같이 다양한 구현체가 존재할 때 유용한 기법이다.
객체 생성과 소멸
DriverManager
Connection
XXXConnection
YYYConnection
<<생성한다>>
Client getConnection()
DB2 JDBC 드라이버
9i JDBC 드라이버
28
29. 1. Static Factory Method
서브타입 생성
서비스 제공자 프레임워크
• 동작
• 구조
객체 생성과 소멸
ServicesClient Provider Service
newInstance(name)
newService()
<<instantiate>>
goodService()
Provider Service
CProvider
Service newService()
CService
goodService()
<<instantiate>>
Services
registerProvider(name, Provider)
Service newInstance(name)
ClientSystem
Register
providers
newInstance
providers
goodService
29
31. 1. Static Factory Method
단점
드러난 생성자가 없는 경우 서브 클래스를 만들 수 없다.
다른 static 메소드와 구분하기 어렵다.
• 명명 규칙을 따르자.
• valueOf : 형변환 메소드로 인자와 같은 값을 갖는 인스턴스 반환한다.
• of : valueOf와 같다. EnumSet에서 주로 쓴다.
• getInstance : 인자에 따라 인스턴스가 결정되나 인자와 같은 값을 가지
지 않는다. 인자가 없는 경우 singleton인 경우가 많다.
• newInstane : getInstance와 같지만 항상 새로운 인스턴스를 반환한다.
• getType : getInstance와 같지만 메소드가 속한 클래스가 아닌 다른 타
입을 생성한다.
• newType : newInstance와 같지만 메소드가 속한 클래스가 아닌 다른
타입을 생성한다.
객체 생성과 소멸
31
32. 2. Builder
왜?
객체 생성에 필요한 인자가 4개가 넘어가면 헷갈린다.
인자가 같은 타입이라면 그야말로 지옥.
요약
객체 생성에 필요한 인자가 많다면 builder를 고려하라. 특
히, 선택 인자가 많은 경우에 유용하다.
객체 생성과 소멸
32
33. 2. Builder
이전 해법
Telescoping Constructor
• 가독성 떨어진다.
• 작성하기 어렵다.
객체 생성과 소멸
33
34. 2. Builder
이전 해법
Java Beans
• 객체 생성을 완성할 때까지 객체가 일관성을 유지할 수 없다.
• 객체 불변 조건을 만족하지 못하는 순간이 있다.
• 동시성 문제가 발생할 수 있다.
객체 생성과 소멸
34
35. 2. Builder
어떻게?
Builder
• 의도 : 복잡한 객체를 구성하는 일을 표현과 분리하여 같은 구성 절
차로 다양한 표현을 만들어 내고 싶다.
객체 생성과 소멸
35
36. 2. Builder
객체 생성과 소멸
장점
VS Telescoping
• 작성하기 쉽다.
• 가독성이 좋다.
Vs JavaBeans
• 불변규칙을 검사할 수 있다.
• 불변 객체를 만들 수 있다.
유연성
• 다수 가변인자
• 빌더 객체 재사용
36
37. 2. Builder
장점
추상 팩토리
• 의도 : 실체 클래스(concrete class)를 정의하지 않고도 관련 객체군
을 생성하는 인터페이스를 제공한다.
• Builder를 이용한 추상 팩토리
• java.lang.Class도 추상 팩토리로 동작하지만 newInstance()는 항상
기본 생성자를 호출하는 한계가 있다.
단점
Builder 객체 생성 비용
객체 생성과 소멸
37
38. 3. Singleton
왜?
정확히 하나의 인스턴스만 생성하는 클래스
요약
public static final field나 method를 제공하는 방법과 Enum
을 이용하는 방식이 있다.
직렬화, 동시성도 고려해야 진정한 singleton을 얻을 수 있다.
객체 생성과 소멸
38
39. 3. Singleton
어떻게?
public static final field
• Serializable로 바꾸면 문제 발생
• 모든 field transient
• readResolve 구현
• Reflection으로 생성자 접근 가능
객체 생성과 소멸
39
40. 3. Singleton
어떻게?
public static final method
• 늦은 초기화
• 동시성 문제 발생
• 동기화 필요
• 성능 감소
• Reflection으로 생성자 접근 가능
객체 생성과 소멸
40
49. 5. 불필요한 객체 생성 막기
고려 사항
생성자가 하는 일이 거의 없는 객체 생성은 비용이 적다. 소득
없이 명확성과 단순성을 희생하지 말자.
데이터베이스 연결 같이 엄청나게 무거운 객체가 아니라면 직
접 객체 풀을 만들지 말자.
객체 생성과 소멸
49
50. 6. 쓸모 없는 객체 참조 제거
왜?
메모리 누수
• 자바에서?
• 스택 :
시스템 전역에서
쓰임
객체 생성과 소멸
50
51. 6. 쓸모 없는 객체 참조 제거
원인
자신의 메모리를 스스로 관리해서 참조가 쓸모 없는지 개발자
만 아는 경우
캐시에 넣어두고 까먹기
콜백을 리스너에 등록해 놓고 지우지는 않기
해법
가비지 컬렉터가 알아차리지 못하는 쓸모 없는 참조는 null로!
캐시, 콜백은 자동 소멸하도록!
객체 생성과 소멸
51
53. 7. Finalizer 쓰지 말기
Finalizer
GC 전에 호출되는 메소드
protected void finalizer() throws Throwable
System.runFilnalizerOnExit
Runtime. runFilnalizerOnExit
문제
신속하게 실행된다는 보장이 없다.
반드시 실행된다는 보장도 없다.
예외가 발생하면 스택도 출력하지 않고 죽는다.
성능이 떨어진다.
객체 생성과 소멸
53
54. 7. Finalizer 쓰지 말기
유혹
자원 회수
• 자원 회수는 자원을 가진 객체가 담당하고 try-finally에서 호출한다.
• Stream의 close, Connection의 close, Timer의 cancel 따위
용도
자원 회수는 try-finally로 한다.
혹시 자원 회수를 못했을 때 안전망
네이티브 객체 처리 안전망
객체 생성과 소멸
54
55. 7. Finalizer 쓰지 말기
그래도 써야 하는 경우
최악의 경우 자원 회수를 못했을 때 안전 장치
• Stream, Connection, Timer는 finalizer 구현하고 있다.
네이티브 객체 처리 안전 장치
• GC는 new로 생성한 객체만 처리
• 중요한 자원은 별도 종료 메소드로
웬만하면 쓰지 마라!
객체 생성과 소멸
55
56. 7. Finalizer 쓰지 말기
그래도 써야 한다면
반드시 클래스 계층 구조의 모든 finalizer가 호출되도록 한다.
서브 클래스가 반드시 이렇게 구현하리란 보장이 없어……
객체 생성과 소멸
56
57. 7. Finalizer 쓰지 말기
그래도 써야 한다면
Finalizer가 필요하면 Finalizer Guardian 방식으로 구현한다.
객체 생성과 소멸
57
58. II. 계약
공통 계약
equals, hashCode, toString, finalize, clone, compareTo
다른 클래스(특히 Collection)와 문제를 일으키지 않으려면 계
약을 반드시 지켜야 한다.
클래스/인터페이스
자바 기본 단위
계약
58
59. 8. equals
equals를 재정의하지 않는 경우
참조가 같은 메모리를 가리킨다.
• == true
같은지 비교할 이유가 없다.
• Radom
슈퍼 클래스가 알맞게 구현
• AbstractSet << HashSet
equals를 재정의하는 경우
값 자체가 같은지 판단해야 한다.
• Value class
• Map의 key, Set의 element
계약
59
60. 8. equals
계약 조건
재귀성
• Null 아닌 x
x.equals(x) : true
대칭성
• Null 아닌 x, y
x.equals(y) : true y.equals(x) : true
이행성
• Null 아닌 x, y, z
x.equals(y) : true, y.equals(z) : true z.equals(x) : true
일관성
• Null 아닌 x, y
x.equals(y) : true면 x, y가 변하지 않았다면 x.equals(y) : true
Null 아님
• Null 아닌 x
x.equals(null) : false
계약
60
62. 8. equals
계약 이행
재귀성
• 일부러 어기지 않는 한 어길 수 없다.
대칭성
• 어기기 쉽다.
계약
62
63. 8. equals
계약 이행
전이성
• Point와 Point를 상속받은 ColorPoint가 있다. Point p2와
ColorPoint p1, ColorPoint p3가 있다.
• 대칭성
• ColorPoint.equals에서 Point인 경우와 ColorPoint인 경우를 나눠서 처
리하면 된다.
• 전이성
• p1.equals(cp2) : true, p2.equals(p3) : true p1.equals(p3) false
일 수 있다. (세 점이 위치는 같고 p1, p3가 색이 다른 경우)
값을 표현하는 구체 슈퍼 클래스를 상속받아 값을 추가
하는 구체 서브 클래스를 만드는 경우 대칭성과 전이성을
동시에 지킬 수 있나? 불가능!
계약
63
64. 8. equals
왜?
구체 클래스를 상속하려는 의도는 좋지 않다.
• 구체 클래스의 ‘구현’에 의존성이 생기기 때문이다.
• 상속 구조를 포기하고 합성(composition)을 택한다.
• 그나마 추상 클래스를 상속 받는 경우 equals 계약을 지킬 수 있다.
문제 사례
계약
java.util.Date
java.sql.TimeStamp
nanoseconds
64
65. 8. equals
계약 이행
일관성
• 신뢰할 수 없는 자원에 의존하여 equals를 검사하는 경우 깨진다.
• java.net.URL : IP 주소를 얻어와서 equals 실행
널이 아님
• 당연하다.
• 따로 null 확인하는 것보다 instanceOf를 쓰면 된다.
계약
65
66. 8. equals
equals 잘 만들기
== 를 이용해서 먼저 검사한다. 많은 값을 비교해야 하면 성능
에 ‘조금’ 이득이 있다.
instanceOf로 검사한다.
Object 타입인 인자를 형변환한다.
중요한 필드부터 비교한다.
• 기본형은 ==로 비교한다. (float, double 제외)
• float과 doubl은 Float.compare, Doble.compar를 이용한다.
• 객체는 equals로 비교한다.
• 배열은 Arrays.equals로 비교한다. (>JDK 1.5)
• 필드가 null인 경우가 있으므로 null을 확인한다.
계약
66
67. 8. equals
equals 잘 만들기
== 를 이용해서 먼저 검사한다. 많은 값을 비교해야 하면 성능
에 ‘조금’ 이득이 있다.
instanceOf로 검사한다.
Object 타입인 인자를 형변환한다.
중요한 필드부터 비교한다.
• 기본형은 ==로 비교한다. (float, double 제외)
• float과 doubl은 Float.compare, Doble.compar를 이용한다.
• 객체는 equals로 비교한다.
• 배열은 Arrays.equals로 비교한다. (>JDK 1.5)
• 필드가 null인 경우가 있으므로 null을 확인한다.
계약
67
69. 8. equals
equals 잘 만들기
equals()를 재정의했으면 반드시 hashCode()도 재정의한다.
너무 심하게 같은지 비교하지 마라.
중첩(Overloading)하지 마라.
@override 주석을 달아라.
계약
69
70. 9. hashCode
equals를 재정의했다면 반드시 hashCode도 재정의해야
한다.
왜?
Hash 기반 Collection에서 문제가 발생한다.
계약
70
71. 9. hashCode
계약 조건
hashCode는 일관성 있는 정수를 반환한다.
x.equals(y) : true x.hashCode() == y.hashCode()
x.equals(y) : false x.hashCode() != y.hashCode() 면
Hash Collection 성능이 좋아진다.
예
계약
PhoneNumber p1 = new PhoneNumber(10, 8728, 7627);
PhoneNumber p2 = new PhoneNumber(10, 8728, 7627);
Map<PhoneNumber, String> m = new HashMap<PhoneNumber, String>();
m.put(p1, “YHI”);
m.get(p2); // null or “YHI”?
71
72. 9. hashCode
좋은 hashCode
equals가 false인 객체들은 최대한 다른 값 반환
• P47 참조
계약
72
73. 12. compareTo
Comparable<T>
자연스러운 순서를 가지는 객체들
• String : 사전순
• Date : 연대순
public int comapteTo(T t)
순서를 다룰 때
• Arrays.sort, Collections.sort
• TreeSet, TreeMap
계약
73
74. 12. compareTo
계약 조건
sgn(x.compareTo(y)) == - sgn(y.compareTo(x))
x.compareTo(y) > 0 && y.compareTo(z) > 0
x.comapreTo(z) > 0
x.compareTo(y) == 0 sgn((x.compareTo(z)) ==
sgn(y.compareTo(z))
x.compareTo(y) == 0 x.equals(y) == true (선택)
상속 구조일 때 equals와 똑같은 문제 내포
계약
74
75. 12. compareTo
구현 방법
네 번째 조건을 지키지 않았을 때 문서에 명시해야 한다.
• New BigDeciaml(“1.0”) vs New BigDeciaml(“1.00”)
• equals false, compareTo true
• HashSet에는 둘 다 들어간다.
• TreeSet에는 하나만 들어간다.
타입을 비교할 필요 없다.
• Generic Interface
너무 똑똑하게 구현하지 마라.
• Overflow 위험
계약
75
76. 10. toString
좋은 toString
간결하면서 사람이 읽고 이해하기 쉬운 형태로 표현한다.
클라이언트가 이 문자열을 파싱해서 쓰는 일이 없도록 한다.
계약
76
77. 11. clone
Cloneable
복제를 허용한다는 의미를 가진 Mixin interface다.
정작 clone 메소드는 Object에 protecte로 선언한다.
Cloneable인 객체가 clone을 재정의하지 않은 경우 clone을
호출하면 Object.clone이 그 객체를 복제한다.
Cloneable 인터페이스는 슈퍼 클래스인 Object의 메소드
clone이 어떻게 동작하나 규정한다.
계약
77
78. 11. clone
계약 조건
x.clone() != x
x.clone().getClass() == x.getClass()
x.clone().equals(x)
반환하는 객체는 생성자로 생성하지 않는다.
계약
78
79. 11. clone
clone을 overriding 하려면
super.clone을 호출해서 복사본을 얻어 반환해야 한다.
계약
Object
SuperClass
SubClass
super.clone()
super.clone()
79
80. 11. clone
clone 문제점
elements 필드가 final이라면?
객체(result)는 모든 값이 복제 되어야 한다.
• deepCopy vs shalowCopy
계약
80
81. 11. clone
clone 잘 만들기
clone을 재정의하면
• public
• 반환값은 Object가 아닌 자기 타입
• CloneNotSupportedException 생략
상속을 목적으로 만든 추상 클래스
• Cloneable하지 않아야
• Object처럼 clone 구현해야
계약
@override public Foo clone()
81
82. 11. clone
객체 복제는?
복제 생성자, static 팩토리 메소드
• final 사용 제약 없음
• 예외 처리 없음
• 타입 변환 가능 (예: Collection끼리 변환)
계약
82
83. 13,14. 정보 은닉
모듈 설계
무엇을 드러낼 것인가?
드러내는 순간, 귀찮아 진다. 최대한 감추자.
정보 은닉/캡슐화
• 인터페이스로만 메시지 주고 받는다.
• 내부에서 어떻게 하든 상관없다.
클라이언트 중심 설계
• 클라이언트가 쓸 것만 드러낸다.
• 클라이언트가 가능하면 수정하는 일이 없게 드러낸다.
계약
83
84. 13,14. 정보 은닉
무엇이 드러나는가?
계약
pacakge
public class
Protected field
Public field
Serializable field
Public method
84
85. 13,14. 정보 은닉
클래스
public
패키지 전용
private inner class/interface
계약
pacakge
public class
Protected field
Public field
Serializable field
Public method
Package-only class
public class
Private
nested class
85
86. 13,14. 정보 은닉
멤버
Public
Protected
Package-only
Private
계약
86
87. 13,14. 정보 은닉
클래스 설계
Public API를 신중하게 선택한다.
나머지는 private으로 설정한다.
패키지 내부에서 꼭 접근해야 하는 멤버라면 package-only로
변경한다.
Package-only가 자주, 집중해서 한 곳에서 발생하면 응집도가
떨어지고 결합도가 커진 경우(SRP 위배)다.
Protected는 정말 필요한지 고민한다.
Serializable을 구현할 때 주의한다.
인스턴스 필드는 public으로 만들지 마라.
계약
87
88. 13,14. 정보 은닉
클래스 설계
public static final 필드
• Enum을 쓴다.
• 꼭 필요하다면 불변이거나 변하더라도 영향이 없어야 한다.
• 원래 불변객체 : String, BingInteger 따위
• 불변으로 만들기 : Arrays.asList
• 복사본 만들기
계약
88
89. 13,14. 정보 은닉
접근자와 변경자
클라이언트가 접근자와 변경자를 이용해서 뭔가 한다면, 해당
객체가 해야 하는 일이 아닌지 고민한다.
외부에 정보를 제공해야 한다면 자기 책임을 다하는 객체를 제
공한다.
잘 만들어진 객체라면 setter는 거의 필요없다.
데이터를 주고 받지 말고, 메시지를 주고 받으면서 책임을 가진
객체에게 위임한다.
계약
Money a, b, c;
c.setValue(a.getValue() + b.getValue());
Money a, b, c;
c = a.add(b);
public double getBalance(); Public Money getBalance();
89
90. 13,14. 정보 은닉
접근자와 변경자
데이터베이스 연결 같은 최전선 맞닿은 경계에서는 value
object와 getter/setter가 필요하다.
패키지 전용 클래스, private 내부 클래스는 필드를 그대로 드
러내도 된다.
계약
90
91. 22. Static 멤버 클래스가 좋아
중첩(nested) 클래스
Static 멤버
Non-static 멤버
Anonymous
Local
계약
91
92. 22. Static 멤버 클래스가 좋아
Static 멤버
외곽 클래스의 모든 인스턴스가 공유
외곽 클래스의 모든 static 멤버에 접근
Non-static 멤버
외곽 클래스의 특정 인스턴스(Enclosing.this)마다 클래스 하
나씩 생성
외곽 클래스의 모든 멤버에 접근
계약
92
93. 22. Static 멤버 클래스가 좋아
Anonymous
필요한 시점에 선언하고 생성해서 쓰고 버리는 객체
static 환경이면 static으로, non-static 환경이면 non-static
으로 동작
제약
• 이름이 없다.
• Static 멤버 못 가진다.
• 인터페이스 구현 불가, 상속 불가하다.(new Type() {} 형태니까)
계약
93
94. 22. Static 멤버 클래스가 좋아
Local
{}로 둘러 쌓인 영역에서 유효한 클래스
메소드 내부에서 지역 변수들을 하나로 묶어서 쓰고 싶을 때
계약
void foo()
{
class Point
{
int x;
int y;
Point(int x, int y)
{
this.x = x;
this.y = y;
}
}
Point p1 = new Point(0, 0);
}
94
95. 21. 전략 클래스
함수 포인터, 람다식처럼 특정 기능을 수행하는 메소드를
전달하는 방법
Comparable vs Comparator
Comparable
• 자연스러운 객체의 순서
Comparator
• 상황에 따른 여러 가지 객체 순서 정의
95
96. 21. 전략 클래스
함수 포인터, 람다식처럼 특정 기능을 수행하는 메소드를
전달하는 방법
Comparable vs Comparator
Comparable
• 자연스러운 객체의 순서
Comparator
• 상황에 따른 여러 가지 객체 순서 정의
96
97. 21. 전략 클래스
구현
익명 클래스
• 자주 쓰이면 상수로 정의하고 재사용한다.
97
98. 21. 전략 클래스
구현
구체 클래스
• 익명 클래스는 다른 인터페이스를 추가로 구현할 수 없기 때문에 다른
타입이 되려면(예를 들어, Serializable) 별도 장치가 필요하다.
98
99. 15. 불변 객체
불변 객체란?
일단 생성되면 상태가 바뀌지 않는 객체
String, Integer, BigDecimal, BigInteger 따위의 인스턴스
왜?
불변조건을 만족하게 만들기가 쉽다.
쓰레드 안전!
안전하게 다른 객체의 한 부분을 구성할 수 있다.
안전하게 상수로 공유할 수 있다.
같은 타입의 불변 객체들은 내부 구조를 공유할 수 있다.
계약
99
100. 15. 불변 객체
어떻게?
객체 상태를 변경하는 메소드가 없다.
서브 클래스를 만들지 못하게 한다.
• 클래스를 final로 지정 : 패키지 내부에서 확장도 불가능
• static final factory method를 제공하고 생성자를 감추는 방법
모든 필드를 final로 지정한다.
모든 필드를 private으로 지정한다.
가변 객체를 멤버로 가진다면 외부에서 접근하지 못하게 한다.
외부에서 가변 객체를 공급받는다면 방어 복사한다.
계약
100
101. 15. 불변 객체
단점
객체가 가지는 값마다 별개 객체가 필요하다.
• BigInteger는 내부에 100만 비트 저장 공간을 가진다.
• 달랑 1비트만 바뀐 BigInteger가 또 생성
• 패키지 전용 가변 객체를 써서 해결
• 불변 객체에 대응하는 가변 객체 제공
• String StringBuilder
• BingInteger BitSet
계약
101
102. 16. 상속보다 합성
상속(inheritance)
클래스 상속
인터페이스 상속
합성(composition)
이미 존재하는 객체들을 조합해서 원하는 객체를 만든다.
계약
EngineCar
Tire
102
103. 16. 상속보다 합성
왜?
화이트 박스 vs 블랙 박스
계약
AdvancedList
List
(from util)
<<Interface>>
ArrayList
(from util)
AdvancedList
List
(from util)
<<Interface>>
103
104. 16. 상속보다 합성
클래스 상속
슈퍼 클래스 내부 구현을 알아야 한다.
계약
104
105. 16. 상속보다 합성
클래스 상속
슈퍼 클래스 내부 구현을 알아야 한다.
슈퍼 클래스가 변하면 서브 클래스들도 변한다.
Overriding해서 생긴 문제가 아니다.
• 슈퍼 클래스가 나중에 같은 이름을 가진 메소드를 추가하면 문제가 발
생한다.
슈퍼 클래스가 상속에 맞게 설계되지 않았기 때문이다.
• OCP 위배!
계약
105
106. 16. 상속보다 합성
합성
Set을 구현한 모든 객체로부터 생성 가능
Set이 쓰이는 모든 곳에 이용 가능
계약
합성
전달
106
109. 18. 추상 클래스보다 인터페이스
계약
왜?
인터페이스는 기존 클래스에 쉽게 추가할 수 있다.
• 시그니처가 같은 메소드가 없다면 바로 추가하고 구현하면 된다.
• 추상 클래스는 불가능하거나(이미 다른 클래스를 상속하고 있어서) 부
작용이 발생(상속받은 클래스의 서브 클래스도 모두 클래스 계층 구조
에 포함되므로)한다.
인터페이스는 믹스인을 정의하기 쉽다.
• 자그마한 기능들을 추가하기 쉽다.
• Comparable, Serializable
많은 클래스를 만들어 내지 않고도 복잡한 타입 구조를 만들
수 있다.
109
110. 18. 추상 클래스보다 인터페이스
계약
인터페이스 + 뼈대 구현 추상 클래스
Set, List, Map + AbstractSet, AbstractList, AbstractMap
해당 인터페이스 빠르게 구현할 수 있게 지원한다.
110
111. 18. 추상 클래스보다 인터페이스
계약
인터페이스 + 단순 구현 구체 클래스
android.widget.SimpleAdapter
상황에 따라 그대로 쓰거나 상속 받는다.
111
112. 17. 상속을 위한 클래스 설계
계약
상속을 위한 클래스란?
메소드를 재정의(overriding)해도 문제가 없는 클래스
문제
재정의하는 순간 슈퍼 클래스의 구현이 드러날 수 밖에 없다.
112
113. 17. 상속을 위한 클래스 설계
계약
규칙
재정의 가능한 메소드가 소속 클래스의 다른 메소드를 호출하
면 상세한 동작을 문서화한다.
• Iterator.remove가 영향을 준다.
113
114. 17. 상속을 위한 클래스 설계
계약
규칙
제대로 동작하는 서브 클래스를 만들 때 도움이 될 만한
protected 메소드(드물게 필드)를 제공하고 문서화 한다.
114
115. 17. 상속을 위한 클래스 설계
계약
규칙
생성자에서 절대 재정의 가능한 메소드를 호출하지 않는다.
clone(Cloneable), readObject(Serializable)에서도 마찬가지
다.
115
116. 17. 상속을 위한 클래스 설계
계약
규칙
private helper method
Public Class
Constuctor
Overridable
Method
Public Class
Constuctor
Overridable
Method
Private
Helper
116
117. 20. 딱지 클래스는 죄악
계약
OCP를 기억하라.
public void drawAll(Shape[] shapes)
{
for (int i = 0; i < shapes.length; i++)
{
Shape shape = shapes[i];
if (shape.shapeType() == Shape.CIRCLE)
{
((Circle)shape).drawCircle();
}
else if (shape.shapeType() == Shape.SQUARE)
{
((Square)shape).drawSquare();
}
}
}
117
118. 21. 인터페이스는 타입정의 할 때만
타입?
계약
새
타조 기러기
비행기
여객기 전투기
레이더
날 수 있는
고도를
알려달라
118
120. 38. 인자 유효성 검사
유효성 검사
메소드 제일 처음에 인자 유효성을 검사한다.
Public 메소드면 적절한 예외를 던지고 예외상황을 문서화한다.
Public 메소드가 아니면 assert로 검사한다.
나중에 쓰려고 보관한 인자는 특히 신경 써서 검사한다.
계약
120
121. 39. 방어 복사
왜?
클라이언트는 악의든 무지든 객체의 불변 규칙을 깨려고 노력
한다.
계약
121
122. 39. 방어 복사
왜?
클라이언트는 악의든 무지든 객체의 불변 규칙을 깨려고 노력
한다.
계약
122
123. 39. 방어 복사
공격
클라이언트가 공급하는 모든 가변 객체는 방어 복사한다.
• 유효성 검사 전에 복사해서 보관한다.
• 인자가 아니라 복사본을 대상으로 검사해야 한다.
• Date.clone이 Date 서브 클래스 인스턴스를 반환할 수 도 있기 때문
에 clone으로 복사하면 안 된다.
계약
123
124. 39. 방어 복사
공격
클라이언트에게 가변 객체를 제공할 때 복사한다.
원흉은?
계약
124
125. 40. 시그니처 설계
이해하기 쉽고 일관성 있는 이름을 지어라.
너무 편리한 메소드를 제공하려고 애쓰지 마라.
여러 기능을 한 방에 처리하는 메소드를 제공하지 마라. 만약,
정말 꼭 필요하고 자주 쓰일 때만 제공한다.
인자 타입은 클래스보다 인터페이스를 써라.
Hashtable보다 Map이 낫다.
Boolean보다 요소가 두 개인 Enum을 써라.
계약
125
126. 40. 시그니처 설계
너무 많은 인자를 피한다.
인자가 3개 넘어가면 문서 없으면 안 된다. 타입이 같은 인자라
면 지옥.
인자 줄이는 법
• 메소드 쪼개기
• 리스트의 일부에서 특정 객체의 위치를 찾는 메소드 : findInRange(int,
int, Object) subList(int, int).indexOf(Object)
• 인자를 묶어주는 static 멤버 클래스 도입
• 빌더
계약
126
128. 41. 오버로딩
오버로딩 규칙
오버로딩 규칙만 명세서 34쪽! 헷갈린다 헷갈려.
인자 개수가 같은 메소드는 오버로딩하지 않는다.
가변인자를 사용하면 오버로딩하지 않는다.
인자가 확실히 다르면 인자 개수와 상관없이 오버로딩할 수 있
다.
• BigInteger(byte[]) vs BingInteger(String)
• 생성자는 어쩔 수 없지만 static factory method가 더 바람직하다.
• List.remove(int) vs List.remove(Object)
• 서로 혼동할 경우는 거의 없지만 바람직하지 않다.
계약
128
129. 41. 오버로딩
오버로딩 규칙
오토박싱을 조심하라.
• List<E>.remove(int) vs List<E>.remove(E)
계약
129
130. 41. 오버로딩
오버로딩 규칙
기존 클래스가 진화하는 경우
• 어쩔 수 없는 오버로딩
• 오버로딩 메소드들이 똑같은 일을 처리
계약
130
132. 43. 가변인자
Final 배열을 인자로 받는 메소드는 가변인자로 바꿀 수
있다.
Arrays.asList(Object[]) Arrays.asList(T... a)
• JDK <= 1.4 compile error
• JDK >= 1.5 [[I@3e25a5]
배열 출력법(>= 1.5)
계약
132
133. 43. 가변인자
호출 비용
호출할 때마다 배열을 생성하고 초기화하므로 비용이 크다.
인자 3개 이하 호출이 대부분이면 오버로딩한다.
계약
133
134. 43. Null 반환
Null 반환
뭔가 없을 때 null을 반환하면 좋지 않다.
• 클라이언트에서 null을 확인해야 한다.
• Null을 검사해서 따로 처리해야 하므로 구현이 복잡하다.
계약
134
135. 43. Null 반환
길이가 0인 배열과 컬렉션
비용이 거의 들지 않는다.
• 불변 객체
• 재사용
계약
135