일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 31 |
- 멤버변수
- rxjava cold observable
- 안드로이드 라이선스
- 스택 자바 코드
- 안드로이드 레트로핏 crud
- android ar 개발
- rxjava disposable
- 서비스 쓰레드 차이
- 플러터 설치 2022
- 안드로이드 유닛 테스트 예시
- rxjava hot observable
- 안드로이드 레트로핏 사용법
- jvm 작동 원리
- ar vr 차이
- 스택 큐 차이
- 서비스 vs 쓰레드
- android retrofit login
- ANR이란
- 2022 플러터 안드로이드 스튜디오
- Rxjava Observable
- 2022 플러터 설치
- 안드로이드 유닛 테스트
- jvm이란
- 안드로이드 os 구조
- 자바 다형성
- 안드로이드 유닛테스트란
- 큐 자바 코드
- 클래스
- 객체
- 안드로이드 라이선스 종류
- Today
- Total
나만을 위한 블로그
컴포지트 패턴(Composite pattern)이란? 본문
composite의 사전적 정의는 아래와 같다.
합성의, 합성물, 종합적
그래서 이름만 놓고 보면 어떤 것들이 합쳐지는 디자인 패턴이라고 생각된다. 그러나 위키백과에선 사전적 정의와는 다르게 설명한다.
https://ko.wikipedia.org/wiki/%EC%BB%B4%ED%8F%AC%EC%A7%80%ED%8A%B8_%ED%8C%A8%ED%84%B4
컴포지트 패턴이란 객체들의 관계를 트리 구조로 구성해 부분-전체 계층을 표현하는 패턴으로 사용자가 단일 객체와 복합 객체 모두 동일하게 다루도록 한다
https://en.wikipedia.org/wiki/Composite_pattern
복합 패턴은 파티셔닝 디자인 패턴이다. 복합 패턴은 같은 유형의 객체의 단일 인스턴스와 동일한 방식으로 처리되는 객체 그룹을 설명한다. 합성의 목적은 객체를 트리 구조로 "구성"해서 부분-전체 계층 구조를 나타내는 것이다. 복합 패턴을 구현하면 클라이언트가 개별 객체와 구성을 균일하게 처리할 수 있다
정리하면 컴포지트 패턴은 객체 간 관계를 트리 구조로 구성해서 부분-전체 계층을 표현하는 패턴이라고 한다. 트리 구조는 뭐고 부분-전체 계층은 또 뭔가? 이 2가지를 알아야 컴포지트 패턴을 더 잘 알 수 있을 것 같다.
https://en.wikipedia.org/wiki/Tree_(data_structure)
트리는 연결된 노드 집합으로 표시되는 상위 노드가 있는 하위 트리와 루트값을 써서 계층적 트리 구조를 시뮬레이션하는 널리 사용되는 추상 데이터 유형이다. 트리 자료구조는 노드 모음으로 재귀적으로 정의할 수 있다. 여기서 각 노드는 값, 노드에 대한 참조 목록으로 구성된 자료구조다. 트리의 시작은 루트 노드고 참조 노드는 자식이다. 참조가 중복되지 않으며 루트를 가리키는 것도 없다. 또는 트리를 각 노드에 할당된 값을 사용해 순서가 지정된 트리로 전체(전역적으로)로 추상적으로 정의할 수 있다. 트리는 전체적으로 수학적으로 분석될 수 있지만 실제로 자료구조로 표시될 때 일반적으로 노드별로 개별적으로 표시되고 작업된다...(중략)
https://www.cs.cmu.edu/~clo/www/CMU/DataStructures/Lessons/lesson4_1.htm
...(중략) 연결 자료구조(링크드 리스트, 스택, 큐)의 개념을 노드 간에 다중 관계를 가질 수 있는 구조로 확장할 수 있다. 이런 구조를 트리라고 한다. 트리는 방향이 지정된(또는 지정되지 않은) 모서리(=선)로 연결된 노드 모음이다. 트리는 선형 자료구조인 배열, 링크드 리스트, 스택, 큐에 비해 비선형 자료구조다. 트리는 노드 없이 비어 있을 수 없고 루트라는 하나의 노드와 0개 또는 하나 이상의 하위 트리로 구성된 구조다. 트리는 다음과 같은 일반 속성이 있다
- 하나의 노드는 루트로 구별된다
- 루트를 제외한 모든 노드는 하나의 다른 노드로부터의 방향 예지로 연결된다. 방향은 부모 -> 자식이다
https://www.programiz.com/dsa/trees
배열, 링크드 리스트, 스택, 큐와 같은 다른 자료구조는 데이터를 순차적으로 저장하는 선형 자료구조다. 선형 자료구조에서 어떤 연산을 수행하려면 데이터 크기가 커질수록 시간 복잡도가 증가한다. 그러나 트리 자료구조는 비선형 자료구조라서 데이터에 더 빨리 쉽게 접근할 수 있다
트리라는 건 시작점인 하나의 원에서 여러 갈래 또는 한 갈래로 뻗쳐나가는 형태의 자료구조를 말하는 것 같다. 다음으로 부분-전체 계층은 뭘까?
이 용어는 작은 것을 기반으로 더 큰 물체를 구성하는 걸 나타낸다. 컴포지트 패턴의 맥락에서 크고 작은 객체는 모두 같은 클래스의 인스턴스다.
https://faq-qa.com/en/Q%26A/page=f16a99e42bcc3bd6ca5dbbec51c22b36
시스템은 하위 시스템 또는 구성요소로 구성된다. 구성요소는 더 작은 구성요소로 더 나눌 수 있다. 더 작은 구성요소는 더 작게 나눌 수 있다. 이것이 부분-전체 계층이다. 합성(composite)의 목적은 객체를 트리 구조로 구성해서 부분-전체 계층 구조를 나타내는 것이다. 컴포지트 패턴을 구현하면 클라이언트가 개별 객체와 구성을 균일하게 처리할 수 있다
작은 것들을 모아서 큰 것을 구성하는 경우 부분-전체 계층이라고 하는 듯하다. 이걸 토대로 생각해보면 컴포지트 패턴은 다르게 말해서 작은 객체들을 트리 구조 형태로 엮어서 큰 프로그램을 만들 때 사용할 수 있는 패턴이라고 짐작된다.
컴포지트 패턴은 디자인 패턴 중 구조 패턴에 속하는데 구조 패턴의 정의는 아래와 같다.
https://readystory.tistory.com/131
구조 패턴은 작은 클래스들을 상속, 합성을 이용해 더 큰 클래스를 생성하는 방법을 제공하는 패턴이다. 이 패턴을 사용하면 독립적으로 개발한 클래스 라이브러리를 하나처럼 사용할 수 있다. 또 여러 인터페이스를 합성해 서로 다른 인터페이스들의 동일한 추상을 제공한다...(중략)...런타임 단계에서 복합 방법이나 대상을 변경할 수 있다는 점에서 유연성을 갖는다
https://www.baeldung.com/java-core-structural-patterns
구조적 디자인 패턴은 큰 객체 구조 사이의 관계를 식별해서 디자인을 단순화하는 패턴이다. 솔루션으로 반복할 수 있게 클래스, 객체를 구성하는 일반적인 방법을 설명한다.
정리하면 구조 패턴은 객체 간의 관계(책임)을 파악하고 단순한 디자인을 도출해내는 패턴이라고 생각된다.
이제 컴포지트 패턴이 어떤 건지 코드로 확인해보자. 아래 예제 코드는 밑의 사이트에서 가져왔다.
https://www.javatpoint.com/composite-pattern
코드를 보기 전에 먼저 하나의 UML부터 보고 넘어간다.
이 클래스들의 역할은 아래와 같다.
Component
- 컴포지션의 객체에 대한 인터페이스 선언
- 모든 클래스에 공통적인 인터페이스의 기본 동작 구현
- 자식 컴포넌트에 접근, 관리하기 위한 인터페이스 선언
Leaf
- 컴포지션의 Leaf 객체를 나타냄
- Leaf에는 자식이 없고 컴포지션의 객체 동작을 정의한다
Composite
- 자식이 있는 컴포넌트의 동작 정의
- 자식 컴포넌트 저장
- 컴포넌트 인터페이스에서 자식 관련 작업 구현
Client
- 컴포넌트 인터페이스를 통해 컴포지션의 객체를 조작
아래는 위의 설명을 바탕으로 만들 예제 코드의 UML이다.
먼저 클래스들이 수행할 기본 동작을 정의한 인터페이스를 만든다.
public interface Employee {
int getId();
String getName();
double getSalary();
void print();
void add(Employee employee);
void remove(Employee employee);
Employee getChild(int i);
}
그리고 이 인터페이스를 구현하는 Accoutant, BankManager, Cashier 클래스를 만들고 인터페이스의 기능들을 각각 구현한다.
public class Accountant implements Employee {
private int id;
private String name;
private double salary;
public Accountant(int id, String name, double salary) {
this.id = id;
this.name = name;
this.salary = salary;
}
@Override
public int getId() {
return id;
}
@Override
public String getName() {
return name;
}
@Override
public double getSalary() {
return salary;
}
@Override
public void print() {
System.out.println("=========================");
System.out.println("Id = " + getId());
System.out.println("Name = " + getName());
System.out.println("Salary = " + getSalary());
System.out.println("=========================");
}
@Override
public void add(Employee employee) {
// leaf node기 때문에 이 메서드는 구현하지 않음
}
@Override
public void remove(Employee employee) {
// leaf node기 때문에 이 메서드는 구현하지 않음
}
@Override
public Employee getChild(int i) {
// leaf node기 때문에 이 메서드는 구현하지 않음
return null;
}
}
import java.util.ArrayList;
import java.util.List;
// 컴포지트로 취급될 클래스
public class BankManager implements Employee {
private int id;
private String name;
private double salary;
List<Employee> employees = new ArrayList<>();
public BankManager(int id, String name, double salary) {
this.id = id;
this.name = name;
this.salary = salary;
}
@Override
public int getId() {
return id;
}
@Override
public String getName() {
return name;
}
@Override
public double getSalary() {
return salary;
}
@Override
public void print() {
System.out.println("==========================");
System.out.println("Id = " + getId());
System.out.println("Name = " + getName());
System.out.println("Salary = " + getSalary());
System.out.println("==========================");
for (Employee employee : employees) {
employee.print();
}
}
@Override
public void add(Employee employee) {
employees.add(employee);
}
@Override
public void remove(Employee employee) {
employees.remove(employee);
}
@Override
public Employee getChild(int i) {
return employees.get(i);
}
}
// leaf로 처리되는 클래스
public class Cashier implements Employee {
private int id;
private String name;
private double salary;
public Cashier(int id, String name, double salary) {
this.id = id;
this.name = name;
this.salary = salary;
}
@Override
public int getId() {
return id;
}
@Override
public String getName() {
return name;
}
@Override
public double getSalary() {
return salary;
}
@Override
public void print() {
System.out.println("==========================");
System.out.println("Id = " + getId());
System.out.println("Name = " + getName());
System.out.println("Salary = " + getSalary());
System.out.println("==========================");
}
@Override
public void add(Employee employee) {
// leaf node기 때문에 이 메서드는 구현하지 않음
}
@Override
public void remove(Employee employee) {
// leaf node기 때문에 이 메서드는 구현하지 않음
}
@Override
public Employee getChild(int i) {
// leaf node기 때문에 이 메서드는 구현하지 않음
return null;
}
}
이제 메인에서 각 클래스의 생성자를 호출해 원하는 값을 넣어 인터페이스를 통해 객체를 조작한다.
public class Main {
public static void main(String[] args) {
Employee emp1 = new Cashier(101, "Sohan Kumar", 20000.0);
Employee emp2 = new Cashier(102, "Mohan Kumar", 25000.0);
Employee emp3 = new Accountant(103, "Seema Mahiwal", 30000.0);
Employee manager1 = new BankManager(100, "Ashwani Rajput", 100000.0);
manager1.add(emp1);
manager1.add(emp2);
manager1.add(emp3);
manager1.print();
}
}
이 코드를 실행하면 아래와 같은 화면이 나온다.
==========================
Id = 100
Name = Ashwani Rajput
Salary = 100000.0
==========================
==========================
Id = 101
Name = Sohan Kumar
Salary = 20000.0
==========================
==========================
Id = 102
Name = Mohan Kumar
Salary = 25000.0
==========================
=========================
Id = 103
Name = Seema Mahiwal
Salary = 30000.0
=========================
이런 컴포지트 패턴은 언제 사용하면 좋고 언제 사용하지 말아야 할까?
https://www.geeksforgeeks.org/composite-design-pattern/
컴포지트 패턴은 클라이언트가 객체의 구성, 개별 객체 간의 차이를 무시해야 할 때 써야 한다. 개발자가 같은 방식으로 여러 객체를 쓰고 있으며 각각을 처리하기 위해 거의 동일한 코드를 쓰는 경우가 많다면 컴포지트 패턴이 좋은 선택이며 이 상황에선 기본(primitives) 및 복합을 같은 종류로 취급하는 게 덜 복잡하다
1. 객체 수가 적으면 메모리 사용량이 줄고 OutOfMemoryError 같은 메모리 관련 오류가 발생하지 않게 관리한다
2. 자바에서 객체를 생성하는 것은 빠르지만 객체를 공유함으로써 프로그램 실행 시간을 줄일 수 있다
- 언제 컴포지트 패턴을 쓰지 말아야 하는가?
1. 컴포지트 패턴을 쓰면 복합 컴포넌트의 유형을 제한하기가 어려워진다. 따라서 객체의 전제 또는 부분 계층을 나타내지 않으려는 경우엔 쓰지 말아야 한다
2. 컴포지트 패턴은 디자인을 지나치게 일반적으로 만들 수 있다. 컴포지트의 컴포넌트를 제한하기가 더 어렵다. 때로는 함성물에 특정 컴포넌트만 포함되기를 원할 수 있다. 컴포지트 패턴을 쓰면 이런 제약 조건을 적용하기 위해 타입 시스템에 의존할 수 없다. 대신 런타임 검사를 써야 한다
'개인 공부 > 디자인 패턴' 카테고리의 다른 글
Repository 패턴이란? (0) | 2023.07.17 |
---|---|
프록시 패턴(Proxy pattern)이란? (0) | 2023.03.05 |
데코레이터(Decorator) 패턴이란? (0) | 2022.01.16 |
빌더(Builder) 패턴이란? (0) | 2022.01.02 |
팩토리 패턴이란? (0) | 2021.10.05 |