728x90
반응형

 

🔥 Flutter 핵심 개념 정리: 위젯·엘리먼트·렌더오브젝트 완전 이해하기

 

Flutter를 공부하다 보면 꼭 이해해야 하는 3가지 개념이 있다.

 

👉 Widget

👉 Element

👉 RenderObject

 

처음에는 모두 비슷해 보이지만, Flutter의 UI 렌더링 구조를 정확히 이해하려면 이 세 가지가 어떤 역할을 하는지 명확하게 구분할 필요가 있다.

 

이 글에서는

위젯 → 엘리먼트 → 렌더오브젝트의 흐름

가장 쉬운 비유와 실제 예시, 그리고 트리 구조로 이해할 수 있도록 설명한다.

 


 

1. Widget — UI의 “설계도”

 

Widget은 UI가 어떻게 보여야 하는지 정의하는 ‘설계도’이다.

 

  • 불변(immutable)
  • 화면의 실제 위치나 크기 정보 없음
  • 단순히 “이런 모습으로 만들어줘!” 라는 데이터 덩어리

 

예를 들어, 아래 코드는 모두 “위젯”이다.

Center()
Padding(padding: ...)
Column()
Text("Hello")
Icon(Icons.star)

하지만 이 위젯들은 실제로 화면에 그려지지 않는다.

설계도이기 때문이다.

 


 

2. Element — 위젯의 “실제 위치 정보”

 

Element는 위젯의 인스턴스이자,

UI 트리에서 ‘현재 위젯이 어디에 존재하는지’를 나타내는 객체이다.

 

즉,

 

  • 위젯이 어디에 붙어 있는지 (부모/자식 관계)
  • 위젯이 가진 상태(State)
  • 위젯의 생명주기

 

등은 모두 Element가 관리한다.

 

 

BuildContext의 정체 = Element

 

위젯의 build() 메서드에서 받는 BuildContext context

사실 해당 위젯의 Element 자신을 가리키는 참조이다.

 

예:

@override
Widget build(BuildContext context) {
  // context = 이 위젯의 Element
}

그래서 context를 통해

 

  • Theme.of(context)
  • Navigator.of(context)
  • MediaQuery.of(context)

 

처럼 조상 위젯이나 환경 정보에 접근할 수 있는 것이다.

 

 

모든 위젯은 각각 고유한 Element를 가진다

 

PostEmptyView, Center, Padding, Column, Text, Icon…

모두 각각의 Element를 갖는다.

 


 

3. RenderObject — 실제로 그리는 “엔진 객체”

 

RenderObject는 Flutter 엔진과 직접 연결되어

실제 화면에 그림을 그리는 역할을 한다.

 

RenderObject의 역할은 다음과 같다.

 

  • 레이아웃 계산 (size, position)
  • 페인팅 (draw)
  • Hit Test (터치 이벤트 전달)
  • 스크롤, 정렬, 배치 등 물리적 동작

 

예:

 

  • Text → RenderParagraph
  • Column → RenderFlex
  • SizedBox → RenderConstrainedBox

 

RenderObject는 UI를 실제로 구성하는 “진짜 물체(knob, pixel)”이다.

 


 

4. 세 가지의 관계를 한 번에 이해하기

 

정리하면 이렇게 흐른다:

Widget (설계도)
   ↓ createElement()
Element (위치 + 상태 + 트리 정보)
   ↓ createRenderObject()
RenderObject (레이아웃 + 그리기)

 

비유하면?

개념비유역할

Widget 집의 설계도 UI 구조 정의
Element 실제 방의 위치 위젯이 트리에서 어디 있는지 저장
RenderObject 집의 벽/창문 실제 화면에 그려짐

 


 

5. 트리로 보면 더 명확해진다

 

아래 예시를 보자.

PostEmptyView
 └─ Center
      └─ Padding
           └─ Column
                ├─ Icon
                ├─ SizedBox
                ├─ Text
                ├─ SizedBox
                └─ Text

이건 위젯 트리다.

Flutter는 이 위젯 트리를 기반으로 동일한 구조의 Element 트리를 생성하고,

그 Element에서 필요한 것들만 RenderObject 트리를 만든다.

 


 

6. Rebuild 과정에서는 무엇이 재사용될까?

 

Flutter가 성능이 빠른 이유는

모든 것을 새로 만드는 것이 아니라 필요한 것만 업데이트하기 때문이다.

구분상태설명

Widget 매번 새로 생성됨 가벼운 데이터 객체라 부담 없음
Element 재사용됨 (트리 유지) 트리 구조와 State 유지
RenderObject 대부분 재사용됨 속성 변경만 업데이트

이 구조 덕분에 Flutter는 빠르게 rebuild가 가능하다.

 


 

7. BuildContext는 “최상위”가 아니다

 

많은 초보자들이 오해하는 부분이 있다.

 

BuildContext는 “해당 위젯의 위치(Element)”를 의미한다.

 

 

  • PostEmptyView build() 안에서는 PostEmptyView의 context
  • Column 내부 Builder에서는 Column 내부의 context
  • Text 내부에서는 TextElement의 context

 

따라서 위젯마다 각자의 BuildContext(Element)가 존재한다.

“최상위 context 하나만 존재하는 구조”가 아니다.

 


 

8. 정리: 한 장으로 끝내는 개념 총정리

📘 Widget
- UI 정의 (설계도)
- 불변
- 위치 정보 없음

🧩 Element
- 위젯의 실제 인스턴스
- 트리의 구조 담당 (부모/자식)
- 상태(State) 보관
- BuildContext의 정체

🎥 RenderObject
- 화면에 실제로 그리는 객체
- 레이아웃 / 그리기 엔진
- 필요한 위젯에서만 생성됨

 


 

마무리

 

Flutter의 핵심 구조는

Widget → Element → RenderObject

이 세 가지가 어떻게 협력하느냐를 이해하는 것이다.

 

이 원리를 제대로 이해하면:

 

✔ Flutter 렌더링 구조가 명확해지고

✔ BuildContext의 역할이 분명해지고

✔ Rebuild / Layout / Render 단계가 잡히며

✔ 성능 최적화까지 자연스럽게 이해된다.

728x90
반응형
728x90
반응형

🔍 BFS(Breadth-First Search) 완전 정복

 

코딩테스트의 기본 탐색 알고리즘

 

그래프 탐색 문제에서 DFS와 함께 항상 등장하는 알고리즘이 BFS(너비 우선 탐색).

특히 BFS는 가장 가까운 노드부터 탐색하는 특성 덕분에 최단 거리 문제에서 자주 사용되는 방식.

 

 


 

✅ BFS란?

 

BFS(Breadth-First Search)는 시작 노드로부터 가까운 순서대로 탐색하는 그래프 탐색 기법

같은 깊이의 노드들을 먼저 방문한 뒤, 점점 더 멀리 있는 노드를 탐색하는 방식

 

DFS가 깊게 내려가는 구조라면, BFS는 넓게 펼쳐지며 탐색하는 구조라고 이해하면 쉬운 형태

 


 

✔ 핵심 특징

 

  • 큐(Queue) 자료구조 사용
  • 방문한 노드를 체크하기 위한 방문 배열 필요
  • 모든 정점을 한 번씩 방문하며 모든 간선을 확인
  • 시간복잡도: O(V + E)
  • 그래프는 인접 리스트로 표현하는 것이 효율적인 방식

 


 

🧩 동작 방식 시각화 (간단한 이미지 느낌으로 설명)

 

다음 구조의 그래프가 있다고 가정:

1
├─ 2
│  ├─ 4
│  └─ 5
└─ 3

BFS 탐색 순서는 다음과 같은 형태:

 

1 → 2 → 3 → 4 → 5

 

즉, 1과 가까운 2, 3을 먼저 방문,

그 다음 거리인 4, 5를 방문하는 구조

 

DFS는 깊이로 내려가는 방식

BFS는 레벨(층단)별로 방문하는 방식

 


 

⚙ BFS 구현 과정

 

 

1. 탐색 준비

 

  • 그래프를 인접 리스트로 초기화
  • 방문 배열 생성
  • 시작 노드를 큐에 삽입

 

 

2. 큐에서 노드를 하나 꺼내기

 

  • 꺼낸 노드를 방문하고
  • 해당 노드의 인접 노드 중 방문하지 않은 노드만 큐에 삽입

 

 

3. 큐가 empty 상태가 될 때까지 반복

 

  • 큐가 비어있다는 것은 더 이상 탐색할 노드가 없다는 의미
  • 방문 배열을 통해 중복 방문을 방지하는 구조

 


 

📘 예제: 백준 1260 DFS/BFS 문제

 

문제: https://www.acmicpc.net/problem/1260

 

아래 코드는 DFS와 BFS를 모두 구현한 코드

BFS의 기본 흐름을 이해하기 좋은 형태

package c5_search.bfs;

import java.util.*;

public class P1260_4 {
    static boolean visited[];
    static ArrayList<Integer>[] arr;

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int e = sc.nextInt();
        int start = sc.nextInt();

        // 1. 인접리스트 초기화
        arr = new ArrayList[n+1];
        for(int i = 1; i <= n; i++){
            arr[i] = new ArrayList<>();
        }

        // 간선 입력
        for(int i = 0; i < e; i++){
            int first = sc.nextInt();
            int last = sc.nextInt();
            arr[first].add(last);
            arr[last].add(first);
        }

        // 방문 배열 초기화
        visited = new boolean[n+1];

        // 번호가 작은 노드부터 방문하기 위한 정렬
        for(int i = 1; i <= n; i++){
            Collections.sort(arr[i]);
        }

        // DFS 출력
        dfs(start);
        System.out.println();

        // BFS 방문 배열 초기화
        visited = new boolean[n+1];

        // BFS 출력
        bfs(start);
    }

    // DFS (재귀 방식)
    public static void dfs(int node){
        System.out.print(node + " ");
        visited[node] = true;

        for(int next : arr[node]){
            if(!visited[next]) dfs(next);
        }
    }

    // BFS (큐 방식)
    public static void bfs(int node){
        Queue<Integer> queue = new LinkedList<>();
        queue.add(node);
        visited[node] = true;

        while(!queue.isEmpty()){
            int now = queue.poll();
            System.out.print(now + " ");

            for(int next : arr[now]){
                if(!visited[next]){
                    queue.add(next);
                    visited[next] = true;
                }
            }
        }
    }
}

 


 

📝 BFS에서 꼭 기억해야 할 포인트

 

 

✔ 1. BFS는 큐 기반 구조

 

핵심은 선입선출(FIFO)

먼저 발견된 노드를 먼저 방문하는 방식

 

 

✔ 2. 방문 체크는 노드를 큐에 넣을 때 즉시 처리

 

중복 방문 방지 및 불필요한 삽입 차단

 

 

✔ 3. 그래프는 인접 리스트 사용

 

인접 리스트는 메모리 효율성과 순회 성능 모두 최적

 

 

✔ 4. 정렬(Optional)

 

문제 조건에 따라 “작은 번호 먼저 방문” 요구 시 정렬 필수

 


 

💡 BFS는 언제 사용할까?

 

  • 그래프에서 최단 거리를 구해야 하는 경우
  • 미로 탐색
  • 레벨 단위 탐색이 필요한 문제
  • 네트워크/연결 관계를 넓게 탐색하는 문제
  • 토마토 문제처럼 “전파” 형태의 시뮬레이션

 

DFS와 함께 코딩테스트 탐색 문제에서 기본적으로 요구되는 알고리즘 형태

 


 

📌 마무리

 

BFS는 가까운 노드부터 차례로 탐색하는 대표적인 그래프 알고리즘

큐 기반의 구조만 이해하면 구현 난이도가 낮고,

최단 거리·단계별 탐색 문제에서 강력한 효과를 발휘하는 방식

 

DFS와 함께 반드시 익혀야 하는 코딩테스트 기본 탐색 알고리즘

 


 

728x90
반응형
728x90
반응형

🔍 DFS(Depth-First Search) 완전 정복

 

코딩테스트의 기본 탐색 알고리즘

 

그래프 탐색 문제를 풀다 보면 DFS(깊이 우선 탐색) 은 반드시 등장하는 핵심 알고리즘.

지금부터 DFS가 무엇인지, 어떤 특징을 가지는지, 어떻게 구현하는지 차근차근 정리해보는 시간을 가져보자.

 


 

✅ DFS란?

 

DFS(Depth-First Search)는 최대한 깊숙한 곳까지 탐색한 뒤 다시 되돌아오는 방식의 탐색 기법

완전탐색(Brute Force)의 한 종류로, 그래프 문제를 해결할 때 가장 많이 사용되는 탐색 방식

 

 

