Aynı sınıftan oluşturulan nesnelerin benzersiz yöntem tanımları olabilir mi?


14

Biliyorum bu garip bir soru gibi görünüyor, çünkü aynı sınıfı paylaşan iki veya daha fazla nesnenin amacı davranışlarının aynı olması, yani yöntemlerinin aynı olması.

Ancak, nesnelerin yöntemlerini, alanları için farklı değerler atayabileceğiniz şekilde yeniden tanımlamanıza izin veren herhangi bir OOP dili olup olmadığını merak ediyorum. Sonuç, artık aynı davranışı sergilemeyen aynı sınıftan yapılan nesneler olacaktır.

Yanılmıyorsam bu JavaScript'i yapabilir misin? Bu soru ile birlikte, soruyorum, neden birisi bunu yapmak ister ki?


9
Teknik terim "prototip tabanlıdır". Bunu aramak size OOP'un bu lezzeti hakkında çok fazla malzeme verecektir. (Pek çok insanın bu tür yapıları uygun "sınıflar" olarak görmediklerini, tam olarak üniforma nitelik ve yöntemlere sahip olmadıklarını unutmayın.)
Kilian Foth

1
bir düğmenin iki farklı örneğinin tıklandıklarında nasıl farklı şeyler yapabileceğini mi düşünüyorsunuz, çünkü bunların her biri farklı bir düğme işleyiciye bağlanmış mı? Tabii ki, her zaman aynı şeyi yaptıkları iddia edilebilir - düğme işleyicileri diyorlar. Her neyse, diliniz delegeleri veya işlev işaretleyicilerini destekliyorsa, bu kolaydır - delege veya işlev işaretçisini tutan bazı özellik / alan / üye değişkeniniz vardır ve yöntemin uygulanması, sizi çağırır ve uzaklaştırır.
Kate Gregory

2
Karmaşık :) Sapan işlev referanslarının yaygın olduğu dillerde, bazı kodları bir setClickHandler()yönteme geçirmek ve aynı sınıfın farklı örneklerini çok farklı şeyler yapmak kolaydır . Uygun lambda ifadeleri olmayan dillerde, yalnızca yeni bir işleyici için yeni bir anonim alt sınıf oluşturmak daha kolaydır. Geleneksel olarak, geçersiz kılma yöntemleri yeni bir sınıfın ayırt edici özelliği olarak kabul edilirken, niteliklerin değerlerini ayarlamak değildi, ancak özellikle işleyici işlevleriyle, ikisinin çok benzer etkileri vardır, bu nedenle ayrım kelimeler hakkında bir savaş haline gelir.
Kilian Foth

1
Tam olarak sorduğunuz şey değil, ama bu Strateji tasarım deseni gibi görünüyor. Burada, çalışma zamanında türünü etkili bir şekilde değiştiren bir sınıf örneğine sahipsiniz. Biraz konu dışı çünkü buna izin veren bir dil değil ama bahsetmeye değer çünkü "neden birisi bunu yapmak
tony

@Killian Forth Prototip tabanlı OOP'nin tanım gereği sınıfları yoktur ve bu nedenle OP'nin ne istediğiyle tam olarak eşleşmez. Temel fark, bir sınıfın bir örnek olmamasıdır.
eques

Yanıtlar:


9

Çoğu (sınıf tabanlı) OOP dilinde yöntemler türe göre sabitlenir.

JavaScript prototip tabanlıdır, sınıf tabanlıdır ve bu nedenle "sınıf" ile nesne arasında zor bir ayrım olmadığı için örnek başına tabandaki yöntemleri geçersiz kılabilirsiniz; gerçekte, JavaScript'teki bir "sınıf", örneklerin nasıl çalışması gerektiğine dair şablon gibi bir nesnedir.

Birinci sınıf işlevlere izin veren herhangi bir dil, Scala, Java 8, C # (delegeler aracılığıyla), örnek başına yöntem geçersiz kılmalara sahipmiş gibi davranabilir; işlev türüne sahip bir alan tanımlamanız ve ardından her örnekte bu alanı geçersiz kılmanız gerekir.

Scala'nın başka bir olasılığı var; Scala'da, nesne tektonları oluşturabilirsiniz (class anahtar sözcüğü yerine object anahtar sözcüğünü kullanarak), böylece sınıfınızı genişletebilir ve yöntemleri geçersiz kılarak geçersiz kılmalarla bu temel sınıfın yeni bir örneğini oluşturabilirsiniz.

Kim böyle bir şey yapar ki? Düzinelerce sebep olabilir. Davranışın bana çeşitli alan kombinasyonlarının kullanılmasına izin vermekten daha sıkı tanımlanması gerekebilir. Ayrıca kodun ayrıştırılmasını ve daha iyi organize edilmesini sağlayabilir. Bununla birlikte genel olarak, bu vakaların daha nadir olduğunu düşünüyorum ve alan değerlerini kullanarak genellikle daha basit bir çözüm var.


İlk cümleniz mantıklı değil. Sınıf tabanlı OOP'nin sabit tiplerle ne ilgisi var?
Bergi

Demek istediğim, her tür için yöntemlerin kümesi ve tanımları sabittir veya başka bir deyişle, bir yöntem eklemek veya değiştirmek için yeni bir tür oluşturmanız gerekir (örn. Alt tür).
eques

@Bergi: Sınıf tabanlı OOP'de "sınıf" ve "tip" kelimesi aslında aynı anlama gelir. Tamsayı tipinin "merhaba" değerine sahip olmasına izin vermenin bir anlamı olmadığı için, tür Çalışanın sınıf Çalışanı'na ait olmayan değerlere veya yöntemlere sahip olması da anlamsızdır.
slebetman

@slebetman: Sınıf temelli bir OOP dilinin güçlü, statik olarak zorlanmış bir yazımı olması gerekmez.
Bergi

@Bergi: Yukarıdaki cevapta "çoğu" nun anlamı (çoğu anlamına gelmez). Ben sadece "ne yapması gerekiyor" yorumuna yorum yapıyorum.
slebetman

6

Sorunuzun motivasyonunu tahmin etmek zor ve bu nedenle bazı olası cevaplar gerçek ilginizi çekebilir veya etmeyebilir.

Bazı prototip olmayan dillerde bile bu etkiyi tahmin etmek mümkündür.

Örneğin Java'da, anonim bir iç sınıf tanımladığınız şeye oldukça yakındır - yalnızca istediğiniz yöntemi veya yöntemleri geçersiz kılarak orijinalin bir alt sınıfını oluşturabilir ve başlatabilirsiniz. Ortaya çıkan sınıf instanceoforijinal bir sınıf olacaktır, ancak aynı sınıf olmayacaktır .

Bunu neden yapmak istersiniz? Java 8 lambda ifadeleriyle, en iyi kullanım durumlarının çoğunun ortadan kalktığını düşünüyorum. En azından Java'nın önceki sürümlerinde, bu önemsiz, dar kullanımlı sınıfların çoğalmasını önleyebilir. Yani, sadece küçük bir işlevsel şekilde farklılık gösteren çok sayıda ilgili kullanım örneğiniz olduğunda, bunları ihtiyaç duyduğunuz anda enjekte edilen davranış farkı ile neredeyse anında (neredeyse) oluşturabilirsiniz.

Bununla birlikte, J8 öncesi bile, bu, farkı bir veya üç alana kaydırmak ve bunları yapıcıya enjekte etmek için çoğu zaman yeniden düzenlenebilir. J8 ile, elbette, yöntemin kendisi sınıfa enjekte edilebilir, ancak başka bir yeniden düzenleme daha temiz olabiliyorsa (serin değilse) bunu yapmak için bir cazibe olabilir.


6

