인터페이스란 무엇일까요?
자바를 배우면서 인터페이스란 파트는 특히 어려운 파트인데요.
알고나면 그렇게 어려운 부분이 아닙니다.
인터페이스에 대해 무엇인가 보다는
왜? 어떻게? 사용하는지에 대해 알아봅시다.
인터페이스의 사용
인터페이스의 사용은 쉽게 생각해서 갈아끼우기 위해 사용합니다.
갈아끼운다는게 어떤 뜻인가요? 라고 물어보실 수 있습니다.
자 예시를 들어봅시다.
저희는 어떤 회사로부터 회원관리 시스템을 만들어 돌라는 주문을 받았습니다.
[출저 김영한님의 스프링]
회사에서는 고객들에게 보일 수 있는 비지니스 로직 , 회원저장소 그리고 할인정책을 사용 할 수 있는
할인 정책 클래스 총 4개를 만들어 돌라고 했습니다.
하지만 할인정책 클래스는 Vip고객에게는 10퍼센트 할인을 해줄 것인가?
아니면 무조건 천원을 할인 해 줄 것인가에 대해서 고민할 뿐 아직 아무것도 정하지 못했습니다.
그래서 일단은 사장님의 주문조건은 10퍼센트 할인을 적용해달라는 이야기 뿐이였습니다.
저희는 알겠다고 하고 회원관리 시스템을 만들었지만 사장이 갑자기 우리에게 10퍼센트는 안될 것 같다.
그냥 천원으로 해돌라 라고 했습니다. 만약에 이런상황이 온다면 저희는 인터페이스를 사용하지 않는다면
10퍼센트 할인이 적용된 클래스와 의존관계에 있는 코드들을 전부 바꾸어야 합니다.
하지만 인터페이스를 사용한다면 어떻게 될까요?
그냥 코드를 고치지 않고 바꿀 수 외부 의존으로 바꿀 수 있습니다.
(스프링을 쓴다면 더 쉽게 할 수 있지만 안배웠다고 생각하고 예시를 들겠습니다)
[인터페이스의 사용법]
기본적으로 인터페이스는 추상 클래스입니다.
추상 클래스가 어떤 것 일까요?
이렇게 원래 클래스의 경우 class가 붙지만 인터페이스는 interface로 붙게됩니다.
이렇게 인터페이스가 붙는다면 몸체 = 즉 메서드의 상세 내용을 기재할 수 없습니다.
이게 무슨뜻이나면
이런식으로 메서드 선언부만 가능하고 { }블럭안에 내용을 기재할 수 없다는 뜻이됩니다.
그렇다면 인터페이스는 오직 메서드의 선언부만 가질 수 있습니다.
이렇게 말이죠.
이렇게만들면 인터페이스는 완성입니다.
메서드부분이야 자기가 원하는대로 작성하시면 됩니다.
그럼 인터페이스에 대해 의문이 들 것 입니다.
인터페이스의 장점은? 사용하는 이유?는 어떤 것인지 아직 햇갈리실 수 있습니다.
하지만 인터페이스를 좀 더 깊이 얘기하기전에 저희는 다형성 이라는 것을 알아야합니다.
다형성이란?
다형성이라는 것은 정말 간단한 개념인데 다른 블로그들을 보면 내용을 너무 어렵게
설명한다.
다형성이라는 것은 쉽게 생각해서 큰 객체안에 작은 객체를 넣어서
여러 형태로 변환해서 사용하는 것 인데
더 쉽게 생각해보자
package miniproject.springmvc02.blog;
import miniproject.springmvc02.Grade.Grade;
public class Student {
private String name;
private Grade grade;
private String id;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Grade getGrade() {
return grade;
}
public void setGrade(Grade grade) {
this.grade = grade;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
저희는 학생 클래스를 가지고 있습니다.
학생 클래스는 아이디 값 이름 값 등급 값들을 필드에 존재합니다.
public interface School {
}
그리고 저는 아무 메서드도 필드도 가지지 않은 인터페이스를 만들었습니다.
그렇게 이 인터페이스를 가지고 student에 구현시켜줬습니다.
package miniproject.springmvc02.blog;
import miniproject.springmvc02.Grade.Grade;
public class Student implements School{
private String name;
private Grade grade;
private String id;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Grade getGrade() {
return grade;
}
public void setGrade(Grade grade) {
this.grade = grade;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
그렇게 된다면 이 학생클래스는 스쿨인터페이스를 구현받았습니다.
그렇게 된다면 어떻게 만들 수 있을까요?
저희는 예전에는 클래스를 만들기 위해서는
Student school = new Student();
이렇게 만들었던게 기억나십니까?
이거의 뜻이 어떻게 되나요?
student라는 클래스(메모리의 주소를) student라는 인스턴스에 넣음으로써
타입이 Student라는 클래스의 객체의 접근권한을 허용한다는 뜻입니다.
즉 학생 클래스의 메모리 주소를 school 변수에 넣어서 Student의 접근 권한을 허용하겠다.
즉 사용할 수 있게 하겠다 라는 뜻이 됩니다.
자 하지만 인터페이스로 다형성을 적용한다면 이렇게도 만들 수 있습니다.
School school = new Student();
방금위에랑 타입만 바뀌었습니다.
이 뜻을 해석하면 어떻게 되나요?
Student라는 클래스(메모리 주소)를 school 인스턴스에 넣고 School이라는 타입의 접근권한을 허용 시켜 주겠다.
라는 뜻이 됩니다. 하지만 Student랑 School이랑 다른 타입이 아닌가요? 라고 생각 할 수 있지만
이 Student 클래스는 School을 상속받았습니다.
School이 부모객체이기 때문에( 상속을 받았기 때문에) Student 클래스는
타입이 두개가 됩니다. School 타입과 Student 타입을 가지게 되었죠.
이것이 바로 다형성입니다.
이것을 이해했다면 제가 맨위에 설명했던 것들도 이해할 수 있을 것 입니다.
저희는 DiscountPolicy라는 인터페이스를 만들었습니다. 하지만 사장의 요구에 의해서
10퍼센트 할인정책이아닌 Vip고객이면 무조건 천원을 깎아주는 할인정책으로 바꿔돌라고 합니다.
그렇다면 인터페이스가 없다면 코드를 고치고 또 고쳐야하지만 인터페이스를 사용한다면 코드를 고칠 필요가 없습니다.
한번 예시로 보시죠
public interface DiscountPolicy {
public int DiscountPoint(Member member, int price);
}
이것은 일단 DiscountPolicy입니다.
이 인터페이스는 두개를 자식 객체로 두고 있습니다
10퍼센트 할인이 들어가는 RateDiscount
무조건 천원이 할인되는 FixDiscount
이 두개의 객체가 DisocuntPocliy를 상속 받고있습니다.
public class FixDiscount implements DiscountPolicy{
int DiscountAmount = 1000;
public int DiscountPoint(Member member,int price){
if(member.getGrade()== Grade.VIP){ //enum타입은 ==을 써야한다.
return price-DiscountAmount;
}else return 0;
}
FixDiscount
@Override
public int DiscountPoint(Member member, int price) {
if (member.getGrade() == Grade.VIP) {
return price - (price / 10);
}
else return 0;
}
RateDiscount입니다.
그리고 회원관리 서비스를 만들기 위해서
Order이라는 클래스를 만들었습니다.
여기 필드값에 저장된 값과 DiscountPolicy의 타입을 넣어줘야 합니다.
이때 우리가 배운 다형성이 적용이 되죠.
Rate클래스와 Fix 클래스는 둘 다 DisocuntPolicy를 상속 받았기 때문에
이 두개의 클래스들 중 어느 클래스가 들어가도 DisocuntPolicy는 오류를 내지 않습니다.
그 이유는 부모 객체가 DiscountPolicy이기 때문이죠.
자 여기만으로는 새로운 할인정책을 변경할때 코드를 변경해야 합니다.
하지만 외부에서 이 DisocuntPolicy의 값을 넣어준다면 어떻게 될까요?
저는 외부에서 필드 값을 넣기 위해 Conifg라는 클래스를 만들었습니다.
자 여기서 주의 깊게 잘 보셔야 합니다.
DisocuntPocliy와 MemberRepository 메서드를 만들어서 호출시 객체 주소를 반환하게 해놨습니다.
자 여기서 orderService를 호출시 멤버리파지토리와 디스카운터폴리쉬가 들어가게 됩니다.
그렇게 되면 필드 값
이 부분에 값이 들어가게 되고 사용할 수 있게 됩니다.
자 그럼 잘 생각해보도록 하죠. DiscountPoclicy에 무엇이 들어왔나요?
Vip고객이라면 반드시 천원이 할인이되는 FixDiscountPolicy가 들어왔죠?
하지만! 또 사장의 변심으로
아.. 제가 생각해봐도 천원은 너무 짠 것 같습니다..
그냥 10퍼센트로 다시 변경해주셨으면 좋겠습니다.
라고 했다고 합시다. 저희가 인터페이스를 모를땐 하나하나 코드를 또 일일이 고쳐야합니다.
그러다가 오류가나면? 그러다가 코드가 누락된다면? 대형사고가 날 수도 있는 상황이죠?
하지만 인터페이스를 배운 여러분들이라면 쉽게 대처할 수 있을 것 입니다.
그냥 코드한줄로 변경 할 수 있죠?
그러면 이.. RateDiscount의 필드값이
다시 여기로 들어가겠죠? 다형성에 의해
그러면 FixDiscount가 들어가는게 아닌 RateDiscount가 들어가게 됨으로써
10퍼센트 할인이 적용이 됩니다.
이 단 한줄의 코드만으로요
이것이 바로 다형성입니다.
RateDiscount와 FixDiscount는 두개의 타입을 가지죠.
자기 자신의 타입과 부모 객체인 DiscountPolicy
자바는 뭐죠? 같은 타입의 클래스는 같은 타입의 클래스로만 받을 수 있는게 자바입니다.
하지만 부모객체가 DiscountPolicy이기 때문에 "둘 다" 타입이 DiscountPolicy가 됩니다
그렇기에 저런 마법같은 일이 가능하게 된 것 입니다.
사용할때는 저희가 만든 JavaConFig에서 객체를 꺼내면 됩니다.
왜? 자바는 같은 타입은 같은 타입으로만 받을 수 있고
orderService의 메서드 리턴값 타입이 OrderService이기 때문에 OderService를 사용 할 수 있게 됩니다.
package miniproject.springmvc02.Config;
import miniproject.springmvc02.Discount.DiscountPolicy;
import miniproject.springmvc02.Discount.FixDiscount;
import miniproject.springmvc02.Discount.RateDiscount;
import miniproject.springmvc02.Order.OrderService;
import miniproject.springmvc02.Order.Orderimpl;
import miniproject.springmvc02.Repositroy.MemberRepository;
import miniproject.springmvc02.Repositroy.MemberRepositoryimple;
public class JavaConFig {
public DiscountPolicy discountPolicy(){
return new FixDiscount();
}
public MemberRepository memberRepository(){
return new MemberRepositoryimple();
}
public OrderService orderService(){
return new Orderimpl(memberRepository(),discountPolicy());
}
}
[인터페이스의 주의 할 점]
인터페이스를 정말 편리합니다.
하지만 인터페이스에는 여러가지 제약이 따릅니다.
1. 인터페이스의 구현부가 없다면 다형성을 사용 했을때 메서드를 쓸 수 없다
이게 무슨뜻이나면
저희는 RateDiscount에 DiscountPoint라는 메서드가 있는 걸 알고 있습니다.
자바도 알고있기 때문에
이렇게 JavaConifg에서 꺼냈을때 사용이 가능합니다.
자바도 안다는게 무슨 뜻이나면
인터페이스 부분에 저희는 DiscountPoint라는 메서드를 선언했습니다.
하지만 이 DisCountPolicy를 인터페이스에서 빼버리고 구현체 부분(RateDiscount)에만 선언하면 어떻게 될까요?
자 이 메서드 구현부를 주석처리를 하고 이 다형성을 이용해 DiscountPoint부분을 가져와보겠습니다.
엇 하지만 가져올려고 하니 오류가 뜨면서 인터페이스에 DiscountPoint를 생성하라고 뜹니다.
이렇듯 다형성을 이용해 메서드를 쓸려고 한다면 반드시 인터페이스 부분에 메서드를 넣어줘야만 합니다.
만약에 인터페이스에 아무것도 안넣고 구현체부분에만 메서드를 넣고
다형성으로 값들을 꺼낼려고 한다면 꺼내지지 않습니다.
[이유]
그 이유는 바로 DiscountPolicy가 타입이기 떄문이죠.
저희는 알고있습니다.
자바의 타입은 즉 그 클래스의 접근권한을 허용해주는 것을요
하지만 DiscountPolicy의 접근권한을 허용해줘봤자 DisocuntPolicy 클래스에는 아무 메서드도 없습니다.
그렇다면 타입이 DisocuntPolicy이고 이 안에 아무 메서드도 없기때문에 자바는 인식하지 못합니다.
하지만 이 인터페이스안에 메서드가 있다면 반드시 구현체가 있을테죠.
자바는 그것을 알기때문에 그 구현체를 찾습니다. 바로 implement가 붙은 클래스를요
반드시 구현체가 존재가 있을테니까요. 그러면 자바가 구현체를 찾아주는 것 입니다.
그게 이 인터페이스의 원리입니다.
[두 번째 주의 할 점]
인터페이스는 클래스를 구현시켜준다면 그 클래스는 반드시 인터페이스에 있는 메서드들을 반드시
상속 받아야 합니다.
저는 DiscountPolicy라는 인터페이스를 구현받고 작성할려고 하니 인텔리제이에서
인터페이스에 있는 메서드들을 전부 다 상속받으라고 합니다.
그렇기 때문에 인터페이스를 사용할 때는 반드시 메서드들을 상속 받아야 합니다.
[세번째 주의 할 점]
인터페이스는 오버라이딩을 전재로 하고 있습니다.
그렇기 때문에 메서드의 선언부가 다르면 안됩니다.
다르다면 인터페이스 부분을 고쳐주던가 아니면 선언부를 같게 해줘야 합니다.
원래 매개변수 타입이 int였지만 제가 임의로 String으로 바꿨더니 오류가 뜹니다.
바로 오버라이딩의 전제조건이죠. 이것이 불편 할 수 도 있지만 다르게 생각해야 합니다.
인터페이스를 쓰는 이유는 저는 확실한 메서드를 사용하기 위해 만듭니다.
예를 들어 주문서비스를 만들려고 합니다
그러면 String name int price가격이 들어가야 하는데
제가 실수로 String Price로 바꿔버렸습니다
하지만 저는 이 사실을 모른채 이 시스템을 준다면?
서비스 업체에서는 난리가 나겠죠?
그렇기 때문에 엄격하게 잡아주는 인터페이스가 나쁜 부분이 아니라
오히려 좋은 부분이라 할 수 있습니다.
'JAVA 기초' 카테고리의 다른 글
자바,C언어 변수 초기화 안하면 생기는 일 ,변수 초기화 뜻 (0) | 2023.06.29 |
---|---|
[자바 기초] 객체 참조란? 객체 참조에 대해 알아보자. (0) | 2023.06.28 |
[자바]Thread(쓰레드)의 이해와 개념 (1) | 2022.12.12 |
[자바]iterator 개념부터 적용까지 알아보자 (1) | 2022.11.27 |
[자바]Arraylist,LinkedList 사용방법 (1) | 2022.11.26 |