✔ 핵심 특징

 

  • 재귀 함수를 사용해 구현하는 경우가 많음
  • 또는 스택(Stack) 자료구조를 활용
  • 모든 정점을 한 번씩 방문하고 모든 간선을 모두 확인
  • 시간복잡도: O(V + E)
  • 그래프는 보통 인접 리스트로 표현

 


 

🧩 동작 방식 시각화 (간단한 이미지 느낌으로 설명)

 

예를 들어 다음과 같은 그래프가 있다고 가정:

1
├─ 2
│  ├─ 4
│  └─ 5
└─ 3

DFS는 이렇게 방문:

1 → 2 → 4 → (되돌아옴) → 5 → (되돌아옴) → 3

즉, 한 방향으로 끝까지 내려간 뒤 다시 되돌아가서 다른 갈래로 이동하는 방식

 


 

⚙ DFS 구현 과정

 

 

1. 탐색 준비

 

  • 그래프를 인접 리스트로 초기화
  • 방문 여부를 표시할 배열 생성
  • 시작 노드를 스택(또는 재귀)로 삽입

 

 

2. 스택(또는 재귀 호출)에서 노드를 꺼내기

 

꺼낸 노드의 인접 노드를 확인해 아직 방문하지 않았다면 스택에 넣는 과정 반복

 

 

3. 스택이 empty 상태가 될 때까지 반복

 

이미 방문한 노드는 다시 방문하지 않음

방문 배열을 활용하여 중복 방문 방지

 


 

📘 예제: 백준 1260 DFS/BFS 문제

 

문제: https://www.acmicpc.net/problem/1260

 

아래 코드는 DFS와 BFS를 모두 구현한 기본 예제

DFS의 방식이 어떻게 작동하는지 이해하기 좋은 형태

package c5_search.bfs;

import java.util.*;

public class P1260_4 {
    static boolean visited[];
    static ArrayList<Integer>[] arr;

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int e = sc.nextInt();
        int start = sc.nextInt();

        // 1. 인접리스트 초기화
        arr = new ArrayList[n+1];
        for(int i = 1; i <= n; i++){
            arr[i] = new ArrayList<>();
        }

        // 간선 정보 입력
        for(int i = 0; i < e; i++){
            int first = sc.nextInt();
            int last = sc.nextInt();
            arr[first].add(last);
            arr[last].add(first);
        }

        // 방문 배열 초기화
        visited = new boolean[n+1];

        // 번호가 작은 순서대로 방문하기 위한 정렬
        for(int i = 1; i <= n; i++){
            Collections.sort(arr[i]);
        }

        // DFS 출력
        dfs(start);
        System.out.println();

        // BFS 방문 배열 초기화
        visited = new boolean[n+1];

        // BFS 출력
        bfs(start);
    }

    // DFS (재귀 방식)
    public static void dfs(int node){
        System.out.print(node + " ");
        visited[node] = true;

        for(int next : arr[node]){
            if(!visited[next]) dfs(next);
        }
    }

    // BFS (큐 방식)
    public static void bfs(int node){
        Queue<Integer> queue = new LinkedList<>();
        queue.add(node);
        visited[node] = true;

        while(!queue.isEmpty()){
            int now = queue.poll();
            System.out.print(now + " ");

            for(int next : arr[now]){
                if(!visited[next]){
                    queue.add(next);
                    visited[next] = true;
                }
            }
        }
    }
}

 


 

📝 DFS에서 꼭 기억해야 할 포인트

 

 

✔ 1. DFS는 재귀 구현이 가장 직관적

 

재귀 호출 구조가 스택을 그대로 흉내낸 형태

 

 

✔ 2. 방문 체크는 가장 먼저 처리

 

visited[node] = true 를 방문하자마자 수행해야 무한 루프 방지

 

 

✔ 3. 그래프는 인접 리스트 사용

 

인접 리스트가 메모리 절약 + 반복문 처리 효율적

 

 

✔ 4. 정렬(Optional)

 

문제에서 “번호가 작은 순서대로 방문” 조건이 있을 경우 정렬 필수

 


 

💡 DFS는 언제 사용할까?

 

  • 그래프의 한 갈래를 끝까지 탐색해야 하는 문제
  • 모든 경우의 수를 깊게 탐색해야 하는 경우
    • 조합/순열 백트래킹
    • 미로 탐색
    • 연결 요소 찾기
    • 트리 구조 탐색
  •  

 

