컴포지션 패턴은 상속 대신 여러 객체를 조합해 기능을 구성하는 방식이다.
"is-a(상속)" 관계보다는 "has-a" (구성) 관계를 통해 객체를 구성하자.
상속 기반 구조의 한계
class Bird {
void fly() {
System.out.println("I can fly");
}
}
class Penguin extends Bird {
// 펭귄은 날 수 없는데도 fly()를 상속 받음
}
- 문제: Penguin은 Bird를 상속받았지만, 실제로는 날 수 없는 동물입니다.
- 이처럼 상속은 계층 구조가 고정되어 있어 잘못된 기능을 포함시킬 가능성이 있습니다.
컴포지션으로 해결
interface FlyBehavior {
void fly();
}
class CanFly implements FlyBehavior {
public void fly() {
System.out.println("I can fly");
}
}
class CannotFly implements FlyBehavior {
public void fly() {
System.out.println("I can't fly");
}
}
class Bird {
public Bird(FlyBehavior flyBehavior) {
this.flyBehavior = flyBehavior;
}
private FlyBehavior flyBehavior;
public void performFly() {
flyBehavior.fly();
}
public void setFlyBehavior(FlyBehavior flyBehavior) {
this.flyBehavior = flyBehavior;
}
}
public class Main {
public static void main(String[] args) {
Bird eagle = new Bird(new CanFly());
Bird penguin = new Bird(new CannotFly());
eagle.performFly(); // I can fly
penguin.performFly(); // I can't fly
penguin.setFlyBehavior(new CanFly());
penguin.performFly(); // I can fly (이제 날 수 있음!)
}
}
Flutter 예제
CustomTextField
import 'package:flutter/material.dart';
class CustomTextField extends StatelessWidget {
const CustomTextField({
super.key,
required this.label,
required this.controller,
this.validator,
this.obscureText = false,
this.keyboardType = TextInputType.text,
});
final String label;
final TextEditingController controller;
final String? Function(String?)? validator;
final bool obscureText;
final TextInputType keyboardType;
@override
Widget build(BuildContext context) {
return TextFormField(
controller: controller,
obscureText: obscureText,
keyboardType: keyboardType,
validator: validator,
decoration: InputDecoration(
labelText: label,
border: const OutlineInputBorder(),
),
);
}
}
외부에서 UI 구성 요소를 주입 받아서 조합
장점
유지보수 용이성 | 공통 UI 요소를 한 곳에서 수정하면 전체에 반영되어 관리가 편리함 |
재사용성 | 다양한 화면에서 같은 UI 요소를 쉽게 재사용할 수 있어 코드 중복 감소 |
디자인 일관성 확보 | 디자인 변경 시 모든 화면에 동일하게 반영되므로 UI 일관성이 높아짐 |
확장성 | 필요에 따라 새로운 속성만 추가해 기능을 확장할 수 있음 |
가독성 향상 | 주요 로직과 UI 컴포넌트를 분리함으로써 코드가 더 명확해지고 구조화됨 |
단위 테스트 용이 | 각 구성 요소가 작게 쪼개져 있어 독립적으로 테스트하기 수월함 |
단점
오버 엔지니어링 가능성 | 간단한 화면에도 공통 컴포넌트를 만들면 오히려 복잡도 증가 |
상태 전달 어려움 | 여러 위젯으로 쪼개면 부모-자식 간 상태 공유/전달이 번거로울 수 있음 |
초기 러닝커브 증가 | 위젯이 지나치게 많아지면 프로젝트에 익숙하지 않은 개발자에게는 전체 흐름을 이해하기 어려울 수 있음 |
'Design Pattern' 카테고리의 다른 글
옵저버 패턴 (Observer Pattern) (1) | 2023.12.27 |
---|