Abstraction is a fundamental concept in Object-Oriented Programming (OOP), which means simplifying complex systems by modelling classes based on essential features and behaviors while obscuring unnecessary details. It provides a high-level overview of an entity, emphasising what it does rather than how it does it. Let's explore abstraction using the User, AdminUser, and ManagerUser classes:
Abstract Base Class: User
from abc import ABC, abstractmethod
class AbstractUser(ABC):
def __init__(self, user_id, username, email, password):
self._user_id = user_id
self._username = username
self._email = email
self._password = password
self._is_authenticated = False
@abstractmethod
def authenticate(self, entered_password):
pass # Abstract method, concrete implementation in subclasses
@abstractmethod
def get_username(self):
pass
@abstractmethod
def get_email(self):
pass
@abstractmethod
def set_email(self, new_email):
pass
@abstractmethod
def get_id(self):
pass
@abstractmethod
def is_authenticated(self):
pass
def display_info(self):
print(f"User ID: {self._user_id}, Username: {self._username}, Email: {self._email}")
It's not strictly necessary to create an abstract base class for subclasses in Python, but doing so can have several advantages:
Python is a dynamically typed language, and it doesn't enforce strict adherence to abstract classes. Subclasses can choose not to inherit from an abstract base class or may not provide an implementation for all abstract methods.
In the example we discussed earlier, using an abstract base class (User
) with an abstract method (display_info
) was a choice made to emphasize the importance of providing a consistent interface for subclasses. Depending on your specific use case and design preferences, you may choose to use or omit abstract base classes.
Abstract Base Class: AdminUser
from abc import ABC, abstractmethod
class AbstractAdmin(ABC):
def __init__(self, user_id, username, email, password, role):
super().__init__(user_id, username, email, password) # Call the constructor of the superclass (AbstractUser)
self._role = role
@abstractmethod
def promote_to_admin(self, user):
pass # Concrete implementation in subclasses
# Overriding the display_info method for AdminUser
def display_info(self):
print(f"Admin User - User ID: {self._user_id}, Username: {self._username}, Email: {self._email}, Role: {self._role}")
user_id
, username
, email
, password
, and is_authenticated
.Abstract Base Class: ManagerUser
from abc import ABC, abstractmethod
class AbstractManager(ABC):
def __init__(self, user_id, username, email, password):
super().__init__() # Call the constructor of the superclass (User)
self._user_id = user_id
self._username = username
self._email = email
self._password = password
self._auditions = [] # List to store auditions
@abstractmethod
def create_audition(self, audition_name, description, deadline):
pass # Abstract method, implementation will be provided in subclasses
@abstractmethod
def get_auditions(self):
pass # Abstract method, implementation will be provided in subclasses
# Additional abstract methods for audition management:
@abstractmethod
def get_applications(self, audition):
pass
@abstractmethod
def review_application(self, application):
pass
@abstractmethod
def approve_application(self, application):
pass
@abstractmethod
def reject_application(self, application):
pass
def display_info(self):
print(f"Manager User - User ID: {self._user_id}, Username: {self._username}, Email: {self._email}")
Now, the User
class is truly abstract with the addition of the @abstractmethod
decorator for the display_info
method. Subclasses (AdminUser
and ManagerUser
) must implement this method, adhering to the principles of abstraction. This showcases a clearer example of abstraction in action.
user_id
, username
, email
, password
from AbstractUser
, adds role
.promote_to_admin(self, user)
: Promotes another user to admin.manage_users(self)
: Handles user management tasks.view_logs(self)
: Accesses system logs for monitoring and troubleshooting.display_info()
: Prints admin-specific information.Relation to SOLID
principles
The SOLID
principle related to abstraction is the "Interface Segregation Principle" (I in SOLID
).
The Interface Segregation Principle emphasizes that a class should not be forced to implement interfaces it does not use. This principle is closely tied to abstraction because it encourages the creation of specific, client-focused interfaces rather than large, all-encompassing ones.
Abstraction involves simplifying complex systems by modeling classes and interfaces based on the essential features they provide. The Interface Segregation Principle supports this by advocating for small, focused interfaces that are tailored to the needs of the clients using them. This way, abstraction is maintained, and classes only depend on the interfaces that are relevant to their specific requirements.