swift protocol tutorial
Bài đăng này đã không được cập nhật trong 8 năm
Một protocol định nghĩa một kế hoạch chi tiết của phương thức, thuộc tính và nhưng yêu cầu khác để phù hợp với nhiệm vụ cụ thể hoặc một vài tính năng. Protocol có thể sau khi được chấp nhận bởi class, cấu trúc hoặc liết kê (enum) để cung cấp sự thực thi của những yêu cầu đó.
Giao thức có thể yêu cầu kiểu phù hợp có thuộc tính thể hiện, phương thức thể hiện, phương thức kiểu, toán tử, và subscripts thể.
Yêu cầu thuộc tính luôn luôn mô tả như thuộc tính biến với từ khóa var. Thuộc tính có khả năng lấy và thiết lập được chỉ ra rằng viết {get, set} sau khi mô tả kiểu của chúng, và thuộc tính có thể thiết lập bởi phương thức {get}.
Protocol Syntax
Bạn định nghĩa các giao thức trong các tương tự như các class, các cấu trúc, và các liệt kê:
protocol OneProtocol {
// protocol definition goes here
}
Các kiểu tuỳ chỉnh mà chúng chấp nhận một giao thức cụ thể bằng cách đặt tên của giao thức sau tên của kiểu, cách nhau bằng dấu hai chấm, trong phần định nghĩa của chúng. Nhiều giao thức có thể được liệt kê, và được các ra bằng dấu phẩy:
struct OneStructure: OneProtocol, AnotherProtocol {
// structure definition goes here
}
ếu một lớp có một lớp cha, liệt kê tên lớp cha trước bất kỳ giao thức mà nó chấp nhận, theo sau một dấu phẩy:
class OneClass: OneSuperclass, FirstProtocol, AnotherProtocol {
// class definition goes here
}
Property Requirements
Một giao thức có thể yêu cầu bất kỳ kiểu phù hợp để cung cấp một thuộc tính thể hiện hoặc thuộc tính kiểu với một tên và kiểu cụ thể. Giao thức không chỉ ra rằng liệu thuộc tính nên là một thuộc tính lưu trữ hay một thuộc tính tính toán – nó chỉ chỉ ra yêu cầu tên và kiểu của thuộc tính. Giao thức cũng chỉ ra rằng liệu từng thuộc tính phải có thể lấy ra hoặc có thể lấy ra và có thể thiết lập.
Nếu một giao thức yêu cầu một thuộc tính để có thể lấy ra và có thể thiết lập, yêu cầu thuộc tính đó không thể được lấp đầy bởi một thuộc tính lưu trữ không đổi hoặc một thuộc tính tính toán read-only. Nếu giao thức chỉ yêu cầu một thuộc tính để có thể lấy ra, yêu cầu có thể được thoả mãn bởi bất kỳ loại thuộc tính nào, và nó phù hợp cho thuộc tính để cũng có thể thiết lập nếu điều này hữu ích cho mã code riêng của bạn.
Các yêu cầu thuộc tính luôn được khai báo như là các thuộc tính biến đổi, đặt trước với từ khoá var. Các thuộc tính có thể lấy ra và có thể thiết lập được chỉ ra bằng cách viết { get set } sau khai báo kiểu của chúng, và các thuộc tính có thể được lấy ra được chỉ ra bằng cách viết { get }.
protocol OneProtocol {
var oneSettable: Int { get set }
var doesNotNeedToBeSettable: Int { get }
Luôn đặt trước các yêu cầu thuộc tính kiểu với từ khoá class khi bạn định nghĩa chúng chúng một giao thức. Quy tắc này gắn liền mặc dù các yêu cầu thuộc tính kiểu được đặt trước với từ khoá static khi được thực hiện bởi một cấu trúc hoặc liệt kê:
protocol AnotherProtocol {
class var someTypeProperty: Int { get set }
}
Dưới đây là một ví dụ của một giao thức với một yêu cầu thuộc tính thể hiện duy nhất:
protocol FullName {
var name: String { get }
}
Giao thức FullName yêu cầu một kiểu phù hợp để cung cấp một tên đầy đủ điều kiện. Giao thức không chỉ định bất cứ thứ gì khác về bản chất của kiểu phù hợp – nó chỉ chỉ ra rằng kiểu phải có khả năng cung cấp một tên đầy đủ bản thân nó. Giao thức tuyên bố rằng bất kỳ kiểu FullName phải có một thuộc tính thể hiện có thể lấy ra gọi là FullName, nó là kiểu String.
Dưới đây là một ví dụ của một cấu trúc đơn giản mà chấp nhận và phù hợp với giao thức FullName:
struct Person: FullName {
var name: String
}
let john = Person(name: "Pham Ngoc Tam")
// john.name is "Pham Ngoc Tam""
Ví dụ này định nghĩa một cấu trúc gọi là Person, nó biểu diễn một người có tên cụ thể. Nó nói ra rằng nó chấp nhận giao thức FullName trong dòng đầu tiên của định nghĩa của nó.
Mỗi thể hiện của Person có một thuộc tính lưu trưc duy nhất gọi là fullName, nó là kiểu String. Điều này phù hợp với các yêu cầu đơn giản của giao thức FullName, và có nghĩa là Person phù hợp chính xác với giao thức. (Swift báo cáo một lỗi compile-time nếu một yêu cầu giao thức không được thực hiện đầy đủ).
Dưới đây là một lớp phức tạp hơn, nó cũng chấp nhận và phù hợp với giao thức FullName:
class Starship: FullName {
var prefix: String?
var name: String
init(name: String, prefix: String? = nil) {
self.name = name
self.prefix = prefix
}
var fullName: String {
return (prefix != nil ? prefix! + " " : "") + name
}
}
var starshipInfo = Starship(name: "NgocTam", prefix: "VN")
// starshipInfo.fullName is "VN NgocTam”
Lớp này thực hiện các yêu cầu thuộc tính fullName như một thuộc tính tính toán read-only cho một phi thuyền. Mỗi thể thiện lớp Starship lưu một name bắt buộc và một optional prefix. Thuộc tính fullName sử dụng giá trị prefix nếu nó tồn tại, và thêm nó vào đầu name để tạo ra một tên đầy đủ cho phi thuyền.
Method Requirements
Các giao thức có thể yêu cầu các phương thức thể hiện cụ thể và các phương thức kiểu để được thực hiện bởi các kiểu phù hợp. Các phương thức này được viết trong định nghĩa của giao thức một các tương tự như đối với các thể hiện và các phương thức kiểu thông thường, nhưng không có dấu ngoặc hoặc một thân phương thức. Các tham số lệnh biến thiên được cho phép, tuỳ thuộc vào các quy tắc tương tự như đối với các phương thức thông thường.
Chú ý
Các giao thức sử dụng cú pháp tương tự cho các phương thức thông thường, nhưng không được cho phép đẻ chỉ định các giá trị mặc định cho các tham số phương thức.
Như với các yêu cầu thuộc tính, bạn luôn đặt trước các yêu cầu phương thức kiểu với từ khoá class khi chúng được định nghĩa trong một giao thức. Điều này đúng mặc dù các yêu cầu phương thức kiểu được đặt trước với từ khoá static khi được thực hiện vởi một cấu trúc hoặc một liệt kê:
protocol OneProtocol {
class func oneTypeMethod()
}
Ví dụ sau định nghĩa một giao thức với yêu cầu phương thức thể duy nhất:
protocol OneRandomNumber {
func randomType() -> Double
}
Giao thức này, OneRandomNumber, yêu cầu bất kỳ kiểu phù hợp để có một phương thức thể hiện gọi là random, nó trả về một giá trị Double mỗi khi nó được gọi. Mặc dù nó không được chỉ ra trong giao thức, nó được giả sử rằng giá trị này sẽ là một số từ 0.0 tới 1.0.
Giao thức OneRandomNumber không thực hiện bất kỳ giả định nào về cách mỗi số ngẫu nhiên sẽ được tạo ra – nó đơn giản chỉ yêu cầu bộ sinh để cung cấp một các tiêu chuẩn để tạo ra một số ngẫu nhiên.
Dưới đây là một thực hiện của một lớp mà chấp nhận và phù hợp vói giao thức OneRandomNumber. Lớp này thực hiện một thuật toán tạo ra số ngẫu nhiên được biết như linear congruential generator:
class CongruentialGenerator: OneRandomNumber {
var lastRandom = 42.0
let m = 139968.0
let a = 3877.0
let c = 29573.0
func random() -> Double {
lastRandom = ((lastRandom * a + c) % m)
return lastRandom / m
}
}
let oneGenerator = CongruentialGenerator()
println("s a random number: \(oneGenerator.random())")
// prints " a random number: 0.37464991998171"
println(" another one: \(oneGenerator.random())")
// prints " another one: 0.729023776863283"
Mutating Method Requirements
Đôi khi cũng cần một phương thức để chỉnh sửa thể hiện mà nó thuộc về. Đối với các phương thức thể hiện trong các kiểu giá trị (đó là, các cấu trúc và các liệt kê) bạn đặt từ khoá mutating trước từ khoá func của một phương thức để chỉ ra rằng phương thức được cho phép để chỉnh sửa thể hiện mà nó thuộc về và/hoặc bất kỳ thuộc tính của thể hiện đó. Quá trình này được mô tả trong Modifying Value Types from Within Instance Methods.
Nếu bạn định nghĩa một phương thức thể hiện giao thức được dùng để chỉnh sửa thể hiện của bất kỳ kiểu nào chấp nhận giao thức, đánh dấu phương thức với từ khoá mutating trong phần định nghĩa của giao thức. Điều này cho phép các cấu trúc và các liệt kê để chấp nhận giao thức và thoả mãn yêu cầu phương thức đó.
Chú ý
Nếu bạn đánh dấu một yêu cầu phương thức thể hiện giao thức với mutating, bạn không cần viết từ khoá mutating khi viết một thực hiện của phương thức đó cho một lớp. Từ khoá mutating chỉ được sử dụng bởi các cấu trúc và các liệt kê.
Ví dụ bên dưới định nghĩa một giao thức gọi là Togglable, nó định nghĩa một yêu cầu phương thức thể hiện duy nhất gọi là toggle. Như tên gọi của nó cho thấy, phương thức toggle được thiết kế để chuyển đổi hoặc đảo ngược trạng thái của bất kỳ kiểu phù hợp, thường bường cách chỉnh sửa một thuộc tính của kiểu đó.
Phương thức toggle được đánh dấu với từ khoá mutating trong phần định nghĩa giao thức Togglable, để chỉ ra rằng phương thức được chờ đợi để thay đổi trạng thái của một thể hiện phù hợp khi nó được gọi:
protocol Togglable {
mutating func toggle()
}
Nếu bạn thuọc thiện giao thức Togglable cho một cấu trúc hoặc một liệt kê, cấu trúc hoặc liệt kê đó có thể phù hợp với giao thức bằng cách cung cấp một thực hiện của phương thức toggle mà cũng được đánh đấu với mutating.
Ví dụ dưới đây định nghĩa một liệt kê gọi là OnOffSwitch. Liệt kê này chuyển đổi giữa hai trạng thái, được chỉ ra bởi các trường hợp liệt kê On và Off. Việc thực hiện của liệt kê toggle được đánh dấu với mutating, để phù hợp các yêu cầu của giao thức Togglable:
enum OnOffSwitch: Togglable {
case Off, On
mutating func toggle() {
switch self {
case Off:
self = On
case On:
self = Off
}
}
}
var lightSwitch = OnOffSwitch.Off
lightSwitch.toggle()
// lightSwitch is now equal to .On
** Initializer Requirements**
Giao thức có thể yêu cầu các khởi tạo cụ thể để được thực hiện bởi các kiểu phù hợp. Bạn viết các khởi tạo này trong phần định nghĩa của giao thức trong các tương tự như các khởi tạo thông thường, nhưng không có dấu ngoặc hoặc thân khởi tạo:
protocol OneProtocol {
init(someParameter: Int)
}
** Class Implementations of Protocol Initializer Requirements**
Bạn có thể thực hiện một yêu cầu khởi tạo giao thức trong một lớp phù hợp như một khởi tạo designated hoặc một khởi tạo convenience. Trong cả hai trường hợp, bạn phải đánh dấu thực hiện khởi tạo với từ bổ nghĩa required:
class OneClass: OneProtocol {
required init(someParameter: Int) {
// initializer implementation goes here
}
}
Việc sử dụng từ bổ nghĩa required đảm bảo rằng bạn cung cấp một thực hiện rõ ràng hoặc kế thừa của yêu cầu khởi tạo trong tất cả các lớp con của lớp phù hợp, như vậy chúng cũng phù hợp giao thức.
Để biết thêm thông tin trong các khởi tạo yêu cầu, đọc Required Initializers.
Chú ý
Bạn không cần đánh dấu các thực hiện khởi tao giao thức với từ từ bổ nghĩa required tỏng các lớp, nó được đánh dấu với từ bổ nghĩa final, bởi vì các lớp final không thể là lớp con. Để biết thêm từ bổ nghĩa fianl, đọc Preventing Overrides.
Nếu một lớp con ghi đè một khởi tạo designated từ một lớp cha, và cũng thực hiện một yêu cầu khởi tạo phù hợp từ một giao thức, đánh dấu thực hiện khởi tạo với cả hai từ bổ nghĩa required và override:
protocol OneProtocol {
init()
}
class OneSuperClass {
init() {
// initializer implementation goes here
}
}
class OneSubClass: OneSuperClass, OneProtocol {
required override init() {
// initializer implementation goes here
}
}
Protocols as Types
Giao thức không thực sự thực hiện bất kỳ chức năng tự. Tuy nhiên, bất kỳ giao thức bạn tạo ra sẽ trở thành một kiểu đầy đủ để sử dụng trong mã của bạn.
Bởi vì nó là một kiểu, bạn có thể sử dụng một giao thức ở nhiều nơi, nơi các kiểu khác được cho phép, bao gồm:
_ – Như một kiểu tham số hoặc kiểu trả về trong một hàm, phương thức, hoặc khởi tạo
– Như kiểu của một hằng số, biến, hoặc thuộc tính
– Như kiểu của các phần tử trong một mảng, từ điển, hoặc container khác_
Chú ý
Bởi vì các giao thức là các kiểu, bắt đầu tên của chúng với một từ viết hoa (như FullyNamed và RandomNumberGenerator) để phù hợp với tên của các kiểu khác trong Swift (như Int, String, và Double).
Dưới đây là một ví dụ của một giao thức được sử dụng như một kiểu:
class Dice {
let sides: Int
let generator: OneRandomNumber
init(sides: Int, generator: OneRandomNumber) {
self.sides = sides
self.generator = generator
}
func roll() -> Int {
return Int(generator.random() * Double(sides)) + 1
}
}
Ví dụ này định nghĩa một lớp mới gọi là Dice, nó biẻu diễn một xúc xắc n-mặt để sử dụng trong một trò chơi. Các thể hiện Dice có một thuộc tính số nguyên gọi là sides, nó biểu diễn bao nhiêu mặt chúng có, và một thuộc tính gọi là generator, nó cung cấp một bộ sinh số ngẫu nhiên từ đó để tạo ra các giá trị đổ xúc xúc.
Thuộc tính generator là kiểu OneRandomNumber. Do đó, bạn có thể thiết lập nó với một thể hiện của bất kỳ kiểu mà chấp nhận giao thức OneRandomNumber. Không còn gì nữa được yêu cầu cho thể hiện mà bạn gán cho thuộc tính này, ngoại trừ thể hiện phải chấp nhận giao thức OneRandomNumber.
Dice cũng có một khởi tạo, để thiết lập giá trị ban đầu của nó. Khởi tạo này có một tham số gọi là generator, nó cũng là kiểu OneRandomNumber. Bạn cần phải truyền một giá trị của bất kỳ kiểu phù hợp vào trong tham số này khi khởi tạo một thể hiện Dice.
Dice cung cấp một phương thức thể hiện, roll, nó trả về một giá trị nguyên giữa 1 và số của mặt trên con xúc xắc. Phương thức này gọi phương thức random của bộ sinh để tạo ra một số ngẫu nhiên giữa 0.0 và 1.0, và sử dụng số ngẫu nhiên này để tạo ra giá trị một lần đổ xúc xắc trong phạm vi chính xác. Bởi vì generator được biết đến để chấp nhận OneRandomNumber, nó được đảm bảo để có một phương thức random để gọi.
Dưới đây là cách lớp Dice có thể được sử dụng để tạo ra một xúc xắc 6 mặt với thể hiện LinearCongruentialGenerator nhứ bộ sinh số ngẫu nhiên của nó:
var dValue = Dice(sides: 6, generator: LinearCongruentialGenerator())
for _ in 1...5 {
println("Random is \(dValue.roll())")
}
// Random is 3
// Random is 5
// Random is 4
// Random is 5
// Random is 4
Delegation
Uỷ quyền – Delegation – là một mẫu thiết kế cho phép một lớp hoặc cấu trúc uỷ quyền một số trách nhiệm của nó tới một thể hiện của kiểu khác. Mẫu thiết kế này được thực hiện bằng cách định nghĩa một giao thức mà đóng gói các trách nhiệm uỷ quyền, như vậy một kiểu phù hợp được đảm bảo để cung cấp chức năng được uỷ quyền. Uỷ quyền có thể được sử dụng để đáp ứng tới một hành động cụ thể, hoặc để lấy dữ liệu từ một mã nguồn mở rộng mà không cần biết kiểu cơ bản của mã nguồn đó.
Ví dụ dưới đây định nghĩa hai giao thức cho việc sử dụng với trò chơi dice-based:
protocol OneDiceGame {
var dice: Dice { get }
func play()
}
protocol OneDiceGameDelegate {
func gameDidStart(game: DiceGame)
func game(game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int)
func gameDidEnd(game: DiceGame)
}
DiceGame là một giao thức mà có thể được chấp nhận vởi bất kỳ trò chơi có liên quan tới xúc xắc. Giao thức DiceGameDelegate có thể được chấp nhận bởi bất kỳ kiểu nào để theo dõi quá trình của một DiceGame.
Dưới đây là một phiên bản của trò chời Snakes and Ladders game được giới thiệu đầu tiên trong Control Flow. Phiên bản này được điều chỉnh để sử dụng một thể hiện Dice cho các lần đổ xúc xắc của nó; để chấp nhận giao thức DiceGame; và để thông báo một DiceGameDelegate về tiến tình của nó:
class OneSnakesAndLadders: OneDiceGame {
let finalSquare = 25
let dice = Dice(sides: 6, generator: LinearCongruentialGenerator())
var square = 0
var board: [Int]
init() {
board = [Int](count: finalSquare + 1, repeatedValue: 0)
board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
}
var delegate: OneDiceGameDelegate?
func play() {
square = 0
delegate?.gameDidStart(self)
gameLoop: while square != finalSquare {
let diceRoll = dice.roll()
delegate?.game(self, didStartNewTurnWithDiceRoll: diceRoll)
switch square + diceRoll {
case finalSquare:
break gameLoop
case let newSquare where newSquare > finalSquare:
continue gameLoop
default:
square += diceRoll
square += board[square]
}
}
delegate?.gameDidEnd(self)
}
}
Để biết mô tả của trò chơi Snakes and Ladders, đọc phần Break của chương Control Flow:
Phiên bản này của trò chơi được bao bọc như một lớp gọi là OneSnakesAndLadders, nó chấp nhận giao thức OneDiceGame. Nó cung cấp một thuộc tính dice có thể lấy ra và một phương thức play để phù hợp với giao thức. (Thuộc tính dice được khai báo như là một thuộc tính không đổi bởi vì nó không cần thay đổi sau khi khởi tạo, và thuộc tính chỉ yêu cầu rằng nó có thể được lấy ra.)
Thiết lập trò chơi Snakes and Ladders được đặt trong khởi tạo init() của lớp. Tất cả logic trò chơi được chuyển vào trong phương thức play của giao thức, nó sử dụng thuộc tính yêu cầu dice của giao thức để cung cấp giá trị đổ xúc xắc của nó.
Lưu ý rằng, thuộc tính delegate được định nghĩa như một optional OneDiceGameDelegate, bởi vì một uỷ quyền không được yêu cầu để chơi trò chơi. Vì nó là một kiểu optional, thuộc tính delegate được tự động thiết lập với một giá trị ban đầu là nil. Sau đó, thể hiện trò chơi có tuỳ chọn để thiết lập thuộc tính với một uỷ quyền thích hợp.
OneDiceGameDelegate cung cấp ba phương thức cho việc theo dõi tiến trình của một trò chơi. Ba phương thức này từng được đưa vào trong logic trò chơi trong phương thức play bên trên, và được gọi khi một trò chơi mới bắt đầu, một lượt mới bắt đầu, hoặc trò chơi kết thúc.
Bởi vì thuộc tính delegate là một optional OneDiceGameDelegate, phương thức play sử dụng optional chaining mỗi lần nó gọi một phương thức trong uỷ quyền. Nếu thuộc tính delegate là nil, việc gọi uỷ quyền thất bại mà không có lỗi. Nếu thuộc tính delegate là non-nil, phương thức uỷ quyền được gọi, và được truyền thể hiện OneSnakesAndLadders như một tham số:
Ví dụ dưới đây trình bày một lớp gọi là OneDiceGameTracker, nó chấp nhận giao thức OneDiceGameDelegate:
class OneDiceGameTracker: OneDiceGameDelegate {
var numberOfTurns = 0
func gameDidStart(game: DiceGame) {
numberOfTurns = 0
if game is OneSnakesAndLadders {
println("Started a new game of Snakes and Ladders")
}
println("The game is using a \(game.dice.sides)-sided dice")
}
func game(game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int) {
++numberOfTurns
println("Rolled a \(diceRoll)")
}
func gameDidEnd(game: DiceGame) {
println(" game for \(numberOfTurns) turns")
}
}
OneDiceGameTracker thực hiện cả ba phương thức được yêu cầu bởi DiceGameDelegate. Nó sử dụng các phương thức này để theo dõi số lượt chơi một trò chơi thực hiện. Nó đặt lại một thuộc tính numberOfTurns với 0 khi trò chơi bắt đầu, tăng nó mỗi lần một lượt mới bắt đầu, và in ra tổng số lượt tới khi trò chơi kết thúc.
Thực hiện của gameDidStart trình bày ở trên, sử dụng tham số game để in một số thông tin chỉ dẫn của trò chơi để chơi. Tham số game có một kiểu là DiceGame, không phải là OneDiceGameTracker, và ví thể gameDidStart có thể truy cập và sử dụng chỉ các phương thức và các thuộc tính mà được thực hiện trong giao thức OneDiceGame. Trong ví dụ này, nó kiểm tra xem liệu game thực sự là một thể hiện của OneSnakesAndLadders đằng sau trình biên dịch, và in một thông báo thích hợp nếu như vậy.
gameDidStart cũng truy cập thuộc tính dice của tham số game được truyền vào. Vì game được biết đến để phù hợp với giao thức DiceGame, nó được đảm bảo để có một thuộc tính dice, và vì thế phương thức gameDidStart được phép để truy cập và in ra thuộc tính sides của xúc xắc, bất kể loại trò chơi nào được chơi.
Dưới đây là các OneDiceGameTracker trông như thế nào trong hành động:
let oneTracker = OneDiceGameTracker()
let game = OneSnakesAndLadders()
game.delegate = tracker
game.play()
// The game is using a 6-sided dice
// Rolled a 3
// Rolled a 5
// Rolled a 4
// Rolled a 5
// game for 4 turns
Adding Protocol Conformance with an Extension
Bạn có thể mở rộng một kiểu hiện có để chấp nhận và phù hợp một giao thức mới, thậm chú nếu bạn không có quyền truy cập vào mã nguồn cho các kiểu kiện có. Extensions có thể bổ sung các thuộc tính phương thức, và subscripts mới với một kiểu hiện có, và vì thế có thể bổ sung bất kỳ yêu cầu nào mà một giao thức có thể yêu cầu. Để biết thêm về extensions, đọc Extensions.
Chú ý
Các thể hiện hiện có của một kiểu tự động chấp nhận và phù hợp với một giao thức khi sự phù hợp đó được bổ sung vào khiểu của thể hiện trong một extension.
Ví dụ, giao thức này, gọi là OneTextRepresentable, có thể được thực hiện bằng bất kỳ kiểu nào mà có một cách để biểu diễn dưới dạng văn bản. Điều này có thể là một mô tả của bản thân nó, hoặc một phiên bản văn bẩn của trạng thái hiện tại của nó:
protocol OneTextRepresentable {
func asText() -> String
}
Dice từ trước đó có thể được mở rộng để chấp nhận và phù hợp với OneTextRepresentable:
extension Dice: OneTextRepresentable {
func asText() -> String {
return "A \(sides)-sided dice"
}
}
phần mở rộng này chấp nhận giao thức mới trong cách tương tự nếu như Dice từng cung cấp nó trong thực hiện ban đầu của nó. Tên giao thức được cung cấp sau tên kiểu, phân cách bằng dấu hai chấm, và một thực hiện của tất cả các yêu cầu của giao thức được cung cấp bên trong dấu ngoặc của extension.
Bất kỳ thể hiện Dice hiện giờ có thể được coi là OneTextRepresentable:
let d12 = Dice(sides: 12, generator: LinearCongruentialGenerator())
print(d12.asText())
// print "A 12-sided dice”
Tương tự, lớp trò chơi OneSnakesAndLadders có thể được mở rộng để chấp nhận và phù hợp với giao thức OneTextRepresentable:
extension OneSnakesAndLadders: OneTextRepresentable {
func asText() -> String {
return "A game of Snakes and Ladders \(finalSquare) squares"
}
}
println(game.asText())
// print "A game of Snakes and Ladders 25 squares"
Declaring Protocol Adoption with an Extension
Nếu một kiểu từng phù hợp với tất cả các yêu cầu của một giao thức, nhưng chưa từng tuyên bố rằng nó chấp nhận giao thức đó, bạn có thể làm cho nó chấp nận giao thức với một extension rỗng
struct OneHamster {
var name: String
func asText() -> String {
return "A hamster name is \(name)"
}
}
extension OneHamster: OneTextRepresentable {}
hể hiện của OneHamster hiện giờ có thể được sử dụng bất cứ nới đâu OneTextRepresentable là kiểu yêu cầu:
let simonTheHamster = Hamster(name: "Simon")
let somethingTextRepresentable: TextRepresentable = simonTheHamster
println(somethingTextRepresentable.asText())
// prints "A hamster named Simon"
lưu ý
Các kiểu không tự động chấp nhận một giao thức chỉ bằng cách thoả mãn các yêu cầu của nó. Chúng luôn phải khai báo rõ ràng áp dụng của chúng với giao thức.
All rights reserved