🥕
TIL
  • [TIL] Studying tech / computer science knowledge
  • KeyMap
  • 알고리즘
    • 복잡도 계산 ( Computational Complexity )
    • DisjointSet-unionFind
    • Bellman-ford Algorithm
    • Dijkstra's Algorithm
    • DP ( Dynamic Programming , 동적 계획법 )
    • 플로이드-워셜 알고리즘 (Floyd-Warshall algorithm)
    • Kruskal's Algorithm
    • 최장 증가 수열 (Longes Increasing Subsequence)
    • Prim's Algorithm
    • 정렬
    • 시간복잡도 와 공간복잡도 ( Time Complexity & Space Complexity )
    • Topological Sort (위상 정렬)
  • 책 읽고난 후 요약
    • 프로그래밍 대회에서 배우는 알고리즘 문제해결 전략
    • cleancode
    • 도메인 주도 설계로 시작하는 마이크로서비스 개발
    • 오브젝트
  • CDC
    • debzium
    • kafka
  • 개발 상식
    • asciidoctor
    • 컴파일러
    • ELK 스택
    • 엔디안
    • git
    • Gitmoji
    • 테스트 종류
    • 라이브러리와 프레임워크
    • 정규 표현식
    • REST API
    • 동기와 비동기 / Blocking과 NonBlocking
    • Transaction Script와 Domain Model
    • 디자인 패턴
      • 행동 패턴
      • 객체 생성 패턴
        • 추상 팩토리 패턴
        • 빌더 패턴
        • 팩토리 메서드 패턴
        • [생성 패턴] 프로토 타입 (Prototype Parttern)
        • 싱글톤
      • 구조 패턴
        • 어댑터 패턴
        • 브릿지 패턴
        • 컴포짓(Composite) 패턴
        • 데코레이터
        • 프록시
    • refactoring
      • 중복 코드
      • 전역 데이터
      • 긴 함수
      • 긴 매개변수 목록
      • 가변 데이터
      • 이해하기 힘든 이름
  • 자료구조
    • AVL Tree
    • Splay Tree
    • aaTree
    • array-list
    • 자료구조 시간/공간 복잡도
    • 그래프
    • 힙
    • Red Black Tree
    • stack-queue
    • 트리 ( Tree )
  • DevOps
    • MSA
    • Kubernetes
      • AccessingAPI
      • controller
      • dashboard
      • kubernetes
      • object
      • pod
      • service
      • volume
  • Java
    • 어노테이션
    • 제어문
    • 데이터 타입
    • Enum
    • jvm
    • 연산자
    • thread
    • Java8
      • CompletableFuture
      • Date/Time
      • 어노테이션과 메타스페이스
      • 인터페이스
      • 람다식
      • Optional
      • 스트림
  • JavaScript
    • moduleProject
    • webpack-babel
    • 코어 자바스크립트
      • array
      • 함수 바인딩
      • 데코레이터와 포워딩
      • Class
      • 비교 연산자
      • Date 내장 객체
      • destructuring-assignment
      • function
      • 함수의 prototype 프로퍼티
      • 가비지 컬렉션 ( Garbage Collection )
      • JSON (JavaScript Object Notation)
      • map-set
      • 내장 프로토타입
      • new연산자와 생성자 함수
      • 객체
      • Object.keys, values, entries
      • 옵셔널 체이닝 '?.'
      • 프로퍼티 플래그
      • 프로퍼티 종류
      • 프로토 타입
      • 호출 스케줄링 ( scheduling a call )
      • scope
      • this
      • type-conversions
      • type
      • 함수의 자료형
      • var_let_const
  • Linux
    • 기본 명령어
    • 파일 종류
    • 리눅스
  • 네트워크
    • 응용 계층 ( Application Layer )
    • 오류 검출과 오류 정정
    • Http
    • Http Header
    • 컴퓨터 네트워크란
    • 네트워크 계층
    • 네트워크 제어 영역
    • 전송 계층 ( Transport Layer )
  • PHP
    • Facade
    • composer
    • scopeResolutionOperator
    • Laravel
      • SocialProvider
      • architecture
      • blade
      • controller
      • db
      • dbArchitecture
      • debug
      • eloquent
      • email
      • event
      • exceptionHandling
      • middleware
      • model
      • modelFactory
      • pagingLoading
      • queryBuilder
      • route
      • scout
      • seeding
      • tntsearch
      • validate
      • view
  • React
    • Next.js
    • React 란?
  • Spring
    • Controller
    • 요청이 들어왔을때 스프링이 처리하는 방법 ( 내부구조 )
    • ConfigurationProperties
    • Entity / DTO / VO
    • Maven
    • Repository와 DAO
    • 스프링 빈
    • Spring Framework
    • MVC 패턴
    • 도메인 입력값 검증
    • Spring Cloud
      • Spring Cloud
      • Eureka
    • Spring Data
      • JPA
      • JPA 어노테이션
      • 엔티티 비교
      • 복합 키와 식별 관계 매핑
      • JPA 예외처리
      • 객체지향 쿼리
      • EntityManagerFactory와 EntityManager
      • JPA 최적화
      • 프록시와 연관관계 맵핑
      • 연관관계
      • 상속관계 맵핑
      • 트랜잭션 범위와 영속성 컨텍스트
      • 데이터 타입
      • MySQL 연결
      • Pageable
    • Spring Project들과 library
      • Custom Serialize
      • Elasticsearch Index API
      • Spring HATEOAS
      • lombok (롬복)
      • Model Mapper
      • Object Mapper
      • Representation Model
      • Spring REST Docs
      • Spring Boot
    • Spring Security
      • Spring Security
      • Authentication
      • Authentication Filter
      • Authorization Filter
      • Filter Chain
      • SecurityContext
      • Spring OAuth2.0
    • Spring Test
      • AssertJ
      • Junit5
      • JunitParams
      • Mock Object
  • DataBase
    • ALIAS
    • CONCAT
    • CTE
    • Group By
    • HAVING
    • IFNULL
    • 인덱스
    • JOIN
    • ORDER BY
    • ROLLUP
    • SELECT
    • SELECT DISTINCT
    • SQL
    • WHERE
  • Web 상식
    • OAuth
    • WAS
    • HTTP통신 기반 인증
    • 브라우저
    • CSR 과 SSR
    • HTTPS
    • Web
Powered by GitBook
On this page
  • 장점과 단점
  • 장점
  • 단점
  • 컴포짓 패턴을 사용하는 Swing 라이브러리
  • 컴포짓 패턴의 방식
  • 안정성을 추구하는 방식
  • 일관성을 추구하는 방식
  1. 개발 상식
  2. 디자인 패턴
  3. 구조 패턴

컴포짓(Composite) 패턴

Previous브릿지 패턴Next데코레이터

Last updated 3 years ago

객체들의 관계를 트리 구조로 구성하여 부분-전체 계층을 표현하는 패턴으로, 사용자가 단일 객체와 복합 객체 모두 동일하게 다루도록 함

Untitled

위의 다이어그램의 각각의 역할은 다음과 같다.

  • Component → 모든 객체(부분, 전체)의 추상 인터페이스

    • 공통적인 기능을 선언

  • Leaf → Component 인터페이스의 단일 객체 구현체

  • Composite → Component 인터페이스의 전체 관리 객체 구현체

    • Leaf와 똑같은 Component의 구현체이지만 Composite은 Leaf들을 관리하는 기능(add, remove)이 있다.

컴포짓 패턴은 전체(composite)-부분(Leaf) 관계(Ex: Directory-File)를 갖는 객체들 사이의 관계 정의할 때 사용 → 즉 클라이언트에서 composite 구현체를 이용해 Leaf를 관리하는 것인데 이것을 클라이언트는 Component라는 추상 인터페이스로만 기능을 사용하면 된다.

바로 예제를 보자.

//component
public interface Saramin {
    int getWorkerNum();
    int getMaleNum();
    int getFemaleNum();
}

//composite
public class ITLaboratory implements Saramin {
    private List<Saramin> saramins = new ArrayList<>();
    @Override
    public int getWorkerNum() {return saramins.stream().mapToInt(Saramin::getWorkerNum).sum();}
    @Override
    public int getMaleNum() {return saramins.stream().mapToInt(Saramin::getMaleNum).sum();}
    @Override
    public int getFemaleNum() {return saramins.stream().mapToInt(Saramin::getFemaleNum).sum();}
    public void addSaramin(Saramin saramin) {this.saramins.add(saramin);}
}

//composite
public class PlanDepartment implements Saramin {
    private List<Saramin> saramins = new ArrayList<>();
    @Override
    public int getWorkerNum() {return saramins.stream().mapToInt(Saramin::getWorkerNum).sum();}
    @Override
    public int getMaleNum() {return saramins.stream().mapToInt(Saramin::getMaleNum).sum();}
    @Override
    public int getFemaleNum() {return saramins.stream().mapToInt(Saramin::getFemaleNum).sum();}
    public void addSaramin(Saramin saramin) {this.saramins.add(saramin);}
}

//composite의 composite?
public class Department implements Saramin {
    private List<Saramin> saramins = new ArrayList<>();
    @Override
    public int getWorkerNum() {return saramins.stream().mapToInt(Saramin::getWorkerNum).sum();}
    @Override
    public int getMaleNum() {return saramins.stream().mapToInt(Saramin::getMaleNum).sum();}
    @Override
    public int getFemaleNum() {return saramins.stream().mapToInt(Saramin::getFemaleNum).sum();}
    public void addSaramin(Saramin saramin) {this.saramins.add(saramin);}
}

//leaf
@AllArgsConstructor
public class Planner implements Saramin {
    private String sex;
    @Override
    public int getWorkerNum() {return 1;}
    @Override
    public int getMaleNum() {return sex.equals("male") ? 1 : 0;}
    @Override
    public int getFemaleNum() {return sex.equals("female") ? 1 : 0;}
}

//leaf
@AllArgsConstructor
public class Developer implements Saramin {
    private String sex;
    @Override
    public int getWorkerNum() {return 1;}
    @Override
    public int getMaleNum() {return sex.equals("male") ? 1 : 0;}
    @Override
    public int getFemaleNum() {return sex.equals("female") ? 1 : 0;}
}

//client
public class Main {
    public static void main(String[] args) {
        ITLaboratory itLaboratory = new ITLaboratory();
        Developer d1 = new Developer("male");
        Developer d2 = new Developer("female");
        itLaboratory.addSaramin(d1);
        itLaboratory.addSaramin(d2);

        PlanDepartment planDepartment = new PlanDepartment();
        Planner p1 = new Planner("male");
        Planner p2 = new Planner("female");
        planDepartment.addSaramin(p1);
        planDepartment.addSaramin(p2);

        Department department = new Department();
        department.addSaramin(itLaboratory);
        department.addSaramin(planDepartment);

        client(d1);
        client(itLaboratory);
        client(planDepartment);
        client(department);
    }
    public static void client(Saramin saramin) {
        System.out.printf("worker:%d  male:%d  female:%d\n", saramin.getWorkerNum(), saramin.getMaleNum(), saramin.getFemaleNum());
    }
}

//결과
worker:1  male:1  female:0
worker:2  male:1  female:1
worker:2  male:1  female:1
worker:4  male:2  female:2

컴포짓 패턴에 매칭 시키면 다음과 같다.

  • Saramin → Component

  • Department → Composite (어찌보면 Composite Of Composite)

  • ITLaboratory → Composite

  • PlanDepartment → Composite

  • Developer → Leaf

  • Planner → Leaf

위의 코드를 해석하면 부서와 직원은 모두 Saramin에 소속되어 있다. 그리고 직원은 개발자 ,기획자가 있고 개발자는 IT연구소 소속이고 기획자는 기획부서 소속이다.

클라이언트에서는 Leaf, Composite를 구별하지 않고 Saramin이라는 인터페이스의 추상 기능만으로 사용할 수 있다. 만약 추후 IT연구소에 다른 직원군이 생겨도 Saramin을 상속하고 구현만 하면 되고 클라이언트 코드는 변경될 필요가 없기 때문에 OCP를 만족하게 된다.

장점과 단점

장점

  • 복잡한 트리 구조를 편하게 사용할 수 있습니다.

    • 클라이언트는 Component의 getPrice 메서드만 사용하면 되기 때문

  • 다형성과 재귀를 활용할 수 있습니다.

    • 하나의 getPrice 메서드가 구현체 마다 다르게 동작하는 다형성과 Leaf 객체를 찾기 위해 DFS와 같은 재귀를 활용하게 됩니다.

  • 클라이언트 코드를 변경하지 않고 새로운 구현체를 추가할 수 있습니다.

컴포짓 패턴을 사용함으로써 OCP(Open-Closed Principle) 즉, 개방 폐쇄 원칙을 지키면서 프로그래밍을 할 수 있다는 것을 알 수 있습니다.

단점

  • 트리를 만들야 하기 때문에 (공통된 인터페이스를 정의해야 하기 때문에) 지나치게 일반화 해야 하는 경우가 생길 수 있습니다.

    • 예를 들어, 가격이 존재하지 않는 객체가 있을 수도 있는데, 이 객체는 getPrice가 굳이 필요하지 않지만 가방에 넣으려면 Component를 상속받아야 하기 때문에 지나친 일반화가 발생하는 경우라고 할 수 있습니다.

컴포짓 패턴을 적용하다가 억지로 일반화해야하는 경우가 발생한다면, 해당 구조가 컴포짓 패턴으로 구현하는 게 맞는지 다시 한 번 생각해봐야 합니다.

컴포짓 패턴을 사용하는 Swing 라이브러리

스윙(Swing)은 자바 언어에서 GUI의 구현하기 위해 제공되는 라이브러리입니다. 자바에서 추구하는 WORE(Wirte Once, Run Everywhere)을 구현하기 위해 JDK 1.2 버전부터 사용되었습니다.

import javax.swing.*;

public class SwingExample {

    public static void main(String[] args) {
      	// 프레임을 만듬
        JFrame frame = new JFrame();

      	// 텍스트 필드 박스를 만들고 프레임에 추가
        JTextField textField = new JTextField();
        textField.setBounds(200, 200, 200, 40);
        frame.add(textField);

      	// 버튼을 만들고 프레임에 추가
        JButton button = new JButton("click");
        button.setBounds(200, 100, 60, 40);
        button.addActionListener(e -> textField.setText("Hello Swing"));
        frame.add(button);

      	// 프레임 크기 설정 후 보여주기
        frame.setSize(600, 400);
        frame.setLayout(null);
        frame.setVisible(true);
    }
}

여기서 JFrame, JTextField, JButton은 컴포짓 패턴으로 이루어져 있습니다. 이 3개의 객체는 전부 Component 라는 추상 클래스를 상속받고 있습니다.

프레임의 add 메서드는 아래 처럼 되어 있는데, 위 예시의 Bag(Composite) 같은 Component를 상속하는 객체들을 리스트로 가지고 있는 것을 알 수 있습니다.

public Component add(Component comp) {
        addImpl(comp, null, -1);
        return comp;
}

protected void addImpl(Component comp, Object constraints, int index) {
        synchronized (getTreeLock()) {
          
          	.... 생략
              
            // component라는 리스트에 파라미터로 받은 comp를 넣습니다.
            if (index == -1) {
                component.add(comp);
            } else {
                component.add(index, comp);
            }
            
          	.... 생략
              
        }
    }

컴포짓 패턴의 방식

컴포짓 패턴에서 Composite 클래스는 자식들을 관리하기 위한 추가적인 메서드가 필요합니다. 이러한 메서드의 설계 방식에 따라 2가지 형태의 방식으로 나눌 수 있습니다.

안정성을 추구하는 방식

안정성을 추구하는 방식은 자식을 다루는 add(), remove() 와 같은 메소드들은 오직 Composite 만 정의되었다. 그로 인해, Client는 Leaf와 Composite을 다르게 취급하고 있습니다. 하지만 Client에서 Leaf객체가 자식을 다루는 메소드를 호출할 수 없기 때문에, 타입에 대한 안정성을 얻게 됩니다.

먼저 예시로 들었던, Bag과 Item을 생각하면 됩니다.

일관성을 추구하는 방식

일관성을 추구하는 방식은 자식을 다루는 메소드들을 Composite가 아닌 Component에 정의하는 방식입니다. 그로 인해, Client는 Leaf와 Composite를 일관되게 취급할 수 있습니다. 하지만 Client는 Leaf 객체가 자식을 다루는 메소드를 호출할 수 있기 때문에, 타입의 안정성을 잃게 됩니다.

Swing라이브러리가 일관성을 추구하는 방식으로 되어있습니다.


Untitled
Untitled
composite-way

참고 :

코딩으로 학습하는 GoF의 디자인 패턴
컴포지트 패턴(Composite Pattern) :: 마이구미