Örnek başına yöntem sağlayan herhangi bir dil istediniz. Javascript için zaten bir cevap var, bu yüzden bunun EQL uzmanlarını kullanabileceğiniz Common Lisp'de nasıl yapıldığını görelim:

;; define a class
(defclass some-class () ())

;; declare a generic method
(defgeneric some-method (x))

;; specialize the method for SOME-CLASS
(defmethod some-method ((x some-class)) 'default-result)

;; create an instance named *MY-OBJECT* of SOME-CLASS
(defparameter *my-object* (make-instance 'some-class))

;; specialize SOME-METHOD for that specific instance
(defmethod some-method ((x (eql *my-object*))) 'specific-result)

;; Call the method on that instance
(some-method *my-object*)
=> SPECIFIC-RESULT

;; Call the method on a new instance
(some-method (make-instance 'some-class))
=> DEFAULT-RESULT

Neden?

EQL uzmanları, gönderime konu olan argümanın eqlmantıklı bir türü olması gerektiğinde yararlıdır : bir sayı, bir sembol, vb. Genel olarak konuşursak, buna ihtiyacınız yoktur ve sadece sorun tarafından gerekli. Ancak bazen, yalnızca bir sembol olan bir parametreye göre göndermeniz gerekir: bir caseifade, gönderim işlevindeki bilinen durumlar ile sınırlı olurken, yöntemler her zaman eklenebilir ve kaldırılabilir.

Ayrıca, çalışan uygulamanızdaki belirli bir nesneyle ne olduğunu geçici olarak denetlemek istediğinizde, örnekler üzerinde uzmanlaşma hata ayıklama amacıyla yararlıdır.


5

Bunu Ruby'de singleton nesneleri kullanarak da yapabilirsiniz:

class A
  def do_something
    puts "Hello!"
  end
end

obj = A.new
obj.do_something

def obj.do_something
  puts "Hello world!"
end

obj.do_something

üretir:

Hello!
Hello world!

Kullanımlara gelince, aslında Ruby sınıf ve modül yöntemlerini böyle yapar. Örneğin:

def SomeClass
  def self.hello
    puts "Hello!"
  end
end

Aslında nesne helloüzerinde bir singleton yöntemi tanımlar .ClassSomeClass


Cevabınızı modüller ve genişletme yöntemi ile uzatmak ister misiniz? (Nesneleri yeni yöntemlerle genişletmenin başka yollarını göstermek için)?
knut

@knut: extendAslında sadece nesne için singleton sınıfı oluşturur ve daha sonra modülü singleton sınıfına aktarır.
Linuxios

5

Örnek başına yöntemleri, kendi sınıfınızı çalışma zamanında birleştirmenize izin vermek olarak düşünebilirsiniz. Bu, iki sınıfı bir araya getirmek için bir araya getirmekten başka bir amacı olmayan bir çok tutkal kodunu ortadan kaldırabilir. Mixinler aynı tür problemlere biraz daha yapısal bir çözümdür.

Blub paradoksundan biraz acı çekiyorsunuz , aslında bu özelliği gerçek bir programda kullanana kadar bir dil özelliğinin değerini görmek zor. Bu yüzden işe yarayabileceğini düşündüğünüz fırsatları arayın, deneyin ve neler olduğunu görün.

Yalnızca bir yöntemle farklılık gösteren sınıf grupları için kodunuza bakın. Tek amacı diğer iki sınıfı farklı kombinasyonlarda birleştirmek olan sınıfları arayın. Başka bir nesneye çağrı yapmaktan başka bir şey yapmayan yöntemleri arayın. Karmaşık yaratıcı kalıplar kullanılarak başlatılan sınıfları arayın . Bunların hepsi, örnek başına yöntemlerle değiştirilecek potansiyel adaylardır.


1

Diğer yanıtlar, bunun dinamik nesne yönelimli dillerin ortak bir özelliği olduğunu ve birinci sınıf işlev nesnelerine (örneğin c #'daki delegeler, c ++ 'da operatörü () geçersiz kılan nesneler) sahip statik bir dilde önemsiz bir şekilde nasıl taklit edilebileceğini göstermiştir. . Böyle bir işlevi olmayan statik dillerde daha zordur, ancak yine de Strateji modelinin bir kombinasyonunu ve uygulanmasını stratejiye devreten bir yöntem kullanılarak elde edilebilir. Bu aslında temsilcilerle c # 'da yapacağınız şeyle aynıdır, ancak sözdizimi biraz daha karışıktır.


0

C # ve benzer birçok dilde böyle bir şey yapabilirsiniz.

public class MyClass{
    public Func<A,B> MyABFunc {get;set;}
    public Action<B> MyBAction {get;set;}
    public MyClass(){
        //todo assign MyAFunc and MyBAction
    }
}

1
Programcılar olduğu konusunda kavramsal soruları ve cevapları şeyleri açıklamak bekleniyor. Açıklama yerine kod dökümlerini atmak kodu IDE'den beyaz tahtaya kopyalamak gibidir: tanıdık gelebilir ve hatta bazen anlaşılabilir olabilir, ancak garip hissediyor ... sadece garip. Beyaz Tahta Derleyici Yok
Gnat

0

Kavramsal olarak, Java gibi bir dilde, bir sınıfın tüm örneklerinin aynı yöntemlere sahip olması gerekse de, muhtemelen iç içe sınıflarla birlikte fazladan bir dolaylama katmanı ekleyerek yokmuş gibi görünmesini sağlamak mümkündür.

Örneğin, bir sınıf Foostatik arka iç içe sınıf tanımlayabilir QuackerBasebir yöntem içerir quack(Foo)kaynaklanan, hem de çok sayıda diğer statik iç içe sınıfları QuackerBasekendi tanımı, her biri quack(Foo)bir dış sınıf bir alan varsa, o zaman quackerÇeşidi QuackerBase, o zaman olabilir bu alanı, iç içe sınıflarından herhangi birinin (muhtemelen tekil) bir örneğini tanımlamak için ayarlayın. Bunu yaptıktan sonra, invoke , örneği o alana atanan sınıfın yöntemini quacker.quack(this)yürütür quack.

Bu oldukça yaygın bir model olduğundan, Java uygun türleri otomatik olarak bildirmek için mekanizmalar içerir. Bu tür mekanizmalar, sadece sanal yöntemler ve isteğe bağlı olarak iç içe geçmiş statik sınıflar kullanılarak gerçekleştirilemeyen hiçbir şey yapmazlar, ancak tek amacı tek bir yöntem adına tek bir yöntem çalıştırmak olan bir sınıf üretmek için gerekli olan kazan plakasını büyük ölçüde azaltırlar. başka bir sınıf.


0

Bunun ruby, groovy & Javascript (ve daha birçoğu) gibi bir "Dinamik" dilin tanımı olduğuna inanıyorum. Dinamik (en azından kısmen), bir sınıf örneğinin anında nasıl davranabileceğini dinamik olarak yeniden tanımlama yeteneğini ifade eder.

Genel olarak harika bir OO uygulaması değildir, ancak birçok dinamik dil programcısı için OO ilkeleri en önemli önceliği değildir.

Monkey-Patching gibi bazı zorlayıcı işlemleri kolaylaştırır; burada kapalı bir kütüphaneyle önceden bilmedikleri bir şekilde etkileşimde bulunmanıza izin verir.


0

Bunun iyi bir şey olduğunu söylemiyorum, ama bu Python'da önemsiz bir şekilde mümkün. Başımın üstünden iyi bir kullanım durumu yapamam, ama eminim varlar.

    class Foo(object):
        def __init__(self, thing):
            if thing == "Foo":
                def print_something():
                    print "Foo"
            else:
                def print_something():
                    print "Bar"
            self.print_something = print_something

    Foo(thing="Foo").print_something()
    Foo(thing="Bar").print_something()
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.