수퍼 클래스를 상속받는 서브 클래스들이 있을 때, 변화하는 부분을 단순 상속으로 처리하지 말고 인터페이스로 캡슐화한다. 변화하는 부분은 해당 캡슐에 위임하면 유지보수가 간편해진다.
여러 오리를 구현하는 코드를 작성한다고 해보자. 수퍼 클래스로 오리를 만들고, 서브 클래스는 청둥오리, 집오리, 고무오리, 나무오리 등이 있을 수 있다. 아직까지는 오리의 울음소리와 떠다니는 기능만 구현되어 있고, 모든 서브 클래스는 이를 상속하고 있다. 만약 날아다니는 기능을 추가하고자 한다면 단순히 수퍼 클래스에 fly라는 메서드를 추가하고 서브 클래스가 상속받으면 끝일까? 답은 당연히 아니다. 고무오리와 나무오리는 날 수 없기 때문이다.
상속은 코드의 재사용성을 높일 수 있지만 위처럼 유지보수에는 별로 좋지 않다. 클래스의 특징을 제어하기 어렵고, 위처럼 전부 구현된 코드에서 하나의 기능만 추가해도 큰 공사를 하게 될 수 있기 때문이다. 그렇다면 어떻게 해결할 수 있을까? 날아다니는 기능을 인터페이스로 뽑아서 서브 클래스들이 구현하게 할 수도 있다. 위의 문제점을 보자마자 이 생각을 했는데, 바로 다음 장에서 아주 바보같은 아이디어라고 극딜 당했다. 이 해결 방법은 재사용성이 너무 떨어진다. 자바의 인터페이스는 메서드 구현이 불가능하므로 모든 서브 클래스에서 인터페이스의 메서드를 오버라이드해줘야 한다.
상속도, 새로운 인터페이스도 장단점이 명확하다. 이 둘의 중간으로 캡슐화를 사용한다. 자바를 처음 배울 때 클래스에서 getter, setter 사용해서 변수를 은닉하는 그런 캡슐화가 아니다. 어떤 행동에 대해 인터페이스를 생성하고, 해당 인터페이스를 상속받는 여러 부속 행동들에 대한 클래스를 생성한다. 클래스들을 인터페이스로 감쌌으므로 캡슐화라고 한다. 위의 오리로 예시를 들면 날아다니는 행동에 대한 인터페이스를 생성하고, 날 수 있다와 날 수 없다라는 클래스를 만들어서 구현시킨다. 이제 오리라는 수퍼 클래스는 행동에 대한 인터페이스 객체를 갖고, 청둥오리라는 서브 클래스에서 날 수 있다라는 클래스로 구체화해주면 되고, 고무오리는 날 수 없다라는 클래스로 구체화해주면 된다.
날아다니는 행동에 대해 해결했으므로 울음소리도 코드를 수정할 수 있다. 모든 오리가 꽥꽥하고 울지는 않으므로(고무오리는 삑삑, 나무오리는 울 수 없다.) 이에 대해 인터페이스로 꽥꽥, 삑삑, 울지 못함 등의 클래스들을 캡슐화하고, 오리라는 수퍼 클래스와 그 서브 클래스들은 이를 갖다 쓰기만 하면 된다.
물론 위의 예시에 나온 모든 오리들은 물에 떠다닐 수 있으므로 이는 캡슐화할 필요가 없이 상속만으로 처리가 가능하고, 이게 훨씬 이득이다.(재사용성 및 유지보수를 고려)
이처럼 변화할 가능성이 있는 것들에 대해서는 다른 인터페이스(캡슐)에 위임하고, 그렇지 않은 것들은 상속으로 처리한다.
물론 특정 구현에 맞춰서 프로그래밍을 하면 안 된다지만, 아직 첫 장이니까 넘어가주자.
캡슐화 실습 코드
import java.util.*;
interface FlyBehavior {
public void fly();
}
class FlyWithWings implements FlyBehavior {
public void fly() {
System.out.println("난다요");
}
}
class FlyNoWay implements FlyBehavior {
public void fly() {
System.out.println("못 난다요");
}
}
/////////////////////////////////////////////
interface QuackBehavior {
public void quack();
}
class Quack implements QuackBehavior {
public void quack() {
System.out.println("꽥");
}
}
class MuteQuack implements QuackBehavior {
public void quack() {
System.out.println("매너모드 꽥");
}
}
class Squeak implements QuackBehavior {
public void quack() {
System.out.println("삑");
}
}
////////////////////////////////////////////
abstract class Duck {
FlyBehavior flybehavior;
QuackBehavior quackbehavior;
public Duck() { }
public void perfromFly() {
flybehavior.fly();
}
public void performQuack() {
quackbehavior.quack();
}
public void swim() {
System.out.println("뜬다요");
}
}
class MallardDuck extends Duck {
public MallardDuck() {
quackbehavior = new Quack();
flybehavior = new FlyWithWings();
}
public void display() {
System.out.println("나는 물오리");
}
}
public class Main {
public static void main(String[] args) {
Duck md = new MallardDuck();
md.performQuack();
md.perfromFly();
}
}
'ACC > 디자인 패턴 스터디' 카테고리의 다른 글
[디자인 패턴 스터디] 7. 어댑터 패턴과 퍼사드 패턴 (0) | 2023.09.16 |
---|---|
[디자인 패턴 스터디] 6. 커맨드 패턴 (0) | 2023.09.16 |
[디자인 패턴 스터디] 5. 싱글턴 패턴 (0) | 2023.09.08 |
[디자인 패턴 스터디] 4. 팩토리 패턴 (0) | 2023.09.07 |
[디자인 패턴] 3. 데코레이터 패턴 (0) | 2023.09.02 |