2020-09-03 TIL
상속
- 상속의 종류
- 1) specialization
- 가장 많이 사용하는 방법으로 수퍼 클래스를 상속 받아 서브 클래스를 만드는 것이다.
- 수퍼클래스에 새 특징을 추가하거나 새 기능을 추가하여 더 특별한 일을 수행하는 서브클래스를 만든다.
- 그래서 이런 상속을 “특수화/전문화(specialization)”이라 부른다.
- 2) generalization
- 리팩토링 과정에 수행하는 방법이다.
- 서브클래스들의 공통 분모를 추출하여 수퍼클래스를 정의하는 방법을 말한다.
- 그래서 이런 상속을 “일반화/표준화(generalization)”이라 부른다.
- 1) specialization
전문화와 일반화
- 상속(inheritance)을 구현하는 방법에는 두 가지가 있다.
- 전문화(specialization) 와 일반화(generalization) 이다.
전문화
- 기존 클래스의 기능을 그대로 활용할 수 있도록 연결하고
- 여기에 새 기능을 추가하거나 기존 기능을 변경하여 좀 더 특수 목적의 서브 클래스를 만드는 기법이다.
- 마치 부모로부터 무언가를 물려 받는 것과 같아서 상속 이라는 문법의 대표적인 기법으로 알려져 있다.
- 그래서 객체지향 프로그래밍의 상속을 얘기할 때는 대부분 전문화 를 가르키는 말이다.
일반화
- 클래스들의 공통 분모를 추출하여 수퍼 클래스를 정의하는 기법이다.
- 그리고 새로 정의한 수퍼 클래스와 부모/자식 관계를 맺는다.
- 프로그래밍 처음부터 상속을 고려하여 수퍼 클래스를 정의하는 것이 아니라 코드를 리팩토링하는 과정에서 수퍼 클래스를 정의하는 것이기 때문에 초보 개발자에게 적합하다.
- 보통 일반화를 통해 추출된 수퍼 클래스는 서브 클래스에게 공통 분모를 상속해주는 것이 목적이다.
- 직접 인스턴스를 생성하고 사용하기 위해 만든 클래스가 아니다.
- 그래서 일반화를 통해 도출한 수퍼 클래스는 보통 추상 클래스로 정의한다.
- 수퍼 클래스를 만든 다음 서브 클래스가 수퍼 클래스를 상속하도록 클래스명에 extends XXX 를 해준다.
의존성 주입(dependency injection; DI)
- Handler가 의존하는 객체를 내부에서 생성하지 않고 생성자를 통해 외부에서 주입 받는 것.
- 이를 통해 의존 객체 교체가 쉽도록 만드는 것.
다형적 변수(Polymorphic Variables)
- Handler에서 사용할 목록 관리 객체를 수퍼 클래스의 레퍼런스로 선언하는 것.
- 이를 통해 List의 서브 객체로 교체하기 쉽도록 만드는 것.
일반화
- 서브 클래스에서 구현하는 방법이 다른 메서드는 수퍼 클래스에서 구현하지 않고 서브 클래스에서 오버라이딩 하게 한다.
- 그럼 수퍼 클래스에 이 메서드를 둘 필요가 있는가?
- 서브 클래스들이 갖춰야 할 기능을 정의한다는 면에서 의미가 있다.
- 즉 서브 클래스들에게 반드시 수퍼 클래스의 기능을 갖도록 강제하는 효과가 있다.
- 목록을 다루는데 필요한 의존 객체를 특정 클래스로 제한하지 않고 상위 클래스의 레퍼런스를 사용하여 여러 서브 클래스를 사용할 수 있도록 유연성을 제공한다.
- 다형적 변수의 문법을 활용
- 의존 객체를 해당 클래스에서 직접 생성하지 않고 외부로부터 주입받는다.
- 생성자의 특성을 사용한다.
- 생성자? 객체가 작업하는데 필요한 값 또는 의존 객체를 준비하는 메서드.
추상 클래스와 추상 메서드
추상 클래스
- 서브 클래스에 기본 기능 및 공통 분모를 상속해 주는 역할을 하는 클래스다.
- new 명령을 통해 인스턴스를 생성할 수 없다.
- 상속의 기법 중에서 일반화를 통해 수퍼 클래스를 정의한 경우 보통 추상 클래스로 선언한다.
- 추상 메서드를 가질 수 있다.
- 추상 메서드는 아무 일도 하지 않는다.
- 일반화를 통해 만든 수퍼 클래스는 일반 클래스로 만들어진다. 일반 클래스는 인스턴스를 만들 수 있으나 일반화를 통해 만들어진 수퍼 클래스가 추상 메서드를 가지고 있을때는 인스턴스를 만들 수 없다. 그러므로 수퍼 클래스를 이용하여 인스턴스를 만들지 못하도록 수퍼 클래스를 추상 클래스로 변경한다.
- 인스턴스를 만들 수 없기 때문에 레퍼런스를 만들어 사용한다.
- 추상 클래스는 일반 메서드나 일반 변수도 가질 수 있다.
추상 메서드
- 서브 클래스에 따라 구현 방법이 다른 경우 보통 추상 메서드로 선언한다.
- 서브 클래스에서 반드시 구현해야 하는 메서드다.
- 즉 서브 클래스를 정의할 때 반드시 해당 메서드를 구현하도록 강제하고 싶다면 추상 메서드로 선언한다.
- 수퍼 클래스가 추상 클래스이고 수퍼 클래스의 추상 메서드를 상속받았다면 서브 클래스 역시 추상 메서드를 가질 수 있는 추상 클래스가 되어야 한다.
- 서브 클래스가 일반 클래스일때 수퍼 클래스의 인스턴스를 호출하는데 수퍼 클래스는 추상 클래스이기 때문에 인스턴스를 만들 수 없다. 그러므로 서브 클래스는 구현되지 않는 메서드를 가지고 있게 되며 오류로 인식한다. 이것을 해결하기 위해 서브 클래스 역시 추상 클래스가 된다.
- 추상 클래스와 인터페이스 만이 추상 메서드를 가질 수 있다.
- 수퍼 클래스에서는 추상 메서드를 구현하면 안된다. 고로 {}를 붙일 수 없다.
- 서브 클래스에서 구체적인 실행을 구현하라는 의미이다.
- 서브 클래스마다 구현 내용이 다를 수 있다면 추상 메서드로 선언한다.
- 또는 서브 클래스에게 구현하도록 강제하고 싶을때도 추상 메서드로 선언한다.
- 수퍼 클래스에 존재하는 추상 메서드는 추상 메서드를 상속하는 서브 클래스에서 구현해야 한다.
- 수퍼 클래스에서 추상 메서드의 의미는 해당 역할을 수행하는데 반드시 있어야 하는 기능을 정의하는데 의미가 있다.
일반 클래스와 추상 메서드
- 상속받은 추상 클래스의 추상 메서드를 구현하지 않는다면 해당 클래스는 추상 클래스로 선언해야 한다.
- 상속받은 추상 메서드를 오버라이드를 붙여서 구현한다면 해당 서브 클래스는 일반 클래스로 선언할 수 있다.
템플릿 메서드 패턴 (디자인 패턴)
- 기본적인 실행 패턴은 수퍼 클래스에서 정의하고 구체적인 실행에 관해서는 서브 클래스에서 정의하는 설계 패턴을 템플릿 메서드 패턴이라고 한다.
- 추상 클래스인 수퍼 클래스에서 전체적인 논리 흐름을 일반 메서드로 정의한다.
- 서브 클래스들은 이 메서드를 그대로 상속받기 때문에 오버라이딩 하지 않는 이상은 그대로 작동한다.
- 추상 메서드로 구체적인 실행 방법이 정의되지 않은 템플릿 메서드를 선언한다.
- 각각의 서브 클래스에 추상 메서드를 오버라이딩 하여 구체적인 행위를 정의한다.
인터페이스(interface)
- caller(호출자)와 callee(피호출자) 사이의 호출 규칙을 정의하는 문법.
- 메서드 형식을 의미한다.
- 메서드 몸체는 정의하지 않는다.
- 메서드 몸체는 이 규칙에 따라 만드는 클래스에서 정의하는 것이다.
- 인터페이스를 이행하는 클래스는 인터페이스에 선언된 모든 메서드를 반드시 구현해야만 일반 클래스(concrete class)가 된다.
- 만약 구현하지 않으면 추상 메서드인채로 존재한다.
- 따라서 추상 메서드를 가진 클래스가 된다.
- 추상 메서드는 추상 클래스만이 가질 수 있기 때문에 추상 클래스로 바꿔야 한다.
- 인터페이스를 이행하는 클래스를 구현체라고 한다.
- 인터페이스 구현체(implementor)는 인터페이스에 선언된 모든 메서드를 구현해야 한다.
- 문법
- class 클래스명 implements 인터페이스명, 인터페이스명, … {…}
- 같은 사용 규칙에 따라 만든 클래스는 한 인터페이스 레퍼런스에 그 객체 주소를 저장할 수 있어서 편리하다.
- 인터페이스에 선언된 메서드를 구현한다고 해서 다 되는 것이 아니다.
- 반드시 implements 키워드를 사용해서 인터페이스를 구현한다고 클래스명에 선언해야 한다.
- implements 키워드를 사용해서 인터페이스를 구현한다고 선언하지 않는다면 공식적으로 구현체가 아니기 때문에 레퍼런스에 담을 수 없다.
- 사용하려는 객체가 같은 인터페이스를 구현하였다면 동일한 사용 규칙에 따라 메서드를 호출할 수 있어 편리하다.
- 호출에 일관성이 있어 유지 보수에 좋다.
- 인터페이스 레퍼런스?
- 해당 인터페이스에 따라 작성된(사용규칙을 준수하는) 클래스의 인스턴스 주소를 저장한다.
- 다음과 같이 표현할 수 있다.
- 인터페이스 사용 규칙에 따라 작성된 클래스의 인스턴스 주소를 저장하는 변수.
- 인터페이스를 구현한 클래스의 인스턴스 주소를 저장하는 변수.
- 구현체의 인스턴스 주소를 저장하는 변수.
- 구현체의 객체 주소를 저장하는 변수.
- 구현 객체를 저장하는 변수.
- 객체를 저장하는 변수.
- 객체를 가리키는 변수.
인터페이스 문법
01
- 인터페이스에 정의하는 메서드는 호출 규칙이다.
- 규칙은 공개되어야 한다.
- 그러므로 인터페이스에 선언되는 모든 메서드는 퍼블릭이다.
- 생략된 것은 퍼블릭이 생략된 것이다.
- 그러므로 인터페이스에 선언되는 모든 메서드는 퍼블릭이다.
- 규칙은 클래스가 따라야 한다.
- 따라서 인터페이스에 선언되는 모든 메서드는 몸체를 구현하지 않는다.
- 몸체를 구현하지 않아야 하기 때문에 추상 메서드로 선언한다.
- 추상 메서드임을 나타내기 위해 abstract를 붙인다.
- 몸체를 구현하지 않아야 하기 때문에 추상 메서드로 선언한다.
- 따라서 인터페이스에 선언되는 모든 메서드는 몸체를 구현하지 않는다.
- 규칙은 공개되어야 한다.
- 결론
- 따라서 인터페이스에 선언하는 모든 메서드는 퍼블릭이고 abstract 이다.
02
- 인터페이스는 규칙이기 때문에 인스턴스를 만들 수 없다.
- 따라서 new 명령어로 인스턴스를 생성할 수 없다.
- 인터페이스에 선언되는 모든 변수는 퍼블릭으로 공개되고 스태틱으로 클래스 변수가 된다.
- 규칙은 변경되어선 안되기 때문에 모든 변수는 final로 선언되고 값을 바꿀 수 없다.
- final 변수는 선언과 동시에 특정 값으로 초기화해야 한다.
- 스태틱 블록을 둘 수 없다.
- 따라서 new 명령어로 인스턴스를 생성할 수 없다.
- 결론
- 인터페이스의 모든 변수는 퍼블릭이고 스태틱이고 파이널 변수이다.
- 퍼블릭 스태틱 파이널은 생략 가능하다.
03
- 인터페이스의 모든 메서드는 퍼블릭이다.
- 인터페이스를 구현체는 내부에 존재하는 메서드의 공개 범위를 퍼블릭보다 줄일 수 없다.
04
- 서브 인터페이스 구현 : 수퍼 인터페이스의 메서드까지 구현해야 한다.
- 인터페이스 레퍼런스는 그 인터페이스를 구현한 클래스의 인스턴스를 가리킬 수 있다.
- 그리고 해당 인터페이스에 선언된 메서드만 호출할 수 있다.
- 물론 수퍼 인터페이스의 메서드도 포함한다.
- 인터페이스 레퍼런스는 그 인터페이스를 구현한 클래스의 인스턴스를 가리킬 수 있다.
05
- 다중 인터페이스 구현 : 한 클래스는 여러개의 인터페이스(규칙)를 구현할 수 있다.
- 규칙들 중에 메서드가 겹치더라도 메서드 명, 파라미터, 리턴 타입이 같다면 구현할 수 있다.
- 다중 인터페이스 구현을 했을 때 인터페이스를 레퍼런스에 담는다면 각 인터페이스의 규칙에 따라 사용할 수 있다.
06
- 다중 인터페이스 구현이 불가능한 경우
- 메서드 명과 파라미터는 같지만 리턴 타입이 다른 경우이다.
- 클래스는 이름이 같고 파라미터 형식이 다른 메서드를 여러개 정의할 수 있지만(오버로딩), 이름이 같고 파라미터 형식도 같고 리턴 타입이 다른 메서드를 여러개 정의할 수 없다.
- 리턴 타입만 다른 메서드를 여러개 만들 수 없기 때문에 리턴 타입이 다른 메서드를 가지고 있는 각각의 인터페이스를 한 클래스에서 구현할 수 없다.
- 다른 규칙을 가지고 있으면 동시에 만족시킬 수 없다.
- 리턴 타입만 다른 메서드를 여러개 만들 수 없기 때문에 리턴 타입이 다른 메서드를 가지고 있는 각각의 인터페이스를 한 클래스에서 구현할 수 없다.