본문 바로가기
Book

[독서 기록] 헤드퍼스트 디자인 패턴 1장 | 디자인 패턴 소개와 전략 패턴 (에릭 프리먼, 엘리자베스 롭슨, 케이시 시에라, 버트 베이츠 지음, 서환수 옮김, 한빛미디어)

by Renechoi 2022. 11. 14.

 

 
헤드 퍼스트 디자인 패턴
이유 1. 흥미로운 이야기와 재치 넘치는 구성이 담긴 〈헤드 퍼스트〉 시리즈! 하나의 패턴에 하나의 이야기를 담았습니다. 틀에 박히지 않아 지루할 틈이 없는 구성과 친구와 이야기하듯 편안한 대화체로 이야기를 풀어냅니다. 이야기 속에 다양한 방법으로 해결할 수 있는 질문과 90개 이상의 연습문제를 담았습니다. 마치 게임 퀘스트를 해결하듯 문제를 하나하나 해결하다 보면 학습한 내용이 머릿속에 강렬하게 남습니다. 이유 2. 원스톱으로 배우는 14가지 GoF 핵심 디자인 패턴과 9가지 객체지향 디자인 원칙! 현장에서 자주 사용되는 옵저버, 어댑터, MVC 패턴 등 14가지 GoF 객체지향 패턴을 중점으로 패턴의 정의, 사용 시기, 사용처, 사용 이유, 즉시 디자인에 적용하는 방법을 알려줍니다. 이와 더불어 객체지향 프로그래밍에 광범위하게 적용할 수 있는 OCP, 할리우드 원칙 등 9가지 객체지향 디자인 원칙과 패턴으로 생각하는 방법도 알려줍니다. 이유 3. 시대의 변화에 맞춘 개정과 한국 독자만을 위한 특별판! 자바 8과 자바 16 이상에서 무리 없이 동작할 수 있도록 예제 코드를 수정했으며, 부가적인 설명과 Q&A 질문을 추가했습니다. 또한 16여 년 만의 개정을 기념해 오직 한국 독자만을 위한 새로운 삽화를 사용하고 한글 친화적인 구성했습니다. 원서를 읽을 때보다 더욱 편안하게 디자인 패턴을 학습할 수 있습니다. ▶ 이 책을 읽어야 하는 당신! ● 소프트웨어 출시는 완벽 그 자체! “어?~ 코드 수정하려고 다시 보니까 난리…” → 유지보수만 생각하면 그저 눈물인 주니어 (자바) 개발자 ● 코딩 실력은 장판파의 장비! “어?~ 팩토리 메소드 패턴을 이렇게 적용했던가?” → 디자인 패턴을 다시 한번 살펴보고 싶은 시니어 (자바) 개발자 ● 혼자 공부해서 다진 프로그래밍 언어 실력! “어?~ 근데 패턴이 뭐야?” → 개발 현장의 소프트웨어 디자인 방법이 궁금한 개발자 지망생
저자
에릭 프리먼, 엘리자베스 롭슨, 케이시 시에라, 버트 베이츠
출판
한빛미디어
출판일
2022.03.16

 

헤드퍼스트 디자인 패턴 1장 | 디자인 패턴 소개와 전략 패턴 

(에릭 프리먼 외 4인, 서환수 옮김, 한빛미디어)

 


 

누군가가 이미 여러분의 문제를 해결해 놓았습니다.

- 37p 

 

 

모든 서브클래스에 날거나 꽥꽥거리는 기능이 있어야 하는 것은 아니므로 상속이 올바른 방법은 아닙니다. 서브클래스에서 Flyable, Quackable을 구현해서 (고무 오리가 날아다니는 것과 같은) 일부 문제점은 해결할 수 있지만, 코드를 재사용하지 않으므로 코드 관리에 커다란 문제가 생깁니다. 물론 날 수 있는 오리 중에서도 날아다니는 방식이 서로 다를 수 있다는 문제도 포함해서 말이죠. 

- 43p

 

 

소프트웨어 개발 불변의 진리 

=> 변화 

- 44p

 

디자인 원칙 : 애플리케이션에서 달라지는 부분을 찾아내고, 달라지지 않는 부분과 분리한다.

- 45p

 

바뀌는 부분은 따로 뽑아서 캡슐화한다. 그러면 나중에 바귀지 않는 부분에는 영향을 미치지 않고 그 부분만 고치거나 확장할 수 있다.

