안녕하세요 린생입니다.

오늘은 SingleTon Pattern의 대하여 알아보겠습니다.

싱글톤이라 쉽게 생각해서 어플리케이션이 실행을 하여 단 한번만 메모리에 할당 된 후 

그 메모리의 인스턴스를 할당 받아 사용하는 디자인 패턴입니다.

우리가 싱글톤을 사용하는 이유는 여러가지 있지만 대표적인 이유를 적어보겠습니다.

1. 메모리 낭비를 방지 할 수 있음
2. 데이터를 공유 할 수 있다.

즉 인스턴스가 절대적으로 한개만 존재하는 것을 보증하고 싶을 경우 사용을 합니다.

우리가 프로그래밍을 하면서 대표적으로 하나의 인스턴스가 존재해야 하는 경우는 무엇이 있을까요?

바로 network 객체 입니다.

네트워크는 하나의 세션으로 유지하다 연결이 이상시 알려주어야 합니다.

그러면 코드를 작성해 보겠습니다. 코드 작성 법은 아주 쉽습니다.

initialization을 private로 설정 해주면 됩니다.

class SingleTon {
    private static let sharedInstance = SingleTon()
   
    private init() { }
}

객체를 만들고 Instance는 static 메모리에 할당을 하고 initialzation을 private 설정 합니다.

그러면 정말로 싱클톤인지 확인을 해볼까요??

싱글톤 객체의 프로퍼티를 추가 하겠습니다.

class SingleTon {
    static let sharedInstance = SingleTon()
   
    private lazy var name: String = ""
    private lazy var age: Int = 0
   
    private init() { }
   
    func setProperty() {
        self.name = "린생"
        self.age = 28
    }
   
    func getProperty() -> (String, Int){
        return (self.name, self.age)
    }
}

이렇게 만들고 main에서 실행 하겠습니다.

func main() {
    let firstInstance = SingleTon.sharedInstance
    let secondInstance = SingleTon.sharedInstance
   
    firstInstance.setProperty()
   
    print(secondInstance.getProperty())
   
}

이렇게 실행 하면 결과 값은 어떻게 나올까요??

결과

("린생", 28)

이렇게 나오는 걸 알수 있습니다. ㅎㅎ

'Swift > 디자인패턴' 카테고리의 다른 글

Delegate Pattern (Swift)  (402) 2019.06.05
Builder pattern(swift)  (2) 2018.07.03
Observer Pattern(swift)  (6) 2018.06.08
Factory Pattern(swift)  (3) 2018.05.30
빌더 패턴는 복잡한 객체의 생성을 그 객체의 표현과 분리하여, 생성 절차는 항상 동일하되 결과는 다르게 만드는 패턴입니다.

우리가 객체를 만들다보면 생성 당시의 많은 요소를 한번에 넣어주는 요소 하나를 일력을 받아서 생성을 하는 방식 입니다.

Swift에서는 Builder pattern을 잘 사용하지는 않지만 우리는 코드 베이스를 View 객체를 만들다 보면 속성 값을 지정 해 주어야 합니다.

그 속성을 넣어주는 방식은 주로 다음 과 같을 거 같습니다.

let labelView: UILabel = {
   let label = UILabel()
   label.text = "린생"
   label.textColor = .black
   label.font = .systemFont(sizeOf: 20)
   return label    
}()

이런게 생성된 labelView는 생성 당시 많은 값을 속성 값을 주어야 됩니다. 
빌더 패턴을 사용하면 이러한 속성을 체이닝 형식으로 만들어 줄 수 있습니다.

Builder Pattern은 우선 역할을 나누는 것이 중요합니다.

역할은 크게 3가지로 나눌수 있습니다. 

Director : 객체 생성 방식에 대한 책임
Builder  : 객체를 생성하는 추상 인터페이스
ConCreateBuilder : Builder의 구현 클래스, 객체 생성 결과에 대한 구체적인 구현 책임
Product : Builder를 이용해서 Director가 만들어 낸 최종 객체

Builder는 추상화 하는 작업이 필요합니다. 즉 Protocol을 통해 객체가 무엇을 할 수 있는지 나타내 주는 역할을 합니다.

ConCreateBuilder는 Builder를 채택하여 구현하는 클래스로 객체 생성 결과에 대한 구체적인 구현 책임을 나타내게 됩니다.
위의 let labelView를 보면 text를 저장하고 textColor를 저장하고 font를 저장합니다. 그런 속성을 구체적으로 저장하는 역할 입니다.

Director는 객체를 생성 하는 책임을 가지게 됩니다. 즉 객체가 생성을 할려면 Director를 통해서 생성을 이루어져야 하는 것입니다.

Product는 말 그대로 제품 == 객체를 말합니다. 

그럼 구현을 해보겠습니다.

먼저 Builder를 만들어줍니다.

protocol Builder {
    var label: UILabel { get }
    func setText(with text: String) -> Builder
    func setTextColor(with textColor: UIColor) -> Builder
    func setFontSize(with textFontSize: CGFloat) -> Builder
}

Builder는 protocol로 구현을 하게 됩니다. 중요한 점은 return type을 Builder로 해주는 것입니다.

다음으로 ConCreateBuilder를 만들어 줍니다.

class ConCreateBuilder: Builder {
    var label: UILabel = UILabel()
   
    func setText(with text: String) -> Builder {
        label.text = text
        return self
    }
   
    func setTextColor(with textColor: UIColor) -> Builder {
        label.textColor = textColor
        return self
    }
   
    func setFontSize(with textFontSize: CGFloat) -> Builder {
        label.font = .systemFont(ofSize: textFontSize)
        return self
    }
}

ConCreateBuilder는 Builder를 채택 후 상세한 구현 내용을 구현해주어야합니다.
즉, text를 받아서 text속성을 넣어 주는 형태를 만들어주는것이 중요합니다. 

다음으로 Director를 만들어줍니다.

class Director {
    func makeLabel(builder: Builder) -> UILabel {
        let build = builder
        build.setText(with: "린생")
        build.setTextColor(with: .black)
        build.setFontSize(with: 40)
        return build.label
    }
}

Director는 값을 Builder를 파라미터로 받아서 기본 세팅을 하여 넘겨주는 형태 입니다. 마지막에 build.label을 해주어 모든 세팅을 끝나 후 UILabel 타입을 return해주면 됩니다.

즉 객체를 생성하여 return을 해주는 책임을 가지게 됩니다.

위의 3개의 Builder, ConCreateBuilder, Director를 이제 main에서 어떻게 쓰는 보여 드리겠습니다!! ㅎㅎ

class ViewController: UIViewController {
    private let director: Director = Director()

    override func viewDidLoad() {
        super.viewDidLoad()
        let label = director.makeLabel(builder: ConCreateBuilder())
        self.view.addSubview(label)
        label.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
       
    }
}

director를 통해서 객체를 생성하고 주요 속성은 ConCreateBuilder 통해 지정을 해주고 Builder에서 그 추상화 작업을 통해 속성을 만들어 주면 됩니다!ㅎㅎ

결과 값



또한 Builder pattern의 장점 중 속성을 체인 형태로 지정을 할 수 있습니다.

class ViewController: UIViewController {
   
    private let director: Director = Director()
   
    // 체인형태의 속성 지정
    private let tmp_Label: UILabel = ConCreateBuilder()
        .setText(with: "린생 2")
        .setTextColor(with: .red)
        .setFontSize(with: 30)
        .label
   
    override func viewDidLoad() {
        super.viewDidLoad()
       
        // 1
        let label = director.makeLabel(builder: ConCreateBuilder())
        self.view.addSubview(label)
        label.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
       
        // 2
        self.view.addSubview(tmp_Label)
        tmp_Label.frame = CGRect(x: 0, y: 200, width: 100, height: 100)
    }
}



위의 체인 형태로 속성을 주는 형태로도 만들수 있다는건 좋은 거 같습니다. ㅎㅎ


'Swift > 디자인패턴' 카테고리의 다른 글

Delegate Pattern (Swift)  (402) 2019.06.05
SingleTon Pattern(swift)  (0) 2018.07.06
Observer Pattern(swift)  (6) 2018.06.08
Factory Pattern(swift)  (3) 2018.05.30
안녕하세요. 린생입니다.

오늘은 Swift Design Pattern 중 Observer Pattern을 소개하겠습니다.

번역을 하자면 Observer -> 관찰자라고 번역 할 수 있습니다.

왜 관찰자 일까요??

Observer Pattern은 해당 프로퍼티가 변경 되는걸 관찰하고 있다가 변경 되는 시점에서 update가 수행 되게 되는 형태를 말합니다.

쉽게 설명을 하자면 제가 지금 한 대상을 보고 있다가 그 대상이 어떻게 하는지를 보고 있다가 그 대상이 변화가 일어나면 제가 행동을 하는 것입니다.

우리가 코드를 만들다 보면 특정 값이 변경이 발생 할 때 다른 연쇄적으로 그 값을 참조 하고 하고 있는 값들이 자동 적으로 변경이 이루어 져야 될 때 사용 하면 유용한 패턴입니다.

그러면 코드로 생성 해 볼까요?

먼저 프로토콜을 생성 해줍니다.

protocol Observer {
    func update(_ notifyValue: Int) 
}

그리고 대상을 객체를 만들어줍니다.

class Subject {
    private var observers: [Observer] = [Observer]()
    private var value: Int = Int()

    // 변경을 되면 notify()를 호출
    var number: Int {
        set {
            value = number
            notify()
        }
        get {
            return value 
        }
    }

    // 옵저버 등록
    func attachObserver(_ observer: Observer) {
        observers.append(observer)
    }
    
    // 옵저버 알림
    func notify() {
        for observer in observers {
            observer.update(number)
        }
    }
}

처음 옵저버 패턴을 공부하시는 분들께서는 이 부분이 잘 이해가 되지 않을 거같습니다.

일단 이 Subject class를 설명 하자면 이 클래스는 관찰자들이 보고 있는 대상입니다.
즉 이 값의 value가 변경이 이루어 지면 observer들은 모두 그것을 인지할 수 있는 것입니다.

그래서 var number 에서 set 에 notify() 함수를 호출하게 됩니다.

이제 그러면 옵저버들을 생성 하겠습니다.

class Linsaeng: Observer {
    private var subject: Subject = Subject()
    
    init(_ subject: Subject) {
        self.subject = subject
        self.subject.attachObserver(self)
    }

    func update(_ notifyValue: Int) {
        print(“린생이 \(notifyValue)” 값으로 변경 된 것을 인식 하였습니다.)
    }
}

classclass Harry: Observer {
    private var subject: Subject = Subject()
    
    init(_ subject: Subject) {
        self.subject = subject
        self.subject.attachObserver(self)
    }

    func update(_ notifyValue: Int) {
        print(“해리가 \(notifyValue)” 값으로 변경 된 것을 인식 하였습니다.)
    }
}

옵저버들에게는 생성 될 때 관찰 대상을 넣어주고 초기화를 해주고 update 함수에서 필요한 작업을 하면 됩니다.

이제 main 함수에서 실행을 해볼까요?

func main() {
    let subject = Subject()
    let _ = Linsaeng(subject)
    let _ = Harry(subject)

    subject.number = 2    // 린생이 2 값으로 변경 된 것을 인식 하였습니다.
                          // 해리가 2 값으로 변경 된 것을 인식 하였습니다.

    subject.number = 4    // 린생이 4 값으로 변경 된 것을 인식 하였습니다.
                          // 해리가 4 값으로 변경 된 것을 인식 하였습니다.
}

이렇게 값들이 자기 저장되거나 변경 되는 순간에 Linsaeng, Harry 클래스에서 update 함수가 호출 되는 것을 확인 할 수 있습니다. ㅎㅎ


'Swift > 디자인패턴' 카테고리의 다른 글

Delegate Pattern (Swift)  (402) 2019.06.05
SingleTon Pattern(swift)  (0) 2018.07.06
Builder pattern(swift)  (2) 2018.07.03
Factory Pattern(swift)  (3) 2018.05.30
안녕하세요. 린생입니다. 
오늘은 디자인 패턴 중에 팩토리 패턴을 소개를 할까 합니다.
영어로는 factory patten이라고 하는데 
왜 factory이라고 이름을 지었을까요?
우리가 어떤 제품을 공장에 요청 할 때 공장에서는 어떤 요구가 들어올지 알 수 가 없습니다.
즉 공장에서는 제품을 만들지만 요청에 따라서 A 제품이 나올수도 있고 B 제품이 나올수가 있습니다.
하지만 공장의 생산 라인이 달라지는 것은 아닙니다. 제품이 달라지는 거죠 
즉 위의 설명중
제품 : 객체 
공장 : protocol
생산라인 : protocol을 준수하는 메소드
이렇게 정의 할 수 있습니다.
protocol을 먼저 선언해 줍니다.
protocol Factoryable {
    func makeProduct() -> String
}

그리고 Factoryable을 채택하는 객체인 가방, 펜을 만들어 줍니다.

struct Pen: Factoryable {
    func makeProduct() -> String {
        return "펜"
    }
}

struct Bag: Factoryable {
    func makeProduct() -> String {
        return "가방"
    }
}

그리고 제품을 실행 합니다.

func outputView(_ product: Factoryable) {
    print(“공장에서 \(product.makeProduct())을 만들었습니다.")

func main() {
    let pen = Pen()
    let bag = Bag()

    outputView(pen)    // 공장에서 펜을 만들었습니다.
    outputView(bag)    // 공장에서 가방을 만들었습니다.
}

중요한 점은 outputView에서 파라미터로 타입으로 프로토콜인 Fatoryable을 받아서 실행 한다는 점입니다.

여기서 Contoller는 main() 
view는 OutputView(_ product: Factoryable) 입니다.

즉 view에서는 이 대상이 무엇인지는 알 수가 없습니다. 
그냥 protocol을 받아서 실행을 하는 것뿐입니다.

즉 실행을 될 때까지 view에서 펜인지 가방인지 알 수 가 없습니다. 

이렇게 Factory패턴은 View가 해당 제품을 모르고 생상될 때 알 수 없게 하는 것이 중요합니다.




'Swift > 디자인패턴' 카테고리의 다른 글

Delegate Pattern (Swift)  (402) 2019.06.05
SingleTon Pattern(swift)  (0) 2018.07.06
Builder pattern(swift)  (2) 2018.07.03
Observer Pattern(swift)  (6) 2018.06.08

+ Recent posts