오브젝트

2 분 소요

개요

해당 개시글은 책 오브젝트를 읽고 객체지향 설계에서 알아두면 좋을 것들을 정리한 게시글이다. 평소에 감으로 알고 있던 것들을 정리해두는 느낌이고, 독서 감상문 보다는 기억해둬야 할 것들을 작성자의 언어로 풀어쓴 글이라 생각하면 편할 것 같다.

1장

소프트웨어 모듈이 가져야 하는 세 가지 기능

  • 실행 가능성
  • 변경 가능성
  • 이해 가능한 코드

소프트웨어는 끊임없이 변화한다. 우리가 오늘 작성한 코드의 요구사항은 당장이라도 바뀔 수 있는 상황이며 이에 따라 우리는 변화하는 조건 속에서 변경의 취약하지 않는 유연한 코드를 짜야한다.

결합도와 응집력

객체 사이의 의존성이 높은 경우 결합도(Coupling)가 높다고 말한다. 의존성이라는 말 속에는 어떤 객체가 변경될 때 그 객체에게 의존하는 다른 객체도 함께 변경될 수 있다는 사실이 내포되어 있다. 즉, 결합도가 높은 객체는 변경에 매우 취약하다.

모듈 내에서 밀접하게 연관된 작업들만을 수행하고, 그 외의 것들은 다른 객체에게 위임할 경우 응집력(Cohesion)이 높다고 말한다. 함께 바뀌고 머무는 코드라고 하면 이해하기 쉽다.

구조는 응집력이 높고 결합도가 낮을 때 안정적이다.

의인화

현실에서는 수동적인 존재일지라도 객체지향에서는 모든 것이 능동적이고 자율적인 존재로 바뀐다. 객체지향이 현실에서도 꽤나 그럴법한 형태로 되어있어 인지적이라는 것에는 동의하지만, 이토록 능동적이고 자율적인 존재로의 의인화(Antropomorphism)은 코드 작성 중 어색한 부분이라 할 수 있다. (흐린 눈을 뜨고 봐야할 줄도 알아야 한다)

좋은 설계

좋은 설계의 기준은 여러가지가 있겠지만, 기능을 온전히 수행하면서 변경에 대해 매끄럽게 수용할 수 있는 설계 또한 기준 중의 하나이다.

2장

객체지향

객체지향에서 다른 객체끼리의 상호작용은 두 가지가 존재한다.

  • 메세지를 보낸다
  • 메세지를 수신한다. 수신된 메세지를 처리하기 위한 자신만의 방법이 메서드이며, 위와는 전혀 다른 개념이다. 이는 추후 다형성(Polymorphism) 이란 개념의 출발선임을 유념하자.
public class Foo {
	void bar() {} // bar 메서드의 내부 구현(Implemenation)은 Foo 객체가 bar 메세지를 받았을 때 처리하는 자신만의 방식이다.
}

...

public class Main {
	public static void main(String[] args) {
		Foo f = new Foo();
		f.bar(); // f 에게 bar 라는 메세지를 송신한다.
	}
}


컴파일 시간 의존성과 실행 시간 의존성

코드 의존성과 실행 시점 의존성이 다르면 다를수록 코드의 이해도가 떨어진다. 다만, 이와 반대로 재사용성과 확장가능성은 늘어난다. 항상 읽기 쉬운 코드만이 정답이 아니기에 이 또한 트레이드 오프이다. 특히 다형성은 이를 기반으로 이루어진다. 앞서 말했듯이 객체가 어떤 종류냐에 따라 받은 메세지를 처리하는 방식이 달라진다. 그저 호출자는 공개된 인터페이스만을 사용하며 내부 구현이 어떻게 이루어져 있는지는 모르나 객체의 종류에 따라 다르게 응답할 수 있다.

상속과 합성

상속은 아래와 같은 두 가지 단점을 가지고 있다.

캡슐화를 위반한다.

부모를 상속하기 위해서는 내부에 구조를 이해하고 있어야 한다. 구현이 자식 클래스에게 노출되기 때문에 캡슐화가 약화될 수 밖에 없다.

설계가 유연하지 않다.

오직 상속만을 이용해 구현된 클래스가 있다면, 객체를 다른 타입으로 변경하기 위해선 내부의 모든 정보를 복사해 다른 클래스로 옮겨야 한다. 합성은 이런식으로 변경해야할 클래스를 최소화 시켜주는 역할을 할 수 있다.

이러한 이유로, 설계 시에는 합성과 상속을 같이 이용해야 한다. 적절한 트레이드 오프 지점을 찾아 두 가지를 혼합하여 사용하자.

3장

협력

책임

역할

태그:

카테고리:

업데이트: