Protocol composition
Adding functionalities by parts.
Protocol composition
Adding functionalities by parts.
0
0
Checkbox to mark video as read
Mark as read

As we've explored in Protocol I article, protocols define a blueprint of methods, properties, and other requirements for particular functionalities. When developing complex applications, you may want to combine multiple protocols to create more flexible and reusable components. This is where protocol composition becomes extremely useful.

What is Protocol Composition?

Protocol composition is the ability to combine multiple protocols into a single type requirement. By doing this, you can require that a type conforms to multiple protocols without defining a new protocol or type. This approach keeps your codebase lightweight and flexible, making it easier to add, change, or remove requirements as your application grows.

In Swift, you can create a protocol composition by using the & operator. This operator lets you specify that a type should conform to multiple protocols, such as ProtocolA & ProtocolB. Here’s a simple example:

protocol Drivable {
    func drive()
}

protocol Fuelable {
    var fuelLevel: Int { get }
}

func startTrip(vehicle: Drivable & Fuelable) {
    if vehicle.fuelLevel > 0 {
        vehicle.drive()
        print("Trip started!")
    } else {
        print("Not enough fuel to start the trip.")
    }
}

In this example, startTrip accepts any type that conforms to both Drivable and Fuelable. The function checks the fuel level and starts the trip only if there is enough fuel, utilizing the methods and properties from both protocols.

So the objects we can use here have to conform both protocols:

struct Plane: Drivable, Fuelable {
    let fuelLevel: Int = 100

    func drive() {
        print("Driving plane")
    }
}

When to Use Protocol Composition

Protocol composition is especially useful when you want to define a function or method that operates on types with multiple behaviors, but without introducing a new, single protocol that may be too specific. Some common scenarios include:

  • Combining Similar Behavior: If two protocols provide complementary behavior, like data-fetching and parsing, you might want a function to operate on objects that handle both tasks.
  • Decoupling Code: Protocol composition allows you to create flexible APIs without needing a rigid class hierarchy.
  • Reducing Complexity: Protocol composition can simplify the type requirements in generic functions or methods by avoiding complex class inheritance.

Using Protocol Composition with Associated Types

If a protocol has an associated type, it cannot be directly composed with other protocols. However, by introducing generic constraints, you can work around this limitation to compose protocols that have associated types. Here’s an example:

protocol Identifiable {
    associatedtype ID
    var id: ID { get }
}

protocol Displayable {
    var displayName: String { get }
}

struct User: Identifiable, Displayable {
    var id: Int
    var displayName: String
}

func showDisplayName<T: Identifiable & Displayable>(for item: T) {
    print("Displaying: \(item.displayName)")
}

In this example, showDisplayName can be used with any type that conforms to both Identifiable and Displayable, making it useful for displaying information for different identifiable items.

Protocol Composition with Extensions

You can also extend a protocol composition to add default implementations or additional methods. For example:

protocol Movable {
    func move()
}

protocol Stoppable {
    func stop()
}

extension Movable & Stoppable {
    func startMovingAndThenStop() {
        move()
        print("Moving...")
        stop()
        print("Stopped.")
    }
}

Now, any type that conforms to both Movable and Stoppable will automatically get the startMovingAndThenStop method, reducing code duplication and promoting reuse.

Benefits of Protocol Composition

Protocol composition is a powerful tool in Swift that brings numerous benefits:

  • Increased Code Reusability: You can build flexible APIs that operate on types conforming to multiple protocols, enabling you to write reusable code for different types.
  • Improved Type Safety: By specifying multiple protocol conformances, you ensure that types meet all required functionality, enhancing type safety in your code.
  • Decoupled and Modular Design: Protocol composition encourages a more modular approach, allowing you to decouple implementations and design smaller, specialized protocols.

0 Comments

Join the community to comment

Be the first to comment

Accept Cookies

We use cookies to collect and analyze information on site performance and usage, in order to provide you with better service.

Check our Privacy Policy