코딩테스트에서 매우 자주 등장하는 유형이므로 반드시 익혀야 하는 기본 알고리즘

 


 

📌 마무리

 

DFS는 알고리즘 문제 풀이의 기초이자, 다양한 문제의 기반이 되는 탐색 알고리즘

재귀, 스택, 인접 리스트 구조에 익숙해진다면 탐색 문제는 훨씬 수월하게 해결 가능

728x90
반응형
728x90
반응형

커맨드 패턴이란?

- 요구사항을 객체로 캡슐화해서 객체를 서로 다른 요구사항에 따라 매개변수화할 수 있다.

 

클래스 다이어그램을 살펴보자.

- Client : 커맨드 객체 생성

- Command : 어떤 Recevier를 실행할 지 연결

- Invoker : 주문을 받아서, 실행하기 위해 Command 인터페이스 연결

- Receiver : 실제 명령을 수행

 

간단한 예제를 보면서 살펴보자.

 

문제점

 집의 불을 켜주고, 차고 문을 열어주고, 스피커 전원도 켜주는 등 다양한 기능을 하는 최첨단 만능 IOT 리모컨이 존재한다. 

public class Light {
    public void on(){
        System.out.println("불을 켭니다.");
    }
    public void off(){
        System.out.println("불을 끕니다.");
    }
}

 

public class GarageDoor {
    public void open(){
        System.out.println("차고 문을 엽니다.");
    }
    public void close(){
        System.out.println("차고 문을 닫습니다.");
    }

}

 

public class Stereo {
    public void up(){
        System.out.println("볼륨을 올립니다.");
    }
    public void down(){
        System.out.println("볼륨을 내립니다.");
    }
}

사용하는 객체는 많은데 on/off, up/down 등 객체마다 사용하는 메소드가 달라 복잡하다...

 

해결책

Command 패턴을 적용해 Command 인터페이스를 추가하고 API를 일치시켜준다. 

public interface Command {
    public void execute();
    public void undo();

}

 

public class GarageDoorCommand implements Command{
    GarageDoor gd;
    public GarageDoorCommand(GarageDoor gd){
        this.gd = gd;
    }
    @Override
    public void execute(){
        gd.open();
    }

    @Override
    public void undo() {
        gd.close();
    }
}

 

public class LightCommand implements Command{
    Light light;
    public LightCommand(Light light){
        this.light = light;
    }

    @Override
    public void execute() {
        light.on();
    }

    @Override
    public void undo() {
        light.off();
    }
}

 

public class StereoCommand implements Command{
    Stereo stereo;

    public StereoCommand(Stereo stereo){
        this.stereo = stereo;
    }
    @Override
    public void execute() {
        stereo.up();
    }

    @Override
    public void undo() {
        stereo.down();
    }
}

 불을 켜는 것도, 스테레오의 볼륨을 조절하는 것도, 차고 문을 여는 것도 execute()라는 메소드로 일치시킬 수 있다. 

물론 클래스가 많아진다는 단점이 있지만 객체 사용에 복잡성을 제거하고 감출 수 있게 된다.

728x90
반응형
728x90
반응형

퍼사드 패턴이란?

- 서브 시스템에 있는 여러개의 인터페이스를 통합하는 한 개의 인터페이스를 제공하는 패턴

- 퍼사드는 서브 시스템을 쉽게 사용할 수 있도록 해주는 고급 수준의 인터페이스를 정의한다.

 

 우리가 밴드를 구성해서 공연을 하려고 한다. 그러기 위해서는 마이크의 전원을 켜고, 볼륨을 조절하고, 기타의 톤을 맞추고, 피아노 전원을 켜고... 공연을 시작하기 위해서 할 작업이 너무 많다. 그저 "공연 준비"라는 버튼 하나로 모든 기구의 세팅이 완료되었으면 좋겠다. 퍼사드 패턴을 적용해보자.

 

문제점

마이크 볼륨 조절, 전원, 기타의 톤 세팅 등 공연을 시작할 때 마다 수행해야 할 서브 시스템이 너무 많고 사용하기가 복잡하다.

 Mike

public class Mike {
    public void on(){
        System.out.println("마이크의 전원을 켭니다.");
    }
    public void off(){
        System.out.println("마이크의 전원을 끕니다.");
    }
}

