Dev Book Review/Effective Java

[Effective Java] item6. 불필요한 객체 생성을 피하라

1. 객체 재사용 똑같은 기능의 객체를 매번 사용하기 보다는 객체 하나를 재사용하는 편이 나을 때가 많다. 불변 객체는 언제든 재사용가능하다. String s = new String("hello");// Heap 영역에 존재 String s = "hello"; // String constant pool 영역에 존재 (Perm > Heap) 같은 JVM에서 이와 똑같은 문자열 리터럴을 사용하는 모든 코드가 같은 객체를 재 사용함이 보장된다. string constant pool 영역에 있는지 검색 한 후에, String 객체를 재 사용한다. (== 비교 가능해진다.) Q. string constant pool 영역이 Perm > Heap 일때 이점 [ Java7 ] A. string constant poo..

[Effective Java] item6. 불필요한 객체 생성을 피하라

728x90

1. 객체 재사용

똑같은 기능의 객체를 매번 사용하기 보다는 객체 하나를 재사용하는 편이 나을 때가 많다.

불변 객체는 언제든 재사용가능하다.

String s = new String("hello");	// Heap 영역에 존재
String s = "hello"; // String constant pool 영역에 존재 (Perm > Heap)

같은 JVM에서 이와 똑같은 문자열 리터럴을 사용하는 모든 코드가 같은 객체를 재 사용함이 보장된다.
string constant pool 영역에 있는지 검색 한 후에, String 객체를 재 사용한다. (== 비교 가능해진다.)

Q. string constant pool 영역이 Perm > Heap 일때 이점 [ Java7 ]

A. string constant pool의 모든 문자열도 GC 대상이 될 수 있다.
Perm 영역은 고정된 사이즈고 Runtime에 사이즈가 확장되지 않는다. 따라서 string.intern 메서드 호출은 OutofMemoryException을 발생시킬 수 있었다.

참고 : https://medium.com/@joongwon/string-%EC%9D%98-%EB%A9%94%EB%AA%A8%EB%A6%AC%EC%97%90-%EB%8C%80%ED%95%9C-%EA%B3%A0%EC%B0%B0-57af94cbb6bc
 

Java String 의 메모리에 대한 고찰

Java 언어에서 String은 무심코 사용되는 클래스 중에 하나가 아닐까 생각이 든다. String은 두 가지 생성 방식이 있고 각각의 차이점이 존재한다.

medium.com

 

2. 정적 팩터리 메서드를 제공하는 불변 클래스

Boolean(String); // deprecated
Boolean.valueOf(String); // 권장

팩터리 메서드에서는 호출할 때 마다 새로운 객체를 만들지 않음.
불변객체가 아니라 가변 객체라 해도 사용 중에 변경되지 않음을 안다면 재사용할 수 있다.

 

3. 생성 비용이 비싼 객체라면 캐싱하여 재사용

// 코드 6-1 성능을 훨씬 더 끌어올릴 수 있다!
static boolean isRomanNumeralSlow(String s) {
  return s.matches("^(?=.)M*(C[MD]|D?C{0,3})"
                   + "(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$");
}

// 코드 6-2 값비싼 객체를 재사용해 성능을 개선한다.
private static final Pattern ROMAN = Pattern.compile(
  "^(?=.)M*(C[MD]|D?C{0,3})"
  + "(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$");

static boolean isRomanNumeralFast(String s) {
  return ROMAN.matcher(s).matches();
}

String.matches() : 성능이 중요한 상황에서 반복해 사용하기 적합하지않다.
정규 표현식용 Pattern 인스턴스를 한번 쓰고 버려져서 GC 대상이 되기 때문

성능 개선을 위해 정규 표현식을 표현하는 (불변) Pattern 인스턴스를 클래스 초기화(정적 초기화) 과정에서 직접 생성해 초기화해두고, 이 Pattern 인스턴스를 재사용한다.

지연초기화 : 불필요한 초기화를 없앨 수 있으나 권하지않는다.
코드를 복잡하게 만드는데 성능은 크게 개선되지 않을 때가 많기 때문

 

4. 어댑터 패턴 사용

어댑터(뷰) = 실제 작업은 뒷단 객체에 위임하고, 자신은 제2 인터페이스 역할을 해주는 객체어댑터는 뒷단 객체만 관리한다. 뒷단 객체 하나당 어댑터 하나

Map 인터페이스의 KeySet 메서드 : Map 객체안의 키를 전부 담은 Set 뷰 (어댑터)를 반환

뷰 객체를 여러개 만들거라 생각할 수 있지만, 사실은 매번 같은 인스턴스를 반환한다.

@DisplayName("keyset은 같은 Map을 바라본다")
@Test
void keyset(){
  Map<String, Object> javabom = new HashMap<>();
  javabom.put("Javabom", "Hello");

  Set<String> javabomSet1 = javabom.keySet();
  Set<String> javabomSet2 = javabom.keySet();

  assertThat(javabomSet1).isSameAs(javabomSet2);
}

 

5. 오토박싱(auto boxing)

오토박싱 : 기본타입과 그에 대응하는 박싱된 기본타입의 구분을 흐려주지만 완전히 없애주진 않는다.
성능에서 좋아진다 볼 수 없다.

private static long sum(){
  Long sum = 0L;	
  for(long i =0; i <= Integer.MAX_VALUE; i++){
    sum += i;	// 불필요한 Long 인스턴스가 만들어진다.
  }
  return sum;
}

박싱된 기본타입보다는 기본 타입을 사용하고, 의도치 않은 오토박싱이 숨어들지 않게 주의하자.

 

6. 본인만의 객체 풀(pool)을 만들지 말자

가벼운 객체를 다룰때는 직접 만든 객체 풀보다 훨씬 빠르다. (JVM GC가 잘 되있다.)데이터베이스 연결 - 생성비용이 워낙 비싸니 재사용이 낫다.

아이템 50 : 새로운 객체를 만들어야 한다면 기존 객체를 재사용하지 마라 : 방어적 복사 -> 버그와 보안으로 이어짐
아이템 6 : 불필요한 객체 생성을 피하라 -> 코드형태 성능에 영향

 

댓글

Comments

Dev Book Review/Effective Java

[Effective Java] item5. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라

