Week 3 Lecture 5

Abstraction and Encapsulation

  • Abstraction:

    • Definition: Abstraction is the process of hiding the complexity of the code and exposing only the essential features to the user. It allows users to interact with systems without needing to understand the intricate workings behind them.

    • Functions of Abstraction:

    • Hides complex implementation details.

    • Provides a simplified interface for users.

    • Encourages separation of concerns.

  • Encapsulation:

    • Definition: Encapsulation is the practice of restricting access to certain components of an object and controlling the available methods of interaction. It guards the data contained within an object and prevents unauthorized access and modification.

    • Example:

    • Data is categorized with underscores to indicate access levels:

      • No underscore: Public

      • One underscore: Protected

      • Two underscores: Private

Inheritance

  • Definition: Inheritance is a fundamental concept in object-oriented programming (OOP) that allows a class (known as a child or subclass) to inherit properties and behaviors (methods) from another class (known as a parent or superclass).

  • Purpose of Inheritance:

    • Three main reasons for using inheritance are:

    1. Code Reusability: Minimizes code duplication by allowing the subclass to use the methods and attributes of the superclass.

    2. Logical Identities: Mirrors real-world relationships, making code more intuitive and easier to understand.

    3. Extensibility: Enhances the functionality of existing code without modifying it.

  • Definitions of Relationships:

    • "Is a" relationship is essential for establishing inheritance.

    • Examples of "is a" relationships:

    • A student is a person.

    • A car is a vehicle.

    • A rectangle is a shape.

    • A dog is an animal.

Syntax of Inheritance

  • Overview: Inheritance syntax typically involves specifying parent classes within the child class declaration.

  • Example Syntax:
    ```python
    class Parent:
    def function_one(self):
    pass
    class Child(Parent):
    pass

  - Functionality is inherited from the Parent class into the Child class.

# Example of Inheritance Implementation  

- Consider the following code example illustrating how inheritance functions through classes:

python
class Animal:
def feed(self):
pass
class Dog(Animal):
def bark(self):
pass
d = Dog()
d.feed() # Inherited from Animal
d.bark() # Specific to Dog

# Method Overriding  

- Definition: Method overriding occurs when a child class defines a method that has the same name as a method in its parent class, allowing it to provide specific implementation for that method.
- Example of Method Overriding:  

python
class Animal:
def sound(self):
return "Some sound"
class Dog(Animal):
def sound(self):
return "Bark"

  - In this example, calling the sound method on Dog would output "Bark", overriding the parent implementation.

# Using `super()` Function  

- Definition: The `super()` function is used in a child class to call methods from the parent class, particularly when method overriding is involved. This allows the child class to extend or modify the behavior of the inherited method rather than completely replacing it.
- Example Syntax:  

python
class Parent:
def greet(self):
return "Hello"
class Child(Parent):
def greet(self):
return super().greet() + " from Child"

# When Not to Use Inheritance

- Important Guidelines:
  - Avoid inheritance when there is no "is a" relationship.
  - Refrain from using inheritance if the code needs to change frequently, as it complicates maintenance.
  - If multiple sources reuse code, avoid inheritance to prevent redundancy.

# Common Mistakes with Inheritance

- Frequent errors include:
  - Misusing the `super()` function without extending functionality properly.
  - Attempting to use inheritance when composition would be more appropriate.
  - Forgetting to verify the existence of an "is a" relationship.

# Examples of Real-World Applications of Inheritance

- Common real-world applications:  
  - Vehicles (Cars, Bikes inheriting from Vehicle Classes)
  - Bank Accounts (Checking and Savings inheriting from Account Classes)
  - Shapes (Rectangle, Circle inheriting from Shape classes)
  - UI Elements in programming frameworks, allowing reuse of basic components.

# Polymorphism 

- Definition: Polymorphism allows objects of different classes to be treated as objects of a common superclass. It enhances code flexibility and usability through the concept of having the same method name behave differently across different classes.
- Example Analogy:  
  - Humans speak, Dogs bark, and Cats meow, demonstrating polymorphism where the same action (speak) results in different behaviors based on the object type.
- Practical Outcome:  
  - Using for-loops with polymorphic behavior allows us to interact with different objects smoothly:  

python
class Animal:
def speak(self):
pass
class Cat(Animal):
def speak(self):
return "Meow"
class Dog(Animal):
def speak(self):
return "Bark"
animals = [Cat(), Dog()]
for pet in animals:
print(pet.speak()) # Outputs: Meow, Bark
```

Conclusion

  • Emphasized Practices:

    • Continual learning and practice are fundamental to mastering these OOP principles.

    • Always verify relationships in your architecture—especially when using inheritance or polymorphism— for effective and comprehensible code.

  • Encouragement for Exercises:

    • Regularly practice coding tasks that involve inheritance and polymorphism to reinforce understanding and application of these concepts.