polymorphism ใช้ในโลกแห่งความเป็นจริงอย่างไร? [ปิด]


17

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

Animal animal;

animal = dog;
animal.speak();

animal = cat;
animal.speak();



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

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

โดยทั่วไปหากคุณใช้วิธีการโอเวอร์โหลดบ่อยครั้งเพื่อจัดการกับประเภทที่แตกต่างกันและรหัสนั้นคล้ายกันหรือถ้าคุณเขียนif(x is SomeType) DoSomething()บ่อย ๆ มันอาจจะคุ้มค่าหากใช้ polymorphism สำหรับฉันความแตกต่างคือการตัดสินใจที่คล้ายกับเวลาที่จะทำให้วิธีการแยกต่างหากถ้าฉันพบว่าฉันทำซ้ำรหัสสองสามครั้งฉันมักจะ refactor เป็นวิธีการและถ้าฉันพบว่าฉันกำลังทำif object is this type do thisรหัสบ่อยมันอาจจะ ควรทำการเปลี่ยนค่าใหม่และเพิ่มอินเตอร์เฟสหรือคลาส
jrh

คำตอบ:


35

สตรีมเป็นตัวอย่างที่ดีของความแตกต่าง

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

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

บางคนบอกว่าStreamเป็นตัวอย่างที่ผิดของ polymorphism เพราะมันนิยาม "คุณสมบัติ" มากมายที่ผู้พัฒนาไม่สนับสนุนเช่นเครือข่ายสตรีมเท่านั้นที่อนุญาตให้อ่านหรือเขียน แต่ไม่ใช่ทั้งสองอย่างในเวลาเดียวกัน หรือขาดการแสวงหา แต่นั่นเป็นเพียงคำถามของความซับซ้อนที่Streamสามารถแบ่งออกเป็นหลายส่วนที่สามารถดำเนินการได้อย่างอิสระ


2
ในภาษาที่มีมรดกหลายและเสมือนเช่น C ++ ตัวอย่างนี้ยังสามารถแสดงให้เห็นถึง "หวั่นเพชร" รูปแบบ ... โดย deriving อินพุตและเอาต์พุตสตรีมชั้นเรียนจากระดับกระแสฐานและขยายทั้งในการสร้างกระแส I / O
วงกลม

2
@gyre และทำได้ดีไม่มีเหตุผลที่จะ "กลัว" รูปแบบเพชร จำเป็นต้องระวังคู่ที่ตรงกันข้ามในเพชรและไม่ก่อให้เกิดความขัดแย้งของชื่อกับมันเป็นสิ่งสำคัญความท้าทายและน่ารำคาญและเหตุผลที่จะหลีกเลี่ยงรูปแบบของเพชรที่สามารถนำไปใช้ได้ ... แต่อย่าไปไกลเกินเหตุเมื่อ เพียงแค่พูดการตั้งชื่อสามารถแก้ไขปัญหาได้
KRyan

+1 Streamเป็นตัวอย่างความหลากหลายที่ฉันโปรดปรานตลอดกาล ฉันไม่ได้พยายามสอนคนเกี่ยวกับรูปแบบ 'สัตว์เลี้ยงลูกด้วยนมสุนัข' ที่มีตำหนิอีกต่อไปStreamทำงานได้ดี แต่ดีกว่า
Pharap

@Kyan ฉันไม่ได้แสดงความคิดของตัวเองโดยการเรียกมันว่า "เพชรที่น่าสะพรึงกลัว" ฉันเพิ่งได้ยินว่ามันถูกเรียกว่า ฉันเห็นด้วยอย่างสมบูรณ์; ฉันคิดว่ามันเป็นสิ่งที่นักพัฒนาซอฟต์แวร์ทุกคนควรจะสามารถก้มหน้าก้มตาและใช้อย่างเหมาะสม
วงกลม

@gyre โอ้ใช่ฉันได้รับจริง นั่นเป็นเหตุผลที่ฉันเริ่มต้นด้วย“ และ” เพื่อระบุว่าเป็นส่วนขยายของความคิดของคุณแทนที่จะเป็นความขัดแย้ง
KRyan

7

เกมทั่วไปตัวอย่างที่เกี่ยวข้องจะเป็นชั้นฐานEntityให้สมาชิกทั่วไปเช่นหรือdraw()update()

สำหรับตัวอย่างที่มุ่งเน้นข้อมูลที่บริสุทธิ์มากขึ้นอาจจะมีชั้นฐานSerializableการให้บริการร่วมกันและsaveToStream()loadFromStream()


6

มีความแตกต่างหลากหลายรูปแบบสิ่งหนึ่งที่น่าสนใจคือความแตกต่างหลากหลายแบบพลวัต / พลวัตพลวัต

คำอธิบายระดับสูงมากของ polymorphism แบบรันไทม์คือการเรียกใช้เมธอดทำสิ่งต่าง ๆ โดยขึ้นอยู่กับชนิดของอาร์กิวเมนต์ของมัน: วัตถุนั้นมีหน้าที่ในการแก้ไขการเรียกเมธอด ทำให้มีความยืดหยุ่นสูง

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

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

ทำให้รหัสยากที่จะปฏิบัติตาม ทางเลือกคือการแนะนำอินเทอร์เฟซสำหรับการดำเนินการ foo และเขียนการใช้งานปกติและการใช้งานจำลองของอินเทอร์เฟซนั้นและ "ฉีด" เพื่อการใช้งานที่ต้องการที่รันไทม์ “ การฉีดพึ่งพา” เป็นคำที่ซับซ้อนสำหรับ“ ส่งวัตถุที่ถูกต้องเป็นอาร์กิวเมนต์”

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

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

อัลกอริทึมของฉันรับฟังก์ชั่นจากโรงงานที่ใช้ฝึกโมเดล:

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

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

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

กรณีการใช้งานที่หลากหลายของ polymorphism อยู่ใน GUI ในกรอบ GUI เช่น Java AWT / Swing / ... มีที่แตกต่างกันส่วนประกอบ อินเทอร์เฟซคอมโพเนนต์ / คลาสพื้นฐานอธิบายการดำเนินการต่าง ๆ เช่นวาดภาพตัวเองไปยังหน้าจอหรือตอบสนองต่อการคลิกเมาส์ ส่วนประกอบจำนวนมากเป็นตู้คอนเทนเนอร์ที่จัดการส่วนประกอบย่อย คอนเทนเนอร์ดังกล่าวจะดึงตัวเองได้อย่างไร

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

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

มีปัญหาซ้ำ ๆ มากมายตลอดการพัฒนาซอฟต์แวร์ที่สามารถแก้ไขได้โดยการใช้ความหลากหลายเป็นเทคนิค ปัญหาที่เกิดซ้ำเหล่านี้ - คู่โซลูชันเรียกว่ารูปแบบการออกแบบและบางคู่ถูกรวบรวมในหนังสือที่มีชื่อเดียวกัน ในแง่ของหนังสือเล่มนั้นโมเดลการเรียนรู้ของเครื่องที่ถูกอัดฉีดของฉันจะเป็นกลยุทธ์ที่ฉันใช้เพื่อ“ กำหนดตระกูลอัลกอริธึม, แค็ปซูลแต่ละอันและทำให้พวกมันใช้แทนกันได้” ตัวอย่าง Java-AWT ที่เป็นส่วนประกอบที่สามารถมีองค์ประกอบย่อยคือตัวอย่างของหนึ่งคอมโพสิต

แต่ไม่ใช่ทุกการออกแบบที่จำเป็นต้องใช้ polymorphism (นอกเหนือจากการเปิดใช้งานการฉีดแบบพึ่งพาสำหรับการทดสอบหน่วยซึ่งเป็นกรณีการใช้งานที่ดีจริงๆ) ปัญหาส่วนใหญ่คงที่มาก เป็นผลให้คลาสและวิธีการมักจะไม่ใช้สำหรับ polymorphism แต่เพียงแค่เป็น namespaces ที่สะดวกสบายและสำหรับการเรียกวิธีการสวยไวยากรณ์ เช่นนักพัฒนาหลายคนชอบเรียกวิธีการเช่นผ่านการเรียกใช้ฟังก์ชันเทียบเท่าส่วนใหญ่account.getBalance() Account_getBalance(account)นั่นเป็นวิธีการที่ดีอย่างสมบูรณ์มันเป็นเพียงการเรียกว่า“ เมธอด” จำนวนมากไม่เกี่ยวข้องกับความหลากหลาย


6

คุณเห็นการสืบทอดและ polymorphism มากมายในชุดเครื่องมือ UI ส่วนใหญ่

ยกตัวอย่างเช่นในชุดเครื่องมือ JavaFX UI, ButtonสืบทอดจากButtonBaseที่สืบทอดจากLabeledที่สืบทอดจากControlที่สืบทอดจากRegionที่สืบทอดจากParentที่สืบทอดจากซึ่งสืบทอดมาจากNode Objectเลเยอร์จำนวนมากจะแทนที่วิธีการบางอย่างจากรายการก่อนหน้า

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

ชุดเครื่องมือ UI มีความสำคัญในโลกแห่งความเป็นจริงสูงมากทำให้มีประโยชน์ในการสอนด้วยเหตุผลทั้งในเชิงวิชาการและเชิงปฏิบัติ

อย่างไรก็ตาม UI Toolkits ยังมีอุปสรรคที่สำคัญ: พวกเขามีแนวโน้มที่จะมาก เมื่อวิศวกรซอฟต์แวร์ neophyte พยายามที่จะเข้าใจการทำงานภายในของกรอบ UI ทั่วไปพวกเขามักจะพบมากกว่าหนึ่งร้อยชั้นเรียนส่วนใหญ่ของพวกเขาให้บริการเพื่อวัตถุประสงค์ที่ลึกลับมาก "ห่าReadOnlyJavaBeanLongPropertyBuilderอะไรคืออะไรมันสำคัญไหมฉันต้องเข้าใจสิ่งที่ดีหรือไม่?" ผู้เริ่มต้นสามารถหลงทางในหลุมกระต่ายนั้นได้อย่างง่ายดาย ดังนั้นพวกเขาอาจหนีด้วยความหวาดกลัวหรืออยู่บนพื้นผิวที่พวกเขาเพิ่งเรียนรู้ไวยากรณ์และพยายามที่จะไม่คิดหนักเกินไปเกี่ยวกับสิ่งที่เกิดขึ้นจริงภายใต้ประทุน


3

แม้ว่าจะมีตัวอย่างที่ดีอยู่แล้วที่นี่ แต่มีอีกตัวอย่างหนึ่งคือแทนที่สัตว์ด้วยอุปกรณ์:

  • DeviceสามารถpowerOn(), powerOff(), และสามารถsetSleep()getSerialNumber()
  • SensorDeviceสามารถทำทั้งหมดนี้และให้ฟังก์ชั่น polymorphic เช่นgetMeasuredDimension(), getMeasure(), และalertAt(threashhold)autoTest()
  • แน่นอนว่าgetMeasure()จะไม่ถูกนำไปใช้ในทางเดียวกันสำหรับเซ็นเซอร์วัดอุณหภูมิเครื่องตรวจจับแสงเครื่องตรวจจับเสียงหรือเซ็นเซอร์ปริมาตร และแน่นอนว่าเซ็นเซอร์พิเศษเหล่านี้แต่ละตัวอาจมีฟังก์ชั่นเพิ่มเติมให้ใช้งาน

2

การนำเสนอเป็นแอปพลิเคชั่นที่พบได้บ่อยมากบางทีอาจเป็น ToString () ซึ่งโดยทั่วไปแล้วเป็นสัตว์ Speak (): คุณบอกวัตถุให้ชัดแจ้ง

โดยทั่วไปคุณจะบอกวัตถุว่า "ทำสิ่งนั้น" คิดว่าบันทึกโหลดเริ่มต้นกำจัด ProcessData, GetStatus


2

การใช้งาน polymorphism ครั้งแรกของฉันคือการใช้ Heap ใน java

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

abstract class Heap {  

 abstract boolean compare ( int x , int y );

 boolean insert(int x ) { ... }

 int removeTop() { ... }
}

ดังนั้นเมื่อฉันต้องการ MaxHeap และ MinHeap ฉันก็สามารถใช้มรดกได้

class MaxHeap extends Heap {

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

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

1

นี่คือสถานการณ์ในชีวิตจริงสำหรับความหลากหลายของตารางแอปบนเว็บ :

ฉันใช้ Ruby on Rails เพื่อพัฒนาเว็บแอปและสิ่งหนึ่งที่โครงการของฉันมีเหมือนกันคือความสามารถในการอัปโหลดไฟล์ (ภาพถ่าย, PDF, ฯลฯ ) ตัวอย่างเช่น a Userอาจมีรูปภาพโปรไฟล์หลายภาพและ a Productอาจมีรูปภาพผลิตภัณฑ์จำนวนมาก ทั้งสองมีพฤติกรรมของการอัปโหลดและการจัดเก็บภาพเช่นเดียวกับการปรับขนาดการสร้างภาพขนาดเล็กและอื่น ๆ เพื่อให้อยู่แห้งและแบ่งปันพฤติกรรมสำหรับPictureเราต้องการที่จะทำPictureเพื่อให้ polymorphic ว่ามันสามารถอยู่ในทั้งสองและUserProduct

ใน Rails ฉันจะออกแบบโมเดลของฉันเช่น:

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

และการย้ายฐานข้อมูลเพื่อสร้างpicturesตาราง:

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

คอลัมน์imageable_idและimageable_typeใช้งานโดย Rails ภายใน โดยทั่วไปimageable_typeถือเป็นชื่อของคลาส (คน"User", "Product"ฯลฯ ) และimageable_idเป็นรหัสของการบันทึกที่เกี่ยวข้อง ดังนั้นimageable_type = "User"และimageable_id = 1จะบันทึกในส่วนตารางที่มีusersid = 1

สิ่งนี้ทำให้เราสามารถทำสิ่งต่าง ๆ เช่นuser.picturesเข้าถึงรูปภาพของผู้ใช้รวมproduct.picturesถึงรับรูปภาพของผลิตภัณฑ์ จากนั้นพฤติกรรมที่เกี่ยวข้องกับรูปภาพทั้งหมดจะถูกห่อหุ้มในPhotoคลาส (และไม่ใช่คลาสที่แยกจากกันสำหรับแต่ละรุ่นที่ต้องการรูปถ่าย) ดังนั้นสิ่งต่าง ๆ จึงถูกเก็บไว้แบบ DRY

อ่านเพิ่มเติม: Rails สมาคม


0

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

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

สิ่งที่ฉันอธิบายข้างต้นเป็นตัวอย่างของ runtime polymorphism ในขณะที่วิธี overloading เป็นตัวอย่างของ polymorphsim เวลารวบรวมที่ complier ขึ้นอยู่กับชนิดพารามิเตอร์ i / p และ o / p และจำนวนพารามิเตอร์ผูกโทรด้วยวิธีการที่ถูกต้องเวลา

หวังว่ามันจะชัดเจนขึ้น

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