따봉도치야 고마워

Head First Design Patterns : (12)컴파운드 패턴 본문

프로그래밍/공부

Head First Design Patterns : (12)컴파운드 패턴

따봉도치 2020. 10. 7. 20:22

컴파운드 패턴

  • 두 개 이상의 패턴을 결합해 일반적으로 자주 등장하는 문제들에 대한 해법을 제공

SimUDuck 

 

1. Quackable 인터페이스 정의

public interface Quackable{
    public void quack();
}
  • 각 오리 클래스에서 종류에 맞게 구현하고 ex) MallardDuck, RedheadDuck
  • 시뮬레이터를 생성해 테스트 해보자
void simulate(){
    Quackable mallardDuck = new MallardDuck();
    Quackable duckCall = new DuckCall();
    Quackable rubberDuck = new rubberDuck();
    
    simulate(mallardDuck);
    simulate(duckCall);
    simulate(rubberDuck);
}

void simulate(Quackable duck){
    duck.quack();
    //어떤 오리든 quack()을 호출해서 테스트 가능
}

 

 

2. 거위 클래스(Goose)를 추가하고 Duck이 들어가는 곳에 거위도 들어갈 수 있게 해보자 : 거위용 어댑터

  • 거위는 오리와 다르게 울기 때문에 quack() 메소드 대신 honk() 메소드를 가지고 있다.
  • 거위용 어댑터를 생성해서 거위를 오리처럼 사용해보자
public class GooseAdapter implements Quackable {
    Goose goose;
 
    public GooseAdapter(Goose goose) {
        this.goose = goose;
    }
  
    //quack()메소드가 호출되면 goose의 honk() 메소드를 호출한다.
    public void quack() {
        goose.honk();
    }
}

/*-----------------------------사용 예시---------------------------*/
void simulate(){
    .
    .
    Quackable gooseDuck = new GooseAdapter(new Goose());
    //Goose를 어댑터로 감싸 Duck인척 한다.
    
    simulate(gooseDuck);
}

 

 

3. 오리 클래스는 그대로 두면서 오리떼가 꽥꽥한 회수를 세어보자 : 데코레이터 패턴

  • 꽥 소리를 세주는 기능을 추가하기 위한 데코레이터를 만들어 Duck 객체를 감싸보자
public class QuackCounter implements Quackable {
    Quackable duck;
    static int numberOfQuacks;
    //모든 객체의 회수를 세야하기 때문에 정적 변수 사용
  
    public QuackCounter (Quackable duck) {
        this.duck = duck;
    }
  
    public void quack() {
        //quack 메소드를 호출하면 데코레이터 안에 있는 duck객체에 위임하고
        //numberOfQuacks 를 증가한다.
        duck.quack();
        numberOfQuacks++;
    }
 
    public static int getQuacks() {
        return numberOfQuacks;
    }
}

/*-----------------------------사용 예시---------------------------*/
void simulate(){
    .
    .
    //Quackable 객체를 데코레이터로 감싸준다
    Quackable mallardDuck = new QuackCounter(new MallardDuck());
    Quackable duckCall = new QuackCounter(new DuckCall());
    Quackable rubberDuck = new QuackCounter(new rubberDuck());
    
    simulate(mallardDuck);
    simulate(duckCall);
    simulate(rubberDuck);
    
    System.out.println("The ducks quacked " + QuackCounter.getQuacks() + " times");
}

 

 

4. 오리를 생산하기 위한 팩토리를 만들어보자

  • 오리를 생성할 때마다 데코레이터로 감싸지 않으면 정확히 카운팅을 할 수 없음
  • 오리를 생성하는 작업을 한 군데에서 몰아서 하도록 하자
//추상 팩토리 패턴으로 여러가지 오리들을 생산하는 팩토리 클래스 정의
public abstract class AbstractDuckFactory {
    public abstract Quackable createMallardDuck();
    public abstract Quackable createRedheadDuck();
    public abstract Quackable createDuckCall();
    public abstract Quackable createRubberDuck();
}

//서브클래스 각 메소드에서 다른 종류의 오리를 생성
public class CountingDuckFactory extends AbstractDuckFactory {
    public Quackable createMallardDuck() {
        return new QuackCounter(new MallardDuck());
    }
  
    public Quackable createRedheadDuck() {
        return new QuackCounter(new RedheadDuck());
    }
  
    public Quackable createDuckCall() {
        return new QuackCounter(new DuckCall());
    }
   
    public Quackable createRubberDuck() {
        return new QuackCounter(new RubberDuck());
    }
}



/*-----------------------------사용 예시---------------------------*/
//오리 생성 팩토리를 인자로 받음
void simulate(AbstractDuckFactory duckFactory){
    .
    .
    //직접 객체를 생성하지 않고 팩토리의 메소드를 통해 생성
    Quackable mallardDuck = duckFactory.createMallardDuck();
    Quackable duckCall = duckFactory.createDuckCall();
    Quackable rubberDuck = duckFactory.createRubberDuck();
    
    simulate(mallardDuck);
    simulate(duckCall);
    simulate(rubberDuck);
    
    System.out.println("The ducks quacked " + QuackCounter.getQuacks() + " times");
}

 

 

 

5. 오리떼(Quackable 떼)를 만들어 오리들을 하나로 관리해보자 : 복합 객체 (컴포지트 패턴)

  • 복합 객체 : Flock, 잎 객체 : Quackable
  • 복합 객체에만 자식을 관리하기 위한 기능을 넣음으로 안전성은 높지만, 투명성이 조금 떨어짐
//복합객체는 잎원소와 똑같은 인터페이스를 구현함 = Quackable
public class Flock implements Quackable {
    //ArrayList로 오리떼에 속하는 오리들을 보관함
    ArrayList quackers = new ArrayList();
 
    public void add(Quackable quacker) {
        quackers.add(quacker);
    }
 
    //Flock에 들어있는 모든 오리들의 quack()을 호출해주어야함
    public void quack() {
        Iterator iterator = quackers.iterator();
        while (iterator.hasNext()) {
            Quackable quacker = (Quackable)iterator.next();
            quacker.quack();
        }
    }
 
    public String toString() {
        return "Flock of Quackers";
    }
}


/*-----------------------------사용 예시---------------------------*/
void simulate(AbstractDuckFactory duckFactory){
    Quackable mallardDuck = duckFactory.createMallardDuck();
    Quackable duckCall = duckFactory.createDuckCall();
    Quackable rubberDuck = duckFactory.createRubberDuck();
    
    Flock flockOfDucks = new Flock();
    flockOfDucks.add(mallardDuck);
    flockOfDucks.add(duckCall);
    flockOfDucks.add(rubberDuck);
    
    //위에서 만든 오리떼 하나만 넣어주면 모든 오리에 대한 테스트가 가능해짐
    simulate(flockOfDucks);
    
    System.out.println("The ducks quacked " + QuackCounter.getQuacks() + " times");
}

 

 

6. 실시간으로 꽥꽥거리는 오리들을 각각 추적할 수 있는 기능을 넣어보자 : 옵저버

  • 우선 Observable 인터페이스를 추가
public interface QuackObservable {
    //Observer 인터페이스를 구현하는 객체라면 어떤 객체는 꽥꽥거리는걸 감시할 수 있음
    public void registerObserver(Observer observer);
    public void notifyObservers();
}

 

  • Quackable 에서 위의 인터페이스를 상속
  • Quackable을 구현하는 모든 구상클래스에서 QuackObservable도 구현하도록 해야함
    • 이번엔 일일이 등록 및 연락 메소드를 구현하지말고
    • 등록 및 연락용 코드를 Observable이라는 보조 클래스에 캡슐화해둔 뒤 구성을 통해 QuackObservable에 포함하기
    • 이렇게 하면 실제 코드는 한 군데에 몰아놓고, 필요한 작업을 보조 Observable 클래스에 위임
//QuackObservable의 일을 위임해야하니까 구현하기
public class Observable implements QuackObservable {
    ArrayList observers = new ArrayList();
    QuackObservable duck;
 
    public Observable(QuackObservable duck) {
        this.duck = duck;
    }
  
    public void registerObserver(Observer observer) {
        observers.add(observer);
    }
  
    public void notifyObservers() {
        Iterator iterator = observers.iterator();
        while (iterator.hasNext()) {
            Observer observer = (Observer)iterator.next();
            observer.update(duck);
        }
    }
 
    public Iterator getObservers() {
        return observers.iterator();
    }
}

 

  • Observer 보조 객체와 Quackable 클래스를 결합
  • 각 오리클래스에선 구성을 통해 Observable 객체를 가지고 있고, QuackObservable과 관련된 모든 기능을 해당 객체에 위임
  • 데코레이터에선 감싸고 있는 duck 객체에게 또 다시 위임
  • 오리떼에선 registerObserver()만 구현하고 nofify는 비워놔도 됨(어차피 개별로 연락함)
public class MallardDuck implements Quackable {
    Observable observable;
 
    public MallardDuck() {
        observable = new Observable(this);
    }
 
    public void quack() {
        System.out.println("Quack");
        notifyObservers();
    }
 
    //QuackObservable에서 정의한 메소드들 
    //모두 observable 보조 객체에 작업을 위임한다.
    public void registerObserver(Observer observer) {
	   observable.registerObserver(observer);
    }
 
    public void notifyObservers() {
        observable.notifyObservers();
    }
 
    public String toString() {
        return "Mallard Duck";
    }
}

 

  • 마지막으로 옵저버 쪽 코드 작성 : Observer 인터페이스와 옵저버
public interface Observer {
    //꽥소리를 낸 오리를 인자로 받는 메소드
    public void update(QuackObservable duck);
}

//꽥학자 클래스
public class Quackologist implements Observer {
    public void update(QuackObservable duck) {
        System.out.println("Quackologist: " + duck + " just quacked.");
    }
}


/*-----------------------------사용 예시---------------------------*/
void simulate(AbstractDuckFactory duckFactory){
    //오리 객체 생성
    //오리떼 객체 생성
    
    Quackologist quackologist = new Quackologist();
    floackOfDucks.registerObserver(quackologist);
    
    simulate(flockOfDucks);
    
    System.out.println("The ducks quacked " + QuackCounter.getQuacks() + " times");
}

 

 

패턴은 반드시 상황에 맞게 써야 한다.

MVC 패턴

  • View : 모델을 표현하는 방법을 제공. 일반적으로 화면에 표시하기 위해 필요한 상태 및 데이터는 모델에서 직접 가져옴
  • Controller : 사용자로부터 입력을 받아 그것이 모델에게 어떤 의미가 있는지 파악
  • Model : 모든 데이터, 상태, 로직이 들어있음 

사용자는 뷰하고만 접촉할 수 있다 : 뷰에 대해 이벤트가 발생하면 컨트롤러에게 알린다

컨트롤러에선 사용자의 행동을 분석해 모델에게 상태변경을 요청한다

컨트롤러에서 뷰를 변경하라고 요청할 수 있다

상태가 변경되면 모델에서 뷰에게 그 사실을 알린다

뷰에서 모델에게 상태를 요청한다.

 

 


MVC를 구성하는 여러 패턴들

View-Contrller : 스트래티지 패턴 뷰 객체를 여러 전략을 써서 설정할 수 있음.
- 뷰는 애플리케이션의 겉모습만 신경쓰고
- 인터페이스의 행동에 대한 결정은 모두 컨트롤러에게 맡김.
(다른 전략이 필요하면 컨트롤러를 교환)
View : 포지트 패턴 - 디스플레이는 여러 단계로 겹쳐져 있을 수 있는 일련의 윈도우, 패널, 버튼 등으로 구성
- 각 디스플레이 항목은 복합객체, 잎이 될 수 있음
- 뷰 갱신 시 최상위 뷰에게만 알리면 나머지는 컴포지트 패턴에 의해 자동으로 처리 됨.
Model : 옵저버 패턴 - 모델에선 옵저버 패턴을 써 상태 변경 시 연관된 객체들에게 연락을 함
- 이를 통해 모델과 뷰/컨트롤러를 완전히 독립시킬 수도 있음

 


EX1) DJ 

 

1. 뷰

  • BPM 조회 뷰 : 현재 BPM 수, 비트 막대
  • BPM을 설정하는 뷰 : 텍스트입력 칸, 세팅버튼, BPM 증감 버튼 / 연주 시작, 중지 버튼
//DjView는 매 박자마다 오는 연락과 BPM 변경 시의 연락을 전부 받을 수 있게 옵저버들 구현
public class DJView implements ActionListener,  BeatObserver, BPMObserver {
    //모델과 컨트롤러에 대한 레퍼런스를 모두 가지고 있음
    BeatModelInterface model;
    ControllerInterface controller;
    
    //.. 화면 표시용 구성요소 변수

    public DJView(ControllerInterface controller, BeatModelInterface model) {	
        this.controller = controller;
        this.model = model;
        model.registerObserver((BeatObserver)this);
        model.registerObserver((BPMObserver)this);
    }
    
    public void createView() {
        // 모델용 뷰의 스윙 구성요소를 생성하는 부분
    }
  
  
    public void createControls() {
        // 제어용 뷰의 스윙 구성요소를 생성하는 부분
    }

	.
    .

    //사용자가 버튼을 클릭했을 때 이벤트 처리
    public void actionPerformed(ActionEvent event) {
        //컨트롤러를 통해 모든 작업을 처리한다
        if (event.getSource() == setBPMButton) {
            int bpm = Integer.parseInt(bpmTextField.getText());
            controller.setBPM(bpm);
        } else if (event.getSource() == increaseBPMButton) {
            controller.increaseBPM();
        } else if (event.getSource() == decreaseBPMButton) {
            controller.decreaseBPM();
        }
    }

    //BPMObserver 메소드 - BPM이 변경되면 실행되는 부분
    //모델값을 가져와 세팅해준다
    public void updateBPM() {
        if (model != null) {
            int bpm = model.getBPM();
            if (bpm == 0) {
                if (bpmOutputLabel != null) {
                    bpmOutputLabel.setText("offline");
                }
            } else {
                if (bpmOutputLabel != null) {
                    bpmOutputLabel.setText("Current BPM: " + model.getBPM());
                }
			}
		}
	}
    
    //BeatObserver 메소드 - 새 박자마다 실행될 부분
    //박자 표시 막대를 갱신해준다
    public void updateBeat() {
        if (beatBar != null) {
            beatBar.setValue(100);
        }
    }
}

 

2. 컨트롤러

  • 사용자 입력에 따라 모델 호출
//컨트롤러 인터페이스
public interface ControllerInterface {
    void start();
    void stop();
    void increaseBPM();
    void decreaseBPM();
    void setBPM(int bpm);
}

//컨트롤러
public class BeatController implements ControllerInterface {
    //View와 Model 둘을 이어주는 역할
    BeatModelInterface model;
    DJView view;
   
    public BeatController(BeatModelInterface model) {
        this.model = model;
        view = new DJView(this, model);
        view.createView();
        view.createControls();
        view.disableStopMenuItem();
        view.enableStartMenuItem();
        model.initialize();
    }
  
    //사용자가 start() 버튼을 누르면 
    //모델의 on()메소드를 실행하고
    //뷰의 Start버튼을 비활성화, Stop버튼을 활성화한다.
    public void start() {
        model.on();
        view.disableStartMenuItem();
        view.enableStopMenuItem();
    }
  
    public void stop() {
        model.off();
        view.disableStopMenuItem();
        view.enableStartMenuItem();
    }
    
    public void increaseBPM() {
        int bpm = model.getBPM();
        model.setBPM(bpm + 1);
    }
    
    public void decreaseBPM() {
        int bpm = model.getBPM();
        model.setBPM(bpm - 1);
    }
  
    public void setBPM(int bpm) {
        model.setBPM(bpm);
    }
}

3. 모델

  • 실제 비트 관련 작업 로직
//모델 인터페이스
public interface BeatModelInterface {
    void initialize();
  
    void on();  
    void off();
  
    void setBPM(int bpm);
    int getBPM();
  
    //매 박자마다 연락받을 옵저버
    void registerObserver(BeatObserver o);
    void removeObserver(BeatObserver o);
  
    //BPM이 변경될 때마다 연락받을 옵저버
    void registerObserver(BPMObserver o);
    void removeObserver(BPMObserver o);
}
//BeatModelInterface와 미디 코드를 사용하기 위한 MetaEventListener 구현
public class BeatModel implements BeatModelInterface, MetaEventListener {
    //실제 소리를 위한 시퀀스 객체
    Sequencer sequencer;
    
    //옵저버를 저장하기 위한 ArrayList
    ArrayList beatObservers = new ArrayList();
    ArrayList bpmObservers = new ArrayList();
    
    int bpm = 90;
    //기타 변수
 
    public void initialize() {
        setUpMidi();
        buildTrackAndStart();
    }
 
    public void on() {
        sequencer.start();
        setBPM(90);
    }
 
    public void off() {
        setBPM(0);
        sequencer.stop();
    }
 
    public void setBPM(int bpm) {
        this.bpm = bpm;
        sequencer.setTempoInBPM(getBPM());
        notifyBPMObservers();
    }
  
    public int getBPM() {
        return bpm;
    }
  
