Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
567
Help us understand the problem. What is going on with this article?
@susieyy

Swiftで書くデザインパターンまとめ

More than 5 years have passed since last update.

デザインパターンをSwiftで実装する場合の記述例がたくさん紹介されています。

Source: Design Patterns implemented in Swift @ Github

まだすべてのパターンが網羅されていませんが、よく利用するものなどが記載されているので参考になるかと思います。

デザインパターンとは

ソフトウェア開発におけるデザインパターン(型紙(かたがみ)または設計パターン、英: design pattern)とは、過去のソフトウェア設計者が発見し編み出した設計ノウハウを蓄積し、名前をつけ、再利用しやすいように特定の規約に従ってカタログ化したものである。

デザインパターン @ Wikipedia

以下各デザインパターンの概要は上記Wikipediaより

記載一覧

2014年8月28日時点

Creational

パターン名 記載
Singleton
Builder
Abstract Factory
Prototype
Factory Method

Structural

パターン名 記載
Composite
Facade
Adapter
Bridge
Decorator
Proxy
Flyweight

Behavioral

パターン名 記載
Chain Of Responsibility
Command
Iterator
Mediator
Memento
Observer
State
Strategy
Template Method
Visitor

Creational (生成に関するパターン)

Singleton

あるクラスについて、インスタンスが単一であることを保証する。

class SingletonClass {
    class var shared : SingletonClass {

        struct Static {
            static let instance : SingletonClass = SingletonClass()
        }

        return Static.instance
    }
}

Usage:

let instance = SingletonClass.shared

Builder

複合化されたインスタンスの生成過程を隠蔽する。

protocol ThreeDimensions {
    var x:Double? {get}
    var y:Double? {get}
    var z:Double? {get}
}

class Point:ThreeDimensions {

    var x:Double?
    var y:Double?
    var z:Double?

    typealias PointBuilderClosure = (Point) -> ()

    init(buildClosure:PointBuilderClosure){
        buildClosure(self)
    }
}

Usage:

let fancyPoint = Point{ point in
    point.x = 0.1
    point.y = 0.2
    point.z = 0.3
}

fancyPoint.x
fancyPoint.y
fancyPoint.z

略記法

let uglierPoint = Point{
    $0.x = 0.1
    $0.y = 0.2
    $0.z = 0.3
}

Abstract Factory

関連する一連のインスタンスを状況に応じて適切に生成する方法を提供する。

class Number
{
    var number:AnyObject

    init(number:AnyObject){
        self.number = number
    }

    convenience init(integer:Int){
        self.init(number:integer)
    }

    convenience init(double:Double){
        self.init(number:double)
    }

    func integerValue() -> Int{
        return self.number as Int
    }

    func doubleValue() -> Double{
        return self.number as Double
    }
}

Usage:

let number = Number(double: 12.1)
let double = number.doubleValue()
let integer = number.integerValue()

Prototype

同様のインスタンスを生成するために、原型のインスタンスを複製する。

class ThieveryCorporationPersonDisplay{

    var name:String?
    let font:String

    init(font:String){
        self.font = font
    }

    func clone() -> ThieveryCorporationPersonDisplay{
        return ThieveryCorporationPersonDisplay(font: self.font)
    }
}

Usage:

let Prototype = ThieveryCorporationPersonDisplay(font:"Ubuntu")

let Philippe = Prototype.clone()
Philippe.name = "Philippe"

let Christoph = Prototype.clone()
Christoph.name = "Christoph"

let Eduardo = Prototype.clone()
Eduardo.name = "Eduardo"

Factory Method

実際に生成されるインスタンスに依存しない、インスタンスの生成方法を提供する。

protocol Currency {
    func symbol() -> String
    func code() -> String
}

class Euro : Currency {
    func symbol() -> String {
        return "€"
    }

    func code() -> String {
        return "EUR"
    }
}

class UnitedStatedDolar : Currency {
    func symbol() -> String {
        return "$"
    }

    func code() -> String {
        return "USD"
    }
}

enum Country {
    case UnitedStates, Spain, France, UK
}

class CurrencyFactory {
    class func currencyForCountry(country:Country) -> Currency? {
        switch country {
        case .Spain, .France :
            return Euro()
        case .UnitedStates :
            return UnitedStatedDolar()
        default:
            return nil
        }

    }
}

Usage:

let noCurrencyCode = "No Currency Code Available"