사용하는 자원에 따라 동작이 달라지는 클래스에는 정적 유틸리티 클래스나 싱글턴 방식이 적합하지 않다. 인스턴스를 생성할 때 생성자에 필요한 자원을 넘겨주는 방식을 사용하자 의존 객체 주입의 형태!! // 정적 유틸리티 클래스 X public class SpellChecker{ private static final Lexicon dictionary = ...; private SpellChecker(){} // 객체 생성 방지 } // 싱글턴 X public class SpellChecker{ private final Lexicon dictionary = ...; private SpellChecker(...){} public static SpellChecker INSTANCE = new SPellChecke..

[Effective Java] item5. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라

728x90

사용하는 자원에 따라 동작이 달라지는 클래스에는 정적 유틸리티 클래스나 싱글턴 방식이 적합하지 않다.

  • 인스턴스를 생성할 때 생성자에 필요한 자원을 넘겨주는 방식을 사용하자
  • 의존 객체 주입의 형태!!
// 정적 유틸리티 클래스 X
public class SpellChecker{
  private static final Lexicon dictionary = ...;
  private SpellChecker(){} // 객체 생성 방지
}

// 싱글턴 X
public class SpellChecker{
  private final Lexicon dictionary = ...;
  private SpellChecker(...){}
  public static SpellChecker INSTANCE = new SPellChecker(...);
}

// 의존 객체 주입 형태 O
public class SpellChecker{
  private final Lexicon dictionary;
  
  public SpellChecker(Lexicon dictionary){
    this.dictionary = Objects.requireNonNull(dictionary);
  }
  
  public boolean isValid(String word){...}
  public List<String> suggestions(String typo){...}
}

스펠 체커가 여러 사전을 사용할 수 있다고 할 때, 싱글턴이나 정적 유틸리티 클래스에서 새로운 사전으로 교체하는 메서드를 추가하기보단, 의존 객체 주입 형태가 올바르다.

  • 불변을 보장한다.
  • 생성자, 정적 팩터리, 빌더 모두에 똑같이 응용할 수 있다.

 

생성자에 자원팩터리를 넘겨주는 방식

팩터리 = 호출할 때마다 특정 타입의 인스턴스를 반복해서 만들어주는 객체 (팩터리 메서드 패턴)

Supplier<T> 인터페이스: 팩터리를 표현함
한정적 와일드 카드 타입 (bounded wildcard type)으로 팩터리 타입 매개변수를 제한한다. (ITypeFactory.class)

IType의 하위타입이어도 map에 put이 가능하다. (리스코프의 치환원칙에 적용 되는 듯)

public class IType {
    private static final int TYPE_Z = 0;
    private static final int TYPE_A = 1;
    private static final int TYPE_B = 2;

    final static Map<Integer, Supplier<? extends ITypeFactory>> map = new HashMap<>();
    static {
	map.put(TYPE_Z, ITypeFactory::new);
        map.put(TYPE_A, A::new);
        map.put(TYPE_B, B::new);
    }
}

class ITypeFactory {}
class A extends ITypeFactory {}
class B extends ITypeFactory {}

 

의존객체 주입 프레임 워크

대거(Dagger), 주스(Guice), 스프링(Spring)
의존객체를 직접 주입하도록 설계된 API를 알맞게 응용해 사용하고 있다.

댓글

Comments

Dev Book Review/Effective Java

[Effective Java] item4. 인스턴스화를 막으려거든 private 생성자를 사용하라

1. 정적 메서드와 정적 필드만을 담을 클래스의 쓰임새 기본타입 값이나 배열 관련 메서드를 모아 둘 때 (java.lang.Math, java.util.Arrays) 특정 인터페이스를 구현하는 객체를 생성해주는 정적 메서드(팩터리)를 모아둘 때 (java.util.Collections) final 클래스와 관련한 메서드 2. 인스턴스화를 막는 방법. 정적 멤버만 담을 때는 인스턴스로 만들어 쓰려고 설계한게 아님. 문제 ) 생성자를 명시하지 않을 때 자동으로 기본 생성자가 만들어짐 (인스턴스화가 가능해진다) 해결 ) private 생성자를 추가해서 클래스의 인스턴스 화를 막을 수 있다 상속을 불가능하게 하는 효과 (하위가 상위 생성자 접근을 할 수 없다.) 직관적이지 않을 수 있으니 적절한 주석을 달자 p..

[Effective Java] item4. 인스턴스화를 막으려거든 private 생성자를 사용하라

728x90

1. 정적 메서드와 정적 필드만을 담을 클래스의 쓰임새

  • 기본타입 값이나 배열 관련 메서드를 모아 둘 때 (java.lang.Math, java.util.Arrays)
  • 특정 인터페이스를 구현하는 객체를 생성해주는 정적 메서드(팩터리)를 모아둘 때 (java.util.Collections)
  • final 클래스와 관련한 메서드

 

2. 인스턴스화를 막는 방법.

정적 멤버만 담을 때는 인스턴스로 만들어 쓰려고 설계한게 아님.

문제 ) 생성자를 명시하지 않을 때 자동으로 기본 생성자가 만들어짐 (인스턴스화가 가능해진다)

해결 ) private 생성자를 추가해서 클래스의 인스턴스 화를 막을 수 있다

  • 상속을 불가능하게 하는 효과 (하위가 상위 생성자 접근을 할 수 없다.)
  • 직관적이지 않을 수 있으니 적절한 주석을 달자
public class UtilityClass {
    // 기본 생성자가 만들어지는 것을 막는다(인스턴스화 방지용).
    private UtilityClass() {
        throw new AssertionError();
    }
    // 나머지 코드는 생략
}

댓글

Comments

Dev Book Review/Effective Java

[Effective Java] item3. private 생성자나 열거 타입으로 싱글턴임을 보증하라

1. 싱글턴이란? 싱글턴(singleton) : 인스턴스를 오직 하나만 생성할 수 있는 클래스 함수와 같은 무상태(stateless) 객체 - 정적 멤버클래스 이야기인가? (Enum 같은거?) 설계상 유일해야하는 시스템 컴포넌트 클래스를 싱글턴으로 만들면 이를 사용하는 클라이언트를 테스트하기 어려울 수 있다. : mock 대체가 불가능 2. 방식 1) public static 멤버가 final 필드 Private 생성자가 public static final 필드를 초기화 할 때 딱 한번만 호출된다. 예외 ) 권한이 있는 클라이언트가 리플렉션 API인 AccessibleObject.setAccessible을 이용해 private 생성자를 호출 방어 ) 생성자를 수정해 두 번째 객체 생성때 예외 던지기 pu..

