08: Interfaces and Protocols

Audience: All Time: 120 minutes Prerequisites: 07-Classes-and-Instances You'll learn: Define interfaces, implement contracts, polymorphism, real-world patterns


The Big Picture

Interfaces define contracts—promises about what an object can do, without specifying how. Instead of depending on concrete classes, you depend on interfaces. This enables: - Swappable implementations — Change how something works without changing callers - Polymorphism — Treat different objects the same way - Testing — Mock implementations easily - Flexibility — Add new implementations without modifying existing code


Defining Interfaces

Simple Interface

// file: 08_interface_basic.zbr

// teaches: interface definition // chapter: 08-Interfaces-and-Protocols

interface Animal def speak as str def move

class Dog implements Animal def speak as str return "Woof!"

def move print "Running on four legs"

class Bird implements Animal def speak as str return "Tweet!"

def move print "Flying through the air"

class Main shared def main var dog as Animal = Dog() print dog.speak() // Woof! dog.move() // Running on four legs

var bird as Animal = Bird() print bird.speak() // Tweet! bird.move() // Flying through the air

Key points: - interface Animal — Define what any animal must do - implements Animal — Promise to implement all methods - Treat objects as their interface type (var dog as Animal)

Multiple Methods

// file: 08_interface_methods.zbr

// teaches: interface with multiple methods // chapter: 08-Interfaces-and-Protocols

interface PaymentProcessor def process(amount as float) as bool def refund(transaction_id as str) as bool def get_status(transaction_id as str) as str

class CreditCardProcessor implements PaymentProcessor def process(amount as float) as bool print "Processing credit card: ${amount}" return true

def refund(transaction_id as str) as bool print "Refunding transaction: ${transaction_id}" return true

def get_status(transaction_id as str) as str return "completed"

class PayPalProcessor implements PaymentProcessor def process(amount as float) as bool print "Processing PayPal: ${amount}" return true

def refund(transaction_id as str) as bool print "PayPal refund: ${transaction_id}" return true

def get_status(transaction_id as str) as str return "pending"


Polymorphism in Action

Using Different Implementations

// file: 08_polymorphism.zbr

// teaches: polymorphic behavior // chapter: 08-Interfaces-and-Protocols

class Store shared def process_payment(processor as PaymentProcessor, amount as float) if processor.process(amount) print "Payment successful" else print "Payment failed"

class Main shared def main var cc_processor = CreditCardProcessor() var paypal_processor = PayPalProcessor()

// Same code, different behavior Store.process_payment(cc_processor, 99.99) Store.process_payment(paypal_processor, 49.99)

Collections of Interface Types

// file: 08_collection_interface.zbr

// teaches: storing different implementations // chapter: 08-Interfaces-and-Protocols

class Zoo var animals as List(Animal) = List()

def add_animal(animal as Animal) animals.add(animal)

def make_them_speak for animal in animals print animal.speak()

def exercise_all for animal in animals animal.move()

class Main shared def main var zoo = Zoo() zoo.add_animal(Dog()) zoo.add_animal(Bird()) zoo.add_animal(Dog())

zoo.make_them_speak() zoo.exercise_all()


Real World: Logging System

// file: 08_logger_system.zbr

// teaches: realistic interface use // chapter: 08-Interfaces-and-Protocols

interface Logger def debug(message as str) def info(message as str) def warn(message as str) def error(message as str)

class ConsoleLogger implements Logger def debug(message as str) print "[DEBUG] ${message}"

def info(message as str) print "[INFO] ${message}"

def warn(message as str) print "[WARN] ${message}"

def error(message as str) print "[ERROR] ${message}"

class FileLogger implements Logger def debug(message as str) # Write to file: [DEBUG] message

def info(message as str) # Write to file: [INFO] message

def warn(message as str) # Write to file: [WARN] message

def error(message as str) # Write to file: [ERROR] message

class Application var logger as Logger

def set_logger(l as Logger) logger = l

def do_work logger.info("Starting work") # Do work logger.info("Work complete")

class Main shared def main var app = Application()

# Use console logger app.set_logger(ConsoleLogger()) app.do_work()

# Switch to file logger (same code, different output) app.set_logger(FileLogger()) app.do_work()


Common Patterns

Strategy Pattern

interface SortStrategy

def sort(items as List(int))

class AscendingSort implements SortStrategy def sort(items as List(int)) # Sort ascending

class DescendingSort implements SortStrategy def sort(items as List(int)) # Sort descending

class Sorter var strategy as SortStrategy

def set_strategy(s as SortStrategy) strategy = s

def sort(items as List(int)) strategy.sort(items)

Adapter Pattern

interface NewSystem

def process(data as str)

class OldSystem def old_process(input as str) # Old implementation

class OldSystemAdapter implements NewSystem var old_system as OldSystem = OldSystem()

