做者:Andrew Jaffee,原文連接,原文日期:2018-09-04 譯者:鄭一一;校對:BigNerdCoding,pmst,Forelax;定稿:Forelaxios
本文是個人設計模式系列教程的第三篇。在第一篇文章中,我介紹了 建立型模式中的工廠模式和單例模式。在第二篇文章中,又討論了一下 行爲型模式中的觀察者模式和備忘錄模式。git
在本文中,我會就結構型模式中的外觀模式和適配器模式分別舉一個例子。首先,我建議你先去閱讀前面提到的兩篇文章,這會有助於你更熟悉軟件設計模式的一些概念。除了簡短地介紹一下設計模式的組成,我不會再重複介紹全部關於設計模式的概念了。若是須要了解,均可以在前面寫的 第一篇、 第二篇 中找到。github
接下來的幾節,咱們先來簡單回顧一下設計模式的通用概念。「Gang of Four」 (「GoF」) Erich Gamma,Richard Helm,Ralph Johonson,和 John Vlissides 在他們「設計模式:面向對象軟件設計複用的基本原理」的重要著做裏整理了 23 種經典的設計模式。今天咱們重點關注的是兩種結構型設計模式:外觀模式和適配器模式。web
你可能會發現世面上很是多設計模式教程的示例代碼,仍然是基於面向對象編程原則(OOP)、引用語義和 引用類型(classes)編寫的。因此,我決定編寫一套基於 面向協議編程原則(POP)、值語義和 值類型(structs)的設計模式系列教程。若是你已經看過了我以前寫的兩篇文章,我但願你還可以熟悉一下 OOP 和 POP,引用語義和值語義這些概念。若是你還不是特別熟悉,我強烈建議趕忙去了解一下這些主題。本文所舉的例子是所有基於 POP 和值語義的。編程
設計模式是開發者用於管理軟件複雜性極其重要的工具。做爲常見的模板技術,它很好地對軟件中相似的、重複出現的、容易識別的問題進行了概念化抽象。咱們能夠將它視做最佳實踐,從而應用到平常中會遇到的那些編程場景中。舉一個具體的例子,回想一下你在日常寫代碼過程當中有多少次會使用或寫了遵照 觀察者設計模式 的代碼吧。swift
在觀察者模式中,被觀察者(通常來講是一個關鍵資源)會給全部依賴於本身的觀察者,廣播通知其內部狀態的變化。觀察者必須告知被觀察者本身想接收通知,換句話說,觀察者必須訂閱通知。用戶受權的 iOS 彈窗推送通知,就是一個典型的觀察者模式的例子。設計模式
GoF 將 23 種設計模式概括爲三種類型,分別是「建立型」、「行爲型」、「結構型」。本文會介紹兩種結構型設計模式。先看一下結構這個詞的定義:xcode
「以一種肯定方式構建的事物以及實體中各部分元素之間不一樣關係的彙總。」 - www.merriam-webster.com/dictionary/…bash
結構型設計模式的主要做用是明確一段代碼的功能,並說明如何使用。大部分的結構型設計模式能夠經過編寫易讀接口,來實現對一段代碼的簡化使用。由於一段代碼勢必要與其它代碼聯繫,若是要爲代碼段編寫出良好的接口,必須明確清晰地定義代碼之間的各類關係。app
「外觀能夠定義爲特殊結構化的建築物表面或者錯誤的、表面上的、人爲的外形或效果」。 - www.merriam-webster.com/dictionary/…
大部分狀況下,可使用外觀模式,爲一組複雜接口建立一個簡單接口。或許你已經寫過「封裝」代碼。「封裝」的意思就是對一段複雜代碼的簡化使用。
外觀設計模式示例的 playground 文件,能夠在 GitHub 找到。在這個例子裏展現了,如何經過外觀設計模式,來爲沙盒文件系統建立一個簡單的接口,供全部的 iOS app 使用。iOS 文件系統是一個龐大的操做系統子系統,功能包括建立、讀取、刪除、移動、重命名、拷貝文件和目錄。容許獲取和設置文件和目錄的元數據,好比列出在指定目錄下的全部文件。容許查看文件和目錄的狀態,好比某個指定文件是否可寫。提供蘋果推薦、預約義的目錄名。實際上其包含的功能遠遠不止上面提到的這些。
因爲 iOS 文件系統是一個擁有如此多特性和功能的宏大主題,所以也是一個很是好的例子,用來說解如何經過外觀設計模式來簡化代碼的使用。外觀接口會廢棄掉無關功能和雜亂代碼的部分。另外一方面,外觀接口只會定義在某個具體 app 須要使用到的功能。或者在個人例子中,我將功能縮減到只有常用的那部分。這樣作的好處是保證代碼在不一樣 app 中都是可複用、可擴展,可維護的。
基於面向協議編程和值語義,我將 iOS 文件系統的主要特性進行了劃分,從而將其變成可複用、可擴展的單元:協議和協議擴展。
我將四個協議組合成一個結構體,這個結構體表明瞭能夠在全部 iOS 應用中使用的沙盒 iOS 目錄(還能夠看 這篇文章)。由於將來你確定會更多接觸到更多面向協議編程和值語義相關的主題,要注意術語 composed 和 composition 在這裏屬於同義詞。
除此以外,爲了讓你更專一於理解外觀設計模式的使用,在後面的代碼中,我省略了 Swift 錯誤處理和通用錯誤檢查的代碼。
接下來就看看個人代碼吧。先確保已經下載了我在 GitHub 上的 playground 文件。下面是蘋果官方推薦的用於文件系統操做的預約義目錄。
enum AppDirectories : String {
case Documents = "Documents"
case Inbox = "Inbox"
case Library = "Library"
case Temp = "tmp"
}
複製代碼
經過將文件操做限定在上述目錄中,避免了複雜性,並遵循了人機界面指南的原則。
在探究文件操做的核心代碼以前,先來看看使用外觀設計模式所設計出來的接口吧。我建立了 iOSAppFileSystemDirectory
結構體,做爲文件系統經常使用功能的簡單可讀接口。這個接口適用於 AppDirectories
枚舉下的全部目錄。事實上,我本來還能夠加入諸如 符號化連接的建立,或者使用 FileHandle
類實現對文件的精細控制。可是在實際狀況中,我幾乎不太使用到這些功能,更重要的一點是,我想要保持代碼的簡潔性。
我建立了由四個協議組成的外觀。(我知道你看到下面的代碼中只遵循了三個協議,這實際上是由於其中有一個協議繼承自另外一個協議):
struct iOSAppFileSystemDirectory : AppFileManipulation, AppFileStatusChecking, AppFileSystemMetaData {
let workingDirectory: AppDirectories
init(using directory: AppDirectories) {
self.workingDirectory = directory
}
func writeFile(containing text: String, withName name: String) -> Bool {
return writeFile(containing: text, to: workingDirectory, withName: name)
}
func readFile(withName name: String) -> String {
return readFile(at: workingDirectory, withName: name)
}
func deleteFile(withName name: String) -> Bool {
return deleteFile(at: workingDirectory, withName: name)
}
func showAttributes(forFile named: String) -> Void {
let fullPath = buildFullPath(forFileName: named, inDirectory: workingDirectory)
let fileAttributes = attributes(ofFile: fullPath)
for attribute in fileAttributes {
print(attribute)
}
}
func list() {
list(directory: getURL(for: workingDirectory))
}
} // 完成結構體 iOSAppFileSystemDirectory 的定義
複製代碼
下面是一些用於測試 iOSAppFileSystemDirectory
結構體的代碼:
var iOSDocumentsDirectory = iOSAppFileSystemDirectory(using: .Documents)
iOSDocumentsDirectory.writeFile(containing: "New file created.", withName: "myFile3.txt")
iOSDocumentsDirectory.list()
iOSDocumentsDirectory.readFile(withName: "myFile3.txt")
iOSDocumentsDirectory.showAttributes(forFile: "myFile3.txt")
iOSDocumentsDirectory.deleteFile(withName: "myFile3.txt")
複製代碼
接下來的代碼是在運行了 playground 文件中代碼以後的控制檯輸出:
----------------------------
LISTING: /var/folders/5_/kd8__nv1139__dq_3nfvsmhh0000gp/T/com.apple.dt.Xcode.pg/containers/com.apple.dt.playground.stub.iOS_Simulator.Swift-Facade-Design-Pattern-1C4BD3E3-E23C-4991-A344-775D5585D1D7/Documents
File: "myFile3.txt"
File: "Shared Playground Data"
----------------------------
File created with contents: New file created.
(key: __C.FileAttributeKey(_rawValue: NSFileType), value: NSFileTypeRegular)
(key: __C.FileAttributeKey(_rawValue: NSFilePosixPermissions), value: 420)
(key: __C.FileAttributeKey(_rawValue: NSFileSystemNumber), value: 16777223)
(key: __C.FileAttributeKey(_rawValue: NSFileExtendedAttributes), value: {
"com.apple.quarantine" = <30303836 3b356238 36656364 373b5377 69667420 46616361 64652044 65736967 6e205061 74746572 6e3b>;
})
(key: __C.FileAttributeKey(_rawValue: NSFileReferenceCount), value: 1)
(key: __C.FileAttributeKey(_rawValue: NSFileSystemFileNumber), value: 24946094)
(key: __C.FileAttributeKey(_rawValue: NSFileGroupOwnerAccountID), value: 20)
(key: __C.FileAttributeKey(_rawValue: NSFileModificationDate), value: 2018-08-29 18:58:31 +0000)
(key: __C.FileAttributeKey(_rawValue: NSFileCreationDate), value: 2018-08-29 18:58:31 +0000)
(key: __C.FileAttributeKey(_rawValue: NSFileSize), value: 17)
(key: __C.FileAttributeKey(_rawValue: NSFileExtensionHidden), value: 0)
(key: __C.FileAttributeKey(_rawValue: NSFileOwnerAccountID), value: 502)
File deleted.
複製代碼
咱們來簡單討論下 iOSAppFileSystemDirectory
結構體所遵循的幾個協議。AppDirectoryNames
協議和擴展定義和實現了以 URL
類型獲取 AppDirectories
枚舉中目錄完整路徑的方法。
protocol AppDirectoryNames {
func documentsDirectoryURL() -> URL
func inboxDirectoryURL() -> URL
func libraryDirectoryURL() -> URL
func tempDirectoryURL() -> URL
func getURL(for directory: AppDirectories) -> URL
func buildFullPath(forFileName name: String, inDirectory directory: AppDirectories) -> URL
} // end protocol AppDirectoryNames
extension AppDirectoryNames {
func documentsDirectoryURL() -> URL {
return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
}
func inboxDirectoryURL() -> URL {
return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent(AppDirectories.Inbox.rawValue) // "Inbox")
}
func libraryDirectoryURL() -> URL {
return FileManager.default.urls(for: FileManager.SearchPathDirectory.libraryDirectory, in: .userDomainMask).first!
}
func tempDirectoryURL() -> URL {
return FileManager.default.temporaryDirectory
//urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent(AppDirectories.Temp.rawValue) //"tmp")
}
func getURL(for directory: AppDirectories) -> URL {
switch directory {
case .Documents:
return documentsDirectoryURL()
case .Inbox:
return inboxDirectoryURL()
case .Library:
return libraryDirectoryURL()
case .Temp:
return tempDirectoryURL()
}
}
func buildFullPath(forFileName name: String, inDirectory directory: AppDirectories) -> URL {
return getURL(for: directory).appendingPathComponent(name)
}
} // end extension AppDirectoryNames
複製代碼
AppFileStatusChecking
協議和擴展封裝了獲取文件狀態數據的方法。這些文件一樣存儲於 AppDirectories
枚舉定義下的目錄。經過「狀態」,能夠肯定某個文件是否存在,是否可讀等。
protocol AppFileStatusChecking {
func isWritable(file at: URL) -> Bool
func isReadable(file at: URL) -> Bool
func exists(file at: URL) -> Bool
}
extension AppFileStatusChecking {
func isWritable(file at: URL) -> Bool {
if FileManager.default.isWritableFile(atPath: at.path) {
print(at.path)
return true
}
else {
print(at.path)
return false
}
}
func isReadable(file at: URL) -> Bool {
if FileManager.default.isReadableFile(atPath: at.path) {
print(at.path)
return true
}
else {
print(at.path)
return false
}
}
func exists(file at: URL) -> Bool {
if FileManager.default.fileExists(atPath: at.path) {
return true
}
else {
return false
}
}
} // end extension AppFileStatusChecking
複製代碼
AppFileSystemMetaData
協議和擴展實現了列出目錄內容和獲取擴展文件的功能。 其目錄也是定義在 AppDirectories
枚舉下。
protocol AppFileSystemMetaData {
func list(directory at: URL) -> Bool
func attributes(ofFile atFullPath: URL) -> [FileAttributeKey : Any]
}
extension AppFileSystemMetaData {
func list(directory at: URL) -> Bool {
let listing = try! FileManager.default.contentsOfDirectory(atPath: at.path)
if listing.count > 0 {
print("\n----------------------------")
print("LISTING: \(at.path)")
print("")
for file in listing {
print("File: \(file.debugDescription)")
}
print("")
print("----------------------------\n")
return true
}
else {
return false
}
}
func attributes(ofFile atFullPath: URL) -> [FileAttributeKey : Any] {
return try! FileManager.default.attributesOfItem(atPath: atFullPath.path)
}
} // end extension AppFileSystemMetaData
複製代碼
最後是 AppFileManipulation
協議和擴展,封裝了 AppDirectories
枚舉目錄下的全部文件操做方法,包括了讀、寫、刪除、重命名、移動、拷貝修改文件擴展名等。
protocol AppFileManipulation : AppDirectoryNames {
func writeFile(containing: String, to path: AppDirectories, withName name: String) -> Bool
func readFile(at path: AppDirectories, withName name: String) -> String
func deleteFile(at path: AppDirectories, withName name: String) -> Bool
func renameFile(at path: AppDirectories, with oldName: String, to newName: String) -> Bool
func moveFile(withName name: String, inDirectory: AppDirectories, toDirectory directory: AppDirectories) -> Bool
func copyFile(withName name: String, inDirectory: AppDirectories, toDirectory directory: AppDirectories) -> Bool
func changeFileExtension(withName name: String, inDirectory: AppDirectories, toNewExtension newExtension: String) -> Bool
}
extension AppFileManipulation {
func writeFile(containing: String, to path: AppDirectories, withName name: String) -> Bool {
let filePath = getURL(for: path).path + "/" + name
let rawData: Data? = containing.data(using: .utf8)
return FileManager.default.createFile(atPath: filePath, contents: rawData, attributes: nil)
}
func readFile(at path: AppDirectories, withName name: String) -> String {
let filePath = getURL(for: path).path + "/" + name
let fileContents = FileManager.default.contents(atPath: filePath)
let fileContentsAsString = String(bytes: fileContents!, encoding: .utf8)
print("File created with contents: \(fileContentsAsString!)\n")
return fileContentsAsString!
}
func deleteFile(at path: AppDirectories, withName name: String) -> Bool {
let filePath = buildFullPath(forFileName: name, inDirectory: path)
try! FileManager.default.removeItem(at: filePath)
print("\nFile deleted.\n")
return true
}
func renameFile(at path: AppDirectories, with oldName: String, to newName: String) -> Bool {
let oldPath = getURL(for: path).appendingPathComponent(oldName)
let newPath = getURL(for: path).appendingPathComponent(newName)
try! FileManager.default.moveItem(at: oldPath, to: newPath)
// highlights the limitations of using return values
return true
}
func moveFile(withName name: String, inDirectory: AppDirectories, toDirectory directory: AppDirectories) -> Bool {
let originURL = buildFullPath(forFileName: name, inDirectory: inDirectory)
let destinationURL = buildFullPath(forFileName: name, inDirectory: directory)
// warning: constant 'success' inferred to have type '()', which may be unexpected
// *let success =*
try! FileManager.default.moveItem(at: originURL, to: destinationURL)
return true
}
func copyFile(withName name: String, inDirectory: AppDirectories, toDirectory directory: AppDirectories) -> Bool {
let originURL = buildFullPath(forFileName: name, inDirectory: inDirectory)
let destinationURL = buildFullPath(forFileName: name, inDirectory: directory)
try! FileManager.default.copyItem(at: originURL, to: destinationURL)
return true
}
func changeFileExtension(withName name: String, inDirectory: AppDirectories, toNewExtension newExtension: String) -> Bool {
var newFileName = NSString(string:name)
newFileName = newFileName.deletingPathExtension as NSString
newFileName = (newFileName.appendingPathExtension(newExtension) as NSString?)!
let finalFileName:String = String(newFileName)
let originURL = buildFullPath(forFileName: name, inDirectory: inDirectory)
let destinationURL = buildFullPath(forFileName: finalFileName, inDirectory: inDirectory)
try! FileManager.default.moveItem(at: originURL, to: destinationURL)
return true
}
} // end extension AppFileManipulation
複製代碼
「適配」的含義是「經過修改讓一個事物更適合(用於新用途)。」 - www.merriam-webster.com/dictionary/…
「適配器」的含義是「用於適配不在初始使用意圖範圍內設備的一種附加裝置。」 - www.merriam-webster.com/dictionary/…
適配器設計模式的做用是在不修改已有代碼庫 "A" 的前提下,仍舊可使用與代碼庫 "A" 不兼容的代碼庫 "B",並保證 "A" 能夠正常工做。咱們能夠建立適配器來保證 "A" 和 "B" 能夠一塊兒工做。其中必定要牢記的原則是代碼庫 "A" 是不能被修改的。(這是由於修改會破壞原有代碼或者咱們根本就沒有這段源代碼)
適配器的 playground 文件,能夠在 GitHub 上找到。在這部分代碼中,咱們基於 iOS 文件系統進行適配器模式的討論,並基於 iOS 文件系統設計了一個適配器模式的例子。以前一章,咱們已經實現了將 iOS 文件系統中全部目錄和文件的路徑表示爲 URL
實例。想象一下下面的場景,在原有工程中已經存在了大量關於 iOS 文件系統的代碼,可是全部目錄和文件的路徑都表示成了字符串形式。那咱們就必需要讓基於 URL 和基於 String 的代碼能夠協同工做。
接下來就看看代碼吧。先確保已經下載了在 GitHub 上的 playground 文件。爲了在接下來的分析中更加專一於適配器模式的討論,下面會使用簡化版本的 AppDirectories
枚舉和 AppDirectoryNames
協議和擴展。
enum AppDirectories : String {
case Documents = "Documents"
case Temp = "tmp"
}
protocol AppDirectoryNames {
func documentsDirectoryURL() -> URL
func tempDirectoryURL() -> URL
}
extension AppDirectoryNames {
func documentsDirectoryURL() -> URL {
return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
}
func tempDirectoryURL() -> URL {
return FileManager.default.temporaryDirectory
}
}
複製代碼
一種方法是建立一個「專用」適配器。這個適配器會返回字符串路徑,這些路徑所有歸屬於在 AppDirectories
下的目錄和文件。
// 專用適配器
struct iOSFile : AppDirectoryNames {
let fileName: URL
var fullPathInDocuments: String {
return documentsDirectoryURL().appendingPathComponent(fileName.absoluteString).path
}
var fullPathInTemporary: String {
return tempDirectoryURL().appendingPathComponent(fileName.absoluteString).path
}
var documentsStringPath: String {
return documentsDirectoryURL().path
}
var temporaryStringPath: String {
return tempDirectoryURL().path
}
init(fileName: String) {
self.fileName = URL(string: fileName)!
}
}
複製代碼
下一部分是用於測試 iOSFile
「專用」適配器的代碼,請注意代碼中的註釋。
let iOSfile = iOSFile(fileName: "myFile.txt")
iOSfile.fullPathInDocuments
iOSfile.documentsStringPath
iOSfile.fullPathInTemporary
iOSfile.temporaryStringPath
// 經過 `AppDirectoryNames` 協議,仍然可以訪問到 URL
iOSfile.documentsDirectoryURL()
iOSfile.tempDirectoryURL()
複製代碼
最後是 playground 文件中每一行代碼對應的右側輸出,這些輸出表明瞭運行時每一行代碼的值。參照上一段代碼,咱們能夠進行逐行對照。
iOSFile
"/var/folders/5_/kd8__nv1139__dq_3nfvsmhh0000gp/T/com.apple.dt.Xcode.pg/containers/com.apple.dt.playground.stub.iOS_Simulator.Swift-Adapter-Design-Pattern-0A71F81A-9388-41F5-ACBE-52A1A61A9B99/Documents/myFile.txt"
"/var/folders/5_/kd8__nv1139__dq_3nfvsmhh0000gp/T/com.apple.dt.Xcode.pg/containers/com.apple.dt.playground.stub.iOS_Simulator.Swift-Adapter-Design-Pattern-0A71F81A-9388-41F5-ACBE-52A1A61A9B99/Documents"
"/Users/softwaretesting/Library/Developer/XCPGDevices/52E1A81A-98AF-42DE-ADCF-E69AC8FA2791/data/Containers/Data/Application/F08EFF4F-8C4F-4BB7-B220-980E16344F18/tmp/myFile.txt"
"/Users/softwaretesting/Library/Developer/XCPGDevices/52E1A81A-98AF-42DE-ADCF-E69AC8FA2791/data/Containers/Data/Application/F08EFF4F-8C4F-4BB7-B220-980E16344F18/tmp"
file:///var/folders/5_/kd8__nv1139__dq_3nfvsmhh0000gp/T/com.apple.dt.Xcode.pg/containers/com.apple.dt.playground.stub.iOS_Simulator.Swift-Adapter-Design-Pattern-0A71F81A-9388-41F5-ACBE-52A1A61A9B99/Documents/
file:///Users/softwaretesting/Library/Developer/XCPGDevices/52E1A81A-98AF-42DE-ADCF-E69AC8FA2791/data/Containers/Data/Application/F08EFF4F-8C4F-4BB7-B220-980E16344F18/tmp/
複製代碼
另外,我還傾向爲字符串類型的路徑設計一個適配器協議。這樣就能夠很方便地使用字符串
路徑來替代 URL
路徑。
// Protocol-oriented approach
protocol AppDirectoryAndFileStringPathNamesAdpater : AppDirectoryNames {
var fileName: String { get }
var workingDirectory: AppDirectories { get }
func documentsDirectoryStringPath() -> String
func tempDirectoryStringPath() -> String
func fullPath() -> String
} // end protocol AppDirectoryAndFileStringPathAdpaterNames
extension AppDirectoryAndFileStringPathNamesAdpater {
func documentsDirectoryStringPath() -> String {
return documentsDirectoryURL().path
}
func tempDirectoryStringPath() -> String {
return tempDirectoryURL().path
}
func fullPath() -> String {
switch workingDirectory {
case .Documents:
return documentsDirectoryStringPath() + "/" + fileName
case .Temp:
return tempDirectoryStringPath() + "/" + fileName
}
}
} // end extension AppDirectoryAndFileStringPathNamesAdpater
struct AppDirectoryAndFileStringPathNames : AppDirectoryAndFileStringPathNamesAdpater {
let fileName: String
let workingDirectory: AppDirectories
init(fileName: String, workingDirectory: AppDirectories) {
self.fileName = fileName
self.workingDirectory = workingDirectory
}
} // end struct AppDirectoryAndFileStringPathNames
複製代碼
接下來是用於測試 AppDirectoryAndFileStringPathNames
結構體的代碼。這個結構體遵照了 AppDirectoryAndFileStringPathNamesAdpater
適配器協議。協議繼承自 AppDirectoryNames
協議。注意在代碼中的兩段註釋。
let appFileDocumentsDirectoryPaths = AppDirectoryAndFileStringPathNames(fileName: "myFile.txt", workingDirectory: .Documents)
appFileDocumentsDirectoryPaths.fullPath()
appFileDocumentsDirectoryPaths.documentsDirectoryStringPath()
// 經過 `AppDirectoryNames` 協議仍然能夠訪問 URL
appFileDocumentsDirectoryPaths.documentsDirectoryURL()
let appFileTemporaryDirectoryPaths = AppDirectoryAndFileStringPathNames(fileName: "tempFile.txt", workingDirectory: .Temp)
appFileTemporaryDirectoryPaths.fullPath()
appFileTemporaryDirectoryPaths.tempDirectoryStringPath()
// 經過 `AppDirectoryNames` 協議仍然能夠訪問 URL
appFileTemporaryDirectoryPaths.tempDirectoryURL()
複製代碼
最後是在 playground 文件中右側的輸出。每一行表明了運行時的代碼值,下面的輸出一樣和上一段代碼是逐行對應的。
AppDirectoryAndFileStringPathNames
"/var/folders/5_/kd8__nv1139__dq_3nfvsmhh0000gp/T/com.apple.dt.Xcode.pg/containers/com.apple.dt.playground.stub.iOS_Simulator.Swift-Adapter-Design-Pattern-A3DE7CC8-D60F-4448-869F-2A19556C62B2/Documents/myFile.txt"
"/var/folders/5_/kd8__nv1139__dq_3nfvsmhh0000gp/T/com.apple.dt.Xcode.pg/containers/com.apple.dt.playground.stub.iOS_Simulator.Swift-Adapter-Design-Pattern-A3DE7CC8-D60F-4448-869F-2A19556C62B2/Documents"
file:///var/folders/5_/kd8__nv1139__dq_3nfvsmhh0000gp/T/com.apple.dt.Xcode.pg/containers/com.apple.dt.playground.stub.iOS_Simulator.Swift-Adapter-Design-Pattern-A3DE7CC8-D60F-4448-869F-2A19556C62B2/Documents/
AppDirectoryAndFileStringPathNames
"/Users/softwaretesting/Library/Developer/XCPGDevices/52E1A81A-98AF-42DE-ADCF-E69AC8FA2791/data/Containers/Data/Application/CF3D4156-E773-4BC4-B117-E7BDEFA3F34C/tmp/tempFile.txt"
"/Users/softwaretesting/Library/Developer/XCPGDevices/52E1A81A-98AF-42DE-ADCF-E69AC8FA2791/data/Containers/Data/Application/CF3D4156-E773-4BC4-B117-E7BDEFA3F34C/tmp"
file:///Users/softwaretesting/Library/Developer/XCPGDevices/52E1A81A-98AF-42DE-ADCF-E69AC8FA2791/data/Containers/Data/Application/CF3D4156-E773-4BC4-B117-E7BDEFA3F34C/tmp/
複製代碼
設計模式不只有利於代碼複用,還能保證代碼是不變、易讀、鬆耦合的,從而提升了可維護性和拓展性。當重複出現而且能加以抽象的功能在你的 app 中出現的時候,我但願你能應用一下設計模式,並 封裝進框架 中。這樣子你只須要寫一次代碼,就能夠一直複用啦。
再次感謝你們來 AppCoda 給我捧場。享受工做,堅持學習,下次再見吧!
本文由 SwiftGG 翻譯組翻譯,已經得到做者翻譯受權,最新文章請訪問 swift.gg。