[Effective Java] item3. private 생성자나 열거 타입으로 싱글턴임을 보증하라

728x90

1. 싱글턴이란?

싱글턴(singleton) : 인스턴스를 오직 하나만 생성할 수 있는 클래스

  • 함수와 같은 무상태(stateless) 객체 - 정적 멤버클래스 이야기인가? (Enum 같은거?)
  • 설계상 유일해야하는 시스템 컴포넌트
  • 클래스를 싱글턴으로 만들면 이를 사용하는 클라이언트를 테스트하기 어려울 수 있다. : mock 대체가 불가능

 

2. 방식 1) public static 멤버가 final 필드

Private 생성자가 public static final 필드를 초기화 할 때 딱 한번만 호출된다.

예외 ) 권한이 있는 클라이언트가 리플렉션 API인 AccessibleObject.setAccessible을 이용해 private 생성자를 호출

방어 ) 생성자를 수정해 두 번째 객체 생성때 예외 던지기

public class Elvis {
    public static final Elvis INSTANCE = new Elvis();
    private Elvis() { }

    public void leaveTheBuilding() {...}
}

[ 장점 ]

  • 이 클래스가 싱글턴임이 API에 명백히 들어난다.
  • 간결하다.

 

3. 방식 2) 정적 팩터리 메서드를 public static 멤버로 제공

Elvis.getInstance는 항상 같은 객체의 참조를 반환한다. (리플렉션 예외는 똑같이 적용)

public class Elvis {
	 	private static final Elvis INSTANCE = new Elvis();
    private Elvis() { }
    public static Elvis getInstance() { return INSTANCE; }

    public void leaveTheBuilding() {...}
}

 

[ 장점 ]

  • API를 바꾸지 않고도 싱글턴이 아니게 변경할 수 있다.
  • 정적 팩터리를 제네릭 싱글턴 팩터리로 만들수 있다. (아이템 30)
  • 정적 팩터리의 메서드 참조를 공급자(supplier)로 사용할 수 있다. (아이템 43,44) [예: Instant::now]

 

4. 방식 3) Enum 타입 방식의 싱글턴

public enum Elvis {
    INSTANCE;
    public void leaveTheBuilding() {...}
}

[ 장점 ]

  • 간결하다
  • 추가 노력 없이 직렬화가 가능하다.
  • 직렬화나 리플렉션 공격에서도 제2의 인스턴스가 생기는 일을 막아준다.

조건 : 싱글턴이 부모클래스가 되어야하는 상황이 생기면 사용할 수 없다.

"대부분 상황에서 원소가 하나뿐인 Enum이 싱글턴을 만드는 가장 좋은 방법이다."

 

5. 싱글턴 클래스의 직렬화

Serializable 구현한다고 선언하는 것만으로는 부족하다. 인스턴스 필드를 transient라고 선언하고 readResolve 메서드를 제공해야한다. (readResolve에서 인스턴스를 반환한다. - 새로운 인스턴스 생성을 방지한다.)

참조 링크 : https://github.com/Java-Bom/ReadingRecord/issues/4

 

추가1. 제네릭 싱글턴 팩터리

public class GenericSingletonFactory {
    // 코드 30-4 제네릭 싱글턴 팩터리 패턴 (178쪽)
    private static UnaryOperator<Object> IDENTITY_FN = (t) -> t;

    @SuppressWarnings("unchecked")
    public static <T> UnaryOperator<T> identityFunction() {
        return (UnaryOperator<T>) IDENTITY_FN;
    }

    // 코드 30-5 제네릭 싱글턴을 사용하는 예 (178쪽)
    public static void main(String[] args) {
        String[] strings = { "삼베", "대마", "나일론" };
        UnaryOperator<String> sameString = identityFunction();
        for (String s : strings)
            System.out.println(sameString.apply(s));

        Number[] numbers = { 1, 2.0, 3L };
        UnaryOperator<Number> sameNumber = identityFunction();
        for (Number n : numbers)
            System.out.println(sameNumber.apply(n));
    }
}

코드 요구사항 : IDENTITY_FN를 UnaryOperator<T>로 형변환 하면 비검사 형변환 경고가 발생 Object는 T가 아니기 때문. 그러나 여기 요구사항에서 항등함수를 담은 클래스를 만들고 싶은 것이기 때문에, 비검사 형변환 경고를 숨겨도 안심할 수 있다.

 

추가2. 메서드 참조 & 공급자

아이템 43 참고

메서드 참조 유형 같은 기능을 하는 람다
정적 Integer::parseInt str -> Integer.parseInt(str)
한정적 (인스턴스) Instant.now()::isAfter Instant then = Instatn.now();
t->then.isAfter(t);
비한정적 (인스턴스) String::toLowerCase str -> str.toLowerCase();
클래스 생성자 TreeMap<K,V>::new () -> new TreeMap<K,V>();
배열 생성자 int[]::new len -> new int[len]

공급자 = Supplier

인터페이스 명 추상 메서드 설명
Supplier<T> T get() T 객체를 리턴한다.

댓글

Comments

Dev Book Review/Effective Java

[Effective Java] item2. 생성자에 매개변수가 많다면 빌더를 고려해라

정적 팩터리 + 생성자의 제약 = 선택적 매개변수가 많을 때 적절히 대응하기 어렵다 1. 대안A) 점층적 생성자 패턴 (telescoping constructor pattern) 필수 매개변수 + 선택 매개변수 원하는 매개변수를 모두 포함한 생성자 중 가장 짧은 것을 골라 호출한다 권장하지 않는다. 매개변수 개수가 많아지면 클라이언트 코드를 작성하거나 읽기 어렵다. 관련 코드 : https://github.com/WegraLee/effective-java-3e-source-code/blob/master/src/effectivejava/chapter2/item2/telescopingconstructor/NutritionFacts.java 2. 대안B) 자바빈즈 패턴 (JavaBeans pattern) 자바..

[Effective Java] item2. 생성자에 매개변수가 많다면 빌더를 고려해라

728x90

정적 팩터리 + 생성자의 제약 = 선택적 매개변수가 많을 때 적절히 대응하기 어렵다

 

