Neler yeni

Yazılım Forum

Tüm özelliklerimize erişmek için şimdi bize katılın. Kayıt olduktan ve giriş yaptıktan sonra konu oluşturabilecek, mevcut konulara yanıt gönderebilecek, itibar kazanabilecek, özel mesajlaşmaya erişebilecek ve çok daha fazlasını yapabileceksiniz! Bu hizmetlerimiz ise tamamen ücretsiz ve kurallara uyulduğu sürece sınırsızdır, o zaman ne bekliyorsunuz? Hadi, sizde aramıza katılın!

Ruby'de Miras Alma: Temeller, Modüller ve En İyi Uygulamalar

Miras alma (Inheritance), nesne yönelimli programlamanın temel konseptlerinden biridir ve Ruby'de de güçlü bir şekilde desteklenir. Bu mekanizma, sınıflar arasında "bir türdür" (is-a) ilişkisi kurarak kodun yeniden kullanılabilirliğini artırmayı ve hiyerarşik yapılar oluşturmayı sağlar. Temel olarak, bir sınıfın (alt sınıf veya çocuk sınıf) başka bir sınıfın (üst sınıf veya ebeveyn sınıf) özelliklerini (nitelikler ve davranışlar) devralması anlamına gelir. Bu sayede, ortak işlevsellik tek bir yerde tanımlanabilir ve bu işlevselliği kullanan tüm alt sınıflar otomatik olarak bu özelliklere sahip olur, böylece kod tekrarı önlenir ve bakım kolaylaşır.

Temel Miras Alma Sözdizimi:
Ruby'de miras alma işlemi `<` operatörü ile belirtilir.
Kod:
class EbeveynSinif
  def ebeveyn_metodu
    "Ben Ebeveyn Sinifindayim."
  end

  def ortak_metod
    "Bu metod Ebeveyn Sinifindan geliyor."
  end
end

class CocukSinif < EbeveynSinif
  def cocuk_metodu
    "Ben Cocuk Sinifindayim."
  end

  # Ebeveynin bir metodunu geçersiz kılma (override)
  def ortak_metod
    "Bu metod Cocuk Sinifindan geliyor."
  end
end

ebeveyn = EbeveynSinif.new
puts ebeveyn.ebeveyn_metodu # => Ben Ebeveyn Sinifindayim.
puts ebeveyn.ortak_metod # => Bu metod Ebeveyn Sinifindan geliyor.

cocuk = CocukSinif.new
puts cocuk.ebeveyn_metodu # => Ben Ebeveyn Sinifindayim. (Ebeveyninden miras aldı)
puts cocuk.cocuk_metodu # => Ben Cocuk Sinifindayim.
puts cocuk.ortak_metod # => Bu metod Cocuk Sinifindan geliyor. (Kendi versiyonunu kullandı)
Yukarıdaki örnekte, `CocukSinif`, `EbeveynSinif`'tan miras almıştır. Bu, `CocukSinif` örneklerinin `ebeveyn_metodu`'nu da çağırabileceği anlamına gelir. Ayrıca, `ortak_metod`'u hem ebeveyn hem de çocuk sınıfta tanımladık. Çocuk sınıfın kendi `ortak_metod` versiyonu, ebeveynin metodunu geçersiz kılarak kendi özel davranışını sunmuştur.

`super` Anahtar Kelimesi:
Bazen bir alt sınıfın, miras aldığı metodu tamamen geçersiz kılmak yerine, ebeveyninin davranışını genişletmesi gerekebilir. İşte burada `super` anahtar kelimesi devreye girer. `super`, geçerli metodun ebeveyn sınıfındaki implementasyonunu çağırmak için kullanılır.
Kod:
class Arac
  def basla
    "Motor çalışıyor."
  end
end

class Araba < Arac
  def basla
    "#{super} Vitese takıldı ve yola çıktı!"
  end
end

araba = Araba.new
puts araba.basla # => Motor çalışıyor. Vitese takıldı ve yola çıktı!
`super` ayrıca parametrelerle de kullanılabilir. Eğer `super` parantez olmadan çağrılırsa, çağrıldığı metodun tüm parametrelerini otomatik olarak ebeveyn metoduna iletir. Eğer belirli parametreler iletilmek isteniyorsa, `super(param1, param2)` şeklinde kullanılabilir. Hiçbir parametre iletmek istemiyorsanız `super()` kullanabilirsiniz.
Kod:
class Hesap
  def initialize(bakiye)
    @bakiye = bakiye
  end

  def durum
    "Mevcut Bakiye: #{@bakiye} TL"
  end
