일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 순열
- 전화영어
- 회사폐업
- HeadFirstDesignPatterns
- 부분합알고리즘
- 막대기자르기
- 생애첫계약
- C++
- IT기초
- 알고리즘
- 튜터링
- 자취준비
- 코딩테스트
- 네트워크
- 취업사실신고
- 실업인정인터넷신청
- 정보처리기사개정
- 동적계획법
- 실업급여
- 자료구조
- 사회초년생
- 모여봐요동물의숲
- 프로그래머스
- 정보
- array
- leetcode
- 청년내일채움공제
- 후니의쉽게쓴시스코라우팅
- 캡쳐링
- 후니의쉽게쓴시스코네트워킹
- Today
- Total
따봉도치야 고마워
Head First Design Patterns : (12)컴파운드 패턴 본문
컴파운드 패턴
- 두 개 이상의 패턴을 결합해 일반적으로 자주 등장하는 문제들에 대한 해법을 제공
SimUDuck
- 1장에서 사용했던 예제를 처음부터 다시 만들어보자 (bb-dochi.tistory.com/72)
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" />">
#제어용 부분
<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. 뷰에서는 원래 모델을 조작하지 않도록 되어 있지만, 위의 코드에선 모델의 상태를 변경하는 메소드에 대한 접근이 열려있는데?
- 올바른 지적임. 일부 메소드에만 접근할 수 있도록 인터페이스를 고칠 수 있는 디자인 패턴(프록시?)을 사용하면 됨.
'프로그래밍 > 공부' 카테고리의 다른 글
Head First Design Patterns : (11)프록시 패턴 (0) | 2020.10.05 |
---|---|
Head First Design Patterns : (10)스테이트 패턴 (0) | 2020.09.30 |
Head First Design Patterns : (9)이터레이터와 컴포지트 패턴 (0) | 2020.09.24 |
Head First Design Patterns : (8)템플릿 메소드 패턴 (0) | 2020.09.23 |
Head First Design Patterns : (7)어댑터 패턴과 퍼사드 패턴 (0) | 2020.09.21 |