1. 대안A) 점층적 생성자 패턴 (telescoping constructor pattern)

필수 매개변수 + 선택 매개변수

원하는 매개변수를 모두 포함한 생성자 중 가장 짧은 것을 골라 호출한다

권장하지 않는다. 매개변수 개수가 많아지면 클라이언트 코드를 작성하거나 읽기 어렵다.

관련 코드 : https://github.com/WegraLee/effective-java-3e-source-code/blob/master/src/effectivejava/chapter2/item2/telescopingconstructor/NutritionFacts.java

 

2. 대안B) 자바빈즈 패턴 (JavaBeans pattern)

자바빈즈 패턴의 단점

  • 객체 하나를 만들려면 메서드를 여러개 호출해야 한다. (객체 1개 : 메서드 호출 N개)
  • 객체가 완전히 생성되기 전까지는 일관성(consistency)이 무너진 상태에 놓인다. -> 런타임문제 디버깅 하드해진다.
    • 클래스를 불변으로 만들 수 없다. -> thread 안전하지 않다.

단점 해소를 위한 freezing 방법이 있지만, freeze 메서드를 확실히 호출했는지 컴파일러가 보증할 방법이 없어서 런타임에 취약하다.

관련 코드 : https://github.com/WegraLee/effective-java-3e-source-code/blob/master/src/effectivejava/chapter2/item2/javabeans/NutritionFacts.java

 

3. 대안 C) 빌더 패턴 (Builder pattern)

  • 필수 매개변수만으로 생성자(정적 팩터리)를 호출해 빌더 객체를 얻는다.
  • 일종의 세터메서드로 원하는 선택 매개변수를 설정한다.
  • build 메서드를 호출해 필요한 객체를 얻는다 (주로 불변)

관련 코드 : https://github.com/WegraLee/effective-java-3e-source-code/blob/master/src/effectivejava/chapter2/item2/builder/NutritionFacts.java

빌더 패턴의 메서드 호출 연결 : 풀루언트 API(fluent API) or 메서드 연쇄(method chaining)

주의 : build() 메서드에서 호출하는 생성자에서 여러 매개변수의 불변식(invariant)을 검사하자

불변식 : 프로그램이 실행되는 동안, 정해진 기간동안 반드시 만족해야하는 조건 (불변[immutable]은 불변식의 극단적인 예)

 

4. 빌더패턴의 쓰임새

계층적으로 설계된 클래스와 함께 사용하기 좋다.

시뮬레이트한 셀프 타입 (simulated self-type) : 추상메서드 self를 더해 하위클래스에서 형변환 없이 메서드 연쇄를 지원한다. [Pizza.Builder 클래스는 재귀적 타입 한정을 이용하는 제네릭 타입이다.]

재귀적 타입 한정 : 자기 자신이 들어간 표현식을 사용해 타입 매개변수의 허용범위를 한정한다.

Pizza.java : https://github.com/WegraLee/effective-java-3e-source-code/blob/master/src/effectivejava/chapter2/item2/hierarchicalbuilder/Pizza.java

공변 반환 타이핑 (covariant return typing) : 하위 클래스의 메서드가 상위 클래스의 메서드가 정의한 반환 타입이 아닌, 하위 타입을 반환하는 기능
NyPizza.Builder는 NyPizza 반환, Calzone.Builder는 Calzone 반환

빌더를 이용하면 가변인수(varargs) 매개변수를 여러개 사용할 수 있다.
addTopping 메서드

 

5. 빌더 패턴의 단점

객체를 만들때 빌더부터 마들어야하는데, 빌더 생성비용이 크지는 않지만, 성능에 민감한 상황에서는 문제이다.

매개변수가 4개이상은 되어야 값어치를 한다.

댓글

Comments

Dev Book Review/Effective Java

[Effective Java] item1. 생성자 대신 정적 팩터리 메서드를 고려하라

1. 정적 팩터리 메서드의 장점 ㄱ. 이름을 가질 수 있다. 생성자정적 팩터리 메서드 시그니처가 같은 생성자가 여러개 필요할 것 같을때 정적 팩터리 메서드를 사용하자 생성자 정적 팩터리 메서드 특징설명 X O 시그니처 1개 N개 ㄴ. 호출될 때 마다 인스턴스를 새로 생성하지 않아도 된다. 불변클래스: 인스턴스를 미리 만들어 놓거나 인스턴스 캐싱으로 재사용하여 불필요한 객체 생성을 피할 수 있다. ex ) Boolean.valueOf(boolean b); 플라이웨이트 패턴 (Flyweight pattern) : 데이터를 공유하여 메모리를 절약하는 패턴, 공통으로 사용되는 객체는 한번만 사용되고 Pool에의해서 관리, 사용된다. (JVM의 String Pool에서 같은 String이 잇는지 먼저 찾는다. [..

[Effective Java] item1. 생성자 대신 정적 팩터리 메서드를 고려하라

728x90

1. 정적 팩터리 메서드의 장점

ㄱ. 이름을 가질 수 있다.

생성자정적 팩터리 메서드
시그니처가 같은 생성자가 여러개 필요할 것 같을때 정적 팩터리 메서드를 사용하자

  생성자 정적 팩터리 메서드
특징설명 X O
시그니처 1개 N개

 

ㄴ. 호출될 때 마다 인스턴스를 새로 생성하지 않아도 된다.

  •  불변클래스: 인스턴스를 미리 만들어 놓거나 인스턴스 캐싱으로 재사용하여 불필요한 객체 생성을 피할 수 있다.
    ex ) Boolean.valueOf(boolean b);
  •  플라이웨이트 패턴 (Flyweight pattern) : 데이터를 공유하여 메모리를 절약하는 패턴, 공통으로 사용되는 객체는 한번만 사용되고 Pool에의해서 관리, 사용된다.
    (JVM의 String Pool에서 같은 String이 잇는지 먼저 찾는다. [불변객체 String])
  •  인스턴스 통제(instance-controlled) 클래스 : 정적 팩터리 방식의 클래스는 언제 어느 인스턴스를 살아 있게 할지를 철저히 통제할 수 있다.
    • 싱글턴 / noninstatntialbe로 만들 수 있다.
    • 불변 값 클래스에서 동치 인스턴스 하나임을 보장 ex ) Enum : 인스턴스가 하나만 만들어짐

 

 

