개발은 재밌어야 한다
article thumbnail
반응형

정보 은닉(캡슐화)이란?

내부의 데이터구현을 외부로 부터 숨기는 것

→ 이를 통해, A는 자신의 역할만 잘 수행하도록 하고, A를 사용하는 B는 A가 내부적으로 어떻게 수행되는지에는 알 필요가 없다

정보은닉의 장점

  • 시스템 개발속도 향상 → 여러 컴포넌트를 병렬로 개발
  • 시스템 관리비용을 낮춘다. → 각 컴포넌트를 빨리 디버깅가능, 교체 용이
  • 정보 은닉 자체가 성능을 높여주지는 않지만, 성능 최적화에 도움 → 다른 컴포넌트에 영향을 주지않고 특정 컴포넌트만 최적화 가능
  • 소프트웨어 재사용성 향상 → 의존성 없다면 다른곳에서 재사용 가능
  • 큰 시스템 제작 난이도 낮춘다 → 개별 컴포넌트 동작 검증 가능

클래스와 인터페이스의 접근 제한자 사용 원칙

  • 모든 클래스와 멤버의 접근성을 가능한 한 좁혀야 한다.
  • 톱레벨 클래스와 인터페이스에 package-private 또는 public 을 쓸 수 있다.
    • public 으로 선언하면 하위 호환성을 유지하려면 영원히 관리해야한다.
    • 패키지 외부에서 쓰지 않을 클래스나 인터페이스라면package-private으로 선언한다. (인터페이스는 클래스 레벨에서는 public or package-private(default) 만사용 가능)
  • 한 클래스에서만 사용하는 package-private 클래스나 인터페이스는 해당 클래스에private static 으로 중첩시키자 → static class를 만들어서 사용하자는 의미
public class A {
	private int a;
}
public class B {
	private int b;
}

B가 A에서만 사용되는 클래스라면 ? → inner 클래스로 static class를 만들어서 사용

public class A {
	private int a;

	private static class  B {
		private int b;
	}
}

멤버의 접근 제한자의 종류

  • private : 해당 클래스만 접근 가능
  • package-private(default) : 같은 패키지에서 접근 가능
    • 접근 제한자를 명시하지 않을 때 적용되지만 인터페이스의 멤버는 기본적으로 public이 적용된다
  • protected : 같은 패키지 + 하위 클래스에서 접근 가능
  • public : 모든 곳에서 접근 가능

접근 제한자 설정 과정

private → package-privagte → protected → public 처럼 일단 접근성을 좁게 하여 선언한 뒤에 권한을 점점 풀어주는 방식으로 설정한다. 권한을 풀어주는 일이 자주 발생한다면 컴포넌트를 더 분해해야하는 것이 아닌지 고민해야 한다.

반대로 멤버 접근성을 좁히는 지 못하는 제약이 존재할 수 있는데

  • 하위 클래스에서 재정의하는 메소드의 접근성 ≥ 상위 클래스에서의 접근성인 경우
public class Parent {
		protected void something() {}
}
public class Child extends Parent {
		@Override
		void something(){} //컴파일 오류
		
}

public class ChildTwo extends Parent {
		@Override
		public void something(){}  //가능
}

이런 제약이 있는 이유 → LSP (liskov-subsitution-principle) 때문

 

리스코프 치환 원칙이란?

상위 클래스의 인스턴스는 하위 클래스의 인스턴스로 대체해 사용할 수 있어야 한다. (자식 클래스는, 최소한 자신의 부모 클래스에서 가능한 행위는 수행 할 수 있어야 한다.)

Parent p = new ChildTwo();

/*상위 클래스에 정의된 메소드라면 호출할 수 있던게, 하위 클래스 재정의로 인해서 
호출 할 수없게 되면 안된다.*/
p.something(); 
반응형

코드를 테스트 할 때 접근 범위

  • 적당한 수준까지는 넓혀도 된다
  • 예를 들어, public 클래스의 private 멤버를 package-private까지 풀어주는 것은 허용할 수 있다
    • 하지만, 그이상은 안된다
  • 즉, 테스트만을 위해 클래스, 인터페이스 멤버를 공개 API로 만들어서는 안된다 (@VisibleForTesting 를 사용할 수도 있다)

public 클래스의 인스턴스 필드는 되도록 public이 아니여야 한다

  • 필드가 가변 객체를 참조하거나, final이 아닌 인스턴스 필드를 public으로 선언하면 해당 필드와 관련된 것들은 불변식을 보장 할 수 없다.
  • 값을 언제든 바꿀수 있다보니 멀티 스레드 환경에서 사용하려면 별도의 동기화 처리가 필요하게 된다. → 스레드 안전하지 않게 된다.
  • 예외의 경우 static final 상수를 public으로 지정할 수 있다
  • 클래스에서 public static final 배열 필드를 두거나 이 필드를 반환하는 접근자 메서드를 제공해서는 안된다
  • 배열의 내용이 수정 가능해지므로만약 어쩔수 없이 한다면 밑의 2가지 방식으로 표현해야 한다
// 보안 허점이 존재
public static final Thing[] VALUES = {...};

// 1번째 방법 : public을 private로 만들고 public 불변 리스트를 추가한다.
private static final Thing[] PRIVATE_VALUES = {...};
public static final List<Thing> VALUES = 
    Collections.unmodifiableList(Arrays.asList(PRIVATE_VALUES));

// 2번째 방법 : 배열을 private로 만들고 복사본을 반환하는 public 메서드를 추가한다.(방어적복사)
private static final Thing[] PRIVATE_VALUES = {...};
public static final Thing[] values(){
  return PRIVATE_VALUES.clone();
}

정리

  • 접근성은 최소화으로 유지하도록 하자
  • public 클래스는 상수용(public static final)필들 외에는 어떠한 public 필드도 가져서는 안된다
    • public static final필드가 참조하는 객체가 불변인지 확인해라
반응형
profile

개발은 재밌어야 한다

@ghyeong

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!