인터페이스 vs 추상 클래스 (Java)
인터페이스
- 객체의 사용 방법을 정의한다.
- 함수의 구현을 강제한다.
- class 대신 interface 키워드 사용.
- 상수와 메서드만을 가진다.
추상 클래스
- 상속을 위해 사용한다.
- 추상 메서드를 가진다.
- abstract 키워드 사용, new를 통해 객체 생성 불가.
- abstract 메서드는 자식 클래스에서 반드시 구현해야 한다. (오버라이딩)
인터페이스와 추상 클래스 예시
청둥 오리와 모형 오리는 오리(Duck)을 상속한다.
이때 Duck은 추상 클래스다.
청둥 오리(Mallard)는 날 수 있다.
그러나 모형 오리(Model)는 날 수 없다.
따라서 fly()라는 행동은 FlyBehavior 인터페이스로 표현하고
날 수 있는 오리는 FlyWithWings로, 날 수 없는 오리는 FlyNoWay로 구현한다.
구현
인터페이스 FlyBehavior에서 fly를 강제로 구현하도록 한다.
오리가 날 수 있다면 FlyWithWings를, 날 수 없다면 FlyNoWay를 참조하도록 한다.
//head first design patterns 참고
public interface FlyBehavior {
public void fly();
}
public class FlyWithWings implements FlyBehavior {
public void fly() {
System.out.println("날고 있어요");
}
}
public class FlyNoWay implements FlyBehavior {
public void fly() {
System.out.println("저는 못 날아요");
}
}
Java에서 추상 클래스는 abstract 키워드로 간단히 만들 수 있다.
클래스를 abstract로 지정하면 new를 통해 객체를 생성할 수 없다.
또한 abstract로 선언한 메서드는 반드시 구현해야 한다.
추상 클래스 Duck의 자식 클래스들은 FlyBehavior 인터페이스를 가진다.
날든 날지 못하든 interface의 fly()를 이용하여 호출한다.
public abstract class Duck {
FlyBehavior flyBehavior;
public Duck() {/* 생성자 */}
public void performFly() { flyBehavior.fly(); }
public abstract void printName(); /* 추상 메서드는 자식에서 반드시 구현 */
}
public class MallardDuck extends Duck {
public MallardDuck() {
flyBehavior = new FlyWithWings();
}
@Override /* 구현하지 않으면 컴파일 에러 */
public void printName() {System.out.println("mallard");}
}
public class ModelDuck extends Duck {
public ModelDuck() {
flyBehavior = new FlyNoWay(); /* 날지 못한다. */
}
@Override /* 구현하지 않으면 컴파일 에러 */
public void printName() {System.out.println("model");}
}
추상 클래스 Duck은 상속을 위해 사용하였고, abstract 메서드를 이용해 printName 구현을 강제하였다.
인터페이스 FlyBehavior는 fly() 메서드의 구현을 강제한다.
public class Main {
public static void main(String[] args) {
// Duck duck = new Duck(); // 추상 클래스가 아니라면 생성은 가능
// duck.performFly(); // NullPointerException
Duck mallard = new MallardDuck();
mallard.performFly();
mallard.printName();
Duck model = new ModelDuck();
model.performFly(); /* 날지 못한다 */
model.printName();
}
}
즉, 추상 클래스는 상속을 받아 기능을 이용하거나, 확장한다.
인터페이스는 함수의 껍데기만 제공하여 구현을 강제한다. (객체가 fly()를 실행한다는 것을 보장)
인터페이스 vs 추상 클래스 (C++)
c++에서는 interface 키워드가 따로 없다.
따라서 구현이 없고 가상 소멸자와 순수 가상 함수를 이용하여 인터페이스처럼 사용한다.
(Java에서는 가상 소멸자를 따로 구현하지 않는다.)
class FlyBehavior /* interface */
{
public:
virtual ~FlyBehavior() = default;
virtual void fly() = 0;
};
class FlyWithWings : public FlyBehavior
{
public:
void fly() { cout << "날고 있어요" << endl; }
};
class FlyNoWay : public FlyBehavior
{
public:
void fly() { cout << "저는 못 날아요" << endl; }
};
c++에서 순수 가상 함수가 하나라도 있다면 추상 클래스가 된다.
순수 가상 함수를 가지면 인스턴스를 생성할 수 없기 때문이다.
또한 순수 가상 함수를 재정의하지않고 인스턴스를 만들어도 컴파일 에러가 발생한다.
class Duck abstract /* 순수 가상 함수가 있다면 추상 클래스, 이 경우 abstract은 생략 가능 */
{
public:
FlyBehavior* flyBehavior;
virtual ~Duck() = default;
void performFly() { flyBehavior->fly(); }
virtual void printName() = 0;
};
참고로 abstract 키워드를 멤버 함수 뒤에 붙이면 = 0;을 사용하지 않고 순수 가상 함수로 만들 수 있다.
//virtual void printName() = 0;
virtual void printName() abstract;
MallardDuck과 ModelDuck도 c++ ver으로 구현하면 된다.
printName은 Duck의 순수 가상 함수이기 때문에 반드시 구현해야 한다.
override 키워드를 이용해도 되고, 생략해도 된다.
class MallardDuck : public Duck
{
public:
MallardDuck() { flyBehavior = new FlyWithWings(); }
void printName() override { cout << "mallard" << endl; }
};
class ModelDuck : public Duck
{
public:
ModelDuck() { flyBehavior = new FlyNoWay(); }
void printName() /* override 생략 가능 */ { cout << "model" << endl; }
};
main문을 만들어 실행하면 Java와 같은 결과를 얻을 수 있다.
int main(void)
{
// Duck* duck = new Duck(); // 추상 클래스가 아니라면 생성은 가능
// duck->performFly();
Duck* mallard = new MallardDuck();
mallard->performFly();
mallard->printName();
Duck* model = new ModelDuck();
model->performFly();
model->printName();
return 0;
}
전체 코드는 아래와 같다.
#include <stdio.h>
#include <iostream>
#include <memory>
using namespace std;
class FlyBehavior /* interface */
{
public:
virtual ~FlyBehavior() = default;
virtual void fly() = 0;
};
class FlyWithWings : public FlyBehavior
{
public:
void fly() { cout << "날고 있어요" << endl; }
};
class FlyNoWay : public FlyBehavior
{
public:
void fly() { cout << "저는 못 날아요" << endl; }
};
class Duck abstract /* 순수 가상 함수가 있다면 추상 클래스, 이 경우 abstract은 생략 가능 */
{
public:
FlyBehavior* flyBehavior;
virtual ~Duck() = default;
void performFly() { flyBehavior->fly(); }
virtual void printName() = 0;
};
class MallardDuck : public Duck
{
public:
MallardDuck() { flyBehavior = new FlyWithWings(); }
void printName() override { cout << "mallard" << endl; }
};
class ModelDuck : public Duck
{
public:
ModelDuck() { flyBehavior = new FlyNoWay(); }
void printName() /* override 생략 가능 */ { cout << "model" << endl; }
};
int main(void)
{
// Duck* duck = new Duck(); // 추상 클래스가 아니라면 생성은 가능
// duck->performFly();
Duck* mallard = new MallardDuck();
mallard->performFly();
mallard->printName();
Duck* model = new ModelDuck();
model->performFly();
model->printName();
return 0;
}
Java vs C++ 정리
Java 인터페이스
- interface 키워드를 이용.
C++ 인터페이스
- 구현이 없고 가상 소멸자와 순수 가상 함수로만 이루어진 class를 인터페이스로 활용.
Java 추상 클래스
- abstract 키워드를 이용하여 new를 통해 인스턴스를 생성할 수 없도록 함.
- abstract 메서드로 선언하여 자식 클래스에서 반드시 구현하도록 함.
C++ 추상 클래스
- 기본적으로 순수 가상 함수를 포함하면 추상 클래스
- 순수 가상 함수를 가진 클래스는 인스턴스 생성이 불가능하고 자식 클래스에서 반드시 구현.
'개발 > C, C++' 카테고리의 다른 글
원주율 Pi : 라이프니츠 공식 (Leibniz Formula for π) (0) | 2022.08.01 |
---|---|
셔플 Shuffle - 카드 섞기 알고리즘 (0) | 2022.07.08 |
C++ - 순수 가상 클래스 (Pure Virtual Class) (0) | 2021.11.10 |
C++ - 가상 함수 테이블 (Virtual Function Table) (0) | 2021.11.07 |
C++ - 이동 생성자 (move constructor) (0) | 2021.10.29 |
댓글