팩토리패턴의 확장성 문제 #빌더패턴(Builder Pattern)

임혜정·2024년 9월 15일
0
post-thumbnail
post-custom-banner

이전 고민
팩토리패턴으로 만든 UI요소의 반복적인 인스턴스 생성으로 인한 메모리사용 측면에서의 비효율

를 딱히 해결하지 못한 채 너무 바빠서 그냥 진행하게 되었고
이에 더하여

속성이 많아질때의 불편한 문제가 생겼다.

기존 UIComponent

import UIKit

// MARK: - 전체 앱의 뷰 통일감을 위해 ui컴포넌트를 제작

protocol LabelFactory {
    func createLabel(with text: String, color: UIColor) -> UILabel
}

class BaseLabelFactory: LabelFactory {
    func createLabel(with text: String, color: UIColor) -> UILabel {
        let label = UILabel()
        label.textAlignment = .left
        label.text = text
        label.textColor = color
        return label
    }
}

class TitleLabelFactory: BaseLabelFactory {
    override func createLabel(with text: String, color: UIColor) -> UILabel {
        let label = super.createLabel(with: text, color: color)// 매개변수는 레이블 각각이 다 다를 부분
        label.font = UIFont.boldSystemFont(ofSize: 24) // 레이블의 성격따라 다를 부분
        return label
    }
}

class ContentLabelFactory: BaseLabelFactory {
    override func createLabel(with text: String, color: UIColor) -> UILabel {
        let label = super.createLabel(with: text, color: color)
        label.font = .systemFont(ofSize: 16)
        return label
    }
}

이렇게 정의해두는 것은 UI요소 통일성의 목적도 있기 때문에 팀원들에게
아예 이 셋 이외에 다른 레이블 속성을 가지지마시오라고 못박아도 된다.

그러나 시간이 흐르면서 다양한 페이지의 레이아웃이 생기고 텍스트 정렬만 center로 가져가야한다던지 , 텍스트 자리가 충분하지 않아서 폰트사이즈나 굵기만 약간 변형되야한다던지 하는 경우가 많이 생기게 되었다.

  1. 그럼 그 때마다 그에 맞는 클래스를 또 정의해야하나? 어떤 것들은 한두번 사용되고 말 수도 있는데..
  2. 아님 변형이 생길 수 있는 부분에 대해 다 매개변수를 받아서 모든 상황에 대응할 수 있게하나?
    그러나 그렇게되면 보일러 플레이트 코드 가 발생하고 중복되는 부분을 짧게 빨리빨리 생성해서 개발을 빨리 하자는 본래 의도가 퇴색된다.

*발생하는 보일러 플레이트 코드

class TestView: UIView {
    
    private let 특수레이블 = 유연한레이블().createLabel(with: "저는 조금 달라야해요", color: .black, textAlign: .center, font: .boldSystemFont(ofSize: 16))
    
    private let 보통레이블1 = 유연한레이블().createLabel(with: "특수한레이블", color: .black, textAlign: .left, font: .systemFont(ofSize: 16))
    private let 보통레이블2 = 유연한레이블().createLabel(with: "보통보통", color: .black, textAlign: .left, font: .systemFont(ofSize: 16))
    private let 보통레이블3 = 유연한레이블().createLabel(with: "그대로따르는레이블", color: .black, textAlign: .left, font: .systemFont(ofSize: 16))
    

특수 레이블 같은 경우가 생기므로서 정렬과 폰트속성을 받게해 대처할 수 있지만
나머지 보통레이블 같은 경우에는 쭉 일치될 부분인데 계속계속 필수적으로 작성해줘야됨


빌더패턴(Builder Pattern)


import UIKit

class LabelBuilder {
    private var text: String = ""
    private var color: UIColor = .black
    private var font: UIFont = .systemFont(ofSize: 16)
    private var align: NSTextAlignment = .left
    // .. 기타 등등 속성을 커스텀할 일이 생기는 부분에 대해서 정의한다
    
    func setText(_ text: String) -> LabelBuilder {
        self.text = text
        return self
    }
    
    func setColor(_ color: UIColor) -> LabelBuilder {
        self.color = color
        return self
    }
    
    func setFont(_ font: UIFont) -> LabelBuilder {
        self.font = font
        return self
    }
    
    func setAlign(_ align: NSTextAlignment) -> LabelBuilder {
        self.align = align
        return self
    }
    
    func build() -> UILabel {
        let label = UILabel()
        label.text = text
        label.textAlignment = align
        label.textColor = color
        label.font = font
        return label
    }
}

class LabelFactory { //전역에서 공통적으로 쓰일 속성을 정의
    static func titleLabel() -> LabelBuilder {
        return LabelBuilder()
            .setFont(.systemFont(ofSize: 24))
            .setColor(.black)
            .setAlign(.center)
    }
    
    static func contentLabel() -> LabelBuilder {
        return LabelBuilder()
            .setFont(.systemFont(ofSize: 16))
            .setColor(.darkGray)
    }
}

변형될 가능성이 있는 속성에 대해서 정의하고,
공통적으로 쓰일 라지타이틀 레이블, 본문 레이블을 정의해주되 특수한 경우가 생기면 변형이 가능하도록 만들 수 있다.

실제사용


import SnapKit
import UIKit

class MainView: UIView {
    private let titleLabel = LabelFactory.titleLabel()
        .setText("제목")
        .build()
    
    private let customTitleLabel = LabelFactory.titleLabel()
        .setText("조금특수한제목")
        .setAlign(.right)
        .build()
    .
    .
    .
    
 // 초기화 및 오토레이아웃설정
}

예를 들어 별도의 텍스트정렬을 원하지 않는다면 기본인 .center를 적용되게 하므로서 보일러 플레이트 코드 발생을 줄이고, 커스텀정렬이 필요하게 된다면 setAlign부분을 추가하여 커스텀할 수도 있게 하므로서 팀원들이 다양한 상황에 대처할 수 있는 방법도 제공한다.


UI요소에 싱글톤패턴을 사용하는 것

또한 이전부터 고민하던 LabelFactory클래스의 인스턴스가 매번 새로이 생성되서 메모리 성능 면에서 비효율적이지 않은가 고민하던 문제는 , 인스턴스를 한번만 생성하게하는 싱글톤패턴을 적용하여 테스트해본다

//
//  UIComponent.swift
//  BuilerPattern
//
//  Created by 임혜정 on 9/15/24.
//

import UIKit

class LabelBuilder {
    private var text: String = ""
    private var color: UIColor = .black
    private var font: UIFont = .systemFont(ofSize: 16)
    private var align: NSTextAlignment = .left
    // .. 기타 등등 속성을 커스텀할 일이 생기는 부분에 대해서 정의한다
    
    private var label: UILabel
    
    init() {
        self.label = UILabel()
    }
    
    func setText(_ text: String) -> LabelBuilder {
        self.text = text
        return self
    }
    
    func setColor(_ color: UIColor) -> LabelBuilder {
        self.color = color
        return self
    }
    
    func setFont(_ font: UIFont) -> LabelBuilder {
        self.font = font
        return self
    }
    
    func setAlign(_ align: NSTextAlignment) -> LabelBuilder {
        self.align = align
        return self
    }
    
    func build() -> UILabel {
        let label = UILabel()
        label.text = text
        label.textAlignment = align
        label.textColor = color
        label.font = font
        return label
    }
    func reset() {
        label = UILabel()
    }
}

class LabelFactory {
    static let shared = LabelFactory()
    
    private let builder: LabelBuilder
    
    private init() {
        self.builder = LabelBuilder()
    }
    
    func titleLabel() -> LabelBuilder {
        builder.reset()
        return LabelBuilder()
            .setFont(.systemFont(ofSize: 24))
            .setColor(.black)
            .setAlign(.center)
    }
    
    static func contentLabel() -> LabelBuilder {
        return LabelBuilder()
            .setFont(.systemFont(ofSize: 16))
            .setColor(.darkGray)
    }
}

테스트용 50개의 레이블생성

싱글톤패턴적용전

싱글톤패턴적용후

왜 늘어나는거지?

인스턴스가 매번 새로이 생성되서 메모리적으로 비효율적이어 보인다는 문제의 개선방안으로 싱글톤패턴 적용은 적절치 않다. 빼는걸로


남은 과제

빌더패턴으로 변경되야하는 이유에 대해 팀원들을 설득하고 동의를 받아야한다. ㅋㅋ

아직 구현해야할 기능이 너무나 많아서 바쁜 와중에 view코드까지 수정하라고 하면 팀원들이 귀찮아질 것이 뻔한데 어떻게 설득할 것인가?

profile
오늘 배운걸 까먹었을 미래의 나에게..⭐️
post-custom-banner

0개의 댓글