Guitar

public class Guitar {
    public void on(){
        System.out.println("일렉기타의 전원을 켭니다.");
    }
    public void off(){
        System.out.println("일렉기타의 전원을 끕니다.");
    }

    public void setTone(){
        System.out.println("일렉기타의 톤을 맞춥니다.");
    }



}

Drum

public class Drum {
    public void on(){
        System.out.println("드럼의 전원을 켭니다.");
    }
    public void off(){
        System.out.println("드럼의 전원을 끕니다.");
    }
}

 

해결책

BandFacade라는 하나의 단순한 인터페이스를 제공하는 객체를 중간에 넣어서 startBand 메소드를 만들어 공연 시작 시 그에 맞는 메소드들을 실행해주게끔 한다.

public class BandFacade {
    Amplifier amp;
    Drum drum;
    Guitar guitar;
    Mike mike;

    public BandFacade(Amplifier amp, Drum drum, Guitar guitar, Mike mike) {
        this.amp = amp;
        this.drum = drum;
        this.guitar = guitar;
        this.mike = mike;
    }

    public void startBand(){
        amp.on();
        drum.on();
        guitar.on();
        mike.on();
        guitar.setTone();
    }

    public void endBand(){
        amp.off();
        drum.off();
        guitar.off();
        mike.off();

    }

}

 

클라이언트 입장에서는 startBand 메소드만 호출함으로서 복잡했던 공연 준비를 할 수 있게 되는 것이다.

728x90
반응형
728x90
반응형

어댑터 패턴이란?

- 클래스의 인터페이스를 클라이언트가 원하는 형태의 또 다른 인터페이스로 변환하는 패턴

- 어댑터는 호환되지 않는 인터페이스 때문에 동작하지 않는 클래스들을 함께 동작할 수 있도록 만들어준다.

 

전기 플러그를 생각해보자. 미국의 플러그 모양은 110V (11자 모형)이다. 미국 전자 제품을 한국에서 쓰려면 어댑터를 사용하여 한국의 플러그 모양에 맞게 변환해서 사용하면 된다. 이 것이 어댑터 패턴의 기본 원리라고 생각하면 된다.

 

헤드 퍼스트 디자인 책의 예시를 통해 문제점과 해결 방안을 살펴보자.

 

문제점

오리에 대한 Duck 인터페이스와 그를 구현한 MallardDuck, 칠면조에 대한 Turkey 인터페이스와 그를 구현한 WildTurkey 클래스가 존재한다. 오리쇼를 하고 싶은데 오리의 숫자가 부족하여 칠면조를 오리의 탈을 씌워 쇼에 참가해야 하는 상황이다. 먼저 클래스와 인터페이스를 살펴보자.

Duck

public interface Duck {
    public void quack();
    public void fly();
}

MallardDuck

public class MallardDuck implements Duck{
    @Override
    public void quack() {
        System.out.println("Quack");
    }

    @Override
    public void fly() {
        System.out.println("I'm flying");
    }
}

Turkey

public interface Turkey {
    public void gobble();
    public void fly();

}

WildTurkey

public class WildTurkey implements Turkey{

    // Turkey 인터페이스와 Duck 인터페이스는 다른 인터페이스다.
    // 문제점 : Duck 객체가 부족하여 Turkey 객체도 Duck 객체처럼 사용하고 싶다.
    // TurkeyAdapter를 사용하여 Turkey 객체도 Duck 객체로 변환하여 사용해보자.
    @Override
    public void gobble() {
        System.out.println("Gobble gobble");
    }

    @Override
    public void fly() {
        System.out.println("I'm flying a short distance");
    }
}

 

해결책

어댑터 패턴을 적용하여 Turkey 객체를 Duck "처럼" 사용해보자.

TurkeyAdapter

public class TurkeyAdapter implements Duck{
    Turkey turkey;

    public TurkeyAdapter(Turkey turkey){
        this.turkey = turkey;
    }

    @Override
    public void quack() {
        turkey.gobble();
    }

    @Override
    public void fly() {
        for(int i =0; i<5; i++){
            turkey.fly();
        }

    }
}

- Turkey 객체를 생성자의 매개 변수로 받아 Turkey의 매소드를 적절하게 Duck의 메소드에 활용한다.

 

728x90
반응형

+ Recent posts