    void beatEvent() {
        notifyBeatObservers();
    }
   
    //옵저버 등록 및 해제 코드
    //음악을 만들어내기 위한 미디 코드
}

 


 

 

스트래티지 패턴 + 어댑터 패턴

  • 위에서 만든 View를 심박수를 보여주는 데에 사용해보자

 

1. HeartModel을 BeatModel에 적응

BeatModel HeartModel
getBPM()
.
.
getHeartRate()
registerBeatObserver()
registerBPMObserver()
//타겟 인터페이스(BeatModelInteraface)를 구현
public class HeartAdapter implements BeatModelInterface {
    HeartModelInterface heart;
 
    public HeartAdapter(HeartModelInterface heart) {
        this.heart = heart;
    }

    public void initialize() {}  
    public void on() {}
    public void off() {}
   
    //getBPM이 호출되면 바로 HeartModel의 getHeartRate() 메소드를 호출
    public int getBPM() {
        return heart.getHeartRate();
    }
  
    public void setBPM(int bpm) {}
   
    //옵저버 메소드들 , 역시 HeartModel의 메소드를 호출
    public void registerObserver(BeatObserver o) {
        heart.registerObserver(o);
    }
    
    public void removeObserver(BeatObserver o) {
        heart.removeObserver(o);
    }
     
    public void registerObserver(BPMObserver o) {
        heart.registerObserver(o);
    }
  
    public void removeObserver(BPMObserver o) {
        heart.removeObserver(o);
    }
}

 

2. HeartModel과 연결된 컨트롤러 생성

public class HeartController implements ControllerInterface {
    HeartModelInterface model;
    DJView view;
  
    //BeatModel이 아니라 HeartModel을 전달받음
    public HeartController(HeartModelInterface model) {
        this.model = model;
        //View에 전달 시에는 어댑터로 감싸주기
        view = new DJView(this, new HeartAdapter(model));
        view.createView();
        view.createControls();
        view.disableStopMenuItem();
        view.disableStartMenuItem();
	}
  
    //심박수는 제어할 수 없으므로 비워두기
    public void start() {}
    public void stop() {}
    
    public void increaseBPM() {}
    public void decreaseBPM() {}
  
    public void setBPM(int bpm) {}
}

 


MVC와 웹 환경 : "모델2"

  • 서블릿과 JSP를 사용해 일반적인 GUI를 사용하는 것 처럼 MVC를 분리해 디자인 할 수 있음
  • 이로인해 개발자들은 서블릿에 집중하고, 웹 제작자들은 JSP와 자바빈에만 집중

그리기 귀찮아서 캡쳐했

사용자가 HTTP 요청 시 서블릿에서 수신한다 + 전달 받은 폼 데이터를 파싱

서블릿이 컨트롤러 역할을 맡아 사용자 요청을 처리해 모델에 전달하게 된다.

  • 모델에서 요청을 처리한 결과는 일반적으로 자바빈 형태로 포장된다.

컨트롤러에서는 컨트롤을 뷰한테 넘긴다

  • 뷰는 JSP에 의해 표현되고 JSP는 자바빈을 통해 얻은 모델의 뷰를 나타내는 페이지를 만들어준다

뷰에서 HTTP를 통해 브라우저한테 페이지를 전달한다.

 

 


EX2 - BeatModel을 웹용으로

  • HTML 인터페이스 > 서블릿으로 요청 > 내용 파싱 후 모델에 명령 > JSP에게 제어권 전달 > HTML 뷰 생성 후 브라우저에 전달

 

1. 모델은 수정할 필요가 없음

  • 모델은 뷰나 컨트롤러에 대해 아무 것도 알지 못하면서, 의존하지 않기 때문에 변경하지 않아도 됨.

 

2. 서블릿 컨트롤러 생성

  • HTTP 요청을 받아 모델에 대해 몇 가지 작업을 처리
  • 처리된 모델을 JSP에 전달
//서블릿 기능을 구현하기 위해 HttpServlet 클래스를 상속받음
public class DJView extends HttpServlet{

    public void init() throws ServletException{
        BeatModel beatModel = new BeatModel();
        beatModel.initialize();
        //객체에 대한 레퍼런스를 서블릿 컨텍스트에 저장
        getServletContext().setAttribute("beatModel", beatModel);
    }
    
    //실제로 작업이 처리되는 부분
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
    {
        //서블릿 컨텍스트로부터 모델을 가져옴
        BeatModel beatModel = (BeatModel)getServletContext().getAttribute("beatModel");
        
        //HTTP 명령/매개변수 등을 모두 가져옴
        string bpm = request.getParameter("bpm");
        if(bpm != null){
            bpm = beatModel.getBPM() + "";
        }
        
        //set 명령어가 있으면 설정할 값을 알아낸 뒤 모델에 설정 요청
        string set = request.getParameter("set");
        if(set != null){
            int bpmNumber = 90;
            bpmNumber = Integer.parseInt(bpm);;
            beatModel.setBPM(bpmNumber);
        }
        
        //increase, decrease 관련 명령어 처리
        //on, off 멸영어 처리
        
        //이제 뷰한테 HTML 뷰를 만들어달라고 요청하기
        //JSP에 모델의 상태가 들어있는 빈을 제공 (이 경우에 모델 == 빈)
        request.setAttribute("beatModel", beatModel);
        
        RequestDispatcher dispatcher = request.getRequestDispatcher("jsp/DJView.jsp");
        dispatcher.forward(request, response);        
    }
}

 

3. HTML 뷰 만들기

  • JSP로 간단한 뷰를 제작, 컨트롤러로부터 자바빈을 받아 사용
<jsp:useBean id="beatModel" scope="request" class="headfirst.combined.djview.BeatModel" />

<html>
<head>
<title>DJ View</title>
</head>
<body>

<h1>DJ View</h1>
#분당 비트 수 출력 - 빈으로 부터 속성을 알아냄
Beats per minutes = <jsp:getProperty name="beatModel" property="BPM"/>
<br/>
<hr>
<br/>

<form method="get" action="djview/servlet/DJView">
BPM: <input type=text name="bpm" value="<jsp:getProperty name="beatModel" property="BPM" />">
&nbsp;

#제어용 부분
<input type ="submit" name="set" value="set"><br/>
<input type ="submit" name="decrease" value="<<"><br/>
<input type ="submit" name="increase" value=">>"><br/>
<input type ="submit" name="on" value="on"><br/>
<input type ="submit" name="off" value="off"><br/>
</form>

</body>
</html>

 

모델2에도 그대로 디자인 패턴들이 적용된 걸까?

 

1. 옵저버 패턴

  • 고전적 의미의 옵저버라곤 할 수 없지만, 여전히 컨트롤러를 통해 간접적으로 연락을 받고 빈을 통해 모델의 상태를 알 수 있음
  • 또한 HTTP 응답을 할 때만 모델이 필요하기 때문에, 그 외 상황에서는 모델의 연락을 받지 않아도 됨

2. 스트래티지 패턴

  • 고전적 MVC처럼 구성을 통해 구현되진 않지만 (뷰 객체에 컨트롤러가 포함되는)
  • 컨트롤러 서블릿이 뷰의 행동을 구현하고 있다는 점, 다른 행동을 원하면 바꿀 수 있다는 점에서 여전히 패턴을 따름

3. 컴포지트 패턴

  • HTML 뷰도 결국 복합 객체 형태

 


Q&A

Q. 컨트롤러에선 뷰에서 입력을 받아 모델에 전달하는 역할만 하는건지? 왜 뷰에 작성하지 않고 컨트롤러가 필요한건지

  • 컨트롤러는 사용자 입력을 해석해서 모델을 조작하는 역할을 함
  • 뷰에 작성하지 않는 이유 : 뷰가 두 가지 역할을 하게되고, 뷰와 모델간의 결합이 강해지기 때문

 

Q. 컨트롤러에서 애플리케이션 로직을 구현하는 경우는 없는지

  • 없음. 뷰를 위한 행동만을 구현. 
  • 애플리케이션 로직 = 데이터를 관리하고 조작하기 위한 코드 = 모델에 들어가야 함

 

Q. 뷰와 컨트롤러의 개수는 1:1이어야 하는지

  • 뷰마다 하나씩의 컨트롤러를 만드는게 일반적이지만, 한 컨트롤러로 여러 뷰를 관리할 수도 있음

 

Q. 뷰에서는 원래 모델을 조작하지 않도록 되어 있지만, 위의 코드에선 모델의 상태를 변경하는 메소드에 대한 접근이 열려있는데?

  • 올바른 지적임. 일부 메소드에만 접근할 수 있도록 인터페이스를 고칠 수 있는 디자인 패턴(프록시?)을 사용하면 됨.
Comments