[우아한테크코스 백엔드 4기] 레벨1 - 클래스와 인스턴스 강의 필기

헌치·2022년 3월 15일
1

우아한테크코스

목록 보기
13/30

클래스와 인스턴스

이전에 읽을 것 : https://codingnuri.com/seven-virtues-of-good-object/

생각해볼 것

  • 둘의 차이
  • 각각이 어떤 유용성/한계를 갖는지

객체와 클래스

개념

객체의 템플릿이 아닌, 객체의 능동적인 관리자!

  • 클래스는 객체의 팩토리(factory)
  • 클래스가 객체를 '인스턴스화한다(instantiate)'

캐싱

public class LottoNumber {
    private static final Map<Integer, LottoNumber> cache = new WeakHashMap(45);

    // ...

    public static LottoNumber of(final int value) {
        return cache.computeIfAbsent(value, LottoNumber::new);
    }
}

computeIfAbsent : 값 캐싱 후 반환. 더 알아보기

생성자

응집도가 높고 견고한 클래스

  • 적은 수의 메서드
  • 상대적으로 더 많은 수의 생성자가 존재
  • 주 생성자(primary constructor) : 초기화 로직
  • 부 생성자(secondary constructor) : 다른 생성자들이 주 생성자를 호출

생성자를 default로? 아니면 getter를?

  • 새로운 클래스로 나누는 것도 방법!

상속과 조합

상속은 코드를 재사용하는 강력한 수단이지만 항상 최선은 아니다. 올바르게 사용하자!

상속

public class Document {
    public int length() {
        return this.content().length;
    }
    public byte[] content() {}
}

public class EncryptedDocument extends Document {
    @Override
    public byte[] content() {}
}

length() 구현을 어떤 클래스에서??

HashSet을 상속?

public class LottoNumbers extends HashSet<LottoNumber> {
    private int addCount = 0;

    @Override
    public boolean add(final LottoNumber lottoNumber) {
        addCount++;
        return super.add(lottoNumber);
    }

    @Override
    public boolean addAll(final Collection<? extends LottoNumber> c) {
        addCount += c.size(); //!!!!!!원하는 결과가 안나옴
        return super.addAll(c);
    }

    public int getAddCount() {
        return addCount;
    }
}
// addAll의 로직은?

    public boolean addAll(final Collection<? extends E> c) {
        boolean modified = false;
        for (E e : c)
            if (add(e)) // 이미 여기서 addCount++ 됨
                modified = true;
        return modified;
    }
  1. 기존 HashSet 함수를 모두 오버라이드 해야 함...
  2. 부모클래스 로직을 이해해야 한다
    • addAll에서 addCount+= c.size -> 안됨!!

상속이 적절한 경우

  1. 하위 클래스가 상위 클래스의 '진짜' 하위 타입
    • 명확한! is-a 관계
  2. 클래스의 행동 확장 X, 정제 O
    • 확장 : 새로운 행동을 덧붙여 기존 행동 부분 보완
    • 정제 : 부분적으로 불완전한 행동을 완전하게 만드는 것
  • 요즘엔 재사용성 보다 유연성이 더 중요한 개념이 됨...
  • 조합이 트렌드?

클래스 상속 여부 미리 선언

  • 상속 x -> final class
    • 명확한 관계 설정(내 자식은 없따)
  • 상속 o -> abstract class

조합

기존 클래스를 확장하는 대신, 새 클래스를 만들고 private 필드로 기존 클래스 인스턴스를 참조

  • 확장이 필요할 때 : 조합 사용

가변 객체와 불변 객체

가변 객체

public class Cash {
    private int dollars;

    public void mul(final int factor) {
        this.dollars *= factor;
    }
}

mul()을 통해 외부에서 Cash 인스턴스 값 수정 가능. 즉, 가변 객체

불변 객체

모든 클래스를 불변 클래스(immutable class)로 만들자!

  • 유지 보수성 UP

불변 객체는 크기가 작다

  • 생성자 안에서만 상태를 초기화할 수 있기 때문

식별자 변경 문제 해결

public class Cash {
    private final int dollars;

    public Cash mul(final int factor) {
        return new Cash(this.dollars * factor);//새 객체 반환
    }
}

map.get(five) 사용 시 식별자 변경(identity mutability)’ 문제가 발생하지 않는다.

즉 DTO 사용 없이, 바로 view로 던져도 된다! (VO)

실패 원자성

public class Cash {
    private int dollars;
    private int cents;

    public mul(final int factor) {
        this.dollars *= factor;

        if (/* 뭔가 잘못 됐다면 */) {
            throw new RuntimeException("oops...");
        }

        this.cents *= factor;
    }
}

객체가 완전하고 견고한 상태가 아니면, 실패!

시간적 결합(temporal coupling) 제거

Cash price=new Cash();
price.setDollars(29);
price.setCents(95);
System.out.println(price);// "$29.95"

기타 장점

1. 스레드 안정성

  • 객체를 여러 스레드에서 동시에(concurrently) 사용 가능
  • 예측 가능한(predictable) 결과를 보장

2. 단순성(simplicity)

  • 객체가 더 단순해질 수록 응집도는 더 높아짐
  • 유지보수가 더 쉬워짐

참고자료

이펙티브 자바 4장 (스터디 자료)

profile
🌱 함께 자라는 중입니다 🚀 rerub0831@gmail.com

0개의 댓글