def process(data as str) // Adapt new interface to old system old_system.old_process(data)


If you're new to programming

> An interface is like a promise or contract. When you say a class "implements" an interface, you promise that it has all the methods the interface requires. > > This lets you write code that works with any object that implements that interface, without knowing exactly which class it is. > > Polymorphism means "many forms"—the same code can work with different types of objects.

If you know Python

<h1>Python (using duck typing)</h1>

class Dog: def speak(self): return "Woof!"

class Bird: def speak(self): return "Tweet!"

def get_sound(animal): return animal.speak()

<h1>Zebra (using interfaces)</h1> interface Animal def speak as str

class Dog implements Animal def speak as str return "Woof!"

class Bird implements Animal def speak as str return "Tweet!"

class Main shared def get_sound(animal as Animal) as str return animal.speak()

Python relies on duck typing ("if it quacks like a duck"). Zebra makes the contract explicit with interfaces.


Common Mistakes

> ❌ Mistake: Not implementing all interface methods > >

> interface Animal >     def speak as str >     def move > > class Dog >     implements Animal >         def speak as str >             return "Woof!" >         # ❌ Missing: def move > 
> > 💡 Why: The compiler requires all methods. You're breaking the contract. > > ✅ Better: >
> class Dog >     implements Animal >         def speak as str >             return "Woof!" >         def move >             print "Running" > 

> ❌ Mistake: Wrong method signature > >

> interface PaymentProcessor >     def process(amount as float) as bool > > class CreditCard >     implements PaymentProcessor >         def process(amount as int) as bool  # ❌ int, not float >             return true > 
> > ✅ Better: >
> class CreditCard >     implements PaymentProcessor >         def process(amount as float) as bool  # ✅ Matches interface >             return true > 

> ❌ Mistake: Forgetting to declare implementation > >

> class Dog  # ❌ Doesn't say implements Animal >     def speak as str >         return "Woof!" > 
> > ✅ Better: >
> class Dog >     implements Animal  # ✅ Explicit contract >         def speak as str >             return "Woof!" > 


Exercises

Exercise 1: Shape Interface

Create an interface for shapes and multiple implementations:

Solution

interface Shape

def area as float def perimeter as float

class Circle var radius as float = 0.0 implements Shape def area as float return 3.14 radius radius def perimeter as float return 2.0 3.14 radius

class Rectangle var width as float = 0.0 var height as float = 0.0 implements Shape def area as float return width * height def perimeter as float return 2.0 * (width + height)

class Main shared def print_shape_info(shape as Shape) print "Area: ${shape.area()}" print "Perimeter: ${shape.perimeter()}"

def main var circle = Circle() circle.radius = 5.0 print_shape_info(circle)

var rect = Rectangle() rect.width = 10.0 rect.height = 5.0 print_shape_info(rect)

Exercise 2: Database Interface

Create a database interface with multiple implementations:

Solution

interface Database

def save(key as str, value as str) as bool def load(key as str) as str? def delete(key as str) as bool

class MemoryDatabase var data as HashMap(str, str) = HashMap() implements Database def save(key as str, value as str) as bool data.put(key, value) return true def load(key as str) as str? if data.contains(key) return data.fetch(key) return nil def delete(key as str) as bool data.remove(key) return true

class Main shared def main var db as Database = MemoryDatabase() db.save("user1", "Alice") db.save("user2", "Bob")

var user = db.load("user1") if user != nil print "Found: ${user}"

db.delete("user1")

Exercise 3: Document Processor

Create an interface for document processors:

Solution

interface DocumentProcessor

def process(content as str) as str def validate(content as str) as bool

class MarkdownProcessor implements DocumentProcessor def process(content as str) as str # Convert markdown to HTML return "<html>${content}</html>" def validate(content as str) as bool return content.len > 0

class JSONValidator implements DocumentProcessor def process(content as str) as str return content # Already valid JSON def validate(content as str) as bool return content.contains("{") and content.contains("}")

class Main shared def process_document(processor as DocumentProcessor, doc as str) if processor.validate(doc) var result = processor.process(doc) print "Processed: ${result}" else print "Invalid document"

def main var md = MarkdownProcessor() process_document(md, "# Hello")

var json = JSONValidator() process_document(json, "{}")


Next Steps

- → 09-Inheritance — Extending classes - → 14-Contracts — More complex interfaces - 🏋️ Project-2-HTTP-Server — Interfaces for handlers and middleware


Key Takeaways

- Interfaces define contracts — What methods must exist, not how they work - Polymorphism lets you write code once, work with many types - Swap implementations without changing callers - Testing is easier — Mock implementations implement the interface - Makes code flexible — Add new implementations without modifying existing code - Forces clear design — Interfaces make relationships explicit


Next: Head to 09-Inheritance to extend classes and share code across hierarchies.