อ็อบเจ็กต์ที่สร้างจากคลาสเดียวกันสามารถมีนิยามเมธอดที่ไม่ซ้ำกันได้หรือไม่?


14

ฉันรู้ว่าสิ่งนี้ดูเหมือนจะเป็นคำถามแปลก ๆ เนื่องจากจุดที่วัตถุสองชิ้นขึ้นไปที่ใช้คลาสเดียวกันนั้นเป็นพฤติกรรมของพวกเขาเหมือนกันนั่นคือวิธีการของพวกเขาเหมือนกัน

อย่างไรก็ตามฉันอยากรู้ว่ามีภาษา OOP ใดที่อนุญาตให้คุณกำหนดวิธีการของวัตถุใหม่ในแบบเดียวกับที่คุณสามารถกำหนดค่าที่แตกต่างกันสำหรับเขตข้อมูลของพวกเขา ผลลัพธ์ที่ได้คือวัตถุที่สร้างจากคลาสเดียวกันจะไม่แสดงพฤติกรรมที่เหมือนกันอีกต่อไป

หากฉันไม่เข้าใจผิดคุณสามารถทำ JavaScript นี้ได้หรือไม่ พร้อมกับคำถามนี้ฉันถามว่าทำไมใครบางคนต้องการทำเช่นนี้?


9
ศัพท์เทคนิคคือ "ต้นแบบ - อิง" การค้นหามันจะทำให้คุณมีเนื้อหาเกี่ยวกับรสชาติของ OOP นี้มาก (โปรดทราบว่าผู้คนจำนวนมากไม่ได้พิจารณาโครงสร้างดังกล่าวเหมาะสม "เรียน" อย่างแม่นยำเพราะพวกเขาไม่ได้มีแอตทริบิวต์เครื่องแบบและวิธีการ.)
Kilian Foth

1
คุณหมายถึงว่าปุ่มที่แตกต่างกันสองปุ่มสามารถทำสิ่งที่แตกต่างกันเมื่อพวกเขาคลิกเพราะพวกเขาแต่ละสายถึงตัวจัดการปุ่มที่แตกต่างกันอย่างไร แน่นอนว่าใคร ๆ ก็เถียงว่าพวกเขาทำสิ่งเดียวกัน - พวกเขาเรียกตัวจัดการปุ่ม อย่างไรก็ตามถ้าภาษาของคุณรองรับผู้ได้รับมอบหมายหรือตัวชี้ฟังก์ชั่นนี่เป็นเรื่องง่าย - คุณมีตัวแปรคุณสมบัติ / ฟิลด์ / สมาชิกบางอย่างที่เก็บผู้รับมอบสิทธิ์หรือตัวชี้ฟังก์ชั่นและการใช้วิธีการนั้น
Kate Gregory

2
มันซับซ้อน :) ในภาษาที่การใช้ฟังก์ชันการโยงเป็นเรื่องธรรมดามันง่ายที่จะส่งรหัสไปยังsetClickHandler()เมธอดและทำให้อินสแตนซ์ที่ต่างกันของคลาสเดียวกันทำสิ่งที่แตกต่างกันมาก ในภาษาที่ไม่มีการแสดงออกแลมบ์ดาที่สะดวกมันง่ายกว่าในการสร้างคลาสย่อยแบบไม่ระบุชื่อใหม่สำหรับผู้จัดการใหม่ ตามเนื้อผ้าวิธีการเอาชนะถือว่าเป็นจุดเด่นของคลาสใหม่ในขณะที่การตั้งค่าของคุณลักษณะไม่ได้ แต่โดยเฉพาะอย่างยิ่งกับฟังก์ชั่นการจัดการทั้งสองมีผลคล้ายกันมากดังนั้นความแตกต่างกลายเป็นสงครามเกี่ยวกับคำ
Kilian Foth

1
ไม่ใช่สิ่งที่คุณขออย่างแน่นอน แต่ดูเหมือนว่ารูปแบบการออกแบบกลยุทธ์ ที่นี่คุณมีอินสแตนซ์ของคลาสที่เปลี่ยนแปลงประเภทของรันไทม์ได้อย่างมีประสิทธิภาพ เป็นบิตปิดหัวข้อเพราะมันไม่ใช่ภาษาที่ช่วยให้นี้ แต่มูลค่าการกล่าวขวัญเพราะคุณบอกว่า "ทำไมจะมีคนต้องการทำเช่นนี้"
tony

@ OOP ที่อิงกับคิลเลียน Forth Prototype ไม่มีคลาสตามคำจำกัดความดังนั้นจึงไม่ตรงกับสิ่งที่ OP กำลังถามเกี่ยวกับ ความแตกต่างที่สำคัญคือคลาสไม่ใช่ตัวอย่าง
eques

คำตอบ:


9

วิธีการในภาษา OOP ส่วนใหญ่ (ตามระดับคลาส) ได้รับการแก้ไขตามประเภท

JavaScript นั้นใช้ต้นแบบไม่ใช่คลาสตามและคุณสามารถแทนที่เมธอดบนฐานต่ออินสแตนซ์ได้เนื่องจากไม่มีความแตกต่างอย่างหนักระหว่าง "คลาส" และวัตถุ ในความเป็นจริง "คลาส" ใน JavaScript เป็นวัตถุซึ่งมีลักษณะเหมือนแม่แบบสำหรับการทำงานของอินสแตนซ์

ภาษาใด ๆ ที่อนุญาตฟังก์ชั่นชั้นหนึ่ง Scala, Java 8, C # (ผ่านตัวแทน) ฯลฯ สามารถทำหน้าที่เหมือนคุณมีการแทนที่เมธอดต่ออินสแตนซ์; คุณจะต้องกำหนดเขตข้อมูลด้วยประเภทฟังก์ชั่นแล้วแทนที่มันในแต่ละอินสแตนซ์

สกาล่ามีความเป็นไปได้อีกอย่างหนึ่ง; ใน Scala คุณสามารถสร้างวัตถุ singletons (การใช้คำหลักวัตถุแทนคำหลักชั้น) เพื่อให้คุณสามารถขยายชั้นเรียนของคุณและแทนที่วิธีการผลในอินสแตนซ์ใหม่ของชั้นฐานที่มีการแทนที่

ทำไมบางคนจะทำเช่นนี้? อาจมีหลายสิบเหตุผล อาจเป็นไปได้ว่าพฤติกรรมที่ฉันต้องการจะกำหนดไว้แน่นกว่าการใช้ชุดค่าผสมเขตข้อมูลต่าง ๆ จะอนุญาต มันสามารถทำให้โค้ดแยกตัวและจัดระเบียบได้ดีขึ้น โดยทั่วไปแล้วฉันคิดว่ากรณีเหล่านี้หายากและมักจะมีวิธีแก้ปัญหาที่ตรงไปตรงมามากกว่าโดยใช้ค่าของเขตข้อมูล


ประโยคแรกของคุณไม่สมเหตุสมผล OOP แบบใช้คลาสเกี่ยวข้องกับประเภทคงที่อย่างไร
Bergi

ฉันหมายถึงว่าต่อประเภทชุดและคำจำกัดความของวิธีการได้รับการแก้ไขหรือในคำอื่น ๆ เพื่อเพิ่มหรือเปลี่ยนวิธีการคุณจะต้องสร้างรูปแบบใหม่ (เช่นโดยการพิมพ์ย่อย)
eques

@Bergi: OOP ในคลาสคำว่า "class" และ "type" เป็นหลักหมายถึงสิ่งเดียวกัน เนื่องจากมันไม่สมเหตุสมผลที่จะอนุญาตให้จำนวนเต็มชนิดมีค่า "hello" ดังนั้นจึงไม่มีเหตุผลที่จะให้ประเภทพนักงานมีค่าหรือวิธีการที่ไม่ได้อยู่ในคลาสพนักงาน
slebetman

@slebetman: ฉันหมายถึงภาษา OOP ที่อิงกับคลาสไม่จำเป็นต้องมีการพิมพ์ที่รัดกุมและบังคับใช้แบบคงที่
Bergi