ㄷ. 반환타입의 하위타입 객체를 반환할 수 있는 능력이 있다.

반환할 객체의 클래스를 자유롭게 선택할 수 있다. (유연성)

인터페이스 기반 프레임워크 : 인터페이스를 정적 팩터리 메서드의 반환 타입으로 사용한다.

  • 실제 구현 클래스가 무엇인지 알아보지 않아도 된다.
  • 정적 팩터리 메서드를 사용하는 클라이언트가 얻은 객체를 인터페이스만으로 다룰 수 있다.

 

ㄹ. 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다.

하위타입이기만 하면 어떤 클래스의 객체를 반환하든 상관없다.

EnumSet 클래스 : public 생성자 없이 오직 정적 팩터리만 제공한다. (public 대신 default 생성자.) 이때 원소의 개수에 따라, 반환 타입이 달라진다. RegularEnumSet . JumboEnumSet

하지만 클라이언트에서는 이 두 객체의 존재를 모르고, 다음 릴리즈에서 이 내용을 변경할 수 있는 유연성을 가진다.

 

ㅁ. 정적 팩터리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 된다.

서비스 제공자 프레임워크(service provider framework)를 만드는 근간이 된다. (JDBS)제공자 (provider) : 서비스의 구현체

service provider framework의 통제하에 provider를 클라이언트에 제공한다. (클라이언트와 구현체의 분리)

service provider framework의 컴포넌트

  • 서비스 인터페이스 (service interface) : 구현체의 동작 정의
    JDBC ) Connection
  • 제공자 등록 API (provider registration API) : provider가 구현체를 등록할 때 사용
    JDBC ) DriverManager.registerDriver
  • 서비스 접근 API (service access API) : 클라이언트가 서비스의 인스턴스를 얻을 때 사용클라이언트가가 이 service access API 사용시 원하는 구현체 조건을 명시할 수 있다.
    JDBC ) DriverManager.getConnection
  • 서비스 제공자 인터페이스(service provider interface) : service interface의 인스턴스를 생성하는 팩터리 객체를 설명해준다. (없으면 리플렉션 사용)
    JDBC ) Driver

https://github.com/Java-Bom/ReadingRecord/blob/4e6cb4ad6b586ffa9a93100ceaa8ecccc23a4788/%EC%9D%B4%ED%8E%99%ED%8B%B0%EB%B8%8C%20%EC%9E%90%EB%B0%94/effectiveJava/src/main/java/item1/jdbc/JdbcSample.java

예제 코드에서 DriverManaer.registerDriver(new Driver()) 이런식으로 등록했기 때문에, driver에 따라서 반환객체가 달라지는 걸로 추정

 

2. 정적 팩터리 메서드의 단점

ㄱ. 정적 팩터리 메서드만 제공하면 하위 클래스를 만들 수 없다.

but 이 제약은 상속의 경우에 일어날 수 있는 문제점을 컴포넌트로 유도해서 해결하는 방식이나, 불변타입으로 만들 때 이 제약을 지켜야한다는 점에서 오히려 장점으로 받아들일 수 있다.

 

ㄴ. 정적 팩터리 메서드는 프로그래머가 찾기 어렵다.

사용자는 정적 팩터리 메서드 방식 클래스를 인스턴스화 할 방법을 문서를 이용해 찾아야한다.

from() | of() | valueOf() | instance | create | getType | newType | type

댓글

Comments

Dev Book Review

[서평] Do it 지옥에서 온 문서관리자 깃&깃허브 입문

이지스퍼블리싱 서평단으로 선정되어 작성한 글입니다. 1. 서평단을 신청하게 된 이유 지금은 사라졌지만 2년 전 생활코딩의 지옥에서 온 깃 강의를 조금이나마 들은 적이 있다. 하지만 역시 깃은 프로젝트를 하면서 부딫히면서 시도하다 보니 git reset --hard 도 잘못해보고 소스코드도 날려보면서 어느새 깃이 두렵지 않아졌다. 그러나 학교에서 팀플을 하거나 할 때 동기들은 git을 이용하지 않고 카카오톡으로 복사 붙여 넣기를 하면서 소스코드를 전달하곤 했다. 그래서 나는 대학생들이 git을 좀 더 잘 사용했으면 해서 발표를 통해 친구들에게 깃을 사용하는 방법을 알려주려 하였다. 그래서 생각한 첫번째 방법이 DSC Ewha에서 깃 세미나를 여는 것이었는데, 아무래도 초보자의 눈높이에서 설명하기가 어려웠다..

[서평] Do it 지옥에서 온 문서관리자 깃&깃허브 입문

728x90

이지스퍼블리싱 서평단으로 선정되어 작성한 글입니다.

 

1. 서평단을 신청하게 된 이유

지금은 사라졌지만 2년 전 생활코딩의 지옥에서 온 깃 강의를 조금이나마 들은 적이 있다.

하지만 역시 깃은 프로젝트를 하면서 부딫히면서 시도하다 보니 git reset --hard 도 잘못해보고 소스코드도 날려보면서  어느새 깃이 두렵지 않아졌다.

그러나 학교에서 팀플을 하거나 할 때 동기들은 git을 이용하지 않고 카카오톡으로 복사 붙여 넣기를 하면서 소스코드를 전달하곤 했다.

그래서 나는 대학생들이 git을 좀 더 잘 사용했으면 해서 발표를 통해 친구들에게 깃을 사용하는 방법을 알려주려 하였다.

그래서 생각한 첫번째 방법이 DSC Ewha에서 깃 세미나를 여는 것이었는데, 아무래도 초보자의 눈높이에서 설명하기가 어려웠다.

그래서 신청을 하게 되었다! 초심자의 눈에서 깃을 다시 바라보기 위해서

 

2. 책에 대한 정보

 

여기서 가장 주목할 점은 이고잉님이 저자라는 것이다.

이고잉님이 진행하는 생활코딩의 git 강의를 바탕으로 제작한 책으로 아래의 강의인 지옥에서 온 깃을 참고해서 기초적인 내용을 엄선한 책이었다.

https://opentutorials.org/course/2708

 

지옥에서 온 Git (새 수업으로 대체) - 생활코딩

