Bisakah objek yang dibangun dari kelas yang sama memiliki definisi metode yang unik?


14

Saya tahu ini sepertinya pertanyaan aneh, karena titik dua atau lebih objek berbagi kelas yang sama adalah bahwa perilaku mereka sama, yaitu metode mereka identik.

Namun, saya ingin tahu apakah ada bahasa OOP yang memungkinkan Anda untuk mendefinisikan kembali metode objek dengan cara yang sama seperti Anda dapat menetapkan nilai yang berbeda untuk bidangnya. Hasilnya adalah objek yang dibangun dari kelas yang sama tidak lagi menunjukkan perilaku yang sama persis.

Jika saya tidak salah, Anda dapat melakukan ini JavaScript? Seiring dengan pertanyaan ini, saya bertanya, mengapa seseorang ingin melakukan ini?


9
Istilah teknisnya adalah "berbasis prototipe". Mencari itu akan memberi Anda banyak bahan tentang rasa OOP ini. (Ketahuilah bahwa banyak orang tidak menganggap struktur seperti itu sebagai "kelas", justru karena mereka tidak memiliki atribut dan metode yang seragam.)
Kilian Foth

1
maksud Anda seperti bagaimana dua contoh tombol yang berbeda dapat melakukan hal yang berbeda ketika diklik, karena masing-masing terhubung ke penangan tombol yang berbeda? Tentu saja, orang selalu bisa berpendapat bahwa mereka melakukan hal yang sama - mereka memanggil penangan tombol mereka. Pokoknya jika bahasa Anda mendukung delegasi atau fungsi pointer maka ini mudah - Anda memiliki beberapa variabel properti / bidang / anggota yang memegang delegasi atau fungsi pointer, dan implementasi metode panggilan memanggil itu dan pergi Anda pergi.
Kate Gregory

2
Ini rumit :) Dalam bahasa di mana referensi fungsi slinging sekitar umum, mudah untuk melewatkan beberapa kode ke dalam setClickHandler()metode dan membuat contoh berbeda dari kelas yang sama melakukan hal yang sangat berbeda. Dalam bahasa yang tidak memiliki ekspresi lambda yang nyaman, lebih mudah untuk membuat subkelas anonim baru hanya untuk penangan baru. Secara tradisional, metode utama dianggap sebagai ciri khas kelas baru , sementara pengaturan nilai atribut tidak, tetapi terutama dengan fungsi handler, keduanya memiliki efek yang sangat mirip, sehingga perbedaan menjadi perang tentang kata-kata.
Kilian Foth

1
Tidak persis apa yang Anda tanyakan, tetapi ini terdengar seperti pola desain Strategi. Di sini Anda memiliki instance kelas yang secara efektif mengubah jenisnya saat runtime. Ini sedikit keluar dari topik karena bukan bahasa yang memungkinkan ini tetapi perlu disebutkan karena Anda mengatakan "mengapa seseorang ingin melakukan ini"
tony

@Killian Forth Prototype-based OOP tidak memiliki kelas menurut definisi dan dengan demikian, tidak cukup cocok dengan apa yang ditanyakan OP. Perbedaan utama adalah kelas bukan turunan.
menyamakan

Jawaban:


9

Metode dalam sebagian besar bahasa OOP (berbasis kelas) ditetapkan berdasarkan jenis.

JavaScript adalah berbasis prototipe, bukan berbasis kelas sehingga Anda dapat mengganti metode pada basis per-instance karena tidak ada perbedaan yang sulit antara "kelas" dan objek; pada kenyataannya, "kelas" dalam JavaScript adalah objek yang seperti templat untuk bagaimana instance harus bekerja.

Bahasa apa pun yang memungkinkan fungsi kelas satu, Scala, Java 8, C # (via delegate) dll, dapat bertindak seperti Anda memiliki metode per-instance override; Anda harus mendefinisikan bidang dengan tipe fungsi dan kemudian menimpanya di setiap instance.

Scala memiliki kemungkinan lain; Di Scala, Anda bisa membuat objek lajang (menggunakan kata kunci objek alih-alih kata kunci kelas), sehingga Anda dapat memperluas kelas Anda dan menimpa metode, menghasilkan contoh baru dari kelas dasar dengan override.

Mengapa seseorang melakukan ini? Mungkin ada lusinan alasan. Mungkin saja perilaku itu perlu saya definisikan lebih ketat daripada menggunakan berbagai kombinasi bidang. Itu juga bisa menjaga kode dipisahkan dan diatur lebih baik. Namun secara umum, saya pikir kasus ini lebih jarang dan sering ada solusi yang lebih mudah menggunakan nilai-nilai bidang.


Kalimat pertama Anda tidak masuk akal. Apa hubungannya OOP berbasis kelas dengan tipe tetap?
Bergi

Maksud saya per jenis set dan definisi metode diperbaiki atau dengan kata lain, untuk menambah atau mengubah metode, Anda harus membuat tipe baru (misalnya dengan subtyping).
menyamakan

@Bergi: Dalam OOP berbasis kelas kata "kelas" dan "ketik" pada dasarnya berarti hal yang sama. Karena tidak masuk akal untuk mengizinkan tipe integer untuk memiliki nilai "halo", juga tidak masuk akal untuk memiliki tipe Karyawan untuk memiliki nilai atau metode yang bukan milik Karyawan kelas.
slebetman

@slebetman: Maksud saya bahwa bahasa OOP berbasis kelas tidak harus memiliki pengetikan yang kuat dan ditegakkan secara statis.
Bergi

@Bergi: Itulah arti "paling" dalam jawaban di atas (sebagian besar berarti tidak semua). Saya hanya mengomentari komentar "apa yang harus Anda lakukan".
Slebetman

6

Sulit untuk menebak motivasi untuk pertanyaan Anda, dan karena itu beberapa kemungkinan jawaban mungkin atau mungkin tidak menjawab minat Anda yang sebenarnya.

Bahkan dalam beberapa bahasa non-prototipe dimungkinkan untuk memperkirakan efek ini.

Di Jawa, misalnya, kelas dalam anonim cukup dekat dengan apa yang Anda gambarkan - Anda dapat membuat dan membuat instance dari subkelas asli, menimpa hanya metode atau metode yang Anda inginkan. Kelas yang dihasilkan akan menjadi instanceofkelas asli, tetapi tidak akan menjadi kelas yang sama .

Mengapa Anda ingin melakukan ini? Dengan ekspresi Java 8 lambda, saya pikir banyak kasus penggunaan terbaik hilang. Dengan versi Java yang lebih lama, setidaknya, ini dapat menghindari perkembangbiakan kelas-kelas sepele dan penggunaan sempit. Artinya, ketika Anda memiliki sejumlah besar kasus penggunaan terkait, berbeda hanya dalam cara fungsional kecil, Anda dapat membuatnya hampir on-the-fly (hampir), dengan perbedaan perilaku disuntikkan pada saat Anda membutuhkannya.

Yang mengatakan, bahkan sebelum J8, ini sering dapat di refactored untuk menggeser perbedaan menjadi satu atau tiga bidang, dan menyuntikkan mereka dalam konstruktor. Dengan J8, tentu saja, metode itu sendiri dapat disuntikkan ke dalam kelas, meskipun mungkin ada godaan untuk melakukannya ketika refactoring lain mungkin lebih bersih (jika tidak sedingin).


6

Anda meminta bahasa apa pun yang menyediakan metode per-instance. Sudah ada jawaban untuk Javascript, jadi mari kita lihat bagaimana hal itu dilakukan di Common Lisp, di mana Anda dapat menggunakan spesialis-EQL:

;; 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

Mengapa?

EQL-specializers berguna ketika argumen subjek pengiriman seharusnya memiliki tipe yang eqlmasuk akal: angka, simbol, dll. Secara umum, Anda tidak memerlukannya, dan Anda hanya perlu mendefinisikan subclass sebanyak dibutuhkan oleh masalah Anda. Tetapi kadang-kadang, Anda hanya perlu mengirim sesuai dengan parameter yang, misalnya, simbol: caseekspresi akan terbatas pada kasus-kasus yang diketahui dalam fungsi pengiriman, sedangkan metode dapat ditambahkan dan dihapus kapan saja.

Juga, spesialisasi pada instance berguna untuk keperluan debugging, ketika Anda ingin memeriksa sementara apa yang terjadi dengan objek tertentu dalam aplikasi yang sedang berjalan.


5

Anda juga dapat melakukan ini di Ruby menggunakan objek tunggal:

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

Menghasilkan:

Hello!
Hello world!

Sedangkan untuk penggunaan, ini sebenarnya bagaimana Ruby melakukan metode kelas dan modul. Sebagai contoh:

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

Sebenarnya mendefinisikan metode singleton hellopada Classobjek SomeClass.


Apakah Anda ingin memperluas jawaban Anda dengan modul dan metode tambahan? (Hanya untuk menunjukkan beberapa cara lain untuk memperluas objek dengan metode baru)?
Knut

@ Knut: Saya cukup yakin bahwa extendsebenarnya hanya menciptakan kelas singleton untuk objek dan kemudian mengimpor modul ke kelas singleton.
Linuxios

5

