Bu yazıda; Encapsulation, Abstraction, Inheritance ve Polymorphism gibi Nesne Yönelimli Programlama (OOP) kavramları temel alınarak geliştirilmiş bir araç kiralama sistemi örneğini ele alacağız.
Uygulama, gerçek hayat senaryolarını temel alan sınıf yapılarıyla tasarlanmış; veri yönetimi ise bellek (cache) üzerinde değil, harici .json dosyaları üzerinden sağlanmıştır.

Proje Mimari Yapısı
Uygulamayı geliştirirken, sorumlulukların net bir şekilde ayrıldığı ve OOP prensiplerini destekleyen katmanlı bir yapı hedefledik. Bu doğrultuda proje, aşağıdaki klasör yapısı üzerine inşa edilmiştir:

Akışta projenin kodları paylaşılmıştır. Yukarıdaki mimariyi sisteminizde oluşturarak projeyi çalıştırabiliriz. Python dilinde sürümden kaynaklı syntax hataları yaşanabilmektedir. Bu proje 3.10.0 sürümü ile geliştirilmiştir.
models/ Domain (iş) Nesneleri
models klasörü, uygulamanın temel iş nesnelerini barındırır. Nesne yönelimli programlama mimarisinin oluşturulduğu katman diyebiliriz.
vehicle.py
Araçlara ait ortak özellik ve davranışların tanımlandığı temel sınıftır.
Bu sınıf üzerinden abstraction sağlanmış ve alt sınıflar için ortak bir yapı oluşturulmuştur. abstraction bu noktada encapsulation yapısını zorunlu hale getirmiş oluyor. Dolayısıyla doğrudan bu class üzerinden nesne yaratamayız.car.py,suv.py,truck.pyVehiclesınıfından türetilen bu alt sınıflar, farklı araç tiplerini temsil eder. Bu yapı sayesinde inheritance ve polymorphism kavramları pratikte uygulanmıştır. Yeni bir araç tipi eklenmek istediğinde vehicle sınıfından kalıtım alınarak yaratılabilir.customer.py
Sistemde araç kiralayan müşterilere ait bilgileri ve davranışları temsil eder.
Bu katmanda yer alan sınıflar yalnızca veri ve davranıştan sorumludur; veri okuma/yazma veya iş akışı yönetimi gibi sorumluluklar bu katmana dahil edilmemiştir. Böylece encapsulation korunmuştur.
managers/ İş Kuralları ve Kontrol Katmanı
managers klasörü, uygulamanın iş mantığını yöneten sınıfları içerir.
data_manager.py
JSON dosyaları üzerinden veri okuma ve yazma işlemlerini merkezi bir noktadan yönetir.
Bu sayede veri erişim mantığı tek bir yerde toplanmış ve tekrar eden kodların önüne geçilmiştir.rental_manager.py
Araç kiralama, teslim alma ve kiralama kayıtlarının yönetildiği iş kurallarını içerir.
Model sınıflarını kullanarak sistemin akışını kontrol eder.
Bu ayrım sayesinde Single Responsibility Principle (SRP) uygulanmış olur.
SRP: Bir sınıf tek bir işi yapmalıdır. Bu prensip sayesinde kod daha okunabilir, bakımı kolaylaşır, değişiklikler diğer alanları etkilemez, test etmek kolaydır.
data/ Veri Kaynağı
Uygulamada kullanılan tüm veriler, bellek üzerinde tutulmak yerine data klasörü altındaki JSON dosyaları üzerinden yönetilmektedir.
vehicles.json→ Araç bilgilericustomers.json→ Müşteri bilgilerirentals.json→ Kiralama kayıtları
Bu yaklaşım, uygulamanın stateless çalışmasını sağlar ve veri katmanını uygulama mantığından ayırır.
app.py Uygulamanın Giriş Noktası
app.py, uygulamanın çalıştırıldığı ana dosyadır. Manager sınıfları üzerinden sistemin akışı burada başlatılır ve kontrol edilir.
Models Katmanı
Bu noktadan itibaren uygulamanın model katmanını oluşturmaya başlıyoruz.models klasörü, sistemin temelini oluşturan iş nesnelerini barındırdığı için projenin en kritik parçalarından biridir.
İlk adımda; araç, müşteri ve araç tiplerine ait sınıfları tanımlayarak nesne yönelimli yapının temelini atacağız. Bu süreçte abstraction, encapsulation ve inheritance gibi OOP kavramlarını pratik örnekler üzerinden ele alacağız. Hiyerarşik yapıysa aşağıdaki görselde yer verilmiştir.

vehicle.py dosyasını kodlayalım:
from abc import ABC, abstractmethod
class Vehicle(ABC):
def __init__(self, brand, model, year, daily_price):
self.brand = brand
self.model = model
self.year = year
self._daily_price = daily_price
self._is_available = True
self._validate_price()
def _validate_price(self):
if self._daily_price <= 0:
raise ValueError("Günlük fiyat pozitif olmalıdır.")
def get_daily_price(self):
return self._daily_price
def set_daily_price(self, new_price):
if new_price <= 0:
raise ValueError("Günlük fiyat pozitif olmalıdır.")
self._daily_price = new_price
def mark_as_rented(self):
self._is_available = False
def mark_as_returned(self):
self._is_available = True
def is_available(self):
return self._is_available
@abstractmethod
def calculate_price(self, days: int):
pass
def to_dict(self):
return {
"type": self.__class__.__name__,
"brand": self.brand,
"model": self.model,
"year": self.year,
"daily_price": self._daily_price,
"is_available": self._is_available
}
Vehicle sınıfı, sistemdeki tüm araç tipleri için ortak bir temel (abstraction) oluşturmak amacıyla tasarlanmıştır. Bu sınıf, Python’un ABC yapısı kullanılarak soyut sınıf olarak tanımlanmış ve alt sınıfların belirli davranışları zorunlu olarak implemente etmesi sağlanmıştır.
Sınıf içerisinde araçlara ait marka, model ve yıl gibi ortak özellikler tanımlanırken; günlük ücret ve müsaitlik durumu protected (_) değişkenler ile encapsulation prensibine uygun şekilde saklanmıştır. Bu sayede ilgili alanlara yalnızca sınıfın kendisi ve alt sınıflar üzerinden erişim sağlanmaktadır.
Fiyat bilgisinin her zaman geçerli olmasını sağlamak adına doğrulama (validation) mekanizması kurulmuş, getter ve setter metodları ile kontrollü erişim uygulanmıştır. Ayrıca aracın kiralanma ve teslim edilme durumları için state management fonksiyonları eklenmiştir. getter ve setter metotları kullanımı anlayabilmek adına vehicle.py ve customer.py dosyasında farklı mantıkta kullanılmıştır. Python dilinde overloading mantığı yoktur. Bunun için @property kavramı kullanılmaktadır (customer.py dosyası incelenebilir).
calculate_price metodu ise soyut (abstract) olarak tanımlanarak, her araç tipinin kendi fiyatlandırma mantığını uygulaması zorunlu hale getirilmiştir. Bu yapı büyük projelerde sıkça kullanıldığı için mantığını iyi anlamak önemlidir. Son olarak to_dict metodu ile araç nesnelerinin JSON formatına dönüştürülmesi sağlanarak veri katmanıyla uyumlu bir yapı oluşturulmuştur.
car.py dosyasını kodlayalım:
from .vehicle import Vehicle
class Car(Vehicle):
def calculate_price(self, days: int):
return self._daily_price * days

suv.py dosyasını kodlayalım:
from .vehicle import Vehicle
class SUV(Vehicle):
def calculate_price(self, days: int):
return self._daily_price * days * 1.3

truck.py dosyasını kodlayalım:
from .vehicle import Vehicle
class Truck(Vehicle):
# calculate_price fonksiyonu abstract bir fonksiyon olduğu için yazmak zorundayız.
def calculate_price(self, days: int):
return (self._daily_price * days)+500

Car, SUV ve Truck sınıfları, Vehicle soyut sınıfından türetilmiş olup sistemdeki farklı araç tiplerini temsil eder. Bu sınıfların temel amacı, ortak özellikleri tekrar etmeden kalıtım (inheritance) yoluyla kullanmak ve araç tipine özgü davranışları tanımlamaktır.
Her üç sınıfta da calculate_price metodu implemente edilmiştir. Bu metodun kullanılma nedeni, her araç tipinin fiyatlandırma mantığının farklı olabilmesidir. Böylece aynı arayüz korunurken, araç tipine göre farklı hesaplama kuralları uygulanabilmektedir. Bu yaklaşım, polymorphism kavramının pratik bir karşılığıdır.
Alt sınıflar yalnızca kendi sorumluluk alanlarına odaklanır; temel alanlar, durum yönetimi ve doğrulama işlemleri Vehicle sınıfı üzerinden devralınır. Bu sayede kod tekrarından kaçınılır, sistem daha okunabilir ve genişletilebilir hale gelir.
Yeni bir araç tipi eklenmek istendiğinde, mevcut yapıyı bozmadan yalnızca Vehicle sınıfından türeyen yeni bir sınıf oluşturmak yeterlidir. Bu mantık uygulamanın yönetilebilir ve sürdürülebilir olmasını sağlar.
customer.py dosyasını kodlayalım:
# uuid kütüphanesi benzersiz numaralar üretmek için kullanılır.
import uuid
class Customer:
def __init__(self, name: str, email: str, phone: str):
self._customer_id = str(uuid.uuid4()) # otomatik benzersiz ID
self.name = name
self.email = email
self.phone = phone
# ---- Encapsulation ------
@property
def customer_id(self):
return self._customer_id
@property
def name(self):
return self._name
@name.setter
def name(self, value):
if len(value) < 2:
raise ValueError("İsim en az 2 karakter olmalıdır.")
self._name = value
@property
def email(self):
return self._email
@email.setter
def email(self, value):
if "@" not in value:
raise ValueError("Geçersiz e-mail formatı.")
self._email = value
@property
def phone(self):
return self._phone
@phone.setter
def phone(self, value):
if len(value) < 10:
raise ValueError("Telefon numarası en az 10 haneli olmalıdır.")
self._phone = value
def to_dict(self):
return {
"customer_id": self.customer_id,
"name": self.name,
"email": self.email,
"phone": self.phone
}
def __str__(self):
return f"{self.name} ({self.email})"
Customer sınıfı, araç kiralama sisteminde müşteri bilgilerini temsil eden modeldir. Her müşteri için sistem içerisinde benzersiz bir kimlik oluşturmak amacıyla uuid kütüphanesi kullanılmış ve müşteri ID’si otomatik olarak üretilmiştir. Bu yaklaşım, veri bütünlüğünü korumak açısından kritik öneme sahiptir.
Sınıf içerisinde yer alan name, email ve phone alanları encapsulation prensibine uygun olarak @property yapısı ile yönetilmektedir. Bu sayede alanlara doğrudan erişim yerine, kontrollü getter ve setter metodları üzerinden erişim sağlanmıştır.
Property setter’lar içerisinde yapılan doğrulamalar sayesinde; isim uzunluğu, e-posta formatı ve telefon numarası gibi alanlar daha nesne oluşturulurken kontrol altına alınır. Böylece hatalı verilerin sistem içerisine girmesi engellenir ve iş kuralları model katmanında merkezi olarak yönetilmiş olur.
to_dict metodu, müşteri nesnesinin JSON formatına dönüştürülmesini sağlarken; __str__ metodu ise müşteri bilgisinin okunabilir bir metin olarak temsil edilmesine imkan tanır.
Managers Katmanı
Managers katmanı, uygulamanın iş kurallarını ve operasyonel akışını yöneten yapıyı temsil eder. Model katmanında tanımlanan nesneler, bu katman aracılığıyla kullanılarak sistemin nasıl çalışacağı belirlenir. Bu katman iki temel kırılımdan oluşur:
- DataManager: Uygulamada kullanılan verilerin harici JSON dosyaları üzerinden okunması ve yazılması süreçlerinden sorumludur.
- RentalManager: Araç kiralama, teslim alma ve kiralama süreçlerine ait iş akışlarını yöneten katmandır.
DataManager.py dosyasını kodlayalım:
import json
import os
class DataManager:
def __init__(self, base_path="data"):
self.base_path = base_path
self.vehicles_file = os.path.join(base_path, "vehicles.json")
self.customers_file = os.path.join(base_path, "customers.json")
self.rentals_file = os.path.join(base_path, "rentals.json")
self._ensure_files()
def _ensure_files(self):
os.makedirs(self.base_path, exist_ok=True)
for file in [self.vehicles_file, self.customers_file, self.rentals_file]:
if not os.path.exists(file):
with open(file, "w", encoding="utf-8") as f:
json.dump([], f)
def _read_json(self, file_path):
with open(file_path, "r", encoding="utf-8") as f:
return json.load(f)
def _write_json(self, file_path, data):
with open(file_path, "w", encoding="utf-8") as f:
json.dump(data, f, indent=4)
def load_vehicles(self):
return self._read_json(self.vehicles_file)
def save_vehicles(self, vehicles):
self._write_json(self.vehicles_file, vehicles)
def load_customers(self):
return self._read_json(self.customers_file)
def save_customers(self, customers):
self._write_json(self.customers_file, customers)
def load_rentals(self):
return self._read_json(self.rentals_file)
def save_rentals(self, rentals):
self._write_json(self.rentals_file, rentals)
DataManager, uygulamada kullanılan tüm verilerin harici JSON dosyaları üzerinden okunması ve yazılmasından sorumludur. Bu sınıf sayesinde veri erişim mantığı tek bir noktada toplanmış ve model ile iş kuralları katmanı dosya sistemi detaylarından bağımsız hale getirilmiştir.
Sınıf içerisinde; araçlar, müşteriler ve kiralama kayıtları için ayrı JSON dosyaları tanımlanmış, uygulama başlatılırken bu dosyaların varlığı kontrol edilerek gerekirse otomatik olarak oluşturulması sağlanmıştır. Dosya yolları oluşturulurken os.path.join kullanılarak işletim sistemine bağımlı yol problemlerinin önüne geçilmiştir.
Veri okuma ve yazma işlemleri ortak yardımcı fonksiyonlar üzerinden yönetilerek kod tekrarından kaçınılmış, yükleme ve kaydetme işlemleri ise daha okunabilir metodlar aracılığıyla dış dünyaya sunulmuştur. Uygulamamız çalıştığında __init__ fonksiyonu içerisinde yer alan _ensure_file() fonksiyonu çalışacağı için vehicles.json, customer.json ve vehicles.json dosyaları otomatik oluşturulacaktır.

RentalManager.py dosyasını kodlayalım:
RentalManager yapısı projenin beyni konumundadır. app.py üzerinden gelen isteklere gerekli çözümler üretmesi beklenir. Dolayısıyla bu noktada app.py dosyasında oluşturulan her yetenek/işlem seçeneği için RentalManager yapısında bir fonksiyon tanımlıyor. Bu noktada uygulamanın yeteneklerine değinmekte fayda var:
1) Araç ekle
2) Araçları listele
3) Müşteri ekle
4) Müşterileri listele
5) Araç kirala
6) Araç teslim al
7) Kiralama kayıtlarını listele
Her bir işlem için RentalManager.py dosyasında fonksiyon yaratacağımıza değindik. Burada işlemleri 3 aşamada inceleyebiliriz.
1.) Araç İşlemleri
from models.car import Car
from models.suv import SUV
from models.truck import Truck
from models.customer import Customer
class RentalManager:
def __init__(self, data_manager):
# RentalManager, veri işlemlerini kendisi yapmaz.
# Dışarıdan bir DataManager nesnesi alır.
self.data_manager = data_manager
# VEHICLE OPERATIONS
# Yeni araç ekleme fonksiyonu
def add_vehicle(self, vehicle_type, brand, model, year, daily_price):
# mevcut araç listesi bir .json dosyasında tutuyoruz.
# Bu listeyi bize "data_manager" altında load_vehicles() fonksiyonunu döndürmektedir.
# Bu fonksiyonunu daha önce hazırlamıştık.
vehicles = self.data_manager.load_vehicles()
# Araç Tipine Göre Nesne Oluşturma (Polimorfizm)
if vehicle_type.lower() == "car":
vehicle = Car(brand, model, year, daily_price)
elif vehicle_type.lower() == "suv":
vehicle = SUV(brand, model, year, daily_price)
elif vehicle_type.lower() == "truck":
vehicle = Truck(brand, model, year, daily_price)
else:
raise ValueError("Geçersiz araç tipi.")
# Bu noktada to_dict metodu çok önemlidir.
# Biz .json dosyası hazırlamıştık fakat vehicles dosyası şu şekilde oluyor. vehicle = Car("BMW", "320i", 2022, 1500)
# Bu yapı bir .json formatında değil. Bunu to_dict ile .joson formatına çeviriyoruz.
vehicles.append(vehicle.to_dict())
# Mevcut listeyi aldık. Listeye yeni kaydı ekledik.
# Fakat bu liste şu an uygulama içerisinde yani .json dosyası üzerine yazmadık.
# son olarak data_manager altında hazırladığımız metodu kullanarak .json dosyasını güncelliyoruz.
self.data_manager.save_vehicles(vehicles)
return vehicle
# Araçları listeliyoruz.
def list_vehicles(self):
return self.data_manager.load_vehicles()
RentalManager, uygulamadaki araç ve kiralama süreçlerini yöneten iş kuralı katmanıdır. Bu sınıf, veri okuma veya yazma işlemlerini doğrudan gerçekleştirmez; bu sorumluluğu dışarıdan enjekte edilen DataManager nesnesine bırakır. Böylece sorumlulukların ayrıştırılması sağlanmış olur.
Araç ekleme sürecinde, sistemde kayıtlı araçlar önce JSON dosyasından alınır ve kullanıcıdan gelen araç tipine göre ilgili sınıf (Car, SUV, Truck) üzerinden yeni bir nesne oluşturulur. Bu yaklaşım, farklı araç tiplerinin aynı arayüz üzerinden yönetilmesini sağlayarak polymorphism kavramını pratikte uygulanabilir hale getirir.
Oluşturulan araç nesnesi, JSON formatıyla uyumlu hale getirilmek için to_dict metodu aracılığıyla dönüştürülür ve mevcut araç listesine eklenir. Güncellenen liste, tekrar veri kaynağına yazılarak kalıcı hale getirilir.
list_vehicles metodu ise sistemde kayıtlı tüm araçları tek bir noktadan listeleyerek, araç verilerine erişimi sade ve kontrollü bir şekilde sunar.
2.) Müşteri İşlemleri
# CUSTOMER OPERATIONS
# Müşteri ekleme fonksiyonu
def add_customer(self, name, email, phone):
# Mevcut .json dosyasında yer alan müşterileri getiriyoruz.
customers = self.data_manager.load_customers()
# Dışarıdan gelen parametreleri kullanarak customer modeli ile obje oluşturuyoruz.
customer = Customer(name, email, phone)
customers.append(customer.to_dict())
# Mevcut .json dosyasını güncelliyoruz.
self.data_manager.save_customers(customers)
return customer
# Müşterileri listeleme
def list_customers(self):
return self.data_manager.load_customers()
RentalManager içerisinde yer alan müşteri işlemleri, sistemdeki müşteri kayıtlarının yönetilmesini sağlar. Yeni bir müşteri eklenirken, mevcut müşteri listesi JSON dosyasından alınır ve dışarıdan gelen bilgiler kullanılarak Customer modeli üzerinden yeni bir nesne oluşturulur.
Oluşturulan müşteri nesnesi, to_dict metodu ile JSON formatına dönüştürülerek veri listesine eklenir ve güncel liste tekrar veri kaynağına yazılır. Bu sayede müşteri bilgileri kalıcı hale getirilmiş olur.
list_customers metodu ise sistemde kayıtlı tüm müşterilerin tek bir noktadan listelenmesini sağlayarak, müşteri verilerine erişimi sade ve kontrollü bir şekilde sunar.
3.) Kiralama İşlemleri
# CUSTOMER OPERATIONS
# Müşteri ekleme fonksiyonu
def add_customer(self, name, email, phone):
# Mevcut .json dosyasında yer alan müşterileri getiriyoruz.
customers = self.data_manager.load_customers()
# Dışarıdan gelen parametreleri kullanarak customer modeli ile obje oluşturuyoruz.
customer = Customer(name, email, phone)
customers.append(customer.to_dict())
# Mevcut .json dosyasını güncelliyoruz.
self.data_manager.save_customers(customers)
return customer
# Müşterileri listeleme
def list_customers(self):
return self.data_manager.load_customers()
# RENT VEHICLE
# müşteri id ve araç id si üzerinden kiralama yapıyoruz.
def rent_vehicle(self, customer_id, vehicle_index, days):
# Kontrolleri sağlamak için; araç, müşteri ve kiralama güncel bilgilerini alıyoruz.
vehicles = self.data_manager.load_vehicles()
customers = self.data_manager.load_customers()
rentals = self.data_manager.load_rentals()
# 1.) müşteri kontrolü
customer = None
for c in customers:
if c["customer_id"] == customer_id:
customer = c
break
if customer is None:
# Bu bilgiyi kullanıcıya print ile verebilirdik.
# fakat print işlemi durdurmaz bir mesaj bildirisi verir.
# raise ValueError bir hata mesajı döndürür
raise ValueError("Müşteri bulunamadı.")
# 2.) araç index kontrolü
if vehicle_index < 0 or vehicle_index >= len(vehicles):
raise ValueError("Geçersiz araç seçimi.")
vehicle = vehicles[vehicle_index]
# 3.) Araç müsait mi?
if not vehicle["is_available"]:
raise ValueError("Araç şu anda kirada.")
# 4.) fiyat hesaplamak için nesne oluştur
vehicle_class_map = {
"Car": Car,
"SUV": SUV,
"Truck": Truck
}
vehicle_class = vehicle_class_map.get(vehicle["type"])
if vehicle_class is None:
raise ValueError("Araç tipi tanınamadı.")
vehicle_obj = vehicle_class(
vehicle["brand"],
vehicle["model"],
vehicle["year"],
vehicle["daily_price"]
)
total_price = vehicle_obj.calculate_price(days)
# 5.) araç artık müsait değil
vehicle["is_available"] = False
self.data_manager.save_vehicles(vehicles)
# 6.) kiralama kaydı
rentals.append({
"customer_id": customer["customer_id"],
"vehicle_index" : vehicle_index,
"customer_name": customer["name"],
"vehicle": f"{vehicle['brand']} {vehicle['model']}",
"days": days,
"total_price": total_price
})
self.data_manager.save_rentals(rentals)
return total_price
# RETURN VEHICLE
def return_vehicle(self, vehicle_index):
vehicles = self.data_manager.load_vehicles()
if vehicle_index < 0 or vehicle_index >= len(vehicles):
raise ValueError("Geçersiz araç indexi.")
vehicles[vehicle_index]["is_available"] = True
self.data_manager.save_vehicles(vehicles)
return True
RentalManager içerisindeki kiralama işlemleri, sistemin en kritik iş akışını temsil eder. rent_vehicle metodu; müşteri, araç ve kiralama kayıtlarını birlikte ele alarak kontrollü bir kiralama süreci yürütür.
Kiralama adımında sırasıyla; müşteri bilgisi doğrulanır, seçilen aracın geçerliliği kontrol edilir ve aracın müsaitlik durumu incelenir. Ardından araç tipine göre ilgili sınıf üzerinden nesne oluşturularak fiyat hesaplama işlemi polymorphism sayesinde gerçekleştirilir. Bu yaklaşım, araç tipine özel fiyatlandırma mantıklarının tek bir arayüz üzerinden çalışmasını sağlar.
Kiralama tamamlandığında aracın durumu güncellenir ve işlemle ilgili bilgiler kiralama kayıtlarına eklenerek kalıcı hale getirilir. Hatalı senaryolarda ise ValueError kullanılarak süreç güvenli bir şekilde sonlandırılır.
return_vehicle metodu ise teslim sürecini yönetir. İlgili aracın durumu tekrar müsait olarak işaretlenir ve güncel bilgiler veri kaynağına yazılır. Bu sayede kiralama ve teslim işlemleri tutarlı ve izlenebilir bir yapı içerisinde yönetilmiş olur.
app.py Yapısı
Projenin tetiklendiği ve başlatıldığı arayüzdür. Bu proje bu noktaya kadar bir arayüze/frontende bağlanabilir. Fakat biz sadece .py formatında ilerlediğimiz için console ekranı üzerinde seçim yaptırıyoruz.
# manager yapısını import ediyoruz.
from managers.data_manager import DataManager
from managers.rental_manager import RentalManager
def print_menu():
print("\n=== ARAÇ KİRALAMA SİSTEMİ ===")
print("1) Araç ekle")
print("2) Araçları listele")
print("3) Müşteri ekle")
print("4) Müşterileri listele")
print("5) Araç kirala")
print("6) Araç teslim al")
print("7) Kiralama kayıtlarını listele")
print("0) Çıkış")
def main():
data_manager = DataManager()
rental_manager = RentalManager(data_manager)
while True:
print_menu()
choice = input("Seçiminiz: ")
# Seçimler ile ilgili hata durumunu kontrol edebilmek için try except kullanıyoruz.
try:
# ARAÇ EKLE
if choice == "1":
vehicle_type = input("Araç tipi (car / suv / truck): ")
brand = input("Marka: ")
model = input("Model: ")
year = int(input("Yıl: "))
daily_price = float(input("Günlük fiyat: "))
rental_manager.add_vehicle(
vehicle_type, brand, model, year, daily_price
)
print("Araç başarıyla eklendi.")
# ARAÇLARI LİSTELE
elif choice == "2":
vehicles = rental_manager.list_vehicles()
if not vehicles:
print("Kayıtlı araç yok.")
continue
print("----------ARAÇLAR----------")
# enumerate, bir koleksiyon (liste vb) üzerinden dönerken hem elemanın kendisini hem de indeksini aynı anda getirir.
# 2 parametre alır. i => Bu noktada indexi getirir.
for i, v in enumerate(vehicles):
status = "Müsait" if v["is_available"] else "Kirada"
print(
f"[{i}] {v['type']} - {v['brand']} {v['model']} "
f"({v['year']}) | {v['daily_price']} TL | {status}"
)
# MÜŞTERİ EKLE
elif choice == "3":
name = input("Ad Soyad: ")
email = input("E-mail: ")
phone = input("Telefon: ")
rental_manager.add_customer(name, email, phone)
print("Müşteri başarıyla eklendi.")
# MÜŞTERİLERİ LİSTELE
elif choice == "4":
customers = rental_manager.list_customers()
if not customers:
print("Kayıtlı müşteri yok.")
continue
print("-----------MÜŞTERİLER------------")
for c in customers:
print(f"{c['customer_id']} | {c['name']} | {c['email']} | {c['phone']}" )
# ARAÇ KİRALA
elif choice == "5":
customer_id = input("Müşteri ID: ")
vehicle_index = int(input("Araç index: "))
days = int(input("Kaç gün kiralanacak: "))
total_price = rental_manager.rent_vehicle(customer_id, vehicle_index, days)
print(f"Araç kiralandı. Toplam tutar: {total_price} TL")
# ARAÇ TESLİM AL
elif choice == "6":
vehicle_index = int(input("Teslim alınacak araç index: "))
rental_manager.return_vehicle(vehicle_index)
print("Araç başarıyla teslim alındı.")
# KİRALAMA KAYITLARI
elif choice == "7":
rentals = data_manager.load_rentals()
if not rentals:
print("Kiralama kaydı yok.")
continue
for r in rentals:
print(f"{r['customer_name']} | {r['vehicle']} | {r['days']} gün | {r['total_price']} TL")
# ÇIKIŞ
elif choice == "0":
print("Çıkış yapılıyor...")
break
else:
print("Geçersiz seçim.")
except Exception as e:
print(f"Hata: {e}")
if __name__ == "__main__":
main()
app.py, araç kiralama sisteminin çalıştırıldığı ana dosyadır. Bu dosya, kullanıcı ile sistem arasındaki etkileşimi yönetir ve tüm işlemleri RentalManager ve DataManager üzerinden koordine eder.
Uygulama, basit bir menü tabanlı arayüz sunarak; araç ekleme, listeleme, müşteri yönetimi, kiralama ve teslim alma gibi işlemleri kullanıcıdan alınan seçimlere göre gerçekleştirir. İş kuralları doğrudan bu dosya içerisinde yazılmak yerine, ilgili manager sınıflarına yönlendirilerek katmanlı mimari korunmuştur.
Kullanıcıdan alınan girdiler try-except bloğu ile kontrol altına alınarak, hatalı senaryolarda uygulamanın beklenmedik şekilde sonlanması engellenmiştir. Böylece sistem, hem okunabilir hem de sürdürülebilir bir akış yapısına kavuşmuştur.
Uygulamanın Sonuçları ve Değerlendirme
Bu uygulama kapsamında, temel araç kiralama süreçlerini yöneten console tabanlı bir uygulama geliştirilmiştir. Müşteri, araç ve kiralama işlemleri ayrıştırılmış yapılar üzerinden ele alınmış; sade ve anlaşılır bir akış ile sistemin işleyişi başarıyla modellenmiştir. Uygulama, daha gelişmiş arayüz ve sistem entegrasyonları için sağlam bir temel oluşturmaktadır.
app.py dosyasını çalıştıralım.

Seçim Ekranı, kullanıcı ile sistem arasındaki etkileşimin sağlandığı ana menüdür. Console üzerinden sunulan bu menü aracılığıyla araç, müşteri ve kiralama işlemleri numaralandırılmış seçenekler üzerinden yönetilmektedir. Kullanıcı, ilgili işlemi seçerek uygulama akışını yönlendirebilir.
Seçim: 1 senaryosu (Araç Ekleme)

Seçenek 1 ile ilerlendiğinde uygulama bizden araç tipi, marka, model, yıl ve günlük fiyat gibi bilgileri istiyorum. Bilgileri girdiğimizde vehicles.json dosyasını kontrol edebiliriz. Aktardığımız bilgiler buradan json formatında görüntülenmelidir.
Seçim: 2 senaryosu (Araç Listeleme)

Bu seçenek yürütüldüğünde sistemde kayıtlı tüm araçlar listelenerek müsait durumu yansıtılmaktadır.
Seçim: 3 senaryosu (Müşteri Ekleme)

Müşteriye ait isim soyisim, e-mail ve telefon bilgileri istenir. Süreç tamamlandığında customers.json dosyası kontrol edildiğinde bilgilerin benzersiz id ile kaydedildiği görüntülenmelidir.
Seçim: 4 senaryosu (Müşteri Listeleme)

Sistem tarafından belirlenen benzersiz ID ile kullanıcı bilgilerini listeleyebiliriz.
Seçim: 5 senaryosu (Araç Kiralama)

Bu aşama önemlidir. Müşteri ile araç ilişkisi kurularak araç müsaitlik durumu güncellenir. Yapılan kiralama süreçleri rental.json dosyasına eklenir.
Seçim: 6 senaryosu (Araç Teslim Al)

Araç teslim alınması durumunda tercih edilecek seçenektir. Seçenek yürütüldüğünde aracın durumu müsait olarak güncellenir.
Seçim: 7 senaryosu (Kiralama Kayıtlarını Listele)

Geçmiş kiralama kayıtları bu seçenek üzerinden listelenir.
Mevcut mimari ile console ekranında yapılacaklar özetle yukarıdaki gibidir. Proje geliştirilebilir veya bir arayüze entegre edilebilir.
Daha fazla bilgi ve destek almak için mail ya da formu kullanarak iletişime geçebilirsiniz.