How should I design protocol hierarchies without overusing inheritance?

In Swift, designing protocol hierarchies effectively requires careful consideration to avoid overusing inheritance. Utilizing protocols promotes code reusability, separation of concerns, and flexibility, while minimizing the downsides of deep inheritance trees.
Swift, protocols, inheritance, protocol design, code reusability, software design
// Define the base protocol protocol Drawable { func draw() } // Extend the Drawable protocol to add functionality protocol ColorfulDrawable: Drawable { var color: String { get } } // Implement a class that conforms to the ColorfulDrawable protocol class Circle: ColorfulDrawable { var radius: Double var color: String init(radius: Double, color: String) { self.radius = radius self.color = color } func draw() { print("Drawing a \(color) circle with radius \(radius)") } } // Implement another class conforming to the Drawable protocol class Rectangle: Drawable { var width: Double var height: Double init(width: Double, height: Double) { self.width = width self.height = height } func draw() { print("Drawing a rectangle of width: \(width) and height: \(height)") } }

Swift protocols inheritance protocol design code reusability software design