Anda dapat menganggap metode per-instance sebagai memungkinkan Anda untuk merakit kelas Anda sendiri pada saat runtime. Ini dapat menghilangkan banyak kode lem, kode yang tidak memiliki tujuan lain selain untuk menyatukan dua kelas untuk berbicara satu sama lain. Mixin adalah solusi yang agak lebih terstruktur untuk jenis masalah yang sama.

Anda menderita sedikit dari paradoks blub , pada dasarnya sulit melihat nilai fitur bahasa sampai Anda menggunakan fitur itu dalam program nyata. Jadi, cari peluang di mana Anda pikir itu mungkin berhasil, cobalah, dan lihat apa yang terjadi.

Lihat di kode Anda untuk kelompok kelas yang hanya berbeda oleh satu metode. Cari kelas yang tujuan utamanya adalah menggabungkan dua kelas lain dalam kombinasi yang berbeda. Cari metode yang tidak melakukan apa pun selain meneruskan panggilan ke objek lain. Cari kelas yang dipakai menggunakan pola kreasi yang kompleks . Itu semua adalah kandidat potensial untuk diganti dengan metode per-instance.


1

Jawaban lain telah menunjukkan bagaimana ini adalah fitur umum dari bahasa berorientasi objek dinamis, dan bagaimana hal itu dapat ditiru secara sepele dalam bahasa statis yang memiliki objek fungsi kelas satu (mis. Delegasi dalam c #, objek yang menimpa operator () dalam c ++) . Dalam bahasa statis yang tidak memiliki fungsi seperti itu, ini lebih sulit, tetapi masih dapat dicapai dengan menggunakan kombinasi pola Strategi dan metode yang hanya mendelegasikan penerapannya ke strategi. Ini sebenarnya adalah hal yang sama yang akan Anda lakukan di c # dengan delegasi, tetapi sintaksnya agak berantakan.


0

Anda dapat melakukan hal seperti ini dalam C # dan sebagian besar bahasa serupa lainnya.

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

1
Programmer adalah tentang pertanyaan konseptual dan jawaban diharapkan dapat menjelaskan banyak hal. Melempar kesedihan alih-alih penjelasannya seperti menyalin kode dari IDE ke papan tulis: mungkin terlihat biasa dan bahkan kadang-kadang dapat dimengerti, tetapi rasanya aneh ... hanya aneh. Papan tulis tidak memiliki kompiler
nyamuk

0

Secara konseptual, meskipun dalam bahasa seperti Java, semua instance kelas harus memiliki metode yang sama, dimungkinkan untuk membuatnya tampak seolah-olah mereka tidak dengan menambahkan lapisan tipuan tambahan, mungkin dalam kombinasi dengan kelas bersarang.

Sebagai contoh, jika sebuah kelas Foodapat mendefinisikan kelas bersarang abstrak statis QuackerBaseyang berisi metode quack(Foo), serta beberapa kelas bersarang statis lain yang berasal QuackerBase, masing-masing dengan definisi sendiri quack(Foo), maka jika kelas luar memiliki bidang quackertipe QuackerBase, maka mungkin setel field itu untuk mengidentifikasi instance (mungkin tunggal) dari salah satu kelas bersarangnya. Setelah melakukannya, menjalankan quacker.quack(this)akan mengeksekusi quackmetode kelas yang instansnya telah ditetapkan untuk bidang itu.

Karena ini adalah pola yang cukup umum, Java menyertakan mekanisme untuk secara otomatis mendeklarasikan tipe yang sesuai. Mekanisme seperti itu tidak benar-benar melakukan apa pun yang tidak dapat dilakukan dengan hanya menggunakan metode virtual dan kelas statis opsional-bersarang, tetapi mereka sangat mengurangi jumlah boilerplate yang diperlukan untuk menghasilkan kelas yang tujuan utamanya adalah menjalankan metode tunggal atas nama kelas lain.


0

Saya percaya itu adalah definisi bahasa "Dinamis" seperti ruby, groovy & Javascript (dan Banyak Banyak Lainnya). Dynamic merujuk (setidaknya sebagian) pada kemampuan untuk secara dinamis mendefinisikan kembali bagaimana instance kelas mungkin berperilaku dengan cepat.

Ini bukan praktik OO yang hebat secara umum, tetapi bagi banyak programmer bahasa yang dinamis, prinsip-prinsip OO bukan prioritas utama mereka.

Itu menyederhanakan beberapa operasi rumit seperti Monkey-Patching di mana Anda dapat mengubah contoh kelas untuk memungkinkan Anda berinteraksi dengan perpustakaan tertutup dengan cara yang tidak mereka lihat.


0

Saya tidak mengatakan itu hal yang baik untuk dilakukan, tetapi ini sepele mungkin dengan Python. Saya tidak bisa menggunakan case yang baik dari atas kepala saya, tapi saya yakin mereka ada.

    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()
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.