다형성이란
다형성은 다른 특성인 추상화와 관련있다. 다형성이란 여러가지(poly)의 형태(morphe)를 가지는 성질을 말한다. 상속, 구현 등으로 관계가 형성된 객체들 중 더 구체적인 객체인 하위 객체의 인스턴스는 추상적인 상위 객체의 형태를 지닐 수 있다. 인스턴스의 타입을 추상체로 할당할 수 있다는 것이다. 코드로 예시를 보는 것이 이해가 빠르다.
객체지향의 특성 - 추상화
객체지향의 특성 중 하나인 추상화는 상속과 관련이 있다. 객체지향의 특성 - 상속객체지향의 특성 중 상속에 대해 기록해보려한다. 상속은 두 객체 사이에서 정의되는 관계다. 두 객체는 다음
jtechtalk.tistory.com
추상화 글에 썼던 로그인 클래스로 예시를 들어보겠다.
interface Login {
void login();
}
class KakaoLogin implements Login {
@Override
void login() {
log.info("kakao login process");
}
}
그리고 카카오 로그인 클래스의 인스턴스를 생성하려면 다음과 같다.
// 인스턴스 생성
new KakaoLogin();
// 인스턴스 생성 후 할당
KakaoLogin login = new KakaoLogin();
클래스의 인스턴스를 생성해 변수에 할당할 땐 변수의 타입이 맞아야 할당이 가능하다. 카카오 로그인 클래스의 인스턴스는 가장 단순하게 카카오 로그인 클래스가 타입으로 올 수 있다.
다형성이란 여기서 카카오 로그인 클래스보다 더 추상적인 객체인 Login 클래스로 타입을 지정할 수 있다는 특성이다.
Login login = new KakaoLogin();
특성 자체는 굉장히 간단하다. 구체화된 구상체의 타입은 추상체의 형태가 될 수 있다. 카카오 로그인 인스턴스는 타입이 카카오 로그인만 되는 것이 아니라, 추상체인 Login 클래스도 가능하다. 여러가지의 형태가 가능하다. 그래서 다형성이다.
구체 클래스가 아닌 추상 클래스로 타입이 지정될 수 있다는 사실은 이해가 쉽지만, 어떻게 쓰일 수 있을까?
같은 메세지, 다른 행위
로그인의 구상체로 카카오 로그인 뿐만 아니라 다른 로그인 종류를 추가했다.
class NaverLogin implements Login {
@Override
void login() {
log.info("naver login process");
}
}
class GoogleLogin implements Login {
@Override
void login() {
log.info("google login process");
}
}
이렇게 되면 Login 클래스를 타입으로 가진 변수를 세가지로 할당시킬 수 있다. Login으로 타입이 지정된 변수 login1,2,3은 각각 login기능을 다르게 수행할 수 있다.
Login login1 = new KakaoLogin();
Login login2 = new NaverLogin();
Login login3 = new GoogleLogin();
login1.login(); // kakao login process
login2.login(); // naver login process
login3.login(); // google login process
같은 타입을 지닌 변수들이지만, Login 인터페이스의 구현체에 따라서 그 동작이 달라진다. login()
이라는 같은 메세지를 던졌지만, 행위는 달라지는 것이다.
기능 제한
여기서 Login 인터페이스에는 선언되지 않은 메서드를 각각의 구현체에 추가했다.
class NaverLogin implements Login {
void portal() {
log.info("naver");
}
@Override
void login() {
log.info("naver login process");
}
}
class GoogleLogin implements Login {
void portal() {
log.info("google");
}
@Override
void login() {
log.info("google login process");
}
}
class KakaoLogin implements Login {
void portal() {
log.info("kakao");
}
@Override
void login() {
log.info("kakao login process");
}
}
그렇다면 이 때 login1,2,3은 각각 portal()을 수행할 수 있을까?
login1.portal();
login2.portal();
login3.portal();
이는 불가능하다. 왜냐하면 변수 login1,2,3의 타입은 Login으로 지정해주었고, 추상체인 Login에는 login()만이 선언되어있다. 실제로 인스턴스들은 메서드들을 지니고 있지만, Login의 타입으로 할당된 인스턴스들은 Login 인터페이스에는 없는 다른 메세지를 받을 수 없다.
하위 객체에 대한 메서드 접근 제한은 Java로 예를 들면 인터페이스를 통해 유의미하게 활용할 수 있다. 쇼핑 애플리케이션이 존재하고 해당 애플리케이션을 사용하는 목적은 다양하다. 고객은 제품을 사기 위해 사용하고, 판매자는 제품을 팔기 위해 사용한다. 관리자는 운영을 위한 데이터나 관리를 해야하고, 엔지니어는 개발을 하거나 기술적인 측면에서 관리를 한다.