@Bergi: นั่นคือความหมายของ "ที่สุด" ในคำตอบข้างต้น (ส่วนใหญ่หมายถึงไม่ทั้งหมด) ฉันแค่แสดงความคิดเห็นในความคิดเห็นของคุณ "มันต้องทำอะไร"
slebetman

6

เป็นการยากที่จะคาดเดาแรงบันดาลใจสำหรับคำถามของคุณและดังนั้นคำตอบที่เป็นไปได้อาจเป็นไปได้หรือไม่สนใจความสนใจที่แท้จริงของคุณ

แม้ในภาษาที่ไม่ใช่ต้นแบบบางอย่างก็เป็นไปได้ที่จะประมาณผลกระทบนี้

ตัวอย่างเช่นใน Java คลาสภายในที่ไม่ระบุชื่อนั้นค่อนข้างใกล้เคียงกับสิ่งที่คุณกำลังอธิบายคุณสามารถสร้างและยกตัวอย่างคลาสย่อยของต้นฉบับโดยแทนที่วิธีหรือวิธีการที่คุณต้องการ คลาสที่ได้จะเป็นinstanceofคลาสดั้งเดิม แต่จะไม่เป็นคลาสเดียวกัน

ทำไมคุณถึงต้องการทำสิ่งนี้? ด้วยการแสดงออกแลมบ์ดาของ Java 8 ฉันคิดว่ากรณีการใช้งานที่ดีที่สุดมากมายหายไป ด้วย Java เวอร์ชันก่อนหน้านี้อย่างน้อยสิ่งนี้สามารถหลีกเลี่ยงการแพร่กระจายของคลาสที่ใช้งานง่ายและแคบ นั่นคือเมื่อคุณมีกรณีการใช้งานที่เกี่ยวข้องจำนวนมากแตกต่างกันในลักษณะการใช้งานเพียงเล็กน้อยคุณสามารถสร้างมันได้เกือบจะทันที (เกือบ) โดยมีความแตกต่างของพฤติกรรมในเวลาที่คุณต้องการ

ที่กล่าวไว้แม้แต่ pre-J8 สิ่งนี้สามารถ refactored เพื่อเปลี่ยนความแตกต่างเป็นสนามหรือสามและฉีดพวกเขาในตัวสร้าง แน่นอนว่าด้วย J8 วิธีการนั้นสามารถถูกฉีดเข้าไปในชั้นเรียนได้แม้ว่าอาจมีการล่อลวงให้ทำเช่นนั้นเมื่อการปรับโครงสร้างอื่นอาจจะสะอาดกว่าเดิม (ถ้าไม่เท่ห์)


6

คุณถามถึงภาษาใด ๆ ที่ให้วิธีการแบบต่อเนื่อง มีคำตอบสำหรับ Javascript อยู่แล้วดังนั้นมาดูกันว่ามันทำงานอย่างไรใน Common LISP ซึ่งคุณสามารถใช้ EQL-specializers ได้:

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

ทำไม?

EQL-specializers มีประโยชน์เมื่ออาร์กิวเมนต์ที่มีการจัดส่งควรมีประเภทที่eqlเหมาะสม: ตัวเลขสัญลักษณ์ ฯลฯ โดยทั่วไปคุณไม่ต้องการมันและคุณต้องกำหนด subclasses ให้มากที่สุดเท่าที่จะทำได้ ต้องการโดยปัญหาของคุณ แต่บางครั้งคุณต้องส่งไปตามพารามิเตอร์ซึ่งก็คือสัญลักษณ์: การcaseแสดงออกจะถูก จำกัด เฉพาะกรณีที่รู้จักในฟังก์ชั่นการจัดส่งในขณะที่วิธีการที่สามารถเพิ่มและลบได้ตลอดเวลา

นอกจากนี้ความเชี่ยวชาญในอินสแตนซ์นั้นมีประโยชน์สำหรับวัตถุประสงค์ในการดีบั๊กเมื่อคุณต้องการตรวจสอบสิ่งที่เกิดขึ้นกับวัตถุเฉพาะในแอปพลิเคชันที่ทำงานอยู่ชั่วคราว


