인터페이스의 장점
인터페이스를 사용하는 이유와 그 장점을 정리해 보면 다음과 같다.
-개발시간을 단축시킬 수 있다.
표준화가 가능하다.
서로 관곙벗는 클래스들에게 관계를 맺어 줄 수 있다.
독립적인 프로그래밍이 가능하다.
- 개발시간을 단축시킬 수 있다.
- 일단 인터페이스가 작성되면, 이를 사용해서 프로그램을 작성하는 것이 가능하다. 메서드를 호출하는 쪽에서는 메서드의 내용에 관계없이 선언부만 알면 되기 때문이다. 그리고 동시에 다른 한쪽에서는 인터페이스를 구현하는 클래스를 작성하게 하면, 인터페이스를 구현하는 클래스가 작성될 때까지 기다리지 않고도 양쪽에서 동시에 개발을 진행할 수 있다.
- 표준화가 가능하다
- 프로젝트에 사용되는 기본 틀을 인터페이스로 작성한 다음, 개발자들에게 인터페이스를 구현하여 프로그램을 작성하도록 함으로써 보다 일관되고 정형화된 프로그램의 개발이 가능하다.
- 서로 관계없는 클래스들에게 관계를 맺어 줄 수 있다.
- 서로 상속관계에 있지도 않고, 같은 조상클래스를 가지고 있지 않은 서로 아무런 관계도 없느 클래스들에게 하나의 인터페이스를 공통적으로 구현하도록 함으로써 관계를 맺어 줄 수 있다.
- 독립적인 프로그래밍이 가능하다.
- 인터페이스를 이용하면 클래스의 선언과 구현을 분리시킬 수 있기 때문에 실제구현에 독립적인 프로그램을 작성하는 것이 가능하다. 클래스와 클래스 간의 직접적인 관계를 인터페이스를 이용해서 간접적인 관계로 변경하면, 한 클래스의 변경이 관련된 다른 클래스에 영향을 미치지 않는 독립적인 프로그래밍이 가능하다.
예를 들어 한 데이터베이스 회사가 제공하는 특정 데이터베이스를 사용하는데 필요한 클래스를 사용햇 프로글매을 작성했다면 이 프로그램은 다른 종류의 데이터베이스를 사용하기 위해서는 전체 프로그램 중에서 데이터베이스 관련된 부분은 모두 변경해야 할 것이다. 그러나 데이터베이스 관련 인터페이스를 정의하고 이를 이용해서 프로그램을 작성하면, 데이터베이스의 종류가 변경되더라도 프로그램을 변경하지 않도록 할 수 있다. 단, 데이터베이스 회사에서 제공하는 클래스도 인터페이스를 구현하도록 요구해야 한다. 데이터베이스를 이용한 응용프로그램을 작성하는 쪽에서는 인터페이스를 이용해서 프로그램을 작성하고, 데이터베이스 회사에서는 인터페이스를 구현한 클래스를 작성해서 제공해야 한다.
실제로 자바에서는 다수의 데이터베이스와 관련된 다수의 인터페이스를 제공하고 있으며, 프로그래머는 이 인터페이스를 이용해서 프로그래밍하면 특정 데이터베이스에 종속되지 않는 프로그램을 작성할 수 있다.
게임에 나오는 모든 유닛들의 최고 조상은 Unit클래스이고 유닛의 종류는 지상유닛(GroundUnit)과 공중유닛(AirUnit)으로 나누어 진다. 그리고 지상유닛에는 Marine, SCV(건설인부), Tank가 있고, 공중유닛으로는 Dropship(수송선)이 있다. SCV에게 Tank와 Dropship과 같은 기계화 유닛을 수리할 수 있는 기능을 제공하기 위해 repair 메서드를 정의한다면 다음과 같을 것이다.
void repair(Tank k) {
//매개변수로 넘겨진 지상유닛(GoundUnit)을 수리한다.
}
void repair(AirUnit au) {
//매개변수로 넘겨진 공중유닛(AirUnit)을 수리한다.
}
이런식으로 수리가 능한 유닛의 개수만큼 다른 버전의 오버로딩된 메서드를 정의해야 할 것이다. 이것을 피하기 위해 매겨변수의 타입을 이 들의 공통 조상으로 하면 좋겠지만 Dropship은 공통 조상이 다르기 떄문에 공통조상의 타입으로 메서드를 정의한다고 해도 최소한 2개의 메서드가 필요할 것이다.
void repair(GroundUnit gu) {}
void repair(AirUnit au) {}
그리고 GroundUnit의 자손 중에는 Marine과 같이 기계화 유닛이 아닌 클래스도 포함될 수 있기 떄문에 repair 메서드의 매개변수 타입으로 GroundUnit은 부적합하다. 현재의 상속관계에서는 이들의 공통점이 없다. 이 때 인터페이스를 이용하면 기존의 상속체계를 유지하면서 이들 기계화 유닛에 공통점을 부여할 수 있다.
다음과 같이 Repairable이라는 인터페이스를 정의하고 수리가 가능한 기계화 유닛에게이 인터페이스를 구현하도록 하면 된다.
interface Repairable () {}
class SCV extends GroundUnit implemnents Repairable{}
class Tank extends GroundUnit implemnents Repairable{}
class Draopship extends GroundUnit implemnents Repairable{}
이제 이 3개의 클래스에는 같은 인터페이스를 구현했다는 공통점이 생겼다. 인터페이스 Repairable에 정의된 것은 아무것도 없고, 단지 인스턴스의 타입체크에만 사용될 뿐이다.
그리고 repair메서드의 매개변수의 타입을 Repairable로 선언하면, 이 메서드의 매개변수로 Repairable인터페이스를 구현한 클래스의 인스턴스만 받아들여질 것이다.
앞으로 새로운 클래스가 추가도리 떄, SCV의 repair메서드에 의해서 수리가 가능하도록 하려면 Repairable인터페이스를 구현하도록 하면 될 것 이다.
class RepairableTest{
public static void main(String[] args) {
Tank tank = new Tank();
Dropship dropship = new Dropship();
Marine marine = new Marine();
SCV scv = new SCV();
scv.repair(tank); // SCV�� Tank�� �����ϵ��� �Ѵ�.
scv.repair(dropship);
// scv.repair(marine);
}
}
interface Repairable {}
class GroundUnit extends Unit {
GroundUnit(int hp) {
super(hp);
}
}
class AirUnit extends Unit {
AirUnit(int hp) {
super(hp);
}
}
class Unit {
int hitPoint;
final int MAX_HP;
Unit(int hp) {
MAX_HP = hp;
}
//...
}
class Tank extends GroundUnit implements Repairable {
Tank() {
super(150); // Tank�� HP�� 150�̴�.
hitPoint = MAX_HP;
}
public String toString() {
return "Tank";
}
//...
}
class Dropship extends AirUnit implements Repairable {
Dropship() {
super(125); // Dropship�� HP�� 125�̴�.
hitPoint = MAX_HP;
}
public String toString() {
return "Dropship";
}
//...
}
class Marine extends GroundUnit {
Marine() {
super(40);
hitPoint = MAX_HP;
}
//...
}
class SCV extends GroundUnit implements Repairable{
SCV() {
super(60);
hitPoint = MAX_HP;
}
void repair(Repairable r) {
if (r instanceof Unit) {
Unit u = (Unit)r;
while(u.hitPoint!=u.MAX_HP) {
/*유닛의 hp 를 증가 시킨다. */
u.hitPoint++;
}
}
}
//...
}
repair메서드의 매개변수 r은 Repairable타입이기 떄문에 인터페이스 Repairable에 정의된 멤버만 사용할 수 있다 .그러나 Repairable에는 정의된 멤버가 없으므로 이 타입의 참조변수로는 할 수 있는 일은 아무 것도 없다. 그래서 instanceof 연산자로 타입을 체크한 뒤 캐스팅하여 Unit클래스에 정의된 HitPoint와 MAX_HP를 사용할 수 있도록 하였다.
그 다음엔 유닛의 현재 체력(hitPoint)이 유닛이 가질 수 있는 최고 체력(MAX_HP)이 될 때 까지 체력을 증가시키는 작업을 수행한다.Marine은 Repairable인터페이스를 구현하지 않았으므로 SCV클래스의 repair메서드의 매개변수로 Marine을 사용하면 컴파일 시에 에러가 발생한다.
건물을 표현하는 클래스 Academy, Bunker, Barrack, Factory가 있고 이들의 조상인 Building클래스가 있다고 하자. 이 때 Barrack클래스와 Factory클래스에 다음과 같은 내용의, 건물을 이동시킬 수 있는, 새로운 메서드를 추가하고자 한다면 어떻게 해야 할까?
void liftOff() {}
void move(int x, int y) {}
void stop() {}
void land() {}
Barrack 클래스와 Factory클래스 모두 위의 코드를 적어주면 되긴 하지만, 코드가 중복 된다는 단점이 있다 .그렇다고 해서 조상클래스인 Building클래스에 코드를 추가해주면, Building클래스의 다른 자손인 Academy와 Bunker클래스도 추가된 코드를 상속받으므로 안 된다. 이런 경우에는 인터페이스를 이용해서 해결할 수가 있다. 우선 새로 추가하고자하는 메서드를 정의하는 인터페이스를 정의하고 이를 구현하는 클래스를 작성한다.
interface Liftable{
void liftoff();
void move(int x, int y):
void stop();
void land();
}
class LiftableImpl implements Liftable{
public void liftOff(){};
public void move(int x, int y){};
public void stop(){};
pulbic void land(){};
}
마지막으로 새로 작성된 인터페이스와 이를 구현한 클래스를 Barrack과 Factory클래스에 적용하면 된다.
Barrack클래스가 Liftable 인터페이스를 구현하도록 하고, 인터페이스를 구현한 LiftableImpl클래스를 Barrack클래스에 포함시켜서 내부적으로 호출해서 사용하도록 한다.
이렇게 함으로써 같은 내용의 코드를 Barrack클래스와 Factory클래스에서 각각 작성하지 않고 LiftableImpl 클래스 한 곳에서 관리할 수 있다. 그리고 작성된 Liftable인터페이스와 이를 구현한 LiftableImple 클래스는 후에 다시 재사용될 수 있을 것이다.
class Barrack extends Building implements Liftable {
LiftableImpl i = new new LiftableImpl();
public void liftOff(){l.liftOff();};
public void move(int x, int y){l.move(x,y);};
public void stop(){i.stop();};
pulbic void land(){i.land();};
}
class factory extends Building implements Liftable {
LiftableImpl i = new new LiftableImpl();
public void liftOff(){l.liftOff();};
public void move(int x, int y){l.move(x,y);};
public void stop(){i.stop();};
pulbic void land(){i.land();};
}
'자바' 카테고리의 다른 글
내부 클래스 (0) | 2023.01.12 |
---|---|
인터페이스의 이해 (0) | 2023.01.12 |
인터페이스 (0) | 2023.01.11 |
생성자(Constructor) (0) | 2022.10.04 |
3.12 클래스 멤버와 인스턴스 멤버간의 참조와 호출 (0) | 2022.08.27 |