고객이나 판매자가 관리를 하거나 기술적인 정보를 열람하면 안되고, 관리자나 엔지니어가 거래에 관여하면 바람직하지 못하다. 이렇게 하나의 시스템에 다양한 사용자가 존재할 때, 목적에 맞는 필터를 사용자와 마주(face)시켜 시스템이 사용자마다 허용하는 범위까지만 접근할 수 있도록 제한할 수 있다.
Login 인터페이스 말고 Portal이라는 또 하나의 인터페이스를 만들어봤다. 그리고 네이버 로그인만 Portal을 구현하도록 했다.
interface Portal {
void portal();
}
class NaverLogin implements Login, Portal {
void portal() {
log.info("naver");
}
@Override
void login() {
log.info("naver login process");
}
}
인터페이스의 장점은 이렇게 하위 객체가 여러 인터페이스를 구현할 수 있다는 점이다. Login 인터페이스를 타입으로 가지는 login1,2,3은 여전히 portal이라는 기능에 대한 접근을 하지 못 한다. 카카오 로그인과 구글 로그인은 Portal을 구현하지 않았으니, 두 로그인과 Portal 사이에 추상화는 존재하지 않고, 타입을 Portal로 지정해 줄 수 없다. 이제 네이버 로그인의 인스턴스를 생성해 Portal 타입을 지정한 변수에 할당해보겠다.
Portal portal = new NaverLogin();
portal.portal(); // naver
인스턴스는 같은 네이버 로그인이지만, login2와 portal이 호출할 수 있는 행동은 다르다. 이렇게 접근하는 대상에 따라 객체가 제공할 수 있는 기능을 필터링해 부분적으로만 제한한다.
다형성과 캡슐화
다형성은 캡슐화와 관련이 있다. 객체가 외부 객체에 의해 함부로 접근이 가능하다면, 객체의 기능성이 망가지고 캡슐화가 망가지게 된다. 하지만 객체지향 프로그래밍은 궁극적으로 객체들끼리의 협력을 통해 동작하는 프로그램이다. 외부 객체로부터의 모든 접근을 막을 수는 없다. 그렇기 때문에, 다형성을 통해 부분적으로 기능에 대한 접근을 허용하면서 캡슐화를 최대한 유지할 수 있다.

위 사진에서 Object 2가 100가지 기능을 가지더라도, Object 1과 Object 3가 필요로 하는 기능들만을 각각 인터페이스에 맞춰 제공할 수 있게 된다. 그렇게 객체들의 독립성을 해치지 않고 객체들 간의 안정적인 협력을 만들 수 있다.
References
프로그래머스 데브코스 - 프레임워크를 위한 Java
'객체지향 프로그래밍' 카테고리의 다른 글
Open-Closed Principle: 개방 폐쇄 원칙 (0) | 2024.07.18 |
---|---|
UML - Class Diagram (0) | 2024.05.25 |
객체지향의 특성 - 추상화 (0) | 2024.05.22 |
객체지향의 특성 - 상속 (0) | 2024.05.21 |
객체지향의 특성 - 캡슐화 (정보은닉) (0) | 2024.05.21 |
다형성이란
다형성은 다른 특성인 추상화와 관련있다. 다형성이란 여러가지(poly)의 형태(morphe)를 가지는 성질을 말한다. 상속, 구현 등으로 관계가 형성된 객체들 중 더 구체적인 객체인 하위 객체의 인스턴스는 추상적인 상위 객체의 형태를 지닐 수 있다. 인스턴스의 타입을 추상체로 할당할 수 있다는 것이다. 코드로 예시를 보는 것이 이해가 빠르다.
객체지향의 특성 - 추상화
객체지향의 특성 중 하나인 추상화는 상속과 관련이 있다. 객체지향의 특성 - 상속객체지향의 특성 중 상속에 대해 기록해보려한다. 상속은 두 객체 사이에서 정의되는 관계다. 두 객체는 다음
jtechtalk.tistory.com
추상화 글에 썼던 로그인 클래스로 예시를 들어보겠다.
interface Login {
void login();
}
class KakaoLogin implements Login {
@Override
void login() {
log.info("kakao login process");
}
}
그리고 카카오 로그인 클래스의 인스턴스를 생성하려면 다음과 같다.
// 인스턴스 생성
new KakaoLogin();
// 인스턴스 생성 후 할당
KakaoLogin login = new KakaoLogin();
클래스의 인스턴스를 생성해 변수에 할당할 땐 변수의 타입이 맞아야 할당이 가능하다. 카카오 로그인 클래스의 인스턴스는 가장 단순하게 카카오 로그인 클래스가 타입으로 올 수 있다.
다형성이란 여기서 카카오 로그인 클래스보다 더 추상적인 객체인 Login 클래스로 타입을 지정할 수 있다는 특성이다.
Login login = new KakaoLogin();
특성 자체는 굉장히 간단하다. 구체화된 구상체의 타입은 추상체의 형태가 될 수 있다. 카카오 로그인 인스턴스는 타입이 카카오 로그인만 되는 것이 아니라, 추상체인 Login 클래스도 가능하다. 여러가지의 형태가 가능하다. 그래서 다형성이다.
구체 클래스가 아닌 추상 클래스로 타입이 지정될 수 있다는 사실은 이해가 쉽지만, 어떻게 쓰일 수 있을까?
같은 메세지, 다른 행위
로그인의 구상체로 카카오 로그인 뿐만 아니라 다른 로그인 종류를 추가했다.
class NaverLogin implements Login {
@Override
void login() {
log.info("naver login process");
}
}
class GoogleLogin implements Login {
@Override
void login() {
log.info("google login process");
}
}
이렇게 되면 Login 클래스를 타입으로 가진 변수를 세가지로 할당시킬 수 있다. Login으로 타입이 지정된 변수 login1,2,3은 각각 login기능을 다르게 수행할 수 있다.
Login login1 = new KakaoLogin();
Login login2 = new NaverLogin();
Login login3 = new GoogleLogin();
login1.login(); // kakao login process
login2.login(); // naver login process
login3.login(); // google login process
같은 타입을 지닌 변수들이지만, Login 인터페이스의 구현체에 따라서 그 동작이 달라진다. login()
이라는 같은 메세지를 던졌지만, 행위는 달라지는 것이다.
기능 제한
여기서 Login 인터페이스에는 선언되지 않은 메서드를 각각의 구현체에 추가했다.
class NaverLogin implements Login {
void portal() {
log.info("naver");
}
@Override
void login() {
log.info("naver login process");
}
}
class GoogleLogin implements Login {
void portal() {
log.info("google");
}
@Override
void login() {
log.info("google login process");
}
}
class KakaoLogin implements Login {
void portal() {
log.info("kakao");
}
@Override
void login() {
log.info("kakao login process");
}
}
그렇다면 이 때 login1,2,3은 각각 portal()을 수행할 수 있을까?
login1.portal();
login2.portal();
login3.portal();
이는 불가능하다. 왜냐하면 변수 login1,2,3의 타입은 Login으로 지정해주었고, 추상체인 Login에는 login()만이 선언되어있다. 실제로 인스턴스들은 메서드들을 지니고 있지만, Login의 타입으로 할당된 인스턴스들은 Login 인터페이스에는 없는 다른 메세지를 받을 수 없다.
하위 객체에 대한 메서드 접근 제한은 Java로 예를 들면 인터페이스를 통해 유의미하게 활용할 수 있다. 쇼핑 애플리케이션이 존재하고 해당 애플리케이션을 사용하는 목적은 다양하다. 고객은 제품을 사기 위해 사용하고, 판매자는 제품을 팔기 위해 사용한다. 관리자는 운영을 위한 데이터나 관리를 해야하고, 엔지니어는 개발을 하거나 기술적인 측면에서 관리를 한다.

