เมื่อใดที่คุณต้องการอ้างอิงสองรายการไปยังวัตถุเดียวกัน


20

ใน Java โดยเฉพาะ แต่มีแนวโน้มในภาษาอื่นเช่นกัน: เมื่อใดจะมีประโยชน์ที่จะมีสองการอ้างอิงไปยังวัตถุเดียวกัน

ตัวอย่าง:

Dog a = new Dog();
Dob b = a;

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


มันเป็นสาระสำคัญที่อยู่เบื้องหลังรูปแบบฟลายเวท

@MichaelT คุณช่วยอธิบายได้ไหม?
Bassinator

7
ถ้าaและb มักจะอ้างอิงเดียวกันDogแล้วมีจุดที่มันไม่มี หากบางครั้งพวกเขาก็อาจจะมีประโยชน์
Gabe

คำตอบ:


45

ตัวอย่างคือเมื่อคุณต้องการให้วัตถุเดียวกันในสองรายการแยก :

Dog myDog = new Dog();
List dogsWithRabies = new ArrayList();
List dogsThatCanPlayPiano = new ArrayList();

dogsWithRabies.add(myDog);
dogsThatCanPlayPiano.add(myDog);
// Now each List has a reference to the same dog

การใช้งานอื่นคือเมื่อคุณมีวัตถุเดียวกันที่เล่นหลายบทบาท :

Person p = new Person("Bruce Wayne");
Person batman = p;
Person ceoOfWayneIndustries = p;

4
ฉันขอโทษ แต่ฉันเชื่อว่าสำหรับตัวอย่างของบรูซเวย์นคุณมีข้อบกพร่องในการออกแบบตำแหน่งแบทแมนและซีอีโอควรเป็นบทบาทสำหรับคนของคุณ
Silviu Burcea

44
-1 เพื่อเปิดเผยตัวตนลับของแบทแมน
Ampt

2
@Silviu Burcea - ฉันเห็นด้วยอย่างยิ่งกับตัวอย่างของ Bruce Wayne ไม่ใช่ตัวอย่างที่ดี ในอีกด้านหนึ่งหากการแก้ไขข้อความทั่วโลกเปลี่ยนชื่อ 'ceoOfWayneIndustries' และ 'batman' เป็น 'p' (สมมติว่าไม่มีการปะทะกันของชื่อหรือการเปลี่ยนแปลงขอบเขต) และความหมายของโปรแกรมเปลี่ยนไป ออบเจ็กต์ที่อ้างถึงแสดงถึงความหมายที่แท้จริงไม่ใช่ชื่อตัวแปรภายในโปรแกรม มีความหมายที่แตกต่างกันมันเป็นวัตถุที่แตกต่างกันหรือมีการอ้างอิงโดยสิ่งที่มีพฤติกรรมมากกว่าการอ้างอิง (ซึ่งควรจะโปร่งใส) และดังนั้นจึงไม่ใช่ตัวอย่างของการอ้างอิง 2+
gbulmer

2
แม้ว่าตัวอย่างของ Bruce Wayne ตามที่เขียนอาจไม่ทำงาน แต่ฉันเชื่อว่าความตั้งใจดังกล่าวนั้นถูกต้อง บางทีตัวอย่างที่ใกล้กว่าอาจเป็นPersona batman = new Persona("Batman"); Persona bruce = new Persona("Bruce Wayne"); Persona currentPersona = batman;- ที่คุณมีค่าที่เป็นไปได้หลายค่า (หรือรายการของค่าที่มีอยู่) และการอ้างอิงถึงค่าที่ใช้งานอยู่ในปัจจุบัน / ที่เลือก
Dan Puzey

1
@ gbulmer: ฉันจะวางตัวว่าคุณไม่สามารถอ้างอิงถึงสองวัตถุ currentPersonaจุดอ้างอิงไปยังวัตถุหนึ่งหรืออีกวัตถุหนึ่ง แต่ไม่เคยทั้งคู่ โดยเฉพาะอย่างยิ่งมันได้อย่างง่ายดายอาจเป็นไปได้ว่าcurrentPersonaจะไม่ตั้งค่าให้bruceซึ่งในกรณีนี้ก็ไม่แน่นอนการอ้างอิงไปยังวัตถุสอง ฉันจะบอกว่าในตัวอย่างของฉันทั้งสองbatmanและcurrentPersonaอ้างอิงถึงอินสแตนซ์เดียวกัน แต่ให้บริการความหมายที่แตกต่างกันในโปรแกรม
Dan Puzey

16

นั่นเป็นคำถามที่ลึกซึ้งอย่างน่าประหลาดใจจริงๆ! ประสบการณ์จาก C ++ ที่ทันสมัย ​​(และภาษาที่ใช้จาก C ++ ที่ทันสมัยเช่น Rust) แสดงให้เห็นว่าบ่อยครั้งมากคุณไม่ต้องการสิ่งนั้น! สำหรับข้อมูลส่วนใหญ่คุณต้องการการอ้างอิงเดียวหรือไม่ซ้ำกัน ("เป็นเจ้าของ") ความคิดนี้ยังเป็นหนึ่งในเหตุผลหลักสำหรับระบบการพิมพ์เชิงเส้น

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

void encounter(Dog a) {
  hissAt(a);
}

void hissAt(Dog b) {
  // ...
}

กรณีทั่วไปน้อยลงเมื่อคุณใช้หนึ่งในสองวัตถุขึ้นอยู่กับเงื่อนไขการทำสิ่งเดียวกันโดยไม่คำนึงถึงสิ่งที่คุณเลือก:

Dog a, b;
Dog goodBoy = whoseAGoodBoy ? a : b;
feed(goodBoy);
walk(goodBoy);
pet(goodBoy);

กลับไปที่การใช้งานทั่วไปมากขึ้น แต่ทิ้งตัวแปรท้องถิ่นไว้ข้างหลังเรากลับไปที่ฟิลด์: ตัวอย่างเช่นวิดเจ็ตในกรอบงาน GUI มักจะมีวิดเจ็ตหลักดังนั้นเฟรมใหญ่ของคุณที่มีสิบปุ่มจะมีการอ้างอิงอย่างน้อยสิบตัวชี้ จากของผู้ปกครองและบางทีอาจจะฟังจากเหตุการณ์และอื่น ๆ ) กราฟวัตถุใด ๆ และต้นไม้วัตถุบางชนิด (ที่มีการอ้างอิง parent / sibling) มีวัตถุหลายรายการที่อ้างอิงถึงวัตถุเดียวกันแต่ละรายการ และแทบทุกชุดข้อมูลเป็นกราฟ ;-)


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

1
@ SJuan76 "กรณีธรรมดาน้อย" เป็นเพียงการเอาตัวแปรท้องถิ่นมาใช้ แต่สิ่งใดก็ตามที่เกี่ยวข้องกับโครงสร้างข้อมูลตกอยู่ในจุดสุดท้าย

การอ้างอิงที่เหมาะสมเพียงหนึ่งครั้งนี้เกิดจากการจัดการหน่วยความจำที่อ้างอิงหรือไม่ ถ้าเป็นเช่นนั้นมันจะคุ้มค่าที่จะกล่าวถึงว่าแรงจูงใจไม่เกี่ยวข้องกับภาษาอื่นที่มีนักสะสมขยะต่าง ๆ (เช่น C #, Java, Python)
MarkJ

@ MarkJ Linear types ไม่ได้เป็นเพียงรหัสที่สมบูรณ์แบบที่มีอยู่มากโดยไม่รู้ตัวเพราะมันเป็นสิ่งที่ถูกต้องทางความหมายสำหรับบางกรณี มันมีข้อดีด้านประสิทธิภาพที่เป็นไปได้ (แต่ไม่ใช่สำหรับการนับการอ้างอิงหรือการติดตาม GCs จะช่วยได้ก็ต่อเมื่อคุณใช้กลยุทธ์ที่แตกต่างกันซึ่งใช้ประโยชน์จากความรู้นั้นเพื่อละเว้นทั้งการนับและติดตาม) ที่น่าสนใจคือแอพพลิเคชั่นสำหรับการจัดการทรัพยากรที่ง่ายและกำหนดขึ้นเอง คิดว่า RAII และ C ++ 11 ความหมายของการย้าย แต่ดีกว่า (ใช้บ่อยกว่าและมีข้อผิดพลาดเกิดขึ้นจากคอมไพเลอร์)

6

ตัวแปรชั่วคราว: พิจารณา pseudocode ต่อไปนี้

Object getMaximum(Collection objects) {
  Object max = null;
  for (Object candidate IN objects) {
    if ((max is null) OR (candidate > max)) {
      max = candidate;
    }
  }
  return max;
}

ตัวแปรmaxและcandidateอาจชี้ไปที่วัตถุเดียวกัน แต่การกำหนดตัวแปรเปลี่ยนแปลงโดยใช้กฎที่แตกต่างกันและในเวลาที่ต่างกัน


3

เพื่อเสริมคำตอบอื่น ๆ คุณอาจต้องการสำรวจโครงสร้างข้อมูลต่าง ๆ โดยเริ่มจากที่เดียวกัน ตัวอย่างเช่นหากคุณมี a BinaryTree a = new BinaryTree(...); BinaryTree b = aคุณสามารถข้ามเส้นทางซ้ายสุดของต้นไม้ด้วยaและเส้นทางขวาสุดด้วยbโดยใช้บางสิ่งเช่น:

while (!a.equals(null) && !b.equals(null)) {
    a = a.left();
    b = b.right();
}

เป็นเวลานานแล้วที่ฉันเขียน Java ดังนั้นรหัสอาจไม่ถูกต้องหรือสมเหตุสมผล ใช้เป็น pseudocode เพิ่มเติม


3

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

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

Tab Tab1 = new Tab();
Tab Tab2 = new Tab();
Tab Tab3 = new Tab();
Tab CurrentTab = new Tab();

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

CurrentTab = Tab3;

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


3

มีหลายสถานการณ์ที่b ต้องมีการอ้างอิงถึง "" ที่ไม่รู้จักaเพื่อที่จะเป็นประโยชน์ โดยเฉพาะอย่างยิ่ง:

  • เมื่อใดก็ตามที่คุณไม่ทราบว่ามีbจุดใดในเวลารวบรวม
  • เวลาใดก็ตามที่คุณต้องการวนซ้ำคอลเลกชันไม่ว่าจะเป็นที่รู้จักกันในเวลารวบรวมหรือไม่
  • ทุกครั้งที่คุณมีขอบเขต จำกัด

ตัวอย่างเช่น:

พารามิเตอร์

public void DoSomething(Thing &t) {
}

t เป็นการอ้างอิงถึงตัวแปรจากขอบเขตภายนอก

ส่งคืนค่าและค่าเงื่อนไขอื่น ๆ

Thing a = Thing.Get("a");
Thing b = Thing.Get("b");
Thing biggerThing = Thing.max(a, b);
Thing z = a.IsMoreZThan(b) ? a : b;

biggerThingและzมีการอ้างอิงไปยังแต่ละอย่างใดอย่างหนึ่งหรือa bเราไม่ทราบว่าจะรวบรวมเวลาใด

Lambdas และค่าตอบแทนของพวกเขา

Thing t = someCollection.FirstOrDefault(x => x.whatever > 123);

xคือพารามิเตอร์ (ตัวอย่าง 1 ด้านบน) และtเป็นค่าส่งคืน (ตัวอย่าง 2 ด้านบน)

คอลเลกชัน

indexByName.add(t.name, t);
process(indexByName["some name"]);

index["some name"]bคือการขอบเขตขนาดใหญ่ที่มีความซับซ้อนมากขึ้นมอง มันเป็นนามแฝงของวัตถุที่สร้างและยัดเข้าไปในคอลเลกชัน

ลูป

foreach (Thing t in things) {
 /* `t` is a reference to a thing in a collection */
}

t เป็นการอ้างอิงถึงไอเท็มที่ส่งคืน (ตัวอย่าง 2) โดยตัววนซ้ำ (ตัวอย่างก่อนหน้า)


ตัวอย่างของคุณยากที่จะติดตาม อย่าเข้าใจฉันผิดฉันต้องผ่านพวกเขา แต่ฉันต้องทำงานให้สำเร็จ ในอนาคตฉันขอแนะนำให้คุณแยกตัวอย่างโค้ดของคุณออกเป็นบล็อกย่อย (โดยมีหมายเหตุบางข้อความอธิบายไว้เป็นพิเศษ) โดยไม่ต้องใส่ตัวอย่างที่ไม่เกี่ยวข้องสองสามอย่างไว้ในบล็อกเดียว
Bassinator

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

2

มันเป็นจุดสำคัญ แต่ IMHO คุ้มค่าที่จะเข้าใจ

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

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

ทุกครั้งที่คุณส่งผ่านaเป็นอาร์กิวเมนต์ / พารามิเตอร์ไปยังฟังก์ชั่นอื่นเช่นการโทร
foo(Dog aDoggy);
หรือใช้วิธีaการรหัสโปรแกรมพื้นฐานทำสำเนาของการอ้างอิงเพื่อสร้างการอ้างอิงที่สองไปยังวัตถุเดียวกัน

นอกจากนี้หากรหัสที่มีการอ้างอิงที่คัดลอกอยู่ในเธรดที่ต่างกันทั้งคู่สามารถใช้พร้อมกันเพื่อเข้าถึงวัตถุเดียวกัน

ดังนั้นในโปรแกรมที่มีประโยชน์มากที่สุดจะมีการอ้างอิงหลายอย่างไปยังวัตถุเดียวกันเพราะนั่นคือความหมายของภาษาการเขียนโปรแกรม OO ส่วนใหญ่

ตอนนี้ถ้าเราคิดถึงมันเพราะการส่งต่อโดยอ้างอิงเป็นกลไกเดียวที่มีอยู่ในหลายภาษา OO (C ++ รองรับทั้งคู่) เราอาจคาดหวังว่ามันจะเป็นพฤติกรรมเริ่มต้นที่ 'ถูกต้อง'

IMHO การใช้การอ้างอิงเป็นค่าเริ่มต้นที่ถูกต้องด้วยเหตุผลสองประการ:

  1. มันรับประกันว่ามูลค่าของวัตถุที่ใช้ในสองสถานที่ที่แตกต่างกันเหมือนกัน ลองนึกภาพวางวัตถุลงในโครงสร้างข้อมูลที่แตกต่างกันสอง (อาร์เรย์รายการ ฯลฯ ) และทำการดำเนินการบางอย่างบนวัตถุที่เปลี่ยนแปลงมัน นั่นอาจเป็นฝันร้ายที่จะแก้ไขข้อผิดพลาด ที่สำคัญมันเป็นวัตถุเดียวกันในโครงสร้างข้อมูลทั้งสองหรือโปรแกรมมีข้อผิดพลาด
  2. คุณสามารถ refactor รหัสอย่างมีความสุขในหลาย ๆ ฟังก์ชั่นหรือรวมรหัสจากฟังก์ชั่นหลาย ๆ เป็นหนึ่งและความหมายไม่เปลี่ยนแปลง หากภาษาไม่ได้ให้ความหมายของการอ้างอิงมันจะซับซ้อนยิ่งขึ้นในการปรับเปลี่ยนรหัส

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

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

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

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

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

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

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

ความหมายอื่นใดจะยากต่อการจัดการ

Dog a = new Dog("rover");  // initialise with name 
DogList dl = new DogList()
dl.add(a)
...
a.setOwner("Mr Been")

ฉันขอแนะนำว่า "rover" ในdlควรเป็นผลกระทบsetOwnerหรือโปรแกรมยากที่จะเขียนทำความเข้าใจแก้ปัญหาหรือแก้ไข ฉันคิดว่าโปรแกรมเมอร์ส่วนใหญ่จะงงหรือตกใจอย่างอื่น

ต่อมาสุนัขจะถูกขาย:

soldDog = dl.lookupOwner("rover", "Mr Been")
soldDog.setOwner("Mr Mcgoo")

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

สรุป: มันเหมาะสมเสมอที่จะมีการอ้างอิงหลาย ๆ อันไปยังวัตถุเดียวกัน


จุดดี แต่นี่เหมาะกว่าเป็นความคิดเห็น
Bassinator

@HCBPshenanigans - ฉันคิดว่าประเด็นอาจสั้นเกินไปและไม่ได้พูดมากเกินไป ดังนั้นฉันจึงขยายขอบเขตการให้เหตุผล ฉันคิดว่านี่เป็นคำตอบ 'เมตา' สำหรับคำถามของคุณ สรุปการอ้างอิงหลายอย่างไปยังวัตถุเดียวกันนั้นมีความสำคัญต่อการทำให้โปรแกรมเขียนได้ง่ายเนื่องจากวัตถุจำนวนมากในโปรแกรมแสดงถึงอินสแตนซ์ที่เฉพาะเจาะจงหรือไม่ซ้ำกันของสิ่งต่าง ๆ ในโลกแห่งความจริง
gbulmer

1

แน่นอนว่ามีอีกสถานการณ์หนึ่งที่คุณอาจจะ:

Dog a = new Dog();
Dog b = a;

คือเมื่อคุณกำลังการรักษารหัสและbใช้เป็นสุนัขที่แตกต่างกันหรือชั้นที่แตกต่างกัน aแต่ตอนนี้เป็นท้องที่

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


1

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

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


0

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


-2

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

ตัวอย่างเช่นหากคุณมีสองตาราง:

สุนัข:

  • id | owner_id | ชื่อ
  • 1 | 1 | เปลือกเคนท์

เจ้าของ:

  • id | ชื่อ
  • 1 | ผม
  • 2 | คุณ

มีหลายวิธีที่ ORM ของคุณสามารถทำได้หากมีการโทรดังต่อไปนี้:

dog = Dog.find(1)  // fetch1
owner = Owner.find(1) // fetch2
superdog = owner.dogs.first() // fetch3
superdog.name = "Superdog"
superdog.save! // write1
owner = dog.owner // fetch4
owner.name = "Mark"
owner.save! // write2
dog.owner = Owner.find(2)
dog.save! // write3

ในกลยุทธ์ไร้เดียงสาการเรียกใช้โมเดลและการอ้างอิงที่เกี่ยวข้องทั้งหมดจะดึงวัตถุแยกออกจากกัน Dog.find(), Owner.find(), owner.dogsและdog.ownerผลในฐานข้อมูลตีรอบครั้งแรกหลังจากที่พวกเขาจะถูกบันทึกไว้ในหน่วยความจำ และอื่น ๆ :

  • ฐานข้อมูลถูกดึงจากอย่างน้อย 4 ครั้ง
  • dog.owner ไม่เหมือนกับ superdog.owner (ดึงแยกต่างหาก)
  • dog.name ไม่เหมือนกับ superdog.name
  • dog และ superdog พยายามเขียนลงในแถวเดียวกันและจะเขียนทับผลลัพธ์ของกันและกัน: write3 จะยกเลิกการเปลี่ยนชื่อใน write1

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

สมมติว่าออมของคุณรู้ว่าการอ้างอิงทั้งหมดไปยังตาราง 1 ของตารางสุนัขควรชี้ไปที่สิ่งเดียวกัน แล้ว:

  • fetch4 สามารถกำจัดได้เนื่องจากมีวัตถุในหน่วยความจำที่สอดคล้องกับ Owner.find (1) fetch3 จะส่งผลให้มีการสแกนดัชนีอย่างน้อยเนื่องจากอาจมีสุนัขตัวอื่นที่เป็นเจ้าของโดยเจ้าของ แต่จะไม่เรียกการดึงข้อมูลแถว
  • dog และ superdog ชี้ไปที่วัตถุเดียวกัน
  • dog.name และ superdog.name ชี้ไปที่วัตถุเดียวกัน
  • dog.owner และ superdog.owner ชี้ไปที่วัตถุเดียวกัน
  • write3 ไม่เขียนทับการเปลี่ยนแปลงใน write1

กล่าวอีกนัยหนึ่งการใช้การอ้างอิงช่วยประมวลหลักการของความจริงเพียงจุดเดียว (อย่างน้อยภายในเธรดนั้น) ซึ่งเป็นแถวในฐานข้อมูล

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