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.
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.”
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.