- 45p

 

디자인 원칙 : 구현보다는 인터페이스에 맞춰서 프로그래밍한다.

- 47p 

 

 

각 행동은 인터페이스(예: FlyBehavior, QuackBehavior)로 표현하고 이런 인터페이스를 사용해서 행동을 구현하겠습니다. 나는 행동과 꽥꽥거리는 행동은 이제 Duck 클래스에서 구현하지 않습니다. 대신 특정 행동("삑삑 소리 내기"와 같은 행동)만을 목적으로 하는 클래스의 집합을 만들겠습니다.

- 47p 

 

 

핵심은 실제 실행시에 쓰이는 객체가 코드에 고정되지 않도록 상위 형식(supertype)에 맞춰 프로그래밍해서 다형성을 활용해야 한다는 점에 있습니다. 그리고 "상위 형식에 맞춰서 프로그래밍하라"는 원칙은 "변수를 선언할 때 보통 추상 클래스나 인터페이스 같은 상위 형식으로 선언해야 한다. 객체를 변수에 대입할 때 상위 형식을 구체적으로 구현한 형식이라면 어떤 객체든 넣을 수 있기 때문이다. 그러면 변수를 선언하는 클래스에서 실제 객체의 형식을 몰라도 된다"라는 뜻으로 생각하면 됩니다.

- 48p 

 

 

Dog d = new Dog();
d.bark();

 변수 d를 dog 형식(Animal을 확장한 구상 클래스)으로 선언하면 구체적인 구현에 맞춰서 코딩해야 합니다.

 

하지만 인터페이스와 상위 형식에 맞춰서 프로그래밍한다면 다음과 같이 할 수 있습니다.

 

Animal animal = new Dog();
animal.makeSound();

Dog라는 걸 알고 있긴 하지만 다형성을 활용해서 Animal 레퍼런스를 써도 됩니다. 

 

더 바람직한 방법은 상위 형식의 인스턴스를 만드는 과정을 (new Dog()) 같은 식으로) 직접 코드를 만드는 대신 구체적으로 구현된 객체를 실행시에 대입하는 것입니다.

 

a = getAnimal();
a.makeSound();

Animal의 하위 형식 가운데 어떤 형식인지는 모릅니다. 단지 makeSound()에 올바른 반응만 할 수 있으면 됩니다. 

 

- 48p

 

 

이런 식으로 디자인하면 다른 형식의 객체에서도 나는 행동과 꽥꽥거리는 행동을 재사용할 수 있습니다. 그런 행동이 더 이상 Duck 클래스 안에 숨겨져 있지 않으니까요. 

 

그리고 기존의 행동 클래스를 수정하거나 날아다니는 행동을 사용하는 Duck 클래스를 전혀 건드리지 않고도 새로운 행동을 추가할 수 있습니다. 

 

- 49p 

 

 

가장 중요한 점은 나는 행동과 꽥꽥거리는 행동을 Duck 클래스(또는 그 서브클래스)에서 정의한 메소드를 써서 구현하지 않고 다른 클래스에 위임한다는 것입니다.

- 51p

 

 

public abstract class Duck{
	QuackBehavior quackBehavior;
    // 기타코드
    
    public void performQuack(){
    	quackBehavior.quack();
    }
}

모든 Duck에는 QuackBehavior 인터페이스를 구현하는 것의 레퍼런스가 있습니다.

 

꽥꽥거리는 행동을 직접처리하는 대신, quackBehavior로 참조되는 객체에 그 행동을 위임합니다.

 

- 51p 

 

 

public class MallardDuck extends Duck {
	
    public MallardDuck() {
    	quackBehavior = new Quack();
        flyBehavior = new FlyWithWings();
    }
    
    public void display(){}
   }
}

MallardDuck이 꽥꽥거리는 행동을 처리할 때는 Quack 클래스를 사용하므로 performQuack()이 호출되면 꽥꽥거리는 행동은 Quack 객체에게 위임됩니다. 결과적으로 진짜 꽥꽥 소리를 들을 수 있겠죠.

 

그리고 FlyBehavior 형식으로는 FlyWithWings를 사용합니다.

 

MallardDuck은 Duck 클래스에서 quackBehavior와 flyBehavior 인스턴스 변수를 상속받는다는 사실을 잊지 마세요. 

 