5

คุณสามารถทำได้ใน Ruby โดยใช้วัตถุ singleton:

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

ผลิต:

Hello!
Hello world!

สำหรับการใช้งานจริง ๆ แล้วนี่เป็นวิธีที่ Ruby ทำคลาสและโมดูลเมธอด ตัวอย่างเช่น:

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

เป็นจริงการกำหนดวิธีการเดี่ยวhelloบนวัตถุClassSomeClass


คุณต้องการที่จะขยายคำตอบของคุณด้วยโมดูลและวิธีการขยาย? (เพียงแสดงวิธีอื่นในการขยายวัตถุด้วยวิธีการใหม่)
knut

@ Knut: ฉันค่อนข้างแน่ใจว่าextendจริง ๆ แล้วเพิ่งสร้างคลาส singleton สำหรับวัตถุแล้วนำเข้าโมดูลลงในคลาส singleton
Linuxios

5

คุณสามารถนึกถึงวิธีการต่ออินสแตนซ์ที่ช่วยให้คุณสามารถประกอบชั้นเรียนของคุณเองที่รันไทม์ สิ่งนี้สามารถกำจัดรหัสกาวจำนวนมากซึ่งเป็นรหัสที่ไม่มีจุดประสงค์อื่นนอกเหนือจากการรวมสองคลาสเข้าด้วยกันเพื่อพูดคุยกัน มิกซ์ซินเป็นวิธีแก้ปัญหาที่มีโครงสร้างมากกว่าสำหรับปัญหาประเภทเดียวกัน

คุณกำลังทุกข์ทรมานเล็กน้อยจากความขัดแย้งที่ร้องไห้สะอึกสะอื้นโดยพื้นฐานแล้วเป็นเรื่องยากที่จะเห็นคุณค่าของคุณลักษณะภาษาจนกว่าคุณจะใช้คุณสมบัตินั้นในโปรแกรมจริง ดังนั้นมองหาโอกาสที่คุณคิดว่ามันอาจใช้งานได้ลองและดูว่าเกิดอะไรขึ้น

ดูในรหัสของคุณสำหรับกลุ่มของคลาสที่แตกต่างกันโดยวิธีเดียวเท่านั้น ค้นหาคลาสที่มีวัตถุประสงค์เพียงอย่างเดียวคือการรวมคลาสสองคลาสอื่น ๆ ค้นหาวิธีการที่ไม่ทำอะไรเลยนอกจากส่งผ่านสายไปยังวัตถุอื่น มองหาชั้นเรียนที่มีการใช้อินสแตนซ์ที่ซับซ้อนCreational รูปแบบ สิ่งเหล่านั้นคือผู้สมัครที่มีศักยภาพทั้งหมดที่จะถูกแทนที่ด้วยวิธีการต่ออินสแตนซ์


1

คำตอบอื่น ๆ แสดงให้เห็นว่านี่เป็นคุณสมบัติทั่วไปของภาษาเชิงวัตถุแบบไดนามิกและวิธีการเลียนแบบภาษาในแบบคงที่ที่มีวัตถุฟังก์ชันชั้นหนึ่ง (เช่นตัวแทนใน c #, วัตถุที่แทนที่โอเปอเรเตอร์ () ใน c ++) . ในภาษาแบบสแตติกที่ขาดฟังก์ชั่นดังกล่าวมันก็ยากกว่า แต่ก็ยังสามารถทำได้โดยใช้รูปแบบกลยุทธ์และวิธีการที่มอบหมายการใช้งานให้กับกลยุทธ์ สิ่งนี้มีผลเช่นเดียวกับที่คุณทำใน c # กับผู้ได้รับมอบหมาย แต่ไวยากรณ์นั้นค่อนข้างยุ่งเหยิง


0

คุณสามารถทำสิ่งนี้ใน C # และภาษาอื่น ๆ ที่คล้ายกันมากที่สุด

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

1
โปรแกรมเมอร์เป็นคำถามเกี่ยวกับแนวคิดและคำตอบที่คาดว่าจะอธิบายสิ่งต่าง ๆ การโยนรหัสทิ้งแทนที่จะอธิบายเป็นเหมือนการคัดลอกรหัสจาก IDE ไปยังไวท์บอร์ด: มันอาจดูคุ้นหูและบางครั้งก็เข้าใจได้ แต่มันให้ความรู้สึกแปลก ๆ ... มันแปลก ๆ ไวท์บอร์ดไม่ได้มีคอมไพเลอร์
ริ้น

0

แนวคิดแม้ว่าในภาษาเช่น Java ทุกอินสแตนซ์ของคลาสจะต้องมีวิธีการเดียวกัน แต่ก็เป็นไปได้ที่จะทำให้มันดูเหมือนว่าพวกเขาไม่ได้โดยการเพิ่มเลเยอร์ทางอ้อมพิเศษอาจรวมกับชั้นที่ซ้อนกัน

ตัวอย่างเช่นถ้าคลาสFooอาจกำหนดคลาสซ้อนซ้อนแบบนามธรรมQuackerBaseซึ่งมีวิธีการquack(Foo)รวมทั้งคลาสซ้อนแบบคงที่อื่น ๆ ที่ได้มาจากQuackerBaseแต่ละคลาสมีคำจำกัดความของตัวเองquack(Foo)แล้วถ้าคลาสภายนอกมีฟิลด์quackerประเภทQuackerBaseแล้วมันอาจ ตั้งค่าฟิลด์นั้นเพื่อระบุอินสแตนซ์ (อาจเป็นซิงเกิล) ของคลาสใด ๆ ที่ซ้อนกัน หลังจากเสร็จสิ้นการเรียกใช้quacker.quack(this)จะเรียกใช้quackเมธอดของคลาสที่อินสแตนซ์ถูกกำหนดให้กับฟิลด์นั้น

เนื่องจากนี่เป็นรูปแบบที่พบได้ทั่วไป Java จึงมีกลไกในการประกาศชนิดที่เหมาะสมโดยอัตโนมัติ กลไกดังกล่าวไม่ได้ทำสิ่งใด ๆ ที่ไม่สามารถทำได้โดยใช้วิธีเสมือนและคลาสแบบคงที่ที่เป็นทางเลือก แต่พวกมันลดจำนวนต้นแบบที่จำเป็นในการสร้างคลาสที่มีวัตถุประสงค์เพียงอย่างเดียวในการใช้วิธีการเดียวแทน ชั้นเรียนอื่น


0

ฉันเชื่อว่าเป็นคำจำกัดความของภาษา "ไดนามิก" เช่น ruby, groovy & Javascript (และอื่น ๆ อีกมากมาย) Dynamic อ้างถึง (อย่างน้อยก็ในบางส่วน) เพื่อความสามารถในการกำหนดค่าใหม่แบบไดนามิกว่าอินสแตนซ์ของคลาสอาจทำงานได้ทันที

มันไม่ได้เป็นแบบฝึกหัด OO ที่ยอดเยี่ยมโดยทั่วไป แต่สำหรับโปรแกรมเมอร์ภาษาแบบไดนามิกจำนวนมากหลักการ OO ไม่ใช่สิ่งที่สำคัญที่สุด

มันทำให้การดำเนินการที่ยุ่งยากบางอย่างง่ายขึ้นเช่น Monkey-Patching ซึ่งคุณอาจปรับแต่งอินสแตนซ์ของชั้นเรียนเพื่อให้คุณสามารถโต้ตอบกับไลบรารีที่ปิดในแบบที่พวกเขาไม่ได้มองข้าม


0

ฉันไม่ได้บอกว่ามันเป็นเรื่องดีที่ต้องทำ แต่สิ่งนี้อาจเกิดขึ้นได้ใน Python ฉันไม่สามารถใช้กรณีที่ดีออกจากหัวของฉัน แต่ฉันแน่ใจว่าพวกเขามีอยู่

    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()
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.