デザインパターンをSwiftで実装する場合の記述例がたくさん紹介されています。
Source: Design Patterns implemented in Swift @ Github
まだすべてのパターンが網羅されていませんが、よく利用するものなどが記載されているので参考になるかと思います。
デザインパターンとは
ソフトウェア開発におけるデザインパターン(型紙(かたがみ)または設計パターン、英: design pattern)とは、過去のソフトウェア設計者が発見し編み出した設計ノウハウを蓄積し、名前をつけ、再利用しやすいように特定の規約に従ってカタログ化したものである。
以下各デザインパターンの概要は上記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
-
Project maintained by: @nsmeme (Oktawian Chojnacki)
-
Playground generated with: Swift Playground Builder by @jasonsandmeyer
-
How to generate playground (+zip) from this README: GENERATE.markdown