Software Design

Protocol-Oriented Programming (POP): Going Beyond OOP's Limits

Protocol-oriented programming (POP) designs types around what they can do, not what they inherit. This article covers where inheritance falls short, how to solve it by composing protocols, and when to avoid it, with Swift examples.

4 min read
Cover image for Protocol-Oriented Programming (POP): Going Beyond OOP's Limits

If you’ve extended functionality through class inheritance, you’ve probably seen the parent class grow increasingly bloated. Keep pushing shared methods into the superclass, and eventually you face a huge parent class you’re afraid to touch.

Protocol-oriented programming (POP) goes beyond OOP’s limits at exactly this point. POP designs types around “what they can do,” not “what they inherit.” Instead of a vertical inheritance hierarchy, you compose and attach the capabilities you need as protocols.

This article examines which OOP limitations led to POP, how it differs in real Swift code, and when you should or should not use it.


Where Does OOP Inheritance Hit a Wall?

Object-oriented inheritance is undeniably powerful. But it often hits walls in three areas.

First, the limit of single inheritance. A Swift class can have only one parent. If you want a type that supports both networking and caching, inheritance alone cannot provide the answer.

Second, the bloated base-class problem. When shared functionality is concentrated in the parent, children inherit every method, including those they never use.

Third, compatibility with value types. Swift’s struct and enum cannot inherit at all. Yet most of the Swift standard library is built from value types.

In other words, an inheritance-only design makes it difficult to benefit from the value types Swift encourages.


What Is Protocol-Oriented Programming (POP)?

POP became widely known in 2015, when Apple declared at WWDC that “Swift is a protocol-oriented language.”

The key is that protocols can provide default implementations. With protocol extensions, you can distribute actual behavior instead of defining only an interface.

For example:

protocol Greetable {
    var name: String { get }
}

extension Greetable {
    func greet() -> String {
        return "Hello, \(name)I am"
    }
}

struct Person: Greetable {
    let name: String
}

print(Person(name: "Jihoon").greet())
// Output: Hello, I am Jihoon

Simply conforming to Greetable gives you greet() for free. The functionality is distributed to a struct without inheritance.

You can also conform to multiple protocols at once. Define networking and caching as separate protocols, then attach only the capabilities each type needs.

Class diagram showing Person conforming to the Greetable and Cacheable protocols
One capability per protocol; attach only what you need.

OOP Inheritance vs. POP: What’s the Difference?

Here’s a summary of the differences:

Category OOP inheritance POP
Code reuse Inherited from a parent class Composed through protocol extensions
Type relationship Vertical (is-a) Horizontal (can-do)
Multiple conformance Single inheritance only Conformance to multiple protocols simultaneously
Value-type support struct/enum unsupported Both struct/enum supported
Coupling Parent and child tightly coupled Loosely separated by capability

In short:

Inheritance says, “You are something,” while a protocol says, “You can do something.”

Xcode screen with an OOP-versus-POP comparison table floating above a desk with coffee
In situations like this, I think first, "Can this be split into protocols?" rather than "Start with inheritance."

When Should You Use or Avoid POP?

POP is not a silver bullet. Choose it according to the situation.

Situation Guidance
Share common functionality across value types (struct/enum) POP is the right choice
Combine different capabilities POP is advantageous
A clear hierarchy (animal-mammal-dog) already exists Inheritance is perfectly suitable
Objects where shared references are central (e.g., view controllers) Class inheritance is natural
Protocols are split so finely that they are hard to trace Over-abstraction; reconsider

The rule of thumb is simple: use POP when you need value types and capability composition; use inheritance when you need a clear hierarchy and shared references. They are not opposing choices, but tools to use together as the situation demands.

This Is How It Comes Up in Interviews

Q. Which limitations of OOP does POP solve?

It addresses single inheritance and bloated base classes. You can combine multiple protocols to attach only the capabilities you need, and use protocol extensions to provide default implementations to value types such as struct/enum, which cannot inherit.

Q. What is the difference between protocol extensions and class inheritance?

Inheritance creates a vertical is-a relationship and allows only one parent, while protocols create a horizontal can-do relationship and allow simultaneous conformance to multiple protocols. Inheritance also applies only to reference-type classes, whereas protocols apply to value types as well.


This is not a binary choice where inheritance is bad and protocols are good. But when developing with Swift, try asking “Can this be split into protocols?” before defaulting to inheritance. That one habit can lead to much more flexible code.

Continue reading