Protocol-Oriented Programming
Introduction to Protocol-Oriented Programming
Core Concepts
protocol Identifiable {
var id: String { get }
func identify()
}
extension Identifiable {
func identify() {
print("My ID is \(id)")
}
}
struct User: Identifiable {
let id: String
}
Protocol Composition
Combining Protocols
protocol Named {
var name: String { get }
}
protocol Aged {
var age: Int { get }
}
typealias Person = Named & Aged
struct Employee: Person {
let name: String
let age: Int
}
Protocol Extensions
Default Implementations
protocol Drawable {
func draw()
var size: CGSize { get }
}
extension Drawable {
func draw() {
print("Drawing with size: \(size)")
}
func scale(by factor: CGFloat) -> CGSize {
return CGSize(width: size.width * factor,
height: size.height * factor)
}
}
Type Constraints
Associated Types
protocol Collection {
associatedtype Element
var items: [Element] { get set }
mutating func add(_ item: Element)
}
struct Stack<T>: Collection {
typealias Element = T
var items: [T] = []
mutating func add(_ item: T) {
items.append(item)
}
}
Best Practices
1. Protocol Inheritance
protocol Animal {
var species: String { get }
}
protocol Pet: Animal {
var name: String { get }
var owner: String { get }
}
struct Dog: Pet {
let species: String = "Canis lupus familiaris"
let name: String
let owner: String
}
2. Protocol Witnesses
protocol DataProvider {
func fetchData() async throws -> Data
}
struct NetworkDataProvider: DataProvider {
let url: URL
func fetchData() async throws -> Data {
let (data, _) = try await URLSession.shared.data(from: url)
return data
}
}
Common Patterns
1. Dependency Injection
protocol APIClient {
func fetch<T: Decodable>(_ type: T.Type, from endpoint: String) async throws -> T
}
class UserService {
private let apiClient: APIClient
init(apiClient: APIClient) {
self.apiClient = apiClient
}
func fetchUser(id: String) async throws -> User {
return try await apiClient.fetch(User.self, from: "/users/\(id)")
}
}
2. Protocol-Based Validation
protocol Validatable {
func validate() throws
}
struct ValidationError: Error {
let message: String
}
struct UserCredentials: Validatable {
let username: String
let password: String
func validate() throws {
guard username.count >= 4 else {
throw ValidationError(message: "Username too short")
}
guard password.count >= 8 else {
throw ValidationError(message: "Password too short")
}
}
}
Next Steps
- Study Testing and Debugging
- Review Memory Management