본문 바로가기
개발/C, C++

인터페이스 vs 추상 클래스 (Java, C++ 비교)

by 피로물든딸기 2022. 2. 5.
반응형

C, C++ 전체 링크

 

인터페이스 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()를 실행한다는 것을 보장)

JavaCode.zip
0.00MB


인터페이스 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++ 추상 클래스

- 기본적으로 순수 가상 함수를 포함하면 추상 클래스

- 순수 가상 함수를 가진 클래스는 인스턴스 생성이 불가능하고 자식 클래스에서 반드시 구현.

반응형

댓글