이 수업은 GITn 시리즈로 완전히 대체 되었습니다. GITn은 보다 많은 내용을 작은 단위로 쪼개서 선택적으로 공부하실 수 있도록 제작된 수업입니다. 아래 주소를 통해서 GITn 을 접할 수 있습니다.  GITn의 입구수업 : https://opentutorials.org/module/3733 지식지도 : https://seomal.org/?i=GIT1 수업소개 이 수업은 Git의 초심자에게는 기본적인 사용법을 중급자는 Git이 동작하는 원리를 소개해드

opentutorials.org

근데 생활코딩을 들어가 보니 이제는 지옥에서 온 git이 아니라 GIT이라는 분류로 들어간다 한다. GITn의 메뉴를 보니, 버전 관리, 브랜치&Conflict, Backup, 협업, Cherrypick&rebase라는 소 분류로 나누어 원하는 부분만 골라 들을 수 있게 되었다.

https://opentutorials.org/course/3838

 

GITn - 생활코딩

생활코딩 > 프로젝트 관리 > GITn GITn 2019-06-27 10:50:18 챕터를 드래그앤드롭해서 위치를 이동시켜 주세요. 저장 취소

opentutorials.org

 

3. 책의 설명 방식

독자들이 책의 설명을 따라 커맨드를 하나하나 따라쳐보면서 그 커맨드를 습득하는 방식의 설명을 택하고 있다.

이때 인상깊었던건, Git Bash에 뜨는 stage과 관련한 설명들도 하나하나 짚어서 설명해주고, 각각의 경우에 어떤 커맨드를 치면 이 설명이 바뀌는지 등등을 자세히 설명해 두었다!

아래 사진이 가장 인상 깊었던 설명이었는데, unstracted file을 보는 방법을 git bash에서 나오는 설명과 대응시키면서 글로 서술하고 있다.

이런 방식의 설명이기 때문에, 사실 책 자체도 글이 많지 않고 오히려 실습을 유도하는 책이라서 책 한 권의 명령어를 그대로 따라 치면서 습득하다 보면 merge나 reset까지는 잘 모르겠지만 아무래도 많이 쓰는 커맨드인 add와 commit을 이용한 버전 관리는 기본으로 할 수 있을 정도가 될 것 같다.

 

4. 책을 통해서 얻어갈 수 있는 것

크게는 세가지를 얻어갈 수 있다.

1. 버전관리 (add commit reset revert)

. git 파일을 init 하여 내 로컬 저장소를 세팅하고, 나의 로컬 저장소를 바탕으로 내가 작성한 코드의 버전을 관리할 수 있다. 깃과 관련해서는 엄청 유명한 말이 있다. 

최종, 정말 최종, 진짜 진짜 최종, 진짜로!! 최종, 마지막 최종...

그동안 이렇게 같은 내용의 파일을 이름만 바꿔서 여러개의 버전을 만들곤 했다 하지만 git을 이용한다면 하나의 파일 안에 내 파일의 모든 버전이 들어가 있게 할 수 있다! (이러기 위해서는 꼭. git 파일을 지우거나 하면 안 된다는 걸 명심) 그리고 현재의 내 파일을 옛날 버전으로 되돌릴 수도 있다!

2 깃허브 백업 (push pull fetch)

내가 깃으로 버전관리한 파일일을 실제로 웹에 google drive처럼 저장을 해두는 방법을 알려준다. 원격 저장소인 repository에 내가 버전 관리한 소스코드를 올림으로써 다른 컴퓨터에서도 내가 버전 관리한 파일을 내려받아 작업을 할 수 있게 해 준다.

3. 협업 (branch merge)

개발자들이 깃을 사용하는 이유라고 볼 수 있다. 개발자들이 각기 다른 내용을 작업하고(branch), 이것들을 실제로 한 코드에 합치는 방법(merge)에 대해 기초적으로 설명해 준다. 

아래 그림이 깃을 이용한 협업에 대해 가장 잘 나타내 주는 것 같다.

추가. 유용한 각종 지식들

위 세 가지 큰 주제의 내용 외에도, 깃을 깃처럼! 깃을 좀 더 개발자처럼 사용할 수 있도록 도와주는 주제들이 있다.

  • 리눅스 명령 연습 : 깃 배시를 사용하기 위해 cd, mkdir 같은 기초적인 리눅스 명령을 알려준다.
  • 깃허브 프로필 관리하기 : 깃허브 사이트에 대한 간략한 설명을 담고 있다.
  • README파일 작성 : 각 레포지토리의 대문과 같은 README 파일 작성을 위한 MD(Markdown) 사용법을 알려준다.
  • github.io 만들기 : 간단하지만 스스로 html 파일을 넣거나 jekyll을 이용해서 깃허브 개인 블로그를 만드는 방법을 소개한다
  • 오픈소스 프로젝트 기여 : 오픈소스 프로젝트에 Pull Request를 날려 한글 번역, 오탈자, 소스 수정 등의 기여방법을 알려준다.

따라서 내가 깃에 대해 모르는 것들이 1, 2, 3에 속하는지를 판단한 후에 이 책을 구입하면 좋을 것 같다. (가장 좋은 방법은 목차를 보는 것!)

01장 깃 시작하기
01-1 지옥에서 온 관리자, 깃
01-2 깃 설치하기
01-3 리눅스 명령 연습하기
01장에서 꼭 기억해야 할 명령
02장 깃으로 버전 관리하기
02-1 깃 저장소 만들기
02-2 버전 만들기
02-3 커밋 내용 확인하기
02-4 버전 만드는 단계마다 파일 상태 알아보기
02-5 작업 되돌리기
02장에서 꼭 기억해야 할 명령
03장 깃과 브랜치
03-1 브랜치란?
03-2 브랜치 만들기
03-3 브랜치 정보 확인하기
03-4 브랜치 병합하기
03-5 브랜치 관리하기
03장에서 꼭 기억해야 할 명령
04장 깃허브로 백업하기
04-1 원격 저장소와 깃허브
04-2 깃허브 시작하기
04-3 지역 저장소를 원격 저장소에 연결하기
04-4 원격 저장소에 올리기 및 내려받기
04-5 깃허브에 SSH 원격 접속하기
04장에서 꼭 기억해야 할 명령
05장 깃허브로 협업하기
05-1 여러 컴퓨터에서 깃허브 저장소 함께 사용하기
05-2 원격 브랜치 정보 가져오기
05-3 협업의 기본 알아보기
05-4 협업에서 브랜치 사용하기
05장에서 꼭 기억해야 할 명령
06장 깃허브에서 개발자와 소통하기
06-1 깃허브 프로필 관리하기
06-2 README 파일 작성하기
06-3 오픈 소스 프로젝트에 기여하기
06-4 깃허브에 개인 블로그 만들기
실무 밀착 꿀팁!

