추상클래스 vs 인터페이스

2022. 9. 1. 12:37BackEnd(Java)/Java

✅ 아래 내용들에 대해서 알아보자

- 추상클래스란?
- 추상클래스를 사용하는 이유

- 추상클래스 사용방법
- 인터페이스란?
- 인터페이스를 사용하는 이유
- 인터페이스 사용방법

 

추상클래스란

 추상 클래스란 A, B, C 클래스들 간에 비슷한 필드와 메서드를 공통적으로 추출해 만들어진 클래스이다.

 

자동차 클래스로 예를 들면 자동차 제조사마다 디자인, 모양, 크기 등은 다르겠지만  엑셀, 브레이크, 핸들, 와이퍼 등의 공통적인 기능들이 존재한다. 이러한 공통적인 기능(추상 메서드)을 추상 클래스 안에서 관리하게 된다. 실제 BMW, 기아, 현대자동차들이 실체가 될 것이다.

 

따라서, 실체클래스는 실제 객체를 생성할 수 있고 구체적이지만 추상클래스는 실체 클래스의 공통적인 부분을 추출해 규격을 잡아놓은 클래스이고 메서드와 필드값이 추상적이기 때문에 객체를 생성할 수 없게 된다!

 

추상클래스 포인트 3가지

  1. 추상클래스는 실제 클래스의 공통적인 부분(변수, 메서드)을 추출해서 틀을 잡아놓은 클래스
  2. 추상클래스는 객체를 생성할 수 없음!  => 아직 실체성이 없고 구체적이지 않으닌깐
  3. 추상클래스와 실체클래스는 상속관계이다

 

추상클래스를 사용하는 이유

 1. 필드, 메서드 공통화를 통한 유지보수/통일성 증가

팀 내에 5명의 개발자가 있다고 하자. 각 개발자들에게 자동차 클래스를 구현하라고 요청하면 각 개발자들은 자기 입맛대로 필드명과 메서드명을 사용할 것이다. 이렇게 돼버리면 추후 코드 변경이 필요하게 되면 각 필드와 메서드들을 다 체크해서 변경해줘야 한다. 이로 인해 유지보수하는데 시간을 많이 소요할 것이고 차라리 새로 개발하는 것이 낫다고 생각이 들것이다.

 

그런데 추상클래스를 만들어 각 개발자들이 상속받아서 구현하라고 하면 추상클래스에 미리 정의된 필드와 메서드들이 존재할 것이고, 실체클래스는 규격에 맞게 개발을 할 수밖에 없게 된다!

 

따라서, 필드/메서드 이름을 통일하여 유지보수성을 높이고 통일성을 유지할 수 있다

 

 

2. 실체 클래스 구현시, 시간절약

추상클래스가 미리 구현이 되어있다고 하면 자동차 클래스에 필요한 공통적인 필드와 메서드들이 존재할 것이고 구현 실체 클래스는 @Override를 통해 해당 메서드를 구현하면 될 것이다.

 

따라서, 구현에만 집중할 수 있게 돼서 시간이 절약된다

 

 

3. 규격에 맞는 클래스 구현 가능

개발은 혼자서 하는 것이 아니다. 따라서 정해진 규격(틀) 안에서 구현하는 것이 유지보수 및 확장성에 좋은 설계 가이드가 될 것이다. 여기서 추상클래스의 강력한 기능이 나온다. 추상클래스를 상속받은 실체클래스들은 반드시 추상 메서드를 재정의(오버라이딩)을 해서 구현을 해야 한다.(컴파일 에러 발생시킨다)

 

따라서, 규격에 맞게 개발이 되어있기 때문에 구현부만 수정하면 변화에 유연하게 대처가 가능하게 된다.

 

 

추상클래스를 사용법

아래 예시 코드를 보자.

 

클래스 앞에 abstract를 붙여주면 추상클래스가 된다.

 

Car라는 클래스에는 name이라는 공통 필드와 run이라는 추상클래스를 구현했다.

따라서 Car 클래스는 상속받는 클래스는 반드시 run() 메서드를 재정의(overried)해야 한다!

 

public abstract class Car {
    protected String name;

    //추상 메서드
    public abstract void run(); //구현부 없음

}

 

Car를 상속받는 BMW 클래스이다. extends라는 키워드를 사용해서 상속받고 있다.

 

Car의 추상메서드(run)를 재정의하고 있으며 Car의 공통필드인 name을 공통적으로 사용할 수 있게 된다.

BMW 클래스에는 해당 실체 클래스에 맞게 run이라는 메서드를 구현할 것이고 다형성이 발생한다는 것을 알 수 있다.

 

