Ortak Arayüz
Coddy'nin Lua Journey'sinin Object Oriented Programming bölümünün bir parçası — ders 40 / 70.
Ördek tiplemesi (Duck typing), ilgisiz sınıfların metot isimlerini paylaşmaları durumunda birlikte çalışabileceğini bize gösterdi. Bu fikir, güçlü bir tasarım kavramına yol açar: ortak arayüz. Birden fazla sınıfı kasıtlı olarak aynı metot imzalarına sahip olacak şekilde tasarladığınızda, birbirinin yerine geçebilen bileşenler oluşturursunuz.
Bir günlükleme sistemi oluşturduğunuzu hayal edin. Mesajları farklı hedeflere—bir dosyaya, ekrana veya hatta bir ağa—göndermek isteyebilirsiniz. Her biri için ayrı kod yazmak yerine, tüm çıktı sınıflarınızı aynı :write() metodunu uygulayacak şekilde tasarlarsınız:
local LogFile = {}
LogFile.__index = LogFile
function LogFile:new(filename)
local obj = {filename = filename}
setmetatable(obj, LogFile)
return obj
end
function LogFile:write(message)
print("[FILE:" .. self.filename .. "] " .. message)
end
local Screen = {}
Screen.__index = Screen
function Screen:new()
local obj = {}
setmetatable(obj, Screen)
return obj
end
function Screen:write(message)
print("[SCREEN] " .. message)
endArtık mesaj çıktısı vermesi gereken herhangi bir kod, her iki türü de kabul edebilir:
local function logMessage(output, msg)
output:write(msg)
end
local file = LogFile:new("app.log")
local screen = Screen:new()
logMessage(file, "Starting up") -- [DOSYA:app.log] Başlatılıyor
logMessage(screen, "Starting up") -- [EKRAN] BaşlatılıyorlogMessage fonksiyonu hangi sınıfı aldığını bilmez veya umursamaz. Sadece bir :write() metodu bekler. Bu, kodunuzu esnek hale getirir; mevcut fonksiyonları değiştirmeden daha sonra yeni çıktı türleri ekleyebilirsiniz.
Görev
KolayFarklı ödeme yöntemlerinin birbirinin yerine kullanılabildiği bir ödeme işleme sistemi kuralım! Ortak bir arayüzü—bir :process(amount) metodunu—paylaşan, tamamen birbirinden bağımsız iki ödeme sınıfı oluşturacaksınız; bu sayede ödemeleri işleyen herhangi bir kod, aradaki farkı bilmeden her iki tiple de çalışabilecektir.
Kodunuzu üç dosya halinde düzenleyeceksiniz:
CreditCard.lua: Kart numarasını saklayan bir:new(cardNumber)yapıcı metoduna (constructor) sahip bir ödeme sınıfı. Bu sınıfın:process(amount)metodu"Charging " .. amount .. " to card " .. self.cardNumber(veyaCharging {amount} to card {cardNumber}formatında) yazdırmalıdır.BankTransfer.lua: Bir:new(accountId)yapıcı metoduna sahip, tamamen ayrı bir ödeme sınıfı. Bu sınıfın:process(amount)metodu"Transferring " .. amount .. " from account " .. self.accountId(veyaTransferring {amount} from account {accountId}formatında) yazdırmalıdır.main.lua: Herhangi bir ödeme nesnesini ve bir tutarı kabul eden, ardından bu nesne üzerinde:process(amount)metodunu çağıranhandlePayment(paymentMethod, amount)adında bir fonksiyon oluşturun. Bu fonksiyonun bir kredi kartıyla mı yoksa banka havalesiyle mi uğraştığını bilmesine gerek yoktur; sadece nesnenin bir:process()metoduna sahip olmasını bekler.
Üç girdi alacaksınız:
- Bir kredi kartı numarası (örneğin,
4532-1234-5678) - Bir banka hesap kimliği (örneğin,
ACC-9876) - Bir ödeme tutarı (örneğin,
150)
Ana dosyanızda, ilk iki girdiyi kullanarak her ödeme tipinden birer örnek (instance) oluşturun. Ardından, verilen tutarı her iki ödeme yöntemiyle işlemek için handlePayment fonksiyonunuzu kullanın—önce kredi kartı, sonra banka havalesi.
Örneğin, girdiler 1111-2222-3333, ACC-5555 ve 75 ise çıktı şu şekilde olmalıdır:
Charging 75 to card 1111-2222-3333
Transferring 75 from account ACC-5555Bu tasarımın güzelliği, handlePayment fonksiyonunuzun :process() metodunu uygulayan herhangi bir nesneyle çalışabilmesidir. Yarın bir PayPal sınıfı ekleyebilirsiniz ve :process(amount) metoduna sahip olduğu sürece, mevcut fonksiyonunuzla hiçbir değişiklik yapmadan çalışacaktır!
Kopya kağıdı
Ortak bir arayüz (common interface), birbiriyle ilişkisiz birden fazla sınıfın kasıtlı olarak aynı metot imzalarını uyguladığı ve böylece birbirlerinin yerine kullanılabildiği bir tasarım desenidir.
Sınıfları metot adlarını paylaşacak şekilde tasarlayarak, bu arayüzü uygulayan herhangi bir nesneyle çalışan fonksiyonlar yazabilirsiniz:
local LogFile = {}
LogFile.__index = LogFile
function LogFile:new(filename)
local obj = {filename = filename}
setmetatable(obj, LogFile)
return obj
end
function LogFile:write(message)
print("[FILE:" .. self.filename .. "] " .. message)
end
local Screen = {}
Screen.__index = Screen
function Screen:new()
local obj = {}
setmetatable(obj, Screen)
return obj
end
function Screen:write(message)
print("[SCREEN] " .. message)
endFonksiyonlar, beklenen metodu uygulayan herhangi bir nesneyi kabul edebilir:
local function logMessage(output, msg)
output:write(msg)
end
local file = LogFile:new("app.log")
local screen = Screen:new()
logMessage(file, "Starting up") -- [FILE:app.log] Starting up
logMessage(screen, "Starting up") -- [SCREEN] Starting uplogMessage fonksiyonunun hangi sınıfı aldığını bilmesine gerek yoktur; yalnızca bir :write() metodu bekler. Bu, kodu esnek hale getirir ve mevcut fonksiyonları değiştirmeden yeni türler eklemeye olanak tanır.
Kendin dene
-- Ödeme sınıflarını içe aktar
local CreditCard = require('CreditCard')
local BankTransfer = require('BankTransfer')
-- Girdileri oku
local cardNumber = io.read()
local accountId = io.read()
local amount = tonumber(io.read())
-- TODO: Herhangi bir ödeme nesnesini ve bir tutarı kabul eden,
-- ardından üzerinde :process(amount) yöntemini çağıran handlePayment fonksiyonunu oluşturun
-- TODO: Girdileri kullanarak CreditCard ve BankTransfer örneklerini oluşturun
-- TODO: Tutarı her iki ödeme yöntemiyle işlemek için handlePayment fonksiyonunu kullanın
-- Önce kredi kartı, ardından banka havalesi
Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.
Object Oriented Programming bölümündeki tüm dersler
1'Self' Kavramı
Fonksiyonlu TablolarAçık 'self'İki Nokta SözdizimiNokta vs İki NoktaÖzet - Hareket Eden Nokta4Proje: Dijital Banka
Proje KurulumuPara Yatırma Metodu7Çok Biçimlilik ve Geçersiz Kılma
Metotları Geçersiz KılmaÜst Sınıf Metotlarını ÇağırmaDuck TypingOrtak ArayüzTür KontrolüÖzet - Çalışan Rolleri2Sınıf Prototip Kalıbı
Prototip Kavramı__index ile Bağlama:new() KurucusuÖznitelikleri BaşlatmaBağımsız ÖrneklerÖzet - Araba Fabrikası5OOP'de Operatör Aşırı Yükleme
Nesneleri ToplamaNesneleri ÇıkarmaNesneleri BirleştirmeNesneleri Karşılaştırma (<, >)Özet - Cüzdan Matematiği8Kapsülleme
Adlandırma KurallarıGizlilik için Closure'larClosure'lar Aracılığıyla ErişimSalt Okunur TablolarDoğrulama MantığıÖzet - Güvenli Kasa11Tasarım Kalıpları (Lite)
Fabrika FonksiyonlarıSingleton TablosuIterator KalıbıObserver (Dinleyici)Özet - Logger Fabrikası3Nesne Durumu ve Davranışı
Örnek DeğişkenleriGetter MetotlarıSetter MetotlarıHesaplanmış ÖzelliklerString BiçimlendirmeEşitlik KontrolleriÖzet - Öğrenci Notu6Kalıtımın Temelleri
Kalıtım KurulumuMetot KalıtımıConstructor'ı GenişletmeAlt Sınıf Metotları EklemePaylaşılan ve BenzersizÖzet - Şekil Hiyerarşisi