비주얼 스튜디오 코드에서 
깃 활용하기


찾아보기
 

 

5. 권장 독자층

요약하자면 깃을 처음 접해보는 사람이 읽기에 좋은 책이다.

깃으로 commit을 해서 잔디를 찍을 수 있다 하는 사람들의 경우엔 내용을 휙휙 읽어버리기 좋다는 생각이 들었다. (본인이 그랬음)

그렇기 때문에 이 책으로 많은 걸 얻어가고 싶다면 무조건 깃을 처음 써보는 사람이거나 깃에 대한 감이 안 잡힌 사람이 읽기에 좋다!

 

6. 마무리

서평단에 처음 선정이 되었는데, 이지스퍼블리싱은 개발 서적에 있어서 사람들에게 챌린지를 많이 시키는 것 같다.

서평단 말고도  책 한 권을 정하고, 팀을 모아서 책을 바탕으로 공부한 내용을 꾸준히 올리면 책을 제공하는 프로그램을 운영하는 것으로 알고 있다.

이렇게 개발 생태계에 기여하는 프로그램을 운영하는 것 초심자들이 Do It 시리즈를 많이 선택하고, 베스트셀러가 되는데 일조하는 것 같다는 생각이 들었다.

이번에는 나가 아는 주제로 책을 골라 서평을 썼지만, 다음에는 내가 모르는 분야의 책 서평을 도전하면 내 스스로가 많이 얻어갈 수 있는 기회가 될 것 같다. 서평 끝~~!!!

댓글

Comments

Dev Book Review

[객체지향의 사실과 오해] 2장 : 이상한 나라의 객체

※ 제가 책 내용을 이해하기 위한 정리와 함께 개인 주관이 들어가 있습니다 :) 1. 객체지향과 인지능력 객체 = 인간이 분명하게 인지하고 구별 할 수 있는 물리적인, 개념적 중계 객체지향 세계 != 현실세계 2. 객체, 그리고 이상한 나라 2-1. 행동과 상태 앨리스의 행동에 따라 상태가 변한다 상태를 결정하는 것 > 행동 행동의 결과를 결정하는 것 > 상태 => 행동의 결과는 상태에 의존적이다. 행동의 순서도 중요하다 : 순서가 올바라야 목적을 달성할 수 있다. 2-2. 앨리스의 행동과 상태 앨리스는 상태를 갖는다. 상태는 변경가능하다. 앨리스의 상태를 변경 시키는 것은 앨리스의 행동이다 행동의 결과는 상태에 의존적이며 상태를 이용해 서술가능하다. 행동의 순서가 결과에 영향을 미친다. 앨리스는 어떤 ..

[객체지향의 사실과 오해] 2장 : 이상한 나라의 객체

728x90

※ 제가 책 내용을 이해하기 위한 정리와 함께 개인 주관이 들어가 있습니다 :) 

1. 객체지향과 인지능력

객체 = 인간이 분명하게 인지하고 구별 할 수 있는 물리적인, 개념적 중계

객체지향 세계 != 현실세계

 

2. 객체, 그리고 이상한 나라

2-1. 행동과 상태

앨리스의 행동에 따라 상태가 변한다

상태를 결정하는 것 > 행동
행동의 결과를 결정하는 것 > 상태

 

=> 행동의 결과는 상태에 의존적이다.

행동의 순서도 중요하다 : 순서가 올바라야 목적을 달성할 수 있다.

 

2-2. 앨리스의 행동과 상태

  1. 앨리스는 상태를 갖는다. 상태는 변경가능하다.
  2. 앨리스의 상태를 변경 시키는 것은 앨리스의 행동이다
    • 행동의 결과는 상태에 의존적이며 상태를 이용해 서술가능하다.
    • 행동의 순서가 결과에 영향을 미친다.
  3. 앨리스는 어떤 상태여도 식별가능하다.

 

3. 객체 그리고 소프트웨어 나라

 

3-1. 상태(state)

어떤 행동의 결과는 과거에 어떤 행동을 했는가? (의존적이다)
행동의 과정과 결과를 판단하기 위함

 

[ hard ] : 이전 행동의 이력을 모두 합하여 현재 다음행동이 가능한지 판단

[ easy ] : 현재 상태를 보고 다음 행동 여부 판단이 훨씬 간단하다

 

현재 기반 행동 방식 이해가능

 

숫자, 문자열, 양 -> 객체 X 객체의 특성 O

 

 

객체의 상태 : 모든 멤버 변수

  • 객체의 프로퍼티 (property) : 상태를 구성하는 모든 요소 : 정적이다.
  • 프로퍼티 값 (property value) : 행동의 결과로 상태 요소 변경 : 동적이다.

프로퍼티의 종류

  • 링크(link) : 객체와 객체사이 의미있는 연결
    - link 통해서만 메세지 주고 받기가 가능하다
  • 속성(attribute) : 링크와 달리 객체를 구성하는 단순 값

객체의 프로퍼티와 프로퍼티 값

public class Example{
	private String name;
    
    public Example(String param){
    	this.name = param
    }
}

여기서 프로퍼티는 java의 멤버변수인 name을 의미하고

프로퍼티 값은 멤버변수인 name에 들어가는 계속해서 변하는 값을 의미하는 것 같다.

위 코드에서는 Example 생성자에서 인자로 들어온 param이 name의 프로퍼티 값이 된다.

 

링크 프로퍼티와 속성 프로퍼티

class Champion{
	private String name;
	private Ability ability;
}

class Ability{
	private String skill;
}

이렇게 주어져 있을 때.

Champion 객체의 name은 속성 프로퍼티

Champion 객체의 Ability는 링크 프로퍼티를 의미 하는 것 같다.

링크 프로퍼티는 reference object를 말하는 것 같다.

 

3-2. 행동(behavior)

= 상태를 변경시킨다.
= 행동이 부수효과(side effect)를 초래한다.