public class BMW extends Car{

    @Override
    public void run() {
        this.name="BMW";
        System.out.println("BMW 시동 걸기");
    }
}

 

 


인터페이스란

 자바에서 지원하는 추상화 방법 중 하나로써 메서드, 상수, 또는 추상 멤버 집합으로 구성된다.

인터페이스는 다형성을 극대화하고 프로그램 유지보수성을 높이기 위한 방법이며 OOP에서의 핵심개념이기도 하다.

 

인터페이스에 대해서 조금 더 알아보도록 해보자. 😁

 

우리가 특정 기능을 개발하는 데 저장소를 클라우드 스토리지를 써야한다고 가정해보자. 클라우드 스토리지에는 Dropbox, Box, 구글 드라이브, 네이버 MYBOX 등 여러가지가 존재한다. 내부적으로 검토 하였고, DropBox를 쓰겟다고 가정하고 개발을 진행하였다.

 

신나게 개발을 다하였고 테스트까지 다 진행하였다.그런데 갑자기 DropBox가 아니라 구글 드라이브로 변경해야 한다는 청천벽력 같은 소식을 듣게 되었다 😲😲

 

그래서 다시 구글 드라이브에 맞게 개발을 진행하였다. 만약 여기서 또 바뀌게 된다면 어떻게 될까? 지옥의 연속이다 ㅠ 

이처럼, 처음부터 공통적인 규격에 맞게 추상화하지 않고 개발하였으면 유지보수 비용이 많아진다.(변경할 부분이 많다)

 

이러한 문제점을 해결하기 위해 인터페이스를 적용할 수 있다.

인터페이스는 다형성을 이용하여 공통적으로 규격에 맞춰 개발할 수 있고 동일한 기능을 보장하게끔 제공하고 코드 수정을 줄이고 유지보수성을 높인다.

 

즉, 실제 구현체(DropBox, 구글 드라이브등)에 의존하는게 아니라 추상화에 의존하는 것이다.

-> 좀더 나아가면 OOP의 원칙 중 DIP, OCP에도 적용이 가능하다.

 

실제 코드를 보면서 이해해보자

 

CloudFileSystem이라는 인터페이스를 만들어서 드롭박스, 박스, 구글 드라이브, 네이버 드라이브 등 각 클라우드 스토리지에 대해 공통적인 규격을 만드는 인터페이스 클래스를 생성하였다. 

공통 클라우드 파일 통합 관리 인터페이스

 

그리고 설계한 인터페이스를 상속받는 구현체들(네이버, DropBox 클라우드 스토리지 클래스)이 파일 목록 조회를 기능을 재정의한다. 

네이버/DropBox 클래스

 

클라이언트는 드롭박스를 사용하든 네이버 클라우든를 사용하든 상위 개념인 인터페이스에 의존하고 있기때문에 new DropBoxFileSystem 이부분만 변경하여 사용하면 되고, 변경해야할 코드가 작아짐에 따라 유지보수 비용이 작아지게 되고 유연하고 확장성 있는 설계가 가능해진다.

 

클라이언트

 

인터페이스 특징

  • 인터페이스는 일종의 추상(논리) 클래스라 할 수 있다.
  • 인터페이스는 추상클래스(Abstract Class)처럼 추상메서드를 갖지만 추상클래스보다 추상화 정도 더 높아 일반 메서드 구현 및 멤버변수를 가질 수 없다.
  • 오직 추상메서드와 상수만을 멤버로 가질 수 있으며, 그 외는 허용하지 않는다.
  • 인터페이스는 디폴트 메소드를 구현하여 기본적으로 메서드를 제공해주지만, 선택적으로 재정의를 통해 변경 가능하게 만들었다.

 

아래의 코드를 보면서 확인해 보자. 실제로 코드를 실행하려고 하면 컴파일 에러가 발생한다. 

위의 인터페이스 개념 중 일반 멤버와 메서드 구현을 할 수 없게 된다.

public interface TestInterface {

    int a;          //일반 멤버 변수를 가질 수 없음

    void func() {   //일반 메서드 구현을 할수 할 수 없음
        System.out.println("a = " + a);
    }
}

 

 

 

 

 


참고자료

https://limkydev.tistory.com/188

반응형

'BackEnd(Java) > Java' 카테고리의 다른 글

Hash에 대해서 알아보자  (0) 2022.12.06
CompletableFuture  (0) 2022.11.22
call by value? call by reference?  (0) 2022.08.22
java Stream 참고 사이트  (0) 2022.08.21
valueOf, parseInt 비교  (0) 2022.04.07