end

class VadeliHesap < Hesap
  def initialize(bakiye, vade)
    super(bakiye) # Ebeveynin initialize metodunu bakiye ile çağır
    @vade = vade
  end

  def durum
    "#{super} (Vade: #{@vade} ay)"
  end
end

vadeli = VadeliHesap.new(1000, 12)
puts vadeli.durum # => Mevcut Bakiye: 1000 TL (Vade: 12 ay)

Metod Arama Yolu (Method Lookup Path):
Ruby'de bir metoda çağrı yapıldığında, Ruby bu metodu nasıl bulur? Bu, metod arama yolu olarak bilinir. Bir objenin metodunu çağırdığınızda, Ruby önce objenin sınıfında metodu arar. Eğer bulamazsa, bir üst sınıfa bakar, sonra onun üst sınıfına ve bu şekilde devam eder, ta ki `BasicObject` sınıfına ulaşana kadar. Bu yol, `ancestors` metodu ile görülebilir.
Kod:
class A; end
class B < A; end
class C < B; end

puts C.ancestors.inspect # => [C, B, A, Object, Kernel, BasicObject]
Bu çıktı, bir `C` objesi için metodun önce `C` sınıfında, sonra `B` sınıfında, sonra `A` sınıfında arandığını gösterir. Daha sonra `Object`, `Kernel` (bir modül ama `Object`'e dahil edilmiş), ve son olarak `BasicObject`'te arandığını görürüz. Bu sıralama, miras zincirindeki metod önceliğini belirler.

Miras Alma ve Modüller (Mixins):
Ruby'de sınıflar tek bir üst sınıftan miras alabilir (tek miras). Yani bir sınıf sadece bir ebeveyne sahip olabilir. Ancak, Ruby bu kısıtlamayı modüller (modules) aracılığıyla "mixin" adı verilen bir konseptle aşar. Modüller, davranışları sınıflara karıştırmak (mix in) için kullanılır. Bu, kod tekrarını önlemenin ve belirli işlevsellikleri farklı sınıflar arasında paylaşmanın çok etkili bir yoludur. İki ana modül mekanizması vardır: `include` ve `extend`.

* include: Bir modülü bir sınıfa `include` ettiğinizde, modülün metodları bu sınıfın _örnek metodları_ haline gelir. Yani, bu sınıftan türetilen nesneler bu metodları çağırabilir. Modül, sınıfın miras zincirine, sınıfın hemen üstüne yerleşir.
Kod:
    module Sesli
      def ses_cikar
        "Ses çıkardım!"
      end
    end

    class Kedi
      include Sesli
      def miyavla
        "Miyav!"
      end
    end

    class Kopek
      include Sesli
      def havla
        "Hav hav!"
      end
    end

    kedi = Kedi.new
    puts kedi.miyavla # => Miyav!
    puts kedi.ses_cikar # => Ses çıkardım!

    kopek = Kopek.new
    puts kopek.havla # => Hav hav!
    puts kopek.ses_cikar # => Ses çıkardım!

    puts Kedi.ancestors.inspect # => [Kedi, Sesli, Object, Kernel, BasicObject]
Gördüğünüz gibi, `Sesli` modülü `Kedi` sınıfının ataları arasına girmiş ve `Kedi` sınıfının metod arama yoluna dahil olmuştur. Bu, modülün metodlarının, sınıfın kendi metodlarından veya miras aldığı ebeveyn metodlarından önce bulunabileceği anlamına gelir (eğer aynı isimde bir metod varsa ve modül daha önce geliyorsa).

* extend: Bir modülü bir sınıfa `extend` ettiğinizde, modülün metodları bu sınıfın _sınıf metodları_ haline gelir. Yani, bu metodlar sınıfın kendisi üzerinden çağrılabilir, örnekleri üzerinden değil. `extend` genellikle yardımcı metodlar veya fabrika metodları gibi sınıf düzeyinde işlevsellik eklemek için kullanılır.
Kod:
    module Sayici
      def sayi_ver
        puts "Rastgele sayı: #{rand(100)}"
      end
    end

    class Ayakkabi
      extend Sayici
      def initialize(boyut)
        @boyut = boyut
      end
    end

    Ayakkabi.sayi_ver # => Rastgele sayı: (örneğin) 42
    # ayakkabi = Ayakkabi.new(42)
    # ayakkabi.sayi_ver # => Hata! NoMethodError
`extend` kullanıldığında, modülün metodları doğrudan sınıfın kendisi tarafından çağrılabilir hale gelir. Bu, özellikle bir sınıfa tekil (singleton) davranışlar veya genel yardımcı işlevler eklemek istediğinizde kullanışlıdır.

`BasicObject` ve `Object` Hiyerarşisi:
Ruby'deki tüm sınıflar nihayetinde `BasicObject`'ten miras alır. `Object` sınıfı, çoğu standart Ruby objesinin varsayılan ebeveynidir ve bir dizi temel metod (örneğin `puts`, `inspect`, `nil?` gibi) sağlar.
  • BasicObject: Ruby'deki en üst düzey sınıftır. Çok az metoda sahiptir ve bu, özellikle proxy objeleri veya metot çağrılarını ele geçiren objeler oluştururken "temiz" bir başlangıç noktası sağlar.
  • Object: `BasicObject`'ten miras alır. Standart Ruby objelerinin sahip olduğu tüm temel metodları (örneğin `==`, `eql?`, `hash`, `inspect`, `to_s`, `method_missing` vb.) içerir. Eğer bir sınıfın ebeveynini belirtmezseniz, Ruby varsayılan olarak `Object`'ten miras aldığını kabul eder.
Kod:
class BenimSinifim # Otomatik olarak Object'ten miras alır
  # ...
end

puts BenimSinifim.ancestors.inspect # => [BenimSinifim, Object, Kernel, BasicObject]

Miras Almanın Avantajları ve Dezavantajları:
  • Avantajlar:
  • Kod Tekrarını Azaltma: Ortak metodları ve özellikleri tek bir üst sınıfta tanımlayarak kod tekrarını önler.
  • Kod Bakımını Kolaylaştırma: Değişiklikler tek bir yerde yapıldığında, bu değişiklikler miras alan tüm sınıflara yansır.
  • Polimorfizm: Farklı sınıflardaki nesnelerin aynı arayüze sahip olmasını ve farklı şekillerde davranmasını sağlar, bu da esnek ve genişletilebilir tasarımlara yol açar.
  • Hiyerarşik Yapılar: Gerçek dünya ilişkilerini (örneğin, Hayvan -> Köpek -> Labrador) modellemeyi kolaylaştırır.
  • Dezavantajlar:
  • Sıkı Bağlılık (Tight Coupling): Alt sınıflar ebeveynlerine sıkı sıkıya bağlı hale gelir. Ebeveyn sınıfta yapılan değişiklikler, alt sınıfları beklenmedik şekillerde etkileyebilir.
  • Kırılgan Taban Sınıf Sorunu (Fragile Base Class Problem): Ebeveyn sınıfın implementasyonundaki değişiklikler, alt sınıfların davranışını bozabilir, çünkü alt sınıflar ebeveynin iç detaylarına bağımlı olabilir.
  • Karmaşıklık: Çok derin veya karmaşık miras hiyerarşileri, kodu anlamayı ve yönetmeyi zorlaştırabilir.
  • Esneklik Eksikliği: Bir sınıfın sadece tek bir ebeveyni olabileceği için, bazen birden fazla bağımsız davranış kümesini bir sınıfa dahil etmek zorlaşır. (Ruby'de modüller bu sorunu bir ölçüde çözer).

Miras Alma Yerine Bileşim (Composition over Inheritance):
Nesne yönelimli tasarımda sıkça vurgulanan bir prensip, "bileşim miras almadan daha iyidir" (composition over inheritance) prensibidir. Bu, "bir türdür" (is-a) ilişkisi yerine "sahiptir" (has-a) ilişkisine odaklanmayı önerir. Yani, bir sınıfın başka bir sınıfın özelliklerini miras almak yerine, o sınıfın bir örneğini içermesi ve onun metodlarını kullanmasıdır.
"Favor composition over inheritance." - Gang of Four, Design Patterns: Elements of Reusable Object-Oriented Software.
Bu yaklaşım, sınıflar arasındaki bağımlılığı azaltır, esnekliği artırır ve kodun daha kolay test edilmesini sağlar. Özellikle, bir sınıfın birçok farklı davranışa sahip olması gerektiğinde veya bu davranışların dinamik olarak değiştirilmesi gerektiğinde bileşim daha avantajlıdır.

Örneğin, bir "Şarkıcı" sınıfı düşünelim. Bir şarkıcı şarkı söyleyebilir. Şarkıcı bir "Müzisyen" mi dir? Evet, bir "Müzisyen" bir tür "Şarkıcı" olabilir. Ama aynı zamanda bir "Gitarcı" da bir "Müzisyen"dir. Eğer miras zinciriyle giderseniz, hiyerarşi karmaşıklaşabilir.
Bileşim ile, bir `Şarkıcı` sınıfının bir `SesCikaranCihaz` (örneğin bir mikrofon) objesine sahip olması gibi düşünebiliriz.
Kod:
class Mikrofon
  def ses_al_ve_yukselt
    "Ses alınıyor ve yükseltiliyor."
  end
end

class Hoparlor
  def ses_ver
    "Ses veriliyor."
  end
end

class Sarkici
  def initialize(mikrofon, hoparlor)
    @mikrofon = mikrofon
    @hoparlor = hoparlor
  end

  def sarki_soyle
    puts "Şarkı başlıyor..."
    puts @mikrofon.ses_al_ve_yukselt
    puts @hoparlor.ses_ver
    puts "Şarkı bitti."
  end
end

mikrofon = Mikrofon.new
hoparlor = Hoparlor.new
sarkici = Sarkici.new(mikrofon, hoparlor)
sarkici.sarki_soyle
# => Şarkı başlıyor...
# => Ses alınıyor ve yükseltiliyor.
# => Ses veriliyor.
# => Şarkı bitti.
Burada `Sarkici`, `Mikrofon` ve `Hoparlor`'ün davranışlarını miras almak yerine, onların nesnelerini içererek kullanıyor. Bu, daha modüler ve esnek bir tasarım sağlar. Eğer bir şarkıcının farklı bir ses ekipmanı kullanması gerekirse, sadece `initialize` metoduna farklı nesneler geçirmek yeterli olur.

Soyut Sınıflar (Abstract Classes) ve Arayüzler (Interfaces):
Ruby'de Java veya C++'daki gibi doğrudan "soyut sınıf" veya "arayüz" kavramları yoktur. Ancak, bu konseptleri Ruby'de modüller ve metod eksikliği (method_missing) gibi özellikler kullanarak taklit edebiliriz.
  • Soyut Sınıf Benzetimi (Modüller ile): Bir modülü belirli metodları içerecek şekilde tanımlayıp, bu modülü `include` eden sınıfların bu metodları implemente etmesini bekleyebiliriz. Eğer bir sınıf beklenen metodu implemente etmezse, çalıştırma zamanında hata verecektir.
    Kod:
        module Sekil
          def alan_hesapla
            raise NotImplementedError, "Bu metodun alt sınıfta implemente edilmesi gerekir."
          end
    
          def cevre_hesapla
            raise NotImplementedError, "Bu metodun alt sınıfta implemente edilmesi gerekir."
          end
        end
    
        class Kare
          include Sekil
          def initialize(kenar)
            @kenar = kenar
          end
    
          def alan_hesapla
            @kenar * @kenar
          end
    
          def cevre_hesapla
            4 * @kenar
          end
        end
    
        class Daire
          include Sekil
          def initialize(yaricap)
            @yaricap = yaricap
          end
    
          def alan_hesapla
            Math::PI * @yaricap**2
          end
        end
    
        kare = Kare.new(5)
        puts "Karenin alanı: #{kare.alan_hesapla}" # => Karenin alanı: 25
        puts "Karenin çevresi: #{kare.cevre_hesapla}" # => Karenin çevresi: 20
    
        daire = Daire.new(3)
        puts "Dairenin alanı: #{daire.alan_hesapla}" # => Dairenin alanı: 28.274...
        # puts daire.cevre_hesapla # => Hata! NotImplementedError
    Burada `Sekil` modülü, `alan_hesapla` ve `cevre_hesapla` metodlarının _var olması gerektiğini_ belirtir. Eğer bir sınıf bu metodları implemente etmezse, çağrıldığında bir `NotImplementedError` hatası fırlatır.
  • Arayüz Benzetimi (Ducks Typing ile): Ruby'nin "Duck Typing" prensibi, belirli bir arayüzü implemente etme zorunluluğu yerine, bir objenin _belli metodlara sahip olup olmadığına_ bakar. Eğer bir obje ihtiyacınız olan metodları çağırabiliyorsa, Ruby için o obje "doğru türdendir". Bu, resmi arayüz tanımlamalarına olan ihtiyacı azaltır ve daha esnek tasarımlara olanak tanır.
    "If it walks like a duck and quacks like a duck, then it must be a duck." - James Whitcomb Riley
    Yani, bir nesnenin bir arayüzü "implemente edip etmediği"ni kontrol etmek yerine, o nesnenin belirli bir metodu "yanıt verip vermediği"ne (`respond_to?` ile) bakabilir veya doğrudan metodu çağırabilirsiniz. Hata alırsanız, o zaman problem vardır.

Sonuç:
Ruby'de miras alma, kodun yeniden kullanılabilirliği ve hiyerarşik yapılandırma için güçlü bir araçtır. Ancak, doğru kullanıldığında faydalıdır. Tek miras sınırlaması, modüllerin `include` ve `extend` mekanizmalarıyla aşılmıştır, bu da Ruby'ye mixin'ler aracılığıyla çoklu miras benzeri yetenekler kazandırır. Metod arama yolu, Ruby'nin dinamik yapısının temelini oluşturur. Modern nesne yönelimli tasarımda, miras almanın getirdiği potansiyel sıkı bağlılık ve kırılganlık nedeniyle "bileşim miras almadan daha iyidir" prensibi sıkça tavsiye edilir. Her iki yaklaşımın da kendine göre avantajları ve kullanım senaryoları vardır; önemli olan, projenizin ihtiyaçlarına en uygun tasarımı seçebilmektir. İyi bir yazılım mühendisliği, sadece araçları bilmek değil, aynı zamanda onları ne zaman ve nasıl kullanacağını da bilmektir. Miras alma, karmaşıklığı azaltmak ve kodu düzenlemek için değerli bir araç olmaya devam etmektedir, ancak her zaman tek çözüm değildir. Özellikle derin hiyerarşilerden kaçınmak ve davranışsal esnekliği sağlamak için bileşim ve modüllerin gücünü anlamak ve kullanmak kritik öneme sahiptir.
 
shape1
shape2
shape3
shape4
shape5
shape6
Üst

Bu web sitenin performansı Hazal Host tarafından sağlanmaktadır.

YazilimForum.com.tr internet sitesi, 5651 sayılı Kanun’un 2. maddesinin 1. fıkrasının (m) bendi ve aynı Kanun’un 5. maddesi kapsamında Yer Sağlayıcı konumundadır. Sitede yer alan içerikler ön onay olmaksızın tamamen kullanıcılar tarafından oluşturulmaktadır.

YazilimForum.com.tr, kullanıcılar tarafından paylaşılan içeriklerin doğruluğunu, güncelliğini veya hukuka uygunluğunu garanti etmez ve içeriklerin kontrolü veya araştırılması ile yükümlü değildir. Kullanıcılar, paylaştıkları içeriklerden tamamen kendileri sorumludur.

Hukuka aykırı içerikleri fark ettiğinizde lütfen bize bildirin: lydexcoding@gmail.com

Sitemiz, kullanıcıların paylaştığı içerik ve bilgileri 6698 sayılı KVKK kapsamında işlemektedir. Kullanıcılar, kişisel verileriyle ilgili haklarını KVKK Politikası sayfasından inceleyebilir.

Sitede yer alan reklamlar veya üçüncü taraf bağlantılar için YazilimForum.com.tr herhangi bir sorumluluk kabul etmez.

Sitemizi kullanarak Forum Kuralları’nı kabul etmiş sayılırsınız.

DMCA.com Protection Status Copyrighted.com Registered & Protected