- 52p 

 

 

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("All ducks float, even decoys!");
    }
}
package headfirst.designpatterns.strategy;

public interface FlyBehavior {
	public void fly();
}
package headfirst.designpatterns.strategy;

public class FlyWithWings implements FlyBehavior {
	public void fly() {
		System.out.println("I'm flying!!");
	}
}
public class MiniDuckSimulator {
 
	public static void main(String[] args) {
    	Duck mallard = new MallardDuck();
        mallard.performQuack();
        mallard.performFly();
    }
}

 

mallardDuck에서 상속받은 performQuack() 메소드가 호출됩니다.

이 메소드에서는 객체의 QuackBehavior에게 할 일을 위임하죠.

(즉 quackBehavior 레퍼런스의 quack() 메소드가 호출됩니다.)

 

performFly() 메소드도 호출합니다. 

 

- 55p 

 

 

 

동적으로 행동 지정하기

 

Duck 클래스에 메소드 2개를 새로 추가합니다. 

 

public void setFlyBehavior(FlyBehavior fb) {
   flyBehavior = fb;
}

public void setQuackBehavior(QuackBehavior qb) {
   quackBehavior = qb;
}

이 두 메소드를 호출하면 언제든지 오리의 행동을 즉석에서 바꿀 수 있습니다. 

 

Duck model = new ModelDuck();

model.performFly(); => 를 처음 호출하면 ModelDuck 생성자에서 설정되었던 flyBehavior, 즉 flynoway 인스턴스가 fly()메서드가 호출
model.setFlyBehavior(new FlyRocketPowered()); => 이러면 상속받은 행동 세터 메서드가 호출됩니다. 이제 모형 오리에 로켓 추진력으로 날 수 있는 능력이 생겼어요.
model.performFly();

실행 중에 오리의 행동을 바꾸고 싶으면 원하는 행동에 해당하는 Duck의 세터 메소드를 호출합니다. 

 

- 56 ~ 57p 

 

 

"A는 B이다"보다 "A에는 B가 있다"가 나을 수 있습니다.

- 59p

 

이런 식으로 두 클래스를 합치는 것을 '구성(composition)'을 이용한다'라고 부릅니다. 여기에 나와 있는 오리 클래스에서는 행동을 상속받는 대신, 올바른 행동 객체로 구성되어 행동을 부여받습니다. 

- 59p

 

디자인 원칙 : 상속보다는 구성을 활용한다. 

- 59p

 

 

전략 패턴은 알고리즘군을 정의하고 캡슐화해서 각각의 알고리즘군을 수정해서 쓸 수 있게 해 줍니다. 전략 패턴을 사용하면 클라이언트로부터 알고리즘을 분리해서 독립적으로 변경할 수 있습니다.

- 60p 

 

 

패턴의 밑바탕에는 객체지향 패턴이 있어요. 그러한 원칙을 알고 있으면 문제에 딱 맞는 패턴을 찾을 수 없을 때 도움이 될 거예요.

원칙이라고요? 그럼 추상화나 캡슐화...

그렇죠. 관리가 용이한 객체지향 시스템을 만드는 비결 가운데 하나가 바로 "나중에 어떻게 바귈 것인지" 생각해 보는 거죠. 지금까지 배운 원칙에 바로 그런 내용이 담겨 있어요.

- 67p 

 

 

핵심 정리 

 

객체지향 기초

- 추상화

- 캡슐화

- 다형성

- 상속 

 

객체지향 원칙

- 바뀌는 부분은 캡슐화한다.

- 상속보다는 구성을 활용한다.

- 구현보다는 인터페이스에 맞춰서 프로그래밍한다.

 

- 패턴은 검증받은 객체지향 경험의 산물입니다.

 

- 패턴은 발명되는 것이 아니라 발견되는 것입니다. 

 

- 대부분의 패턴과 원칙은 소프트웨어의 변경 문제와 연관되어 있습니다.

 

- 대부분의 패턴은 시스템의 일부분을 나머지 부분과 무관하게 변경하는 방법을 제공합니다. 

 

- 많은 경우에 시스템에서 바뀌는 부분을 골라내서 캡슐화해야 합니다. 

 

- 패턴은 다른 개발자와의 의사소통을 극대화하는 전문 용어 역할을 합니다. 

 

- 68p 

 

 

반응형