고객이나 판매자가 관리를 하거나 기술적인 정보를 열람하면 안되고, 관리자나 엔지니어가 거래에 관여하면 바람직하지 못하다. 이렇게 하나의 시스템에 다양한 사용자가 존재할 때, 목적에 맞는 필터를 사용자와 마주(face)시켜 시스템이 사용자마다 허용하는 범위까지만 접근할 수 있도록 제한할 수 있다.
Login 인터페이스 말고 Portal이라는 또 하나의 인터페이스를 만들어봤다. 그리고 네이버 로그인만 Portal을 구현하도록 했다.
interface Portal {
void portal();
}
class NaverLogin implements Login, Portal {
void portal() {
log.info("naver");
}
@Override
void login() {
log.info("naver login process");
}
}
인터페이스의 장점은 이렇게 하위 객체가 여러 인터페이스를 구현할 수 있다는 점이다. Login 인터페이스를 타입으로 가지는 login1,2,3은 여전히 portal이라는 기능에 대한 접근을 하지 못 한다. 카카오 로그인과 구글 로그인은 Portal을 구현하지 않았으니, 두 로그인과 Portal 사이에 추상화는 존재하지 않고, 타입을 Portal로 지정해 줄 수 없다. 이제 네이버 로그인의 인스턴스를 생성해 Portal 타입을 지정한 변수에 할당해보겠다.
Portal portal = new NaverLogin();
portal.portal(); // naver
인스턴스는 같은 네이버 로그인이지만, login2와 portal이 호출할 수 있는 행동은 다르다. 이렇게 접근하는 대상에 따라 객체가 제공할 수 있는 기능을 필터링해 부분적으로만 제한한다.
다형성과 캡슐화
다형성은 캡슐화와 관련이 있다. 객체가 외부 객체에 의해 함부로 접근이 가능하다면, 객체의 기능성이 망가지고 캡슐화가 망가지게 된다. 하지만 객체지향 프로그래밍은 궁극적으로 객체들끼리의 협력을 통해 동작하는 프로그램이다. 외부 객체로부터의 모든 접근을 막을 수는 없다. 그렇기 때문에, 다형성을 통해 부분적으로 기능에 대한 접근을 허용하면서 캡슐화를 최대한 유지할 수 있다.

위 사진에서 Object 2가 100가지 기능을 가지더라도, Object 1과 Object 3가 필요로 하는 기능들만을 각각 인터페이스에 맞춰 제공할 수 있게 된다. 그렇게 객체들의 독립성을 해치지 않고 객체들 간의 안정적인 협력을 만들 수 있다.
References
프로그래머스 데브코스 - 프레임워크를 위한 Java
'객체지향 프로그래밍' 카테고리의 다른 글
Open-Closed Principle: 개방 폐쇄 원칙 (0) | 2024.07.18 |
---|---|
UML - Class Diagram (0) | 2024.05.25 |
객체지향의 특성 - 추상화 (0) | 2024.05.22 |
객체지향의 특성 - 상속 (0) | 2024.05.21 |
객체지향의 특성 - 캡슐화 (정보은닉) (0) | 2024.05.21 |