인터페이스와 추상 클래스의 전략적 사용

 

객체지향 프로그래밍에서 추상화를 구현하는 두 가지 주요 메커니즘인 인터페이스와 추상 클래스는 각각 고유한 특성과 용도를 가지고 있습니다. 이들을 전략적으로 활용하면 더 유연하고 유지보수가 용이한 코드를 작성할 수 있습니다.

인터페이스와 추상 클래스의 차이점

특성 추상 클래스 인터페이스
정의 부모 클래스로 설계된 클래스 클래스의 청사진, 추상 메서드 모음
선언 abstract 키워드 사용 interface 키워드 사용
메서드 유형 추상 메서드와 일반 메서드 모두 포함 가능 오직 추상 메서드만 포함 (Java 8부터 default, static 메서드 지원)
변수 유형 final, non-final, static, non-static 변수 지원 오직 static과 final 변수만 지원
상속 extends 키워드 사용, 단일 상속만 지원 implements 키워드 사용, 다중 구현 지원
추상화 정도 부분적 추상화(0-100%) 완전한 추상화(100%)
생성자 생성자 포함 가능 생성자 포함 불가

추상 클래스 사용이 적합한 경우

  1. 공통 기반 클래스가 필요할 때: 상속을 사용하여 관련 클래스들에게 공통 기반 클래스를 제공하고자 할 때 추상 클래스가 적합합니다.

  2. 비공개 멤버가 필요할 때: 인터페이스의 모든 메서드는 public이어야 하지만, 추상 클래스는 private, protected 멤버를 선언할 수 있습니다.

  3. 향후 메서드 추가가 예상될 때: 인터페이스에 새 메서드를 추가하면 해당 인터페이스를 구현하는 모든 클래스를 변경해야 하지만, 추상 클래스는 기본 구현을 제공할 수 있어 하위 클래스의 변경이 필요 없습니다.

  4. 상태 관리가 필요할 때: 추상 클래스는 상태(인스턴스 변수)를 포함할 수 있어 객체의 상태를 관리해야 하는 경우에 유용합니다.

  5. 컴포넌트 버전 관리가 필요할 때: 추상 클래스를 사용하면 기본 클래스가 업데이트될 때 상속받는 모든 클래스가 자동으로 업데이트됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
abstract class Vehicle {
    protected String modelName;
    public Vehicle(String modelName) {
        this.modelName = modelName;
    }
    public void startEngine() {
        System.out.println(“Engine started for “ + modelName);
    }
    public abstract void changeGears();
}
cs

 


인터페이스 사용이 적합한 경우

  1. 다중 상속이 필요할 때: 자바는 클래스의 다중 상속을 지원하지 않지만, 인터페이스는 다중 구현이 가능합니다.

  2. 완전한 추상화가 필요할 때: 구현 세부 사항을 완전히 숨기고 순수한 계약만 정의하고자 할 때 인터페이스가 적합합니다.

  3. 관련 없는 클래스들에게 공통 기능을 제공할 때: 서로 관련이 없는 여러 클래스가 특정 기능을 구현해야 할 때 인터페이스를 사용합니다.

  4. 보안을 위해: 객체의 중요한 세부 정보만 표시하고 나머지는 숨기고자 할 때 인터페이스를 사용합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
interface Animal {
    void animalSound();
    void sleep();
}
class Pig implements Animal {
    public void animalSound() {
        System.out.println(“The pig says: wee wee”);
    }
    public void sleep() {
        System.out.println(“Zzz”);
    }
}
cs

전략적 조합: 인터페이스와 추상 클래스 함께 사용하기

실제 애플리케이션에서는 인터페이스와 추상 클래스를 함께 사용하여 각각의 장점을 활용할 수 있습니다.

인터페이스 상속

인터페이스는 다른 인터페이스를 확장할 수 있어, 관련 기능을 그룹화하는 데 유용합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
interface Animal {
    void makeSound();
    void sleep();
}
interface Flying extends Animal {
    void fly();
    void flapWings();
}
class Bird implements Flying {
    // Animal과 Flying 인터페이스의 모든 메서드 구현
}
cs

추상 클래스가 인터페이스 구현

추상 클래스가 인터페이스를 구현하고, 일부 메서드에 대한 기본 구현을 제공할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
interface Shape {
    double getArea();
    double getPerimeter();
}
abstract class AbstractShape implements Shape {
    private String color;
    public AbstractShape(String color) {
        this.color = color;
    }
    public String getColor() {
        return color;
    }
    // 추상 메서드 추가
    public abstract void draw();
}
class Circle extends AbstractShape {
    private double radius;
    public Circle(String color, double radius) {
        super(color);
        this.radius = radius;
    }
    @Override
    public double getArea() {
        return Math.PI * radius * radius;
    }
    @Override
    public double getPerimeter() {
        return 2 * Math.PI * radius;
    }
    @Override
    public void draw() {
        System.out.println(“Drawing a circle”);
    }
}
cs

 


설계 모범 사례

  1. “인터페이스에 프로그래밍하라”: 구체적인 구현보다 추상 인터페이스에 의존하여 유연성을 높입니다.

  2. 상속보다 합성을 선호하라: 상속은 강한 결합을 만들 수 있으므로, 가능하면 합성을 사용합니다.

  3. 적절한 추상화 수준 선택: 너무 추상적이거나 구체적이지 않은 적절한 수준을 유지합니다.

  4. 인터페이스는 작게 유지: 인터페이스 분리 원칙에 따라 인터페이스는 작고 집중적으로 유지합니다.

  5. default 메서드 활용: Java 8 이상에서는 인터페이스에 default 메서드를 추가하여 기존 코드를 깨뜨리지 않고 인터페이스를 확장할 수 있습니다.

 

인터페이스와 추상 클래스는 각각 고유한 장점과 용도를 가지고 있습니다.

인터페이스는 완전한 추상화와 다중 구현을 제공하는 반면, 추상 클래스는 상태 관리와 부분적 구현을 제공합니다.

이들을 전략적으로 조합하여 사용하면 더 유연하고 확장 가능한 객체지향 설계를 구현할 수 있습니다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다