# 다형성과 추상 타입

## **상속 개요**

> **상속(Inheritance)** 은 *한 타입을 그대로 사용하면서 구현을 추가할 수 있도록 해주는 방법*을 제공한다.

재정의(Overriding)을 통해, 하위 클래스는 필요에 따라 상위 클래스에 정의된 메서드를 새롭게 구현할 수 있다. 메서드를 재정의하면, 해당 메서드를 실행할 때 상위 타입의 메서드가 아닌 하위 타입에서 재정의한 메서드가 실행된다.

## **다형성과 상속**

> **다형성(Polymorphism)** 은 *한 객체가 여러 가지(Poly) 모습(Morph)을 갖는다는 것을 의미*한다. 여기서 모습이란 타입을 뜻하는데, 결과적으로 **다형성이란 한 객체가 여러 타입을 가질 수 있다** 는 것을 뜻한다.

![](https://1360705058-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LZ0r85EEFTHmLyxK8ag%2F-LZQClozUTRlh5ysloQj%2F-LZQCqPhpy3SegC8nwbm%2FIMG3.png?alt=media\&token=4f587511-2bb6-430b-b0d3-1c7ea294c003)

그림과 같이, 중간에 위치한 객체는 타입A, 타입B, 타입C에 정의된 인터페이스의 구현을 제공하는데, 이 경우 다른 코드에서는 이 객체에게 타입A에 정의된 기능 실행을 요청하거나 타입B 또는 타입C에 정의된 기능 실행을 요청할 수 있다. 즉, 해당 객체를 타입A로도 사용할 수 있고, 타입B로도 사용할 수 있고, 타입C로도 사용할 수 있는 것이다.

자바와 같은 정적 타입 언어에서는 **타입 상속** 을 통해서 다형성을 구현한다.

### **인터페이스 상속과 구현 상속**

**타입 상속** 은 크게 **인터페이스 상속** 과 **구현 상속** 으로 구분해 볼 수 있다. **인터페이스 상속** 은 순전히 타입 정의만을 상속받는 것이다. 자바와 같이 클래스 다중 상속을 지원하지 않는 언어에서는 인터페이스를 이용해서 객체가 다형을 갖게 된다. **구현 상속** 은 클래스 상속을 통해서 이루어진다. 이는 보통 *상위 클래스에 정의된 기능을 재사용하기 위한 목적*으로 사용된다. 또한, 재정의를 통해서 하위 타입은 상위 타입에 정의된 기능을 자신에 맞게 수정할 수 있다.

## **추상 타입과 유연함**

> **추상화(Abstraction)** 는 *데이터나 프로세스 등을 의미가 비슷한 개념이나 표현으로 정의*하는 과정이다.

컴퓨터 분야에서 이런 추상화는 매우 광범위하게 사용되며, 타입도 이런 추상화의 대상이 된다. 추상화된 타입은 오퍼레이션의 시그니처만 정의할 뿐 실제 구현을 제공하지는 못한다. 따라서 의미만 전달할 뿐 상세 구현에 대해서는 알 수 없다.

### **추상 타입과 실제 구현의 연결**

추상 타입과 실제 구현 클래스는 상속을 통해서 연결된다. 즉, 구현 클래스가 추상 타입을 상속받는 방법으로 둘을 연결하는 것이며, *타입 상속을 통해 추상 타입과 구현 클래스 간에 연결* 이 된다.

추상 타입에 대한 구현 클래스는 실제 구현을 제공한다는 의미에서 **콘크리드(Concrete) 클래스** 라고 부른다.

### **추상 타입을 이용한 구현 교체의 유연함**

> 추상화는 공통된 개념을 도출해서 추상 타입을 정의해 주기도 하지만, 또한, 많은 책임을 가진 객체로 부터 **책임을 분리** 하는 촉매제가 되기도 한다. 결론적으로 *추상화는 변경의 유연함*을 증가시켜 준다.

재사용의 중요성으로 봤을때, 하위 수준의 상세 구현보다는 변하지 않는 상위 수준의 로직을 재사용할 수 있도록 설계하는 것이 더 중요하다.

### **변화되는 부분을 추상화하기**

추상화를 잘하기 위해서는 다양한 상황에서 코드를 작성하며 유연한 설계를 만들어 보는 경험을 해봐야 한다. 하지만 다양한 환경에서 많은 경험을 할 수 있는 것이 아니기 때문에, 변화될 부분을 미리 예측해서 추상화하는 것은 어렵다. 이를 위해, 경험하지 않은 분야라 하더라도 추상화할 수 있는 방법은 **변화되는 부분을 추상화하는 것**이다. 요구 사항이 바뀔 때 변화되는 부분은 이후에도 변경될 소지가 많기 때문에 이런 부분을 추상 타입으로 교체하면 향후 변경에도 유연하게 대처할 수 있다.

> 추상화가 되어 있지 않은 코드는 주로 동일 구조를 갖는 if-else 블록으로 드러난다.

추상화를 적용하게 되면, *추상 타입을 사용하는 코드에는 영향을 주지 않으면서, 추상 타입의 실제 구현(콘크리드 클래스)을 변경할 수 있는 유연함*을 얻을 수 있다.

### **인터페이스에 대고 프로그래밍하기**

> 인터페이스에 대고 프로그래밍하기 (Program to Interface)

위는 객체 지향의 유명한 규칙 중 하나로써, 실제 구현을 제공하는 콘크리트 클래스를 사용해서 프로그래밍하지 말고, *기능을 정의한 인터페이스를 사용해서 프로그래밍하라*는 뜻이다. 그러나, 인터페이스는 최초 설계에서 바로 도출되기 보다는 요구 사항의 변화와 함께 점진적으로 도출이 된다. 즉, 인터페이스는 새롭게 발견된 **추상 개념**을 통해서 도출되는 것이다.

추상 타입을 사용하면 기존 코드를 건드리지 않으면서 콘크리트 클래스를 교체할 수 있는 유연함을 얻을 수 있었는데, 주의할 점은 유연함을 얻는 과정에서 타입(추상 타입)이 증가하고 구조도 복잡해지기 때문에 모든 곳에서 인터페이스를 사용해서는 안된다는 것이다. 불필요하게 프로그램의 복잡도만 증가시킬 수 있기 때문에, 변화 가능성이 높은 경우에 한해서만 사용해야 한다.

결과적으로, 변화 가능성이 높은 콘크리트 클래스 대신 이를 추상화한 인터페이스를 사용하면 변경의 유연함이라는 효과를 얻을 수 있지만, 변경 가능성이 매우 희박한 클래스에 대해 인터페이스를 만든다면 오히려 프로그램의 구조만 복잡해지고 유연함의 효과는 누릴 수 없게 된다.

### **인터페이스는 인터페이스 사용자 입장에서 만들기**

> 인터페이스를 작성할 때에는 그 인터페이스를 사용하는 코드 입장에서 작성해야 한다.

### **인터페이스와 테스트**

실제 콘크리트 클래스 대신에 진짜처럼 행동하는 객체를 Mock(가짜, 모의) 객체라고 부르는데, Mock 객체를 사용함으로써 실제 사용할 콘크리트 클래스의 구현 없이 테스트할 수 있다. Mock 객체를 만드는 방법은 다양하게 존재하지만, 사용할 대상을 인터페이스로 추상화하면 좀 더 쉽게 Mock 객체를 만들 수 있게 되며, 이는 사용할 코드의 완성을 기다릴 필요 없이 내가 만든 코드를 먼저 빠르게 테스트할 수 있다.

##

{% hint style="info" %}
**테스트 주도 개발(Test Driven Development)**

**TDD(테스트 주도 개발; Test Driven Development)** 은 *테스트 코드를 먼저 작성하고 실제 코드를 작성함로써, 테스트를 이용해서 실제 제품 코드를 만들어 가는 개발 기법*이다.

**TDD**는 **구현할 코드에 대한 테스트를 먼저 작성**하고, **작성한 테스트를 통과하는 코드를 점진적으로 완성해 나가는 방식으로 개발을 진행**한다. 테스트 주도 개발을 하다 보면 아직 완성하기 힘든 구현 때문에 테스트를 할 수 없는 경우가 발생하는데, 이런 경우 테스트를 할 수 없게 만드는 부분을 별도의 인터페이스로 분리하며 Mock 객체를 만드는 방식으로 테스트를 진행할 수 있다.

이 과정에서, *별도로 분리되는 인터페이스는 **테스트 대상이 되는 클래스와 구분되는 책임**을 갖게 되는 경우가 많으며, 이는 곧 **새로운 책임을 갖는 객체를 도출**하게 된다는 것을 의미*한다.

***객체 지향 설계**가 **객체마다 알맞은 책임을 할당**하고 **각 객체가 주고받는 메시지를 정의**하는 과정임을 생각해 보면, 결국 **TDD는 테스트를 강제함으로써 알맞은 책임을 가진 객체를 도출하도록 유도**한다.*  즉, 객체 지향 설계를 유도하는 좋은 개발 방식이 바로 TDD인 것이다.
{% endhint %}
