Design Pattern 1: Object-Oriented Concepts - Foundation for Understanding Complex Design Patterns
Download the complete Design Pattern series code from the design_pattern repo.
Introduction: The Foundation of Modern Software Design
Object-Oriented Programming (OOP) is the cornerstone of modern software development. Understanding the four core conceptsāEncapsulation, Inheritance, Polymorphism, and Abstractionāis essential for mastering design patterns and building robust, maintainable software systems.
Real-World Applications
Object-oriented concepts are fundamental to:
- Software Architecture: Building modular, scalable systems
- Design Patterns: Implementing proven solutions to common problems
- Framework Development: Creating reusable software components
- API Design: Designing clean, intuitive interfaces
- Database Design: Modeling complex data relationships
The Four Pillars of Object-Oriented Programming
The four core concepts of object-oriented design provide the foundation for understanding complex design patterns. Letās explore each concept with practical examples.
1. Encapsulation (å°č£)
Encapsulation is the practice of hiding internal implementation details within a class, exposing only necessary methods to users. This protects internal properties and methods from unauthorized access.
Real-World Analogy: When driving a car, you only need to know that pressing the gas pedal accelerates and pressing the brake stops the car. You donāt need to understand the engine, battery, or transmission detailsāthese are encapsulated under the hood.
Code Example
class BankAccount {
private var balance: Double = 0.0
private val accountNumber: String
constructor(accountNumber: String) {
this.accountNumber = accountNumber
}
fun deposit(amount: Double): Boolean {
if (amount > 0) {
balance += amount
return true
}
return false
}
fun withdraw(amount: Double): Boolean {
if (amount > 0 && balance >= amount) {
balance -= amount
return true
}
return false
}
fun getBalance(): Double = balance
fun getAccountNumber(): String = accountNumber
}
// Usage
val account = BankAccount("123456789")
account.deposit(1000.0)
println("Balance: ${account.getBalance()}") // 1000.0
// account.balance = -500 // Compilation error - private access
2. Inheritance (ē¹¼ęæ)
Inheritance allows a subclass to inherit properties and methods from a parent class, enabling code reuse and establishing hierarchical relationships.
Real-World Analogy: In nature, dogs and cats are both animals that can breathe and move; flowers and trees are both plants that can perform photosynthesis. This represents the concept of inheritance.
Code Example
open class Animal {
protected var name: String = ""
protected var age: Int = 0
open fun makeSound() {
println("Some animal sound")
}
open fun move() {
println("Moving like an animal")
}
}
class Dog : Animal() {
private var breed: String = ""
fun setBreed(breed: String) {
this.breed = breed
}
override fun makeSound() {
println("Woof! Woof!")
}
override fun move() {
println("Running on four legs")
}
fun fetch() {
println("Fetching the ball")
}
}
class Cat : Animal() {
private var color: String = ""
fun setColor(color: String) {
this.color = color
}
override fun makeSound() {
println("Meow! Meow!")
}
override fun move() {
println("Walking gracefully")
}
fun climb() {
println("Climbing the tree")
}
}
// Usage
val dog = Dog()
dog.makeSound() // Woof! Woof!
dog.fetch() // Fetching the ball
val cat = Cat()
cat.makeSound() // Meow! Meow!
cat.climb() // Climbing the tree
3. Polymorphism (å¤å)
Polymorphism provides a unified interface for different classes, allowing objects of different types to be treated uniformly.
Real-World Analogy: Consider the iPhone 6Sāwhether the chip is manufactured by TSMC or Samsung, users get the same iPhone 6S functionality. This is polymorphism in action.
Code Example
interface PaymentMethod {
fun processPayment(amount: Double): Boolean
fun getPaymentType(): String
}
class CreditCard : PaymentMethod {
private var cardNumber: String = ""
private var expiryDate: String = ""
fun setCardDetails(cardNumber: String, expiryDate: String) {
this.cardNumber = cardNumber
this.expiryDate = expiryDate
}
override fun processPayment(amount: Double): Boolean {
println("Processing $amount via Credit Card")
return true
}
override fun getPaymentType(): String = "Credit Card"
}
class PayPal : PaymentMethod {
private var email: String = ""
fun setEmail(email: String) {
this.email = email
}
override fun processPayment(amount: Double): Boolean {
println("Processing $amount via PayPal")
return true
}
override fun getPaymentType(): String = "PayPal"
}
class PaymentProcessor {
fun processPayment(paymentMethod: PaymentMethod, amount: Double): Boolean {
println("Using ${paymentMethod.getPaymentType()}")
return paymentMethod.processPayment(amount)
}
}
// Usage
val processor = PaymentProcessor()
val creditCard = CreditCard()
val paypal = PayPal()
processor.processPayment(creditCard, 100.0) // Using Credit Card
processor.processPayment(paypal, 50.0) // Using PayPal
4. Abstraction (ę½č±”)
Abstraction hides implementation details and provides only essential features to users through interfaces or abstract classes.
Real-World Analogy: When you install apps on your phone, āAppā is an abstract name for various applications. When you buy fruits at the market, āFruitā is an abstract term for apples, bananas, etc.
Code Example
abstract class Database {
abstract fun connect(): Boolean
abstract fun disconnect()
abstract fun executeQuery(query: String): List<Map<String, Any>>
fun isConnected(): Boolean {
// Common implementation for all databases
return true
}
}
class MySQLDatabase : Database() {
override fun connect(): Boolean {
println("Connecting to MySQL database")
return true
}
override fun disconnect() {
println("Disconnecting from MySQL database")
}
override fun executeQuery(query: String): List<Map<String, Any>> {
println("Executing query on MySQL: $query")
return emptyList()
}
}
class PostgreSQLDatabase : Database() {
override fun connect(): Boolean {
println("Connecting to PostgreSQL database")
return true
}
override fun disconnect() {
println("Disconnecting from PostgreSQL database")
}
override fun executeQuery(query: String): List<Map<String, Any>> {
println("Executing query on PostgreSQL: $query")
return emptyList()
}
}
class DatabaseManager {
fun performOperation(database: Database, query: String): List<Map<String, Any>> {
database.connect()
val result = database.executeQuery(query)
database.disconnect()
return result
}
}
// Usage
val manager = DatabaseManager()
val mysql = MySQLDatabase()
val postgres = PostgreSQLDatabase()
manager.performOperation(mysql, "SELECT * FROM users")
manager.performOperation(postgres, "SELECT * FROM products")
Advanced Concepts and Best Practices
1. Composition vs Inheritance
// Prefer composition over inheritance
class Engine {
fun start() = println("Engine started")
fun stop() = println("Engine stopped")
}
class Car {
private val engine = Engine() // Composition
fun start() = engine.start()
fun stop() = engine.stop()
}
// vs Inheritance (less flexible)
open class Vehicle {
fun start() = println("Vehicle started")
fun stop() = println("Vehicle stopped")
}
class Car : Vehicle() // Inheritance - less flexible
2. Interface Segregation Principle
// Good: Segregated interfaces
interface Readable {
fun read(): String
}
interface Writable {
fun write(data: String)
}
interface Deletable {
fun delete()
}
// Classes implement only what they need
class ReadOnlyFile : Readable {
override fun read(): String = "File content"
}
class WritableFile : Readable, Writable {
override fun read(): String = "File content"
override fun write(data: String) = println("Writing: $data")
}
// Avoid: Fat interface
interface FileOperations {
fun read(): String
fun write(data: String)
fun delete()
fun compress()
fun encrypt()
}
3. Dependency Inversion Principle
// Good: Depend on abstractions
interface Logger {
fun log(message: String)
}
class ConsoleLogger : Logger {
override fun log(message: String) = println("Console: $message")
}
class FileLogger : Logger {
override fun log(message: String) = println("File: $message")
}
class UserService(private val logger: Logger) {
fun createUser(username: String) {
logger.log("Creating user: $username")
// User creation logic
}
}
// Usage
val consoleLogger = ConsoleLogger()
val fileLogger = FileLogger()
val userService1 = UserService(consoleLogger)
val userService2 = UserService(fileLogger)
Performance Considerations
Concept | Memory Usage | Performance | Flexibility | Maintainability |
---|---|---|---|---|
Encapsulation | Low | High | Medium | High |
Inheritance | Medium | Medium | Low | Medium |
Polymorphism | Medium | Medium | High | High |
Abstraction | Medium | Medium | High | High |
Common Anti-Patterns to Avoid
1. Inheritance Abuse
// Avoid: Deep inheritance hierarchies
class Animal
class Mammal : Animal()
class Dog : Mammal()
class GoldenRetriever : Dog()
class MyGoldenRetriever : GoldenRetriever() // Too deep!
2. Breaking Encapsulation
// Avoid: Exposing internal state
class BankAccount {
var balance: Double = 0.0 // Public - breaks encapsulation
var accountNumber: String = "" // Public - breaks encapsulation
}
3. Tight Coupling
// Avoid: Direct dependencies
class UserService {
private val database = MySQLDatabase() // Tight coupling
fun createUser(user: User) {
database.connect()
// Implementation
}
}
Related Design Patterns
- Factory Method: Uses polymorphism to create objects
- Strategy: Uses abstraction to define algorithms
- Decorator: Uses composition and inheritance
- Adapter: Uses abstraction to adapt interfaces
Conclusion
Understanding object-oriented concepts is essential for mastering design patterns and building robust software systems. Key benefits include:
- Modularity: Code is organized into logical, reusable components
- Maintainability: Changes are localized and easier to implement
- Extensibility: New features can be added without modifying existing code
- Reusability: Components can be reused across different parts of the application
These concepts form the foundation for understanding and implementing the 23 classic design patterns that solve common software design problems.
Related Articles
Enjoy Reading This Article?
Here are some more articles you might like to read next: