Polimorfizm gerçek dünyada nasıl kullanılır? [kapalı]


17

Polimorfizmin gerçek bir yaşam projesinde nasıl kullanıldığını anlamaya çalışıyorum, ancak Animalbir yöntemle bir üst sınıf speak()ve bu yöntemi geçersiz kılan birçok çocuk sınıfının klasik örneğini (veya buna benzer bir şey) bulabilirim ve şimdi yöntemi speak()alt nesnelerden herhangi birinde çağırabilirsiniz, örneğin:

Animal animal;

animal = dog;
animal.speak();

animal = cat;
animal.speak();



1
Her gün gördüğünüz ve kullandığınız koleksiyonların kendisi, polimorfizmin ne olduğunu anlamak için yeterlidir. Ancak, polimorfizmin problem çözmede etkili bir şekilde nasıl kullanılacağı, sadece tartışarak değil, deneyimle en çok kazandığınız bir beceridir. Devam et ve ellerini kirlet.
Durgadass S

Hepsi bir tür minimum arabirimi (örneğin çizilmesi gereken bir nesne kümesi) destekleyecek bir dizi kümeniz varsa, bir arabirim çizmek için nesneler arasındaki farkları gizlemek için genellikle iyi bir seçimdir. Ayrıca, bir temel nesneye hizmet edebilecek yöntemlere ve ondan aşağı yukarı aynı şekilde miras alan önemli sayıda türe sahip bir API oluşturuyorsanız (veya birlikte çalışıyorsanız) , polimorfizm arasındaki farkları soyutlamanın en iyi yolu olabilir. bu tür.
jrh

Genel olarak, farklı türleri işlemek için sık sık aşırı yüklenmiş yöntemler yapıyorsanız ve kod benzerse veya if(x is SomeType) DoSomething()sık sık yazıyorsanız , polimorfizm kullanmaya değer olabilir. Benim için polimorfizm ne zaman ayrı bir yöntem yapmak için benzer bir karardır, eğer kodu birkaç kez tekrarladım genellikle bir yönteme refactor ve eğer ben if object is this type do thissık sık kod yapmak bulmak , bu olabilir yeniden düzenleme ve bir arayüz veya sınıf eklemeye değer.
jrh

Yanıtlar:


35

Akış , polimorfizmin harika bir örneğidir.

Akış, "okunabilen veya yazılabilen bayt dizisini" temsil eder. Ancak bu sıra dosya, bellek veya birçok ağ bağlantısından gelebilir. Veya varolan akışı saran ve baytları şifreleme veya sıkıştırma gibi bir şekilde dönüştüren dekoratör olarak hizmet edebilir.

Bu şekilde, Stream kullanan istemcinin baytların nereden geldiğini önemsemesi gerekmez. Sadece sırayla okunabilirler.

Bazıları Stream, polimorfizmin yanlış bir örneği olduğunu söyleyebilir , çünkü uygulayıcıların desteklemediği birçok "özelliği" tanımlar, örneğin ağ akışı yalnızca okuma veya yazmaya izin verir, ancak her ikisini de aynı anda değil. Veya arayış eksikliği. Ancak bu, yalnızca Streambağımsız olarak uygulanabilecek birçok bölüme ayrılabileceği gibi karmaşıklık meselesidir .


2
C ++ gibi çoklu ve sanal kalıtıma sahip dillerde, bu örnek, bir temel akış sınıfından giriş ve çıkış akışı sınıfları türeterek ve her ikisini de bir G / Ç akışı oluşturmak için genişleterek "korkunç elmas" desenini bile gösterebilir
gyre

2
@gyre Ve iyi yapılmışsa, elmas desenini “korkutmak” için hiçbir neden yok. Elmastaki karşıt mevkiinin farkında olmak ve onunla isim çatışmalarına neden olmamak önemlidir ve bir meydan okuma ve sinir bozucu ve uygulanabilir olduğunda elmas deseninden kaçınmak için neden ... ama ne zaman çok korkma sadece bir adlandırma kuralına sahip olmak sorunları çözebilir.
KRyan

+1 Streams, tüm zamanların en sevdiğim polimorfizm örneğidir. İnsanlara artık kusurlu 'hayvan, memeli, köpek' modelini öğretmeye çalışmıyorum, Streamancak daha iyi bir iş çıkarıyorlar.
Pharap

@KRyan "Düşünce elmas" diyerek kendi düşüncelerimi ifade etmiyordum, sadece böyle ifade edildiğini duydum. Tamamen katılıyorum; Bence bu her geliştiricinin başını sarıp uygun bir şekilde kullanabilmesi gereken bir şey.
gyre

@gyre Oh, evet, aslında anladım; bu yüzden bunun bir çelişkiden ziyade düşüncelerinizin bir uzantısı olduğunu belirtmek için “ve” ile başladım.
KRyan

7

Oyunlarla ilgili tipik bir örnek Entity, draw()veya gibi ortak üyeler sağlayan bir temel sınıf olacaktır update().

Daha saf veri odaklı bir örnek Serializableiçin, ortak bir saveToStream()ve sağlayan bir temel sınıf olabilir loadFromStream().


6

Farklı türlerde polimorfizm vardır, ilgilenilenlerden biri genellikle çalışma zamanı polimorfizmi / dinamik gönderisidir.

Çalışma zamanı polimorfizminin çok yüksek bir açıklaması, bir yöntem çağrısının bağımsız değişkenlerinin çalışma zamanı türüne bağlı olarak farklı şeyler yapmasıdır: nesnenin kendisi bir yöntem çağrısını çözmekle sorumludur. Bu büyük miktarda esneklik sağlar.

Bu esnekliği kullanmanın en yaygın yollarından biri bağımlılık enjeksiyonudur , örneğin farklı uygulamalar arasında geçiş yapabilir veya test için sahte nesneler enjekte edebilirim. Önceden sadece sınırlı sayıda olası seçenek olacağını biliyorum eğer onları koşullu, örneğin kodlamak için deneyebilirsiniz:

void foo() {
  if (isTesting) {
    ... // do mock stuff
  } else {
    ... // do normal stuff
  }
}

Bu, kodun izlenmesini zorlaştırır. Alternatif, söz konusu foo-operasyon için bir arayüz sunmak ve normal bir uygulama ve o arayüzün sahte bir uygulamasını yazmak ve çalışma zamanında istenen uygulamaya "enjekte etmek" tir. “Bağımlılık enjeksiyonu” “doğru nesneyi argüman olarak geçirmek” için karmaşık bir terimdir.

Gerçek dünyadaki bir örnek olarak, şu anda bir tür makine öğrenme sorunu üzerinde çalışıyorum. Bir tahmin modeli gerektiren bir algoritmaya sahibim. Ama farklı makine öğrenme algoritmalarını denemek istiyorum. Bu yüzden bir arayüz tanımladım. Tahmin modelimden neye ihtiyacım var? Bazı girdi örnekleri, tahmin ve hataları göz önüne alındığında:

interface Model {
  def predict(sample) -> (prediction: float, std: float);
}

Algoritmam bir modeli eğiten bir fabrika işlevi alır:

def my_algorithm(..., train_model: (observations) -> Model, ...) {
  ...
  Model model = train_model(observations);
  ...
  y, std = model.predict(x)
  ...
}

Şimdi model arayüzünde çeşitli uygulamalara sahibim ve bunları birbiriyle kıyaslayabilirim. Bu uygulamalardan biri aslında iki model daha alır ve bunları güçlendirilmiş bir modelde birleştirir. Bu arayüz sayesinde:

  • algoritmamın belirli modelleri önceden bilmesine gerek yok,
  • Modelleri kolayca değiştirebilirim ve
  • Modellerimi uygulama konusunda çok esnekim var.

GUI'lerde klasik bir polimorfizm kullanım durumu vardır. Java AWT / Swing /… gibi bir GUI çerçevesinde farklı bileşenler vardır . Bileşen arabirimi / taban sınıfı, kendisini ekrana boyamak veya fare tıklamalarına tepki vermek gibi eylemleri açıklar. Birçok bileşen, alt bileşenleri yöneten kaplardır. Böyle bir konteyner kendini nasıl çizebilir?

void paint(Graphics g) {
  super.paint(g);
  for (Component child : this.subComponents)
    child.paint(g);
}

Burada, konteynerin alt bileşenlerin kesin tiplerini önceden bilmesine gerek yoktur - Componentkonteynere arayüze uyduğu sürece konteyner sadece polimorfik paint()yöntemi çağırabilir . Bu bana AWT sınıfı hiyerarşisini keyfi yeni bileşenlerle genişletme özgürlüğü veriyor.

Yazılım geliştirme boyunca, polimorfizm teknik olarak uygulanarak çözülebilen birçok tekrarlanan problem vardır. Bu tekrar eden problem-çözüm çiftlerine tasarım desenleri denir ve bunlardan bazıları aynı isimli kitapta toplanır. Bu kitap açısından, enjekte edilen makine öğrenme modelim “bir algoritma ailesi tanımlamak, her birini kapsüllemek ve bunları değiştirilebilir yapmak” için kullandığım bir strateji olurdu . Bir bileşenin alt bileşenler içerebileceği Java-AWT örneği bir bileşik örneğidir .

Ancak her tasarımın polimorfizm kullanması gerekmez (birim testi için bağımlılık enjeksiyonunu mümkün kılmanın ötesinde, bu gerçekten iyi bir kullanım durumudur). Çoğu problem aksi halde çok durağandır. Sonuç olarak, sınıflar ve yöntemler genellikle polimorfizm için değil, basit ad alanları olarak ve güzel yöntem sözdizimi olarak kullanılır. Örneğin, birçok geliştirici account.getBalance()büyük ölçüde eşdeğer bir işlev çağrısı gibi yöntem çağrılarını tercih eder Account_getBalance(account). Bu mükemmel bir yaklaşım, sadece birçok “yöntem” çağrısının polimorfizmle hiçbir ilgisi yok.


6

Çoğu UI araç setinde çok sayıda kalıtım ve polimorfizm görürsünüz.

Örneğin, JavaFX UI araç içinde, Buttondevralır den ButtonBaseolan devralır arasından Labeledhangi devralır arasından Controlhangi devralır arasından Regionhangi devralır arasından Parenthangi devralır arasından Nodehangi devralır gelen Object. Birçok katman, öncekilerden bazı yöntemleri geçersiz kılar.

Bu düğmenin ekranda görünmesini istediğinizde, alt Paneöğeden devralınan her şeyi kabul edebilecek bir a öğesine eklersiniz Node. Ancak bir Bölme, yalnızca genel bir Düğüm nesnesi olarak gördüğünde bir Düğme ile ne yapılacağını nasıl bilir? Bu nesne herhangi bir şey olabilir. Bölme bunu yapabilir, çünkü Düğme Düğüm yöntemlerini herhangi bir düğmeye özgü mantıkla yeniden tanımlar. Bölme yalnızca Düğümde tanımlanan yöntemleri çağırır ve gerisini nesnenin kendisine bırakır. Bu, uygulanan polimorfizmin mükemmel bir örneğidir.

UI araç takımları, hem akademik hem de pratik nedenlerle öğretilmelerini faydalı kılan, gerçek dünyadaki çok yüksek bir öneme sahiptir.

Ancak, kullanıcı arayüzü, önemli bir dezavantajı var Toolkits: Onlar olma eğilimi büyük . Bir neofit yazılım mühendisi ortak bir UI çerçevesinin iç işleyişini anlamaya çalıştığında , çoğu çok ezoterik amaçlara hizmet eden yüzün üzerinde sınıfla karşılaşırlar . "Halt bir nedir ReadOnlyJavaBeanLongPropertyBuilder? Önemli midir? I Do var ne için 's iyi anlamak için?" Yeni başlayanlar bu tavşan deliğinde kolayca kaybolabilir. Bu yüzden ya teröre kaçabilirler ya da sözdizimini öğrendikleri yüzeyde kalabilirler ve kaputun altında gerçekte neler olduğu hakkında çok fazla düşünmemeye çalışabilirler.


3

Burada zaten güzel örnekler olsa da, bir diğeri hayvanları cihazlarla değiştirmektir:

  • Deviceolabilir powerOn(), powerOff(), setSleep()ve teneke getSerialNumber().
  • SensorDeviceBütün bunları yapmak ve bu nedenle polimorfik fonksiyonları sağlayabilir getMeasuredDimension(), getMeasure(), alertAt(threashhold)ve autoTest().
  • Tabii ki, getMeasure()bir sıcaklık sensörü, bir ışık dedektörü, bir ses dedektörü veya bir hacimsel sensör için aynı şekilde uygulanmayacaktır. Ve elbette, bu daha uzmanlaşmış sensörlerin her birinin ek işlevleri olabilir.

2

Sunum çok yaygın bir uygulamadır, belki de en yaygın olanı ToString () 'dir. Temelde Animal.Speak (): Bir nesneye kendini göstermesini söylersiniz.

Daha genel olarak konuşmak gerekirse, bir nesneye "işini yapmasını" söylersiniz. Save, Load, Initialize, Dispose, ProcessData, GetStatus'u düşünün.


2

İlk pratik polimorfizm kullanımım, Java'da Heap uygulamasıydı.

Ben max ve min arasındaki fark sadece yöntem karşılaştırma nasıl olurdu olurdu yöntem insert, removeTop uygulanması ile temel sınıf vardı.

abstract class Heap {  

 abstract boolean compare ( int x , int y );

 boolean insert(int x ) { ... }

 int removeTop() { ... }
}

Ben MaxHeap ve MinHeap sahip olmak istediğimde, sadece miras kullanabilirsiniz.

class MaxHeap extends Heap {

   MaxHeap(int maxSize) {super(maxSize);}

   @Override
   boolean compare(int x, int y) {
       return x>y; // x<y for minHeap
   }
}

1

İşte web uygulaması / veritabanı tablosu polimorfizmi için gerçek bir yaşam senaryosu :

Ruby on Rails'i web uygulamaları geliştirmek için kullanıyorum ve birçok projemde ortak olan bir şey dosya (fotoğraf, PDF, vb.) Yükleme yeteneğidir. Örneğin, Usera'nın birden fazla profil resmi Productolabilir ve ayrıca birçok ürün resmi olabilir. Hem yükleme ve görüntüleri depolamak davranışını yanı sıra DRY kalmayı ve davranışlarını paylaşmak için vb küçük resimleri oluşturmaya, yeniden boyutlandırma var Picturegerçekleştirmek istediğimiz, Pictureher iki ait olabilir o polimorfik So Userve Product.

Rails'te modellerimi şöyle tasarlarım:

class Picture < ApplicationRecord
  belongs_to :imageable, polymorphic: true
end

class User < ApplicationRecord
  has_many :pictures, as: :imageable
end

class Product < ApplicationRecord
  has_many :pictures, as: :imageable
end

ve picturestablo oluşturmak için bir veritabanı geçişi :

class CreatePictures < ActiveRecord::Migration[5.0]
  def change
    create_table :pictures do |t|
      t.string  :name
      t.integer :imageable_id
      t.string  :imageable_type
      t.timestamps
    end

    add_index :pictures, [:imageable_type, :imageable_id]
  end
end

Sütunlar imageable_idve imageable_typeRaylar tarafından dahili olarak kullanılır. Temel olarak, imageable_typesınıfın (adını tutan "User", "Product"vs.), ve imageable_idilgili kayıtların kimliğidir. Yani imageable_type = "User"ve tablodaki imageable_id = 1kayıt olurdu .usersid = 1

Bu user.pictures, kullanıcının resimlerine erişmek ve product.picturesbir ürünün resimlerini almak gibi şeyler yapmamızı sağlar . Daha sonra, resimle ilgili tüm davranışlar Photosınıfta kapsüllenir (ve fotoğrafa ihtiyaç duyan her model için ayrı bir sınıf değil), bu yüzden şeyler KURU tutulur.

Daha fazla okuma: Polimorfik ilişkileri raylar .


0

Kabarcık sıralaması, ekleme sıralaması, hızlı sıralama, yığın sıralaması vb.

İstemci sıralama arayüzü ile sağlanan sadece girdi olarak dizi sağlamak ve daha sonra sıralanmış dizi almak endişe. Çalışma zamanı sırasında belirli faktörlere bağlı olarak uygun sıralama uygulaması kullanılabilir. Bu, polimorfizmin kullanıldığı gerçek dünya örneğidir.

Yukarıda tarif ettiğim, çalışma zamanı polimorfizminin bir örneğidir, oysa yöntem aşırı yüklemesi, derleyicinin i / p ve o / p parametre tiplerine ve parametrelerin sayısına uygun bir zamanda doğru yöntemi ile bağladığı derleyici zaman polimorfisinin bir örneğidir.

Umarım bu açıklığa kavuşur.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.