תכנות מונחה עצמים (Object-Oriented Programming – OOP)

תכנות מונחה עצמים (OOP) הוא סגנון תכנות המאפשר עבודה עם מחלקות (Classes) ו-אובייקטים (Objects) כדי לארגן קוד בצורה יעילה וניתנת לשימוש חוזר.

מושגים מרכזיים:

  1. מחלקות ואובייקטים:
    מחלקה היא תבנית (Template) ליצירת אובייקטים. אובייקט הוא מופע (Instance) של מחלקה.
  2. ירושה (Inheritance):
    מאפשרת למחלקה אחת לרשת תכונות (Attributes) ושיטות (Methods) ממחלקה אחרת.
  3. פולימורפיזם (Polymorphism):
    מאפשר לפונקציות או שיטות לפעול באופן שונה על סמך סוג האובייקט.

מחלקות ואובייקטים

דוגמת קוד: ניהול עובדים

class Employee:
    """A class to represent an employee."""
    def __init__(self, name, position, salary):
        self.name = name  # Employee's name
        self.position = position  # Employee's position
        self.salary = salary  # Employee's salary

    def describe(self):
        """Print details about the employee."""
        print(f"Name: {self.name}, Position: {self.position}, Salary: ${self.salary}")

    def give_raise(self, amount):
        """Increase the employee's salary."""
        self.salary += amount
        print(f"{self.name} received a raise of ${amount}. New salary is ${self.salary}")

# Creating and using objects
employee1 = Employee("Alice", "Developer", 60000)
employee2 = Employee("Bob", "Designer", 55000)

employee1.describe()  # Outputs: Name: Alice, Position: Developer, Salary: $60000
employee2.give_raise(5000)  # Outputs: Bob received a raise of $5000. New salary is $60000

הסבר על הקוד

  1. מחלקה Employee:
    • __init__: קונסטרקטור שמגדיר שם, תפקיד ומשכורת.
    • describe: שיטה שמדפיסה מידע על העובד.
    • give_raise: שיטה שמעלה את המשכורת של העובד בסכום נתון.
  2. יצירת אובייקטים:
    • employee1 ו-employee2 הם אובייקטים נפרדים של המחלקה Employee.
    • לכל אובייקט נתונים משלו ושיטות שניתן לקרוא להן.

ירושה

דוגמת קוד: מחלקת עובדים ומנהלים

class Employee:
    """A base class to represent an employee."""
    def __init__(self, name, position, salary):
        self.name = name
        self.position = position
        self.salary = salary

    def describe(self):
        """Print details about the employee."""
        print(f"Name: {self.name}, Position: {self.position}, Salary: ${self.salary}")

# Inheriting from Employee
class Manager(Employee):
    """A class to represent a manager."""
    def __init__(self, name, position, salary, team_size):
        super().__init__(name, position, salary)  # Inherit attributes from Employee
        self.team_size = team_size  # Additional attribute for managers

    def describe(self):
        """Print details about the manager."""
        super().describe()  # Call the describe method from Employee
        print(f"Team size: {self.team_size}")

# Creating and using objects
manager = Manager("Charlie", "Team Lead", 80000, 10)
manager.describe()
# Outputs:
# Name: Charlie, Position: Team Lead, Salary: $80000
# Team size: 10

הסבר על הקוד

  1. מחלקה Manager:
    • יורשת את Employee באמצעות class Manager(Employee).
    • הקונסטרקטור משתמש ב-super().__init__ כדי להגדיר תכונות מהמחלקה Employee.
    • מוסיפה תכונה חדשה team_size.
  2. שיטה describe:
    • super().describe(): קוראת לשיטה describe של המחלקה האב כדי להדפיס את פרטי העובד.
    • מוסיפה פרטים על מספר חברי הצוות.

פולימורפיזם

דוגמת קוד: מערכת שכר

class Employee:
    """A base class to represent an employee."""
    def __init__(self, name, salary):
        self.name = name
        self.salary = salary

    def calculate_bonus(self):
        """Base bonus for employees."""
        return self.salary * 0.05

class Manager(Employee):
    """A class to represent a manager."""
    def calculate_bonus(self):
        """Bonus for managers."""
        return self.salary * 0.1

class Intern(Employee):
    """A class to represent an intern."""
    def calculate_bonus(self):
        """No bonus for interns."""
        return 0

# Using polymorphism
employees = [
    Employee("Alice", 60000),
    Manager("Bob", 80000),
    Intern("Charlie", 20000)
]

for employee in employees:
    print(f"{employee.name}'s bonus: ${employee.calculate_bonus()}")
# Outputs:
# Alice's bonus: $3000.0
# Bob's bonus: $8000.0
# Charlie's bonus: $0

הסבר על הקוד

  1. מחלקות עם שיטות זהות:
    • Employee, Manager, ו-Intern כולן מכילות את השיטה calculate_bonus, אך כל אחת מממשת אותה בצורה שונה.
  2. פולימורפיזם:
    • הלולאה for employee in employees מאפשרת לקרוא לשיטה calculate_bonus לכל אובייקט, והתוצאה משתנה בהתאם למחלקה של האובייקט.

תרגילים

תרגיל 1:
צרו מחלקה Product עם תכונות name ו-price, ושיטה שמדפיסה את המוצר.

תרגיל 2:
כתבו מחלקה DiscountedProduct שיורשת מ-Product, ומוסיפה תכונה discount. הוסיפו שיטה שמחשבת את המחיר אחרי הנחה.

תרגיל 3:
צרו מערכת רישום של משתמשים עם מחלקות User, Admin ו-Guest. כל מחלקה תכלול שיטה get_permissions שתשיב הרשאות שונות.

תרגיל 4:
כתבו מחלקה Shape עם שיטה area. צרו מחלקות יורשות Circle ו-Rectangle שמחשבות שטח בדרכים שונות.

תרגיל 5:
אתגר: צרו מחלקה Account עם מחלקות יורשות SavingsAccount ו-CheckingAccount, כאשר כל אחת מהן מחשבת ריבית בצורה שונה.

הפרויקט הגדול: משחק הטריוויה

נשתמש ב-OOP כדי לייצג שאלות, שחקנים, ואת ניהול המשחק כולו.

  1. מחלקה Question – ייצוג שאלה.
  2. מחלקה Player – ייצוג שחקן עם ניקוד.
  3. מחלקה QuizGame – ניהול המשחק.

קובץ question.py

class Question:
    """A class to represent a quiz question."""
    def __init__(self, text, answer):
        self.text = text
        self.answer = answer

    def is_correct(self, user_answer):
        """Check if the user's answer is correct."""
        return str(user_answer).lower() == str(self.answer).lower()

קובץ player.py

class Player:
    """A class to represent a player."""
    def __init__(self, name):
        self.name = name
        self.score = 0

    def add_score(self, points):
        """Add points to the player's score."""
        self.score += points

קובץ quiz_game.py

from question import Question
from player import Player

class QuizGame:
    """A class to manage the quiz game."""
    def __init__(self, questions):
        self.questions = questions  # List of Question objects

    def start(self, player_name):
        """Start the game for a player."""
        player = Player(player_name)
        for question in self.questions:
            print("\n" + question.text)
            user_answer = input("Your answer: ").strip()
            if question.is_correct(user_answer):
                print("Correct!")
                player.add_score(1)
            else:
                print(f"Wrong! The correct answer was: {question.answer}")
        print(f"\n{player.name}, your final score is: {player.score}")

    def manage_questions(self):
        """Allow admin to manage questions."""
        while True:
            action = input("\nDo you want to 'add', 'edit', 'remove', or 'done'? ").lower().strip()
            if action == "done":
                break
            elif action == "add":
                new_text = input("Enter the question text: ").strip()
                new_answer = input("Enter the correct answer: ").strip()
                self.questions.append(Question(new_text, new_answer))
                print("Question added!")
            elif action == "edit":
                question_number = int(input("Enter the question number to edit (1-based index): ")) - 1
                if 0 <= question_number < len(self.questions):
                    new_text = input("Enter the new question text: ").strip()
                    new_answer = input("Enter the new correct answer: ").strip()
                    self.questions[question_number].text = new_text
                    self.questions[question_number].answer = new_answer
                    print("Question updated!")
                else:
                    print("Invalid question number.")
            elif action == "remove":
                question_number = int(input("Enter the question number to remove (1-based index): ")) - 1
                if 0 <= question_number < len(self.questions):
                    removed_question = self.questions.pop(question_number)
                    print(f"Removed question: {removed_question.text}")
                else:
                    print("Invalid question number.")
            else:
                print("Invalid action. Please choose 'add', 'edit', 'remove', or 'done'.")

קובץ main.py

from question import Question
from quiz_game import QuizGame

def load_questions():
    """Load questions from a file and return a list of Question objects."""
    questions = []
    try:
        with open("questions.txt", "r") as file:
            for line in file:
                text, answer = line.strip().split("||")
                questions.append(Question(text, answer))
    except FileNotFoundError:
        print("Questions file not found. Starting with no questions.")
    return questions

def save_questions(questions):
    """Save questions to a file."""
    with open("questions.txt", "w") as file:
        for question in questions:
            file.write(f"{question.text}||{question.answer}\n")

def main():
    """Main function to run the quiz game."""
    questions = load_questions()
    quiz = QuizGame(questions)

    name = input("Enter your name: ").strip()
    if name.lower() == "admin":
        print("Welcome, Admin!")
        quiz.manage_questions()
        save_questions(quiz.questions)
    else:
        print(f"Welcome, {name}!")
        quiz.start(name)

if __name__ == "__main__":
    main()

הסבר על הקוד

  1. פונקציה manage_questions במחלקה QuizGame:
    • מאפשרת למנהל לבצע את הפעולות הבאות:
      • הוספה (add): הוספת שאלה חדשה לרשימה.
      • עריכה (edit): שינוי הטקסט או התשובה של שאלה קיימת.
      • מחיקה (remove): הסרת שאלה מהרשימה.
      • סיום (done): יציאה ממסך ניהול השאלות.
  2. טעינת ושמירת שאלות:
    • פונקציה load_questions טוענת שאלות מקובץ questions.txt וממירה אותן לאובייקטים של Question.
    • פונקציה save_questions שומרת את רשימת השאלות המעודכנת לקובץ.
  3. זרימת התוכנית:
    • אם המשתמש הוא מנהל (admin), מופעלת פונקציית manage_questions.
    • לאחר הניהול, השאלות המעודכנות נשמרות לקובץ.
    • אם המשתמש הוא שחקן רגיל, המשחק מתחיל עם רשימת השאלות הטעונה.

דוגמה להרצה

מנהל נכנס:

Enter your name: admin
Welcome, Admin!

Do you want to 'add', 'edit', 'remove', or 'done'? add
Enter the question text: What is the capital of Japan?
Enter the correct answer: Tokyo
Question added!

Do you want to 'add', 'edit', 'remove', or 'done'? done

שחקן רגיל נכנס:

Enter your name: Alice
Welcome, Alice!

What is the capital of Japan?
Your answer: Tokyo
Correct!