1. 아래와 같은 Duck 클래스가 있을 때, 날아다니는 행동을 추가하려면 어떻게 해야할까?
public abstract class Duck {
public Duck() {}
public abstract void display(); // 모든 오리들의 모양이 다르기 때문에 추상 메소드로 선언
public void quack() { // 모든 오리들은 꽥꽥소리를 낼 수 있음
System.out.println("quack quack");
}
public void swim() { // 모든 오리들은 헤엄을 칠 수 있음
System.out.println("swimming");
}
}
☞ Duck 클래스에 fly() 메서드를 추가하게 되면, 일부 서브클래스에는 부적합한 행동임에도 모든 서브클래스가 상속받게 된다.
2. fly() 메서드를 상속받은 후 아무 것도 하지 않도록 오버라이드하면 안될까?
public class RubberDuck extends Duck {
public abstract void display() {
System.out.println("toy");
}
public void quack() {
// 아무 것도 하지 않도록 오버라이드
}
public void fly() {
// 아무 것도 하지 않도록 오버라이드
}
}
☞ fly() 메서드가 변경될 때마다 모든 서브클래스를 일일이 살펴봐야 한다.
3. 인터페이스를 이용하는 것은 어떨까?
Duck 클래스에서 fly(), quack() 메서드를 제외하고 Flyable, Quackable 인터페이스를 생성한 뒤,
해당하는 오리에 대해서만 인터페이스를 구현해서 각각 필요한 메서드를 집어넣을 수도 있다.
public interface Flyable {
public void fly();
}
public interface Quackable {
public void quack();
}
☞ 날아다니거나 소리를 낼 수 있는 즉, 행동을 취할 수 있는 모든 오리들의 코드를 전부 고쳐야 한다.
자바 인터페이스에는 구현된 코드가 전혀 들어가지 않기 때문에 코드 재사용 불가.
상속을 사용하는 경우 모든 서브 클래스에서 동일한 행동을 취함.
인터페이스를 사용하는 경우 예외적인 행동을 처리할 수 있지만 코드 재사용을 기대할 수 없음.
# 디자인 원칙 1
애플리케이션에서 달라지는 부분을 찾아내고, 달라지지 않는 부분으로부터 분리시킨다.
달라지는 부분을 찾아서 나머지 코드에 영향을 주지 않도록 캡슐화함.
이렇게 함으로써 코드를 변경하는 과정에서 의도하지 않은 일이 일어나는 것을 줄이면서 시스템의 유연성은 향상시킬 수 있음.
# 디자인 원칙 2
구현이 아닌 인터페이스에 맞춰서 프로그래밍한다.
상위 형식에 맞춰서 프로그래밍함을 의미.
실행 시에 쓰이는 객체가 코드에 의해서 고정되지 않도록, 어떤 상위 형식에 맞춰서 프로그래밍함으로써 다형성을 활용해야 한다는 것.
4. 바뀌는 부분과 그렇지 않은 부분 분리하기
fly()와 quack()은 Duck 클래스에서 오리마다 달라지는 부분이다.
각 행동은 FlyBehavior, QuackBehavior 인터페이스로 표현하고 별도의 행동클래스에서 인터페이스를 구현한다.
- Duck 클래스
public abstract class Duck {
FlyBehavior flyBehavior; // 인터페이스 형식의 인스턴스 변수 추가
QuackBehavior quackBehavior; // 인터페이스 형식의 인스턴스 변수 추가
public Duck() {}
public abstract void display();
public void performFly() {
flyBehavior.fly(); // 행동클래스에 위임
}
public void performQuack() {
quackBehavior.quack(); // 행동클래스에 위임
}
public void swim() {
System.out.println("swimming");
}
}
flyBehavior와 quackBehavior라는 인스턴스 변수를 추가하고,
fly(), quak() 메소드 대신 performFly(), performQuack() 메소드를 선언하여 각 변수로 참조되는 객체에 행동을 위임한다.
각 오리 객체에서는 실행 시에 이 변수에 특정 행동 형식(FlyWithWings, Squeak 등) 에 대한 레퍼런스를 다형적으로 설정할 수 있다.
- FlyBehavior 인터페이스
public interface FlyBehavior {
public void fly();
}
- FlyBehavior 인터페이스를 구현한 클래스
public class FlyWithWings implements FlyBehavior {
public void fly() {
System.out.println("flying");
}
}
public class FlyNoWay implements FlyBehavior {
public void fly() {
System.out.println("can't flying");
}
}
- QucakBehavior 인터페이스
public interface QuackBehavior {
public void quack();
}
- QucakBehavior 인터페이스를 구현한 클래스
public class Quack implements QuackBehavior {
public void quack() {
System.out.println("quack");
}
}
public class MuteQuack implements QuackBehavior {
public void quack() {
System.out.println("silence");
}
}
public class Squeak implements QuackBehavior {
public void quack() {
System.out.println("squeak");
}
}
- Example
public class MallardDuck extends Duck {
public MallardDuck() {
quackBehavior = new Quack(); // quackBehavior 인스턴스 변수 상속받음
flyBehavior = new FlyWithWings(); // flyBehavior 인스턴스 변수 상속받음
}
}
public class MiniDuckSimulator {
public static void main(String[] args) {
Duck mallard = new MallardDuck(); // 청둥오리
mallard.performQuack(); // quack
mallard.performFly(); // flying
}
}
☞ 다른 형식의 객체에서도 나는 행동과 소리를 내는 행동을 재사용 할 수 있다.
또한, 기존의 행동 클래스와 Duck 클래스를 건드리지 않고도 새로운 행동을 추가할 수 있다.
5. 추상 수퍼 클래스를 사용해도 되지 않을까?
☞ 동적으로 할당을 할 수 없다.
6. 동적으로 행동을 지정하기
- Duck 클래스에 setter메서드 추가
public void setFlyBehavior(FlyBehavior fb) {
flyBehavior = fb;
}
public void setQuackBehavior(QuackBehavior qb) {
quackBehavior = qb;
}
- 실행 시 setter 메서드 호출
public class MiniDuckSimulator {
public static void main(String[] args) {
Duck mallard = new MallardDuck();
mallard.performFly(); // flying
mallard.setFlyBehavior(new FlyNoWay());
mallard.performFly(); // cant' flying
}
}
# 디자인 원칙 3
상속보다는 구성을 활용한다.
A에는 B가 있다.(구성)
ex) 오리 클래스에서는 행동을 상속받는 대신 올바른 행동 객체로 구성됨으로써 행동을 부여받음.
Strategy Pattern
알고리즘군을 정의하고 각각을 캡슐화하여 교환해서 사용할 수 있음.
클라이언트와는 독립적으로 알고리즘을 변경할 수 있음.
'기타 > 디자인패턴' 카테고리의 다른 글
[디자인패턴] Decorator Pattern (0) | 2019.10.15 |
---|---|
[디자인패턴] Observer Pattern (0) | 2019.10.10 |