수색…


소개

디자인 패턴은 소프트웨어 개발에서 자주 발생하는 문제에 대한 일반적인 솔루션입니다. 다음은 코드 설계 및 설계의 표준화 된 모범 사례 템플릿뿐 아니라 이러한 디자인 패턴이 적절한 공통적 인 상황의 예입니다.

창조적 인 디자인 패턴 은 객체의 생성을 추상화하여 시스템을 생성, 구성 및 표현의 프로세스에보다 독립적으로 만듭니다.

하나씩 일어나는 것

싱글 톤은 프로그램 전체에서 공유되는 클래스의 단일 인스턴스로 구성되는 자주 사용되는 디자인 패턴입니다.

다음 예제에서는 Foo 클래스의 인스턴스를 보유하는 static 속성을 만듭니다. static 속성은 클래스의 모든 객체간에 공유되며 하위 클래스로 덮어 쓸 수 없습니다.

public class Foo
{
    static let shared = Foo()
    
    // Used for preventing the class from being instantiated directly
    private init() {}
    
    func doSomething()
    {
        print("Do something")
    }
}

용법:

Foo.shared.doSomething()

private 이니셜 라이저를 기억하십시오.

이렇게하면 싱글 톤이 정말 독특하고 액세스 제어를 통해 외부 객체가 자신의 클래스 인스턴스를 만드는 것을 방지 할 수 있습니다. 모든 객체는 Swift에서 기본 공용 이니셜 라이저와 함께 제공되므로 init을 무시하고 비공개로 만들어야합니다. 크라켄 데브

공장 방법

클래스 기반 프로그래밍에서 팩토리 메서드 패턴은 팩터 리 메서드를 사용하여 생성 될 개체의 정확한 클래스를 지정하지 않고 개체를 만드는 문제를 처리하는 생성 패턴입니다. 위키 백과 참조

protocol SenderProtocol
{
    func send(package: AnyObject)
}

class Fedex: SenderProtocol
{
    func send(package: AnyObject)
    {
        print("Fedex deliver")
    }
}

class RegularPriorityMail: SenderProtocol
{
    func send(package: AnyObject)
    {
        print("Regular Priority Mail deliver")
    }
}

// This is our Factory
class DeliverFactory
{
    // It will be responsable for returning the proper instance that will handle the task
    static func makeSender(isLate isLate: Bool) -> SenderProtocol
    {
        return isLate ? Fedex() : RegularPriorityMail()
    }
}

// Usage:
let package = ["Item 1", "Item 2"]

// Fedex class will handle the delivery
DeliverFactory.makeSender(isLate:true).send(package)

// Regular Priority Mail class will handle the delivery
DeliverFactory.makeSender(isLate:false).send(package)

그렇게함으로써 우리는 클래스의 실제 구현에 의존하지 않고, 그것을 sender() 에게 sender() 완전히 투명하게 만듭니다.

이 경우 우리가 알아야 할 것은 송신자가 전달을 처리하고 send() 라는 메소드를 노출한다는 것입니다. 몇 가지 다른 이점이 있습니다. 클래스 연결을 줄이고, 테스트하기 쉽고, 누가 사용하고 있는지 변경하지 않고도 새 동작을 더 쉽게 추가 할 수 있습니다.

객체 지향 설계에서 인터페이스는 추상화 레이어를 제공하여 코드의 개념적 설명을 용이하게하고 종속성을 방지하는 장벽을 만듭니다. 위키 백과 참조

관찰자

관찰자 패턴은 주체라고하는 개체가 관찰자라는 종속 항목 목록을 유지하고 대개 상태 변경을 자동으로 알립니다. 일반적으로 해당 방법 중 하나를 호출하여 패턴을 변경합니다. 주로 분산 이벤트 처리 시스템을 구현하는 데 사용됩니다. Observer 패턴은 친숙한 MVC (Model-View-Controller) 아키텍처 패턴의 핵심 부분이기도합니다. 위키 백과 참조

기본적으로 관측자 패턴은 관측자에게 특정 동작이나 상태 변경을 알릴 수있는 객체가있을 때 사용됩니다.

먼저 알림 센터에 대한 전역 참조 (클래스 외부)를 만듭니다.

let notifCentre = NotificationCenter.default

이제 우리는 어디서나 이것을 부를 수 있습니다. 그러면 우리는 관찰자로서 수업을 등록하려고합니다 ...

notifCentre.addObserver(self, selector: #selector(self.myFunc), name: "myNotification", object: nil)

그러면 클래스가 "readForMyFunc"의 옵저버로 추가됩니다. 또한 알림을 받으면 함수 myFunc를 호출해야 함을 나타냅니다. 이 함수는 같은 클래스에 작성해야합니다.

func myFunc(){
    print("The notification has been received")
}

이 패턴의 장점 중 하나는 많은 클래스를 관찰자로 추가하여 하나의 알림 후에 많은 작업을 수행 할 수 있다는 것입니다.

다음과 같이 코드의 거의 모든 위치에서 알림을 간단하게 보내거나 게시 할 수 있습니다.

notifCentre.post(name: "myNotification", object: nil)

알림과 함께 정보를 사전으로 전달할 수도 있습니다

let myInfo = "pass this on"
notifCentre.post(name: "myNotification", object: ["moreInfo":myInfo])

그러나 함수에 알림을 추가해야합니다.

func myFunc(_ notification: Notification){
    let userInfo = (notification as NSNotification).userInfo as! [String: AnyObject]
    let passedInfo = userInfo["moreInfo"]
    print("The notification \(moreInfo) has been received")
    //prints - The notification pass this on has been received
}

책임의 사슬

객체 지향 설계에서, 책임 체인 패턴은 command 객체의 소스와 일련의 processing 객체로 구성된 설계 패턴입니다. 각 processing 객체에는 processing 할 수있는 명령 객체의 유형을 정의하는 논리가 포함됩니다. 나머지는 체인의 다음 processing 객체로 전달됩니다. 이 체인의 끝에 새로운 processing 객체를 추가하는 메커니즘도 있습니다. 위키피디아

책임의 사슬을 구성하는 클래스 설정.

먼저 모든 processing 객체에 대한 인터페이스를 만듭니다.

protocol PurchasePower {  
var allowable : Float { get }
  var role : String { get }
  var successor : PurchasePower? { get set }
}

extension PurchasePower {
  func process(request : PurchaseRequest){
    if request.amount < self.allowable {
      print(self.role + " will approve $ \(request.amount) for \(request.purpose)")
    } else if successor != nil {
      successor?.process(request: request)
    }
  }
}

그런 다음 command 객체를 만듭니다.

struct PurchaseRequest {
  var amount : Float
  var purpose : String
}

마지막으로, 책임의 사슬을 구성하는 객체를 생성합니다.

class ManagerPower : PurchasePower {
  var allowable: Float = 20
  var role : String = "Manager"
  var successor: PurchasePower?
}

class DirectorPower : PurchasePower {
  var allowable: Float = 100
  var role = "Director"
  var successor: PurchasePower?
}

class PresidentPower : PurchasePower {
  var allowable: Float = 5000
  var role = "President"
  var successor: PurchasePower?
}

함께 시작하고 연결하기 :

let manager = ManagerPower()
let director = DirectorPower()
let president = PresidentPower()

manager.successor = director
director.successor = president

여기에 객체를 연결하는 메커니즘은 속성 액세스입니다.

실행 요청 만들기 :

manager.process(request: PurchaseRequest(amount: 2, purpose: "buying a pen"))  // Manager will approve $ 2.0 for buying a pen
manager.process(request: PurchaseRequest(amount: 90, purpose: "buying a printer")) // Director will approve $ 90.0 for buying a printer

manager.process(request: PurchaseRequest(amount: 2000, purpose: "invest in stock")) // President will approve $ 2000.0 for invest in stock

반복자

컴퓨터 프로그래밍에서 반복자는 프로그래머가 컨테이너, 특히 목록을 탐색 할 수있게 해주는 객체입니다. 위키피디아

struct Turtle {
  let name: String
}

struct Turtles {
  let turtles: [Turtle]
}

struct TurtlesIterator: IteratorProtocol {
  private var current = 0
  private let turtles: [Turtle]

  init(turtles: [Turtle]) {
    self.turtles = turtles
  }

  mutating func next() -> Turtle? {
    defer { current += 1 }
    return turtles.count > current ? turtles[current] : nil
  }
}

extension Turtles: Sequence {
  func makeIterator() -> TurtlesIterator {
    return TurtlesIterator(turtles: turtles)
  }
}

그리고 사용 예제는

let ninjaTurtles = Turtles(turtles: [Turtle(name: "Leo"),
                                     Turtle(name: "Mickey"),
                                     Turtle(name: "Raph"),
                                     Turtle(name: "Doney")])
print("Splinter and")
for turtle in ninjaTurtles {
  print("The great: \(turtle)")
}

작성자 패턴

빌더 패턴은 오브젝트 작성 소프트웨어 설계 패턴 입니다. 추상 팩토리 패턴과 다형성을 가능하게하려는 팩토리 메소드 패턴과는 달리, 빌더 패턴의 의도는 텔레 스코핑 생성자 안티 패턴에 대한 솔루션을 찾는 것입니다. 텔레 스코핑 생성자 방지 패턴은 객체 생성자 매개 변수 조합의 증가가 생성자의 지수 목록으로 연결될 때 발생합니다. 수많은 생성자를 사용하는 대신 빌더 패턴은 단계별로 각 초기화 매개 변수를 수신 한 다음 작성된 결과 객체를 한 번에 리턴하는 다른 객체 인 빌더를 사용합니다.

- 위키 백과

빌더 패턴의 주요 목표는 객체 작성을위한 기본 구성을 설정하는 것입니다. 그것은 객체가 구축 될 것 인간에 중개자이고 다른 모든 객체는 그것을 구축하는 것과 관련됩니다.

예:

더 명확하게하기 위해 Car Builder 예제를 살펴 보겠습니다.

Car 클래스에는 다음과 같이 객체를 만드는 많은 옵션이 포함되어 있다고 가정합니다.

  • 색깔.
  • 좌석 수.
  • 바퀴 수.
  • 유형.
  • 기어 유형.
  • 모터.
  • 에어백 가용성.
import UIKit

enum CarType {
    case
    
    sportage,
    saloon
}

enum GearType {
    case
    
    manual,
    automatic
}

struct Motor {
    var id: String
    var name: String
    var model: String
    var numberOfCylinders: UInt8
}

class Car: CustomStringConvertible {
    var color: UIColor
    var numberOfSeats: UInt8
    var numberOfWheels: UInt8
    var type: CarType
    var gearType: GearType
    var motor: Motor
    var shouldHasAirbags: Bool
    
    var description: String {
        return "color: \(color)\nNumber of seats: \(numberOfSeats)\nNumber of Wheels: \(numberOfWheels)\n Type: \(gearType)\nMotor: \(motor)\nAirbag Availability: \(shouldHasAirbags)"
    }
    
    init(color: UIColor, numberOfSeats: UInt8, numberOfWheels: UInt8, type: CarType, gearType: GearType, motor: Motor, shouldHasAirbags: Bool) {
        
        self.color = color
        self.numberOfSeats = numberOfSeats
        self.numberOfWheels = numberOfWheels
        self.type = type
        self.gearType = gearType
        self.motor = motor
        self.shouldHasAirbags = shouldHasAirbags
        
    }
}

자동차 개체 만들기 :

let aCar = Car(color: UIColor.black,
               numberOfSeats: 4,
               numberOfWheels: 4,
               type: .saloon,
               gearType: .automatic,
               motor: Motor(id: "101", name: "Super Motor",
                            model: "c4", numberOfCylinders: 6),
               shouldHasAirbags: true)

print(aCar)

/* Printing
 color: UIExtendedGrayColorSpace 0 1
 Number of seats: 4
 Number of Wheels: 4
 Type: automatic
 Motor: Motor(id: "101", name: "Super Motor", model: "c4", numberOfCylinders: 6)
 Airbag Availability: true
*/

문제 는 자동차 개체를 만들 때 많은 구성 데이터를 만들어야한다는 것입니다.

Builder Pattern을 적용하기 위해 필요한 경우 이니셜 라이저 매개 변수의 기본값 을 변경할 수 있어야합니다 .

CarBuilder 클래스 :

class CarBuilder {
    var color: UIColor = UIColor.black
    var numberOfSeats: UInt8 = 5
    var numberOfWheels: UInt8 = 4
    var type: CarType = .saloon
    var gearType: GearType = .automatic
    var motor: Motor = Motor(id: "111", name: "Default Motor",
                             model: "T9", numberOfCylinders: 4)
    var shouldHasAirbags: Bool = false
    
    func buildCar() -> Car {
        return Car(color: color, numberOfSeats: numberOfSeats, numberOfWheels: numberOfWheels, type: type, gearType: gearType, motor: motor, shouldHasAirbags: shouldHasAirbags)
    }
}

CarBuilder 클래스는 생성 된 자동차 객체의 값을 편집하기 위해 변경할 수있는 속성을 정의합니다.

CarBuilder 를 사용하여 새 자동차를 만들어 보겠습니다.

var builder = CarBuilder()
// currently, the builder creates cars with default configuration.

let defaultCar = builder.buildCar()
//print(defaultCar.description)
/* prints
 color: UIExtendedGrayColorSpace 0 1
 Number of seats: 5
 Number of Wheels: 4
 Type: automatic
 Motor: Motor(id: "111", name: "Default Motor", model: "T9", numberOfCylinders: 4)
 Airbag Availability: false
*/

builder.shouldHasAirbags = true
// now, the builder creates cars with default configuration,
// but with a small edit on making the airbags available

let safeCar = builder.buildCar()
print(safeCar.description)
/* prints
 color: UIExtendedGrayColorSpace 0 1
 Number of seats: 5
 Number of Wheels: 4
 Type: automatic
 Motor: Motor(id: "111", name: "Default Motor", model: "T9", numberOfCylinders: 4)
 Airbag Availability: true
 */

builder.color = UIColor.purple
// now, the builder creates cars with default configuration
// with some extra features: the airbags are available and the color is purple

let femaleCar = builder.buildCar()
print(femaleCar)
/* prints
 color: UIExtendedSRGBColorSpace 0.5 0 0.5 1
 Number of seats: 5
 Number of Wheels: 4
 Type: automatic
 Motor: Motor(id: "111", name: "Default Motor", model: "T9", numberOfCylinders: 4)
 Airbag Availability: true
*/

빌더 패턴을 적용하는 혜택은 기본 값을 설정하여 구성의 대부분을 포함해야 객체를 생성의 용이성, 또한, 이러한 기본 값을 변경의 용이성.

그것을 가져라 :

기본 값을 필요로하는 모든 속성은 클래스 자체와 빌더에 의해 구현되어야하는 분리 된 프로토콜 에 있어야합니다.

이 예제를 CarBluePrint 라는 새로운 프로토콜을 CarBluePrint .

import UIKit

enum CarType {
    case
    
    sportage,
    saloon
}

enum GearType {
    case
    
    manual,
    automatic
}

struct Motor {
    var id: String
    var name: String
    var model: String
    var numberOfCylinders: UInt8
}

protocol CarBluePrint {
    var color: UIColor { get set }
    var numberOfSeats: UInt8 { get set }
    var numberOfWheels: UInt8 { get set }
    var type: CarType { get set }
    var gearType: GearType { get set }
    var motor: Motor { get set }
    var shouldHasAirbags: Bool { get set }
}

class Car: CustomStringConvertible, CarBluePrint {
    var color: UIColor
    var numberOfSeats: UInt8
    var numberOfWheels: UInt8
    var type: CarType
    var gearType: GearType
    var motor: Motor
    var shouldHasAirbags: Bool
    
    var description: String {
        return "color: \(color)\nNumber of seats: \(numberOfSeats)\nNumber of Wheels: \(numberOfWheels)\n Type: \(gearType)\nMotor: \(motor)\nAirbag Availability: \(shouldHasAirbags)"
    }
    
    init(color: UIColor, numberOfSeats: UInt8, numberOfWheels: UInt8, type: CarType, gearType: GearType, motor: Motor, shouldHasAirbags: Bool) {
        
        self.color = color
        self.numberOfSeats = numberOfSeats
        self.numberOfWheels = numberOfWheels
        self.type = type
        self.gearType = gearType
        self.motor = motor
        self.shouldHasAirbags = shouldHasAirbags
        
    }
}

class CarBuilder: CarBluePrint {
    var color: UIColor = UIColor.black
    var numberOfSeats: UInt8 = 5
    var numberOfWheels: UInt8 = 4
    var type: CarType = .saloon
    var gearType: GearType = .automatic
    var motor: Motor = Motor(id: "111", name: "Default Motor",
                             model: "T9", numberOfCylinders: 4)
    var shouldHasAirbags: Bool = false
    
    func buildCar() -> Car {
        return Car(color: color, numberOfSeats: numberOfSeats, numberOfWheels: numberOfWheels, type: type, gearType: gearType, motor: motor, shouldHasAirbags: shouldHasAirbags)
    }
}

디폴트 값을 필요로하는 프로퍼티를 프로토콜에 선언하는 이점은 새로운 추가 된 프로퍼티를 구현하는 것이다. 클래스가 프로토콜을 준수하면 모든 클래스의 속성 / 메서드를 선언해야합니다.

"배터리 이름"이라는 자동차를 만드는 청사진에 추가되어야하는 필수 새로운 기능이 있다고 생각하십시오.

protocol CarBluePrint {
    var color: UIColor { get set }
    var numberOfSeats: UInt8 { get set }
    var numberOfWheels: UInt8 { get set }
    var type: CarType { get set }
    var gearType: GearType { get set }
    var motor: Motor { get set }
    var shouldHasAirbags: Bool { get set }
    
    // adding the new property
    var batteryName: String { get set }
}

새 속성을 추가 한 후 CarBluePrint 프로토콜을 준수하면 'batteryName'속성을 선언해야한다는 것을 알리는 두 가지 컴파일 타임 오류가 발생합니다. 이를 통해 CarBuilderbatteryName 속성에 대한 기본값을 선언하고 설정합니다.

CarBluePrint 프로토콜에 batteryName 새 속성을 추가 한 후에 CarCarBuilder 클래스를 모두 구현해야합니다.

class Car: CustomStringConvertible, CarBluePrint {
    var color: UIColor
    var numberOfSeats: UInt8
    var numberOfWheels: UInt8
    var type: CarType
    var gearType: GearType
    var motor: Motor
    var shouldHasAirbags: Bool
    var batteryName: String
    
    var description: String {
        return "color: \(color)\nNumber of seats: \(numberOfSeats)\nNumber of Wheels: \(numberOfWheels)\nType: \(gearType)\nMotor: \(motor)\nAirbag Availability: \(shouldHasAirbags)\nBattery Name: \(batteryName)"
    }
    
    init(color: UIColor, numberOfSeats: UInt8, numberOfWheels: UInt8, type: CarType, gearType: GearType, motor: Motor, shouldHasAirbags: Bool, batteryName: String) {
        
        self.color = color
        self.numberOfSeats = numberOfSeats
        self.numberOfWheels = numberOfWheels
        self.type = type
        self.gearType = gearType
        self.motor = motor
        self.shouldHasAirbags = shouldHasAirbags
        self.batteryName = batteryName
    }
}

class CarBuilder: CarBluePrint {
    var color: UIColor = UIColor.red
    var numberOfSeats: UInt8 = 5
    var numberOfWheels: UInt8 = 4
    var type: CarType = .saloon
    var gearType: GearType = .automatic
    var motor: Motor = Motor(id: "111", name: "Default Motor",
                             model: "T9", numberOfCylinders: 4)
    var shouldHasAirbags: Bool = false
    var batteryName: String = "Default Battery Name"
    
    func buildCar() -> Car {
        return Car(color: color, numberOfSeats: numberOfSeats, numberOfWheels: numberOfWheels, type: type, gearType: gearType, motor: motor, shouldHasAirbags: shouldHasAirbags, batteryName: batteryName)
    }
}

CarBuilder 를 사용하여 새 자동차를 제작 해 CarBuilder .

var builder = CarBuilder()

let defaultCar = builder.buildCar()
print(defaultCar)
/* prints
 color: UIExtendedSRGBColorSpace 1 0 0 1
 Number of seats: 5
 Number of Wheels: 4
 Type: automatic
 Motor: Motor(id: "111", name: "Default Motor", model: "T9", numberOfCylinders: 4)
 Airbag Availability: false
 Battery Name: Default Battery Name
*/

builder.batteryName = "New Battery Name"

let editedBatteryCar = builder.buildCar()
print(editedBatteryCar)
/*
 color: UIExtendedSRGBColorSpace 1 0 0 1
 Number of seats: 5
 Number of Wheels: 4
 Type: automatic
 Motor: Motor(id: "111", name: "Default Motor", model: "T9", numberOfCylinders: 4)
 Airbag Availability: false
 Battery Name: New Battery Name
 */


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow