Ce diaporama a bien été signalé.
Nous utilisons votre profil LinkedIn et vos données d’activité pour vous proposer des publicités personnalisées et pertinentes. Vous pouvez changer vos préférences de publicités à tout moment.

Effective java

3 405 vues

Publié le

Effective Java Lecture

Publié dans : Logiciels
  • Soyez le premier à commenter

Effective java

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

×