빌더
복잡한 객체의 생성과정을 단계별로 제어하고 캡슐화하여 다양한 구성의 객체 생성,
많은 선택적 매개변수를 가진 객체 생성시 코드 가독성과 유지보수성 향상
interface Btn {
//버튼 필요요소
name: string; type: string; onClick: () => void;
}
interface Input{
//input 필요요소
name: string; type: string; onChange: () => void; value: string|number;
}
//확정된 필요요소가 아니라서 유연하게 만들고자 함
↓
class GrimpanMenuBtn{
private name: string;
private type: string;
private onClick?: () => void;
private onChange?: () => void;
}
constructor(name: string, type: string, onClick?: () => void, onChange?:() => void, active?: boolean,
this.name = name;
this.type = type;
this.onClick = onClick;
this.onChange = onChange;
}
setName(name: string){
this.name = name;
}
setType(type: string){
this.type = type;
}
setActive(active: boolean){
this.active = active;
}
setBalue(value: boolean){
this.value = value;
}
//backBtn이 완성된 버튼이라고 확신x
const backBtn = new GrimpanMenuBtn('뒤로', 'back', () => {
console.log('뒤로가기');
});
class GrimpanMenuBtn{
private name: string;
private type: string;
private onClick?: () => void;
private onChange?: () => void;
}
constructor(name: string, type: string, onClick?: () => void, onChange?:() => void, active?: boolean,
this.name = name;
this.type = type;
this.onClick = onClick;
this.onChange = onChange;
}
export class GrimpanMenuBtn{
//builer가 아니면 만들 수 없도록
private constructor(menu: GrimpanMenu, name: string, onClick?: () => void, active?: boolean) {
super(menu, name);
this.active = active;
this.onClick = onClick;
}
draw() {
const btn = document.createElement('button');
btn.textContent = this.name;
if (this.onClick) {
btn.addEventListener('click', this.onClick.bind(this));
}
this.menu.dom.append(btn);
}
//버튼만 전문적으로 만드는 Builder
//Builder를 여러개 선택해야할 때 외부에 두기도 함
static Builder = class GrimpanMenuBtnBuilder extends GrimpanMenuElementBuilder {
override btn: GrimpanMenuBtn; (= class GrimpanMenuBtn)
//필수값
constructor(menu: GrimpanMenu, name: string) {
super();
this.btn = new GrimpanMenuBtn(menu, name);
}
//optional
setOnClick(onClick: () => void) {
this.btn.onClick = onClick;
return this; //메서드 체이닝
}
setActive(active: boolean) {
this.btn.active = active;
return this;
}
build(){
return this.btn;
}
}
}
const backBtn = new GrimpanMenuBtn.Builder('뒤로', 'back'); //필수
.setOnClick(() => {}) //옵셔널
.setActive(false)
.build(); //완성된 버튼이므로 build를 함
const backBtnBuilder = new GrimpanMenuBtn.Builder('뒤로', 'back');
.setOnClick(() => {})
//오래걸리는 작업
backBuilder
.setValue(longValue)
.setActive(false)
.build()
class GrimpanMenuBtn{
name: string; //외부에서 사용하므로 private x, director만 참조
type: string;
onClick?: () => void;
onChange?: () => void;
}
constructor(name: string, type: string, onClick?: () => void, onChange?:() => void, active?: boolean,
this.name = name;
this.type = type;
this.onClick = onClick;
this.onChange = onChange;
}
//builder가 여러개 일때 외부에 둠
interface GrimpanMenuBuilder{
setOnClick(onClick: () => void): this;
setActive(active: boolean) : this;
build(): this;
}
//Chrome 그림판 메뉴 버튼
class ChromeGrimpanMenuBtnBuilder implements GrimpanMenuBtnBuilder{
btn: GrimpanMEnuBtn;
constructor(name: string, type: string){
this.btn = new GrimpanMEnuBtn(name, type);
}
setOnclick(onClick: () => void){
this.btn.onClick = onClick;
return this;
}
setActive(active: () => boolean){
this.btn.active = active;
return this;
}
}
//director를 통해서만 btn을 수정함
export class GrimpanMenuBtnDirector{
static createBackBtn(builder: GrimpanMenuBuilder){
const backBtnBuilder = builder
.setOnClick(() => {})
.setActivE(false);
return backBtnBuilder;
}
}
GrimpanMenuBtnDirector.createBackBtn(new ChromeGrimpanMenuBtnBuilder('뒤로', 'back'))
import {GrimpanMenu} from './GrimpanMenu.js';
export class GrimpanMenuBtn {
private menu: GrimpanMenu;
private name: string;
private onClick?: () => void;
private constructor(menu: GrimpanMenu, name: string, type: string, onClick?: () => void,
this.menu = menu;
this.name = name;
this.onClick = onClick;
}
draw(){
//단일책임원칙 위반
if(this.type === 'button'){
const btn = document.createElement('button');
btn.textContent = this.name;
if(this.onClick){
btn.addEventListener('click', this.onClick.bind(this)));
}
}else if(this.type === 'input'){
const btn = document.createElement('input');
btn.type = 'color';
if(this.onChange){
btn.addEventListener('change', this.onChange.bind(this));
}
this.menu.dom.append(btn);
}
}
}
import {GrimpanMenu} from './GrimpanMenu.js';
export abstract class GrimpanMenuElement {
private menu: GrimpanMenu;
private name: string;
private onClick?: () => void;
private constructor(menu: GrimpanMenu, name: string, type: string, onClick?: () => void,
this.menu = menu;
this.name = name;
this.onClick = onClick;
}
draw(){
const btn = document.createElement('input');
~~
}
//단일책임원칙 위반으로 분리
class GrimpanMenuBtn extends GrimpanMenuElement{
~~
}
class GrimpanMenuInput extends GrimpanMenuElement{
~~
}
//GrimpanMenuBtn.ts
import { GrimpanMenu } from "./GrimpanMenu.js";
class GrimpanMenuElementBuilder {
btn!: GrimpanMenuElement;
constructor() {}
build() {
return this.btn;
}
}
class GrimpanMenuElement {
protected menu: GrimpanMenu;
protected name: string;
protected constructor(menu: GrimpanMenu, name: string) {
this.menu = menu;
this.name = name;
}
abstract draw(): void;
}
export class GrimpanMenuInput extends GrimpanMenuElement {
onChange;
value;
constructor(menu:, name, onChange, value) {
super(menu, name);
this.onChange = onChange;
this.value = value;
}
draw() {
const btn = document.createElement('input');
btn.type = 'color';
btn.title = this.name;
if (this.onChange) {
btn.addEventListener('change', this.onChange.bind(this));
}
this.menu.dom.append(btn);
}
//공통 분리
static Builder = class GrimpanMenuInputBuilder extends GrimpanMenuElementBuilder {
override btn: GrimpanMenuInput;
constructor(menu: GrimpanMenu, name: string //필수요소) {
super();
this.btn = new GrimpanMenuInput(menu, name);
}
setOnChange(onChange: () => void) {
this.btn.onChange = onChange;
return this;
}
setValue(value: string | number) {
this.btn.value = value;
return this;
}
}
}
const backBtnBuilder = new GrimpanMenuBtn.Builder('뒤로', 'back');
.setOnClick(() => {})
//오래걸리는 작업
backBuilder
.setValue(longValue)
.setActive(false)
.build()
반응형
'JS > [inflearn] TS JS 디자인패턴' 카테고리의 다른 글
생성 패턴 (Creational Pattern) _ 프로토타입(Prototype) (0) | 2025.01.18 |
---|---|
생성 패턴 (Creational Pattern) _ 추상 팩토리(Abstract Factory) (0) | 2025.01.17 |
생성 패턴 (Creational Pattern) _ 팩토리 메서드(Factory Method) (0) | 2025.01.16 |
생성 패턴 (Creational Pattern) _ 싱글턴 (Singleton) (0) | 2025.01.16 |
SOLID 원칙 (0) | 2025.01.16 |