3-2-1. 상태와 행동

  • 객체의 행동은 상태에 영향 받는다. -> 상호작용이 현재 상태에 어떤 방식으로 의존하는가
  • 객체의 행동은 상태를 변경시킨다. -> 상호작용이 어떻게 현재 상태를 변경시키는가

 

3-2-2. 협력과 행동

상호작용이란?

= 다른 객체와의 협력

= 다른 객체에 요청을 보내기 (메세지 따라 행동 -> 자신의 상태 변경)
= 객체의 행동
= 다른 객체의 상태를 변경하는 것도 가능하다.

  1. 객체 자신의 상태 변경
  2. 행동 내에서 협력하는 다른 객체에 대한 메시지 전송

3-2-3. 상태 캡슐화

캡슐화의 역할

  1. 감추기 : private member 변수를 의미하는 듯 (상태)
  2. 노출하기 : public으로 정의한 메소드를 의미하는 듯 (행동) - 다른객체에 접근 할 수 있는 유일한 방법

상태의 변경 여부는 그객체의 자율에 맞긴다

캡슐화 > 자율성 향상 > 지능 향상 > 협력을 유연하고 간결하게

 

3-3. 식별자(identity)

객체 = 인간의 인지 능력을 이용해 식별 가능한 경계를 가진 모든 사물

식별자란? : 객체를 구분 가능한 특정 property

3-3-1. 값(value)

  • 식별자가 없다
  • 변하지 않는 값을 모델링
  • 불변상태 (immutable state) > 불변하니까 필요하지 않다.
  • 상태가 같은지로 판단한다.
  • 동등성 (equality) : 상태를 이용해 두 값이 같은지 판단가능

== 으로 비교해서 같은게 value인 것 같다!

 

3-3-2. 객체(object)

  • 식별자가 있다
  • 시간에 따라 변경되는 상태를 포함한다
  • 가변상태 (mutable state)
  • 두 객체의 상태가 모두 같아도 두 객체는 다르다 : 식별자가 필요한 이유
  • 동일성 (identical) : 식별자 기반으로 객체가 같은지 판단가능
  • 식별자가 상태에 독립적이다.
...더보기

.equal() 으로 비교해서 같은게 object인 것 같다!

만약 상태로 객체를 구분할 때
행동에 따라 상태가 변하면 그건 다른 객체일 것.

객체 (object) 값 객체 (value object)

= 참조 객체 (reference object)
= 엔티티 (entity)
= 식별자를 지닌 객체

= 식별자를 가지지 않는 값
  1. 객체는 상태를 가지며 상태는 변경 가능하다.
  2. 객체의 상태를 변경시키는 것은 객체의 행동이다.
    • 행동의 결과는 상태에 의존적이며 상태를 이용해 서술할 수 있다.
    • 행동의 순서가 실행 결과에 영향을 미친다.
  3. 객체는 어떤 상태에 있더라도 유일하게 식별 가능하다.

 

4. 기계로서의 객체

객체 상태 조회 객체 상태 변경
쿼리 (query) 명령 (command)

 

기계 버튼

버튼 : 상태조회 / 변경 = 객체 행동 유발 위해 메시지 전송

사용자는 버튼으로 객체 접근 = 인터페이스

 

5. 행동이 상태를 결정한다.

  1. 선 상태 > 후 행동 (bad)
    • 상태가 공용 인터페이스 그대로 노출 가능성 ↑
    • 객체가 협력자 X 고립된 섬 O
    • 객체 재 사용성 저하
  2. 선 행동 > 후 상태 (good)
    • 어떤 행동 - 어떤 객체에 적합 (적합성 결정)
    • 객체의 행동 - 협력에서 완수해야하는 책임 > 책임-주도 설계 : Responseibility-Driven Design (RDD)

'선 상태 > 후 행동'의 의미

 

ㄱ. 공용 인터페이스 그대로 노출가능성이 높아진다

 

멤버 변수를 public으로 놓았을 때를 이야기 하는거 같다.

이 경우 다른 객체에서 멤버변수에 접근해 다른 값으로 그냥 할당할 수 있다.

즉, 멤버변수의 값을 다른 객체에서 바꿀 수 있는 것은 다른 객체가 외부 객체의 상태를 바꿀 수 있다는 말이 된다.

 

그러다보면, public으로 객체의 상태를 변화하는거에 익숙한 메소드, 인터페이스를 만들게 되어,

상태의 노출이 심해지는 사태를 가리키고자 함축한 한 줄인 것 같다.

 

ㄴ. 객체가 고립된 섬이 된다 

 

상태만 넣어두면 슈퍼객체가 된다!!

 

객체 지향을 따르면 여러 객체가 나눠가져야 하는 상태를 한 객체에 몰아넣는다!!

public class Alice{
	int age;
}

public class Juice{
	int amount;
}

원래는 Alice와 Juice가 분리되어, Alice가 Juice를 마시면, method를 이용해서 Juice의 상태 값을 바꾸는게 올바른 설계라면

public class Alice{
	int age;
	int juiceAmout;
}

 Alice 객체 안에 juice의 양을 적어두는 설계를 이야기 하는 것 같다!!

 

찬인 ) 앨리스의 어깨? 팔?에 주스 달려있는거 같은 느낌인데??ㅋㅋㅋㅋ

6. 은유와 객체

6-1. 통념

'객체 지향이란 현실 세계의 모방'

현실세계의 추상화 : 자신이 원하는 특성만 취한다.

현실을 간추리고 요약하여 모방한다.

 

6-2. 의인화 - anthropomorphism

SW 객체 : 추가적인 능력! 현실보다 더 많은 일 가능

 

6-3. 은유 - metaphor

현실 객체 특징 ⊂ SW 객체 특징

프로그램 객체는 현실 객체의 은유이다.

  1. 표현적 차이 (representation gab)
  2. 의미적 차이 (senmantic gab)

차이 = SW 생각하는 모습, 실제 SW 표현의 차이

은유 관계의 실체 객체이름을 SW 객체 이름으로 사용하라 > 표현적 차이 ↓ > 이해 good > 유지보수 good

 

깔끔하게 현실세계 무시하라! = 나만의 새로운 SW 세계 창조하기

 

의인화랑 은유랑 왜 분리한건지 혼돈이었는데

결국은 같은 말을 하고싶었던거겠지 하고 수긍했다ㅋㅋ

댓글

Comments