Protocols and Protocol-Oriented Programming
Defining shared requirements with protocol, and why Swift encourages composing behavior over inheriting it.
읽는 데 2분
A protocol defines a set of properties and methods a type must implement, without providing the implementation itself — similar to an interface in languages like Java or TypeScript. Any struct, class, or enum can conform to a protocol by implementing its requirements.
Defining and conforming to a protocol
protocol Greetable {
var name: String { get }
func greet() -> String
}
struct Person: Greetable {
var name: String
func greet() -> String {
return "Hi, I'm \(name)."
}
}
struct Robot: Greetable {
var name: String
func greet() -> String {
return "GREETINGS. I AM \(name.uppercased())."
}
}Person and Robot share no inheritance relationship at all, but both satisfy Greetable. That means code can work with either through the shared protocol, without caring which concrete type it actually received:
func introduce(_ thing: Greetable) {
print(thing.greet())
}
introduce(Person(name: "Ada")) // "Hi, I'm Ada."
introduce(Robot(name: "T-800")) // "GREETINGS. I AM T-800."Why "protocol-oriented," not just "object-oriented"
Apple explicitly pitches Swift as favoring protocol-oriented programming over classic class inheritance, and the reason comes down to a limitation of inheritance: a class can only inherit from one superclass, but a type can conform to many protocols at once.
protocol Flyable {
func fly()
}
protocol Swimmable {
func swim()
}
struct Duck: Flyable, Swimmable {
func fly() { print("Flying") }
func swim() { print("Swimming") }
}Duck can't cleanly inherit from both a Bird class and a Fish class — single inheritance forbids it. But conforming to both Flyable and Swimmable composes exactly the behaviors Duck needs, without forcing an artificial class hierarchy just to share code.
Protocol extensions: default implementations
A protocol can supply a default implementation for its own requirements, via an extension (covered fully in the next lesson) — so conforming types only need to override what's actually different for them:
extension Greetable {
func greet() -> String {
return "Hello, I'm \(name)." // a default every conformer gets for free
}
}
struct Guest: Greetable {
var name: String
// no greet() needed — it uses the protocol's default
}
print(Guest(name: "Visitor").greet()) // "Hello, I'm Visitor."This pattern — protocols plus default implementations — is how much of Swift's own standard library shares behavior across wildly different types (Array, String, and Set all conform to Sequence and all get map, filter, and for-in support from it) without a single shared superclass anywhere in sight.