CurrencyFactory.currencyForCountry(.Spain)?.code() ?? noCurrencyCode
CurrencyFactory.currencyForCountry(.UnitedStates)?.code() ?? noCurrencyCode
CurrencyFactory.currencyForCountry(.France)?.code() ?? noCurrencyCode
CurrencyFactory.currencyForCountry(.UK)?.code() ?? noCurrencyCode

Structural (構造に関するパターン)

Composite

再帰的な構造を表現する。

/**
 *  Component
 */
protocol Shape {
    func draw(fillColor:String)
}

/**
 * Leafs
 */
class Square : Shape {
    func draw(fillColor: String) {
        print("Drawing a Square with color \(fillColor)")
    }
}

class Circle : Shape {
    func draw(fillColor: String) {
        print("Drawing a circle with color \(fillColor)")
    }
}

/**
* Composite
*/
class Whiteboard : Shape {
    lazy var shapes = [Shape]()

    init(_ shapes:[Shape]) {
        self.shapes = shapes
    }

    func draw(fillColor:String) {
        for shape in self.shapes {
            shape.draw(fillColor)
        }
    }
}

Usage:

var whiteboard = Whiteboard([Circle(), Square()])
whiteboard.draw("Red")

Facade

複数のサブシステムの窓口となる共通のインタフェースを提供する。

class Eternal{

    class func setObject(value: AnyObject!, forKey defaultName: String!){
        let defaults:NSUserDefaults = NSUserDefaults.standardUserDefaults()
        defaults.setObject(value,forKey:defaultName)
        defaults.synchronize()
    }

    class func objectForKey(defaultName: String!) -> AnyObject!{
        let defaults:NSUserDefaults = NSUserDefaults.standardUserDefaults()
        return defaults.objectForKey(defaultName)
    }

}

Usage:

Eternal.setObject("Disconnect me. I’d rather be nothing",forKey:"Bishop")

Eternal.objectForKey("Bishop")

Adapter

元々関連性のない2つのクラスを接続するクラスを作る。

// WARNING: This example uses Point class from Builder pattern!

class PointConverter{

    class func convert(#point:Point, base:Double, negative:Bool) -> Point{

        var pointConverted = Point{
            if let x = point.x{ $0.x = x * base * (negative ? -1.0 : 1.0) }
            if let y = point.y{ $0.y = y * base * (negative ? -1.0 : 1.0) }
            if let z = point.z{ $0.z = z * base * (negative ? -1.0 : 1.0) }
        }

        return pointConverted
    }
}

extension PointConverter{

    class func convert(#x:Double!, y:Double!, z:Double!, base:Double!, negative:Bool!) -> (x:Double!,y:Double!,z:Double!){

        var point = Point{ $0.x = x; $0.y = y; $0.z = z }
        var pointCalculated = self.convert(point:point, base:base, negative:negative)

        return (pointCalculated.x!,pointCalculated.y!,pointCalculated.z!)
    }

}

Usage:

var tuple = PointConverter.convert(x:1.1, y:2.2, z:3.3, base:2.0, negative:true)

tuple.x
tuple.y
tuple.z

Bridge

クラスなどの実装と、呼出し側の間の橋渡しをするクラスを用意し、実装を隠蔽する。

Decorator

あるインスタンスに対し、動的に付加機能を追加する。Filterとも呼ばれる。

Proxy

共通のインタフェースをもつインスタンスを内包し、利用者からのアクセスを代理する。Wrapperとも呼ばれる。

Flyweight

多数のインスタンスを共有し、インスタンスの構築のための負荷を減らす。

Behavioral (振る舞いに関するパターン)

Chain Of Responsibility

イベントの送受信を行う複数のオブジェクトを鎖状につなぎ、それらの間をイベントが渡されてゆくようにする。

Command

複数の異なる操作について、それぞれに対応するオブジェクトを用意し、オブジェクトを切り替えることで操作の切替えを実現する。

protocol FileOperationCommand {
    init(file: String)
    func execute()
}

class FileMoveCommand : FileOperationCommand {
    let file:String
    required init(file: String) {
        self.file = file
    }

    func execute() {
        print("\(file) moved")
    }
}

class FileDeleteCommand : FileOperationCommand {
    let file:String
    required init(file: String) {
        self.file = file
    }

    func execute() {
        print("\(file) deleted")
    }
}

class FileManager {
    let deleteCommand: FileOperationCommand
    let moveCommand: FileOperationCommand

    init(deleteCommand: FileDeleteCommand, moveCommand: FileMoveCommand) {
        self.deleteCommand = deleteCommand
        self.moveCommand = moveCommand
    }

    func delete () {
        deleteCommand.execute()
    }

    func move () {
        moveCommand.execute()
    }
}

Usage:

let deleteCommand = FileDeleteCommand(file: "/path/to/testfile")
let moveCommand = FileMoveCommand(file: "/path/to/testfile")
let fileManager = FileManager(deleteCommand:deleteCommand , moveCommand: moveCommand)

fileManager.delete()
fileManager.move()

Interpreter

構文解析のために、文法規則を反映するクラス構造を

Iterator

複数の要素を内包するオブジェクトのすべての要素に順にアクセスする方法を提供する。反復子。

Mediator

オブジェクト間の相互作用を仲介するオブジェクトを定義し、オブジェクト間の結合度を低くする

Memento

データ構造に対する一連の操作のそれぞれを記録しておき、以前の状態の復帰または操作の再現が行えるようにする。

Observer

インスタンスの変化を他のインスタンスから監視できるようにする。Listenerとも呼ばれる。

State

オブジェクトの状態を変化させることで、処理内容を変えられるようにする。

class Context {
    private var state: State = UnauthorizedState()
    func changeStateToAuthorized(#userId: String) {
        state = AuthorizedState(userId: userId)
    }
    func changeStateToUnauthorized() {
        state = UnauthorizedState()
    }
    var isAuthorized: Bool {
        get { return state.isAuthorized(self) }
    }
    var userId: String? {
        get { return state.userId(self) }
    }
}

protocol State {
    func isAuthorized(context: Context) -> Bool
    func userId(context: Context) -> String?
}

class UnauthorizedState: State {
    func isAuthorized(context: Context) -> Bool { return false }
    func userId(context: Context) -> String? { return nil }
}

class AuthorizedState: State {
    let userId: String
    init(userId: String) { self.userId = userId }
    func isAuthorized(context: Context) -> Bool { return true }
    func userId(context: Context) -> String? { return userId }
}

Usage:

let context = Context()
(context.isAuthorized, context.userId)
context.changeStateToAuthorized(userId: "admin")
(context.isAuthorized, context.userId) // now logged in as "admin"
context.changeStateToUnauthorized()
(context.isAuthorized, context.userId)

Strategy

データ構造に対して適用する一連のアルゴリズムをカプセル化し、アルゴリズムの切替えを容易にする。

protocol PrintStrategy {
    func printString(string: String) -> String
}

class Printer {

    let strategy: PrintStrategy

    func printString(string:String)->String{
        return self.strategy.printString(string)
    }

    init(strategy: PrintStrategy){
        self.strategy = strategy
    }
}

class UpperCaseStrategy: PrintStrategy{
    func printString(string:String)->String{
        return string.uppercaseString
    }
}

class LowerCaseStrategy: PrintStrategy{
    func printString(string:String)->String{
        return string.lowercaseString
    }
}

Usage:

var lower = Printer(strategy: LowerCaseStrategy())
lower.printString("O tempora, o mores!")

var upper = Printer(strategy: UpperCaseStrategy())
upper.printString("O tempora, o mores!")

Template Method

あるアルゴリズムの途中経過で必要な処理を抽象メソッドに委ね、その実装を変えることで処理が変えられるようにする。

Visitor

データ構造を保持するクラスと、それに対して処理を行うクラスを分離する。

protocol PlanetVisitor {
    func visit(planet: PlanetEarth)
    func visit(planet: PlanetMars)
    func visit(planet: PlanetGliese581C)
}

protocol Planet {
    func accept(visitor: PlanetVisitor)
}

class PlanetEarth: Planet {
    func accept(visitor: PlanetVisitor) { visitor.visit(self) }
}
class PlanetMars: Planet {
    func accept(visitor: PlanetVisitor) { visitor.visit(self) }
}
class PlanetGliese581C: Planet {
    func accept(visitor: PlanetVisitor) { visitor.visit(self) }
}

class NameVisitor: PlanetVisitor {
    var name = ""
    func visit(planet: PlanetEarth)      { name = "Earth" }
    func visit(planet: PlanetMars)       { name = "Mars" }
    func visit(planet: PlanetGliese581C) { name = "Gliese 581 C" }
}

Usage:

let planets: [Planet] = [PlanetEarth(), PlanetMars(), PlanetGliese581C()]

let names = planets.map { (planet: Planet) -> String in
    let visitor = NameVisitor()
    planet.accept(visitor)
    return visitor.name
}

names

LICENSE

567
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
susieyy
フリーランス - スタートアップエンジニアリングアドバイザー - iOS技術顧問 - プロトタイプ開発

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
567
Help us understand the problem. What is going on with this article?