Swift Language
디자인 패턴 - 창조적 인
수색…
소개
디자인 패턴은 소프트웨어 개발에서 자주 발생하는 문제에 대한 일반적인 솔루션입니다. 다음은 코드 설계 및 설계의 표준화 된 모범 사례 템플릿뿐 아니라 이러한 디자인 패턴이 적절한 공통적 인 상황의 예입니다.
창조적 인 디자인 패턴 은 객체의 생성을 추상화하여 시스템을 생성, 구성 및 표현의 프로세스에보다 독립적으로 만듭니다.
하나씩 일어나는 것
싱글 톤은 프로그램 전체에서 공유되는 클래스의 단일 인스턴스로 구성되는 자주 사용되는 디자인 패턴입니다.
다음 예제에서는 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'속성을 선언해야한다는 것을 알리는 두 가지 컴파일 타임 오류가 발생합니다. 이를 통해 CarBuilder
는 batteryName
속성에 대한 기본값을 선언하고 설정합니다.
CarBluePrint
프로토콜에 batteryName
새 속성을 추가 한 후에 Car
및 CarBuilder
클래스를 모두 구현해야합니다.
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
*/