알고리즘군을 정의하고 캡슐화 해서 각각의 알고리즘군을 수정해 사용할 수 있게 해줍니다.
클라이언트로부터 알고리즘을 분리해서 독립적으로 변경할 수 있습니다.
이것만 들어서는 잘 감이 안올겁니다. 좀 더 자세한 예제를 들어봅시다.
Duck 슈퍼클래스
quack() 모든 오리가 가능하다
swim() 모든 오리가 가능하다
display() 모든 오리 모양이 다르니 추상메소드 이다.
Mallard Duck 클래스
display() //적당한 모양 표시
Redhead Duck
display()//적당한 모양 표시
오리를 만드는 코드 입니다.
여기서 오리를 날게 하려면 Duck 슈퍼클래스에 fly()메소드를 추가하면 됩니다.
Duck 슈퍼클래스
quack() 모든 오리가 가능하다
swim() 모든 오리가 가능하다
display() 모든 오리 모양이 다르니 추상메소드 이다.
fly() <-- 추가
근데 하필 이상태에서 고무오리 클래스를 추가하게 되었습니다.
Rubber Duck
quack()// 소리 안내게 오버라이드
display() //적당한 모양 표시
곤란하죠? 고무오리는 날면 안됩니다. 그리고 quack소리도 다릅니다.
그래서 fly()와 quack()을 오버라이드를 해주겠습니다
Rubber Duck
quack()// 소리 안내게 오버라이드
fly() //날지 못하게 코드 작성
display() //적당한 모양 표시
이제 문제가 생겼습니다. 앞으로 오리가 추가될때마다 일일히 오버라이드 할건가요?
유지보수가 힘들지 않을까요??
이 문제를 어떤방식으로 해결할까요.
해결하기 위한 원칙을 알아봅시다.
디자인 원칙 : 애플리케이션이 달라지는 부분을 찾고 달라지지 않는 부분과 분리한다.
바뀌는 부분만 찾아서 캡슐화 한다
fly()와 quack()은 Duck 클래스에 있는 오리 종류에 따라 달라집니다.
fly()와 quack()을 Duck 클래스로부터 분리하려면, 2개의 메소드 모두 Duck 클래스에서 끄집어내서 각 행동을 나타낼 클래스 집합을 새로 만들어야 합니다.
Duck안에 fly()와 quack()을 슈퍼클래스에서 빼서 interface로 만들겠습니다.
디자인 원칙 : 구현보다는 인터페이스(상위형식)에 맟춰 프로그래밍 한다.
각 행동을 인터페이스로 표현하고, 이 인터페이스를 사용해서 행동을 구현합니다.
꼭 인터페이스가 아니여도 된다.상위 형식에 맞춰서 프로그래밍 하라는 소리입니다.
이러면 다른 형식의 객체에서도 나는 행동과 꽥꽥거리는 행동을 재사용 할 수 있습니다.
그리고 기존의 행동 클래스를 수정하거나 날아다니는 행동을 사용하는 Duck 클래스를 건드리지 않고도 새로운 행동을 추가할 수 있습니다.
오리 행동 통합하기
포인트는 나는 행동과 꽥꽥거리는 행동을 슈퍼클래스 에서 정의한 메소드를 써서 구현하지 않고 다른 클래스에 위임한다는 것 입니다.
행동변수는 행동 인터페이스 형식으로 선언합니다.
인스턴트 변수 실행시, 특정 행동의 레퍼런스가 실행됩니다.
perform Quack를 구현해봅시다.
public abstract class Duck {
QuackBehavior quackBehavior;
//기타코드
public void performQuack(){
quackBehavior.quack();
}
}
이제 꽥꽥 거리고 싶을 땐 객체의 종류에는 전혀 신경 쓸 필요 없이 quack()을 실행할 줄 만 알면 됩니다.
public class 청둥오리 extends Duck {
public 청둥오리() {
quackBehavior = new Quack();
flyBehavior = new FlyWithWings();
}
public void display() {
System.out.println("저는 물오리입니다")
}
}
물론 setter를 이용해 행동을 동적으로 지정할 수 있습니다.
두 클래스를 합치는 방법
A에는 B가 있다" 관계를 생각해 봅시다. 각 오리에는 FlyBehavior와 QuackBehavior가 있으며, 각각 나는 행동과 꽥꽥거리는 행동을 위임받습니다. 이런식으로 두 클래스를 합치는 것을 '구성(composition)을 이용한다'라고 부릅니다
Duck 슈퍼 클래스
public abstract class Duck {
FlyBehavior flyBehavior;
QuackBehavior quackBehavior;
public Duck() {}
public abstract void display();
public void performQuack() {
quackBehavior.quack();
}
public void swim() {
System.out.println("모든 오리는 물에 뜹니다. 가짜 오리도 뜨죠");
}
}
FlyBehavior 인터페이스
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("저는 못 날아요!!!");
};
}
QuackBehavior 인터페이스
public interface QuackBehavior {
public void fly();
}
-------------------------------------------------
public class Quack implements QuackBehavior {
public void quack() {
System.out.println("꽥!");
};
}
-------------------------------------------------
public class MuteQuack implements QuackBehavior {
public void quack() {
System.out.println("<< 조용 ~ >> ");
};
}
-------------------------------------------------
public class Squeak implements QuackBehavior {
public void quack() {
System.out.println("삑");
};
}
실행코드
public class MiniDuckSimulator {
public static void main(String[] args) {
Duck mallard = new mallardDuck();
mallard.performQuack();
mallard.performFly();
}
}
디자인 원칙3 : 상속보다는 구성을 사용하자.