เมื่อได้รับฝูงม้าฉันจะค้นหาความยาวแตรโดยเฉลี่ยของยูนิคอร์นทั้งหมดได้อย่างไร


30

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

ฉันคิดวิธีการกรอบ NET อย่างน้อยหนึ่งวิธีที่มีจุดประสงค์เพื่อแก้ไขปัญหานี้เช่นเดียวกับEnumerable.OfType<T>วิธีการ แต่ความจริงที่ว่าท้ายที่สุดคุณก็สอบปากคำชนิดของวัตถุที่รันไทม์ไม่ได้อยู่กับฉัน

นอกเหนือจากการถามม้าแต่ละตัว "คุณเป็นยูนิคอร์นหรือไม่" วิธีการต่อไปนี้อยู่ในใจ:

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

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

ฉันยังสนใจในข้อมูลเชิงลึกใด ๆ ว่าปัญหานี้ยังคงมีอยู่ในรหัสการทำงานหรือไม่

สิ่งนี้ถูกตั้งค่าสถานะว่าซ้ำซ้อนกับคำถามต่อไปนี้: จะหลีกเลี่ยงการดาวน์สตรีมได้อย่างไร

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

ไม่มีกHornMeasurer. คำตอบที่ได้รับการยอมรับจะสะท้อนวิธีการตามข้อยกเว้นที่ระบุไว้ข้างต้น

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


22
ม้าไม่มีเขาดังนั้นค่าเฉลี่ยจึงไม่ได้กำหนด (0/0)
Scott Whitlock

3
@ moarboilerplate ได้ทุกที่ตั้งแต่ 10 ถึงอินฟินิตี้
พี่เลี้ยง

4
@StephenP: นั่นไม่ได้ผลทางคณิตศาสตร์สำหรับกรณีนี้ 0s ทั้งหมดนั้นจะเบี่ยงเบนค่าเฉลี่ย
Mason Wheeler

3
หากคำถามของคุณมีคำตอบที่ดีที่สุดโดยไม่มีคำตอบแสดงว่าคำถามนั้นไม่ได้อยู่ในเว็บไซต์ถามตอบ reddit, quora หรือเว็บไซต์อื่น ๆ ที่ใช้การอภิปรายสร้างขึ้นสำหรับสิ่งที่ไม่ใช่คำตอบประเภท ... ที่กล่าวว่าฉันคิดว่ามันอาจจะตอบได้อย่างชัดเจนถ้าคุณกำลังมองหารหัส @MasonWheeler ให้ถ้าไม่คิดว่าฉันไม่มีความคิด สิ่งที่คุณกำลังพยายามถาม ..
Jimmy Hoffa

3
@JimmyHoffa "คุณทำผิด" เกิดขึ้นกับ "ไม่ตอบ" และยอมรับได้ดีกว่า "ดีนี่เป็นวิธีหนึ่งที่คุณทำได้" - ไม่จำเป็นต้องมีการอภิปรายเพิ่มเติม
moarboilerplate

คำตอบ:


11

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

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

case class Horse(val hornLength: Option[Double])

val horse = Horse(None)
val unicorn = Horse(Some(12.0))
val anotherUnicorn = Horse(Some(6.0))

val herd = List(horse, unicorn, anotherUnicorn)
val hornLengths = herd flatMap {_.hornLength}
val averageLength = hornLengths.sum / hornLengths.size

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


7
แน่นอนว่านี้ถือว่าความแตกต่างเพียงอย่างเดียวระหว่างม้าธรรมดากับยูนิคอร์นคือเสียงแตร หากไม่เป็นเช่นนั้นสิ่งต่าง ๆ ก็จะซับซ้อนมากขึ้นอย่างรวดเร็วมาก
Mason Wheeler

@MasonWheeler เฉพาะในวิธีที่สองที่นำเสนอ
moarboilerplate

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

1
ประนีประนอมหากคุณสรุปคุณสมบัติของยูนิคอร์นในทรัพย์สินแบบสแตนด์อโลนและระมัดระวังอย่างยิ่ง - ทำไมต้องทำให้ชีวิตยากลำบากสำหรับตัวคุณเอง ใช้ typeof โดยตรงและบันทึกปัญหาในอนาคตมากมาย
gbjbaanb

@gbjbaanb ฉันจะพิจารณาวิธีการที่เหมาะสมจริง ๆ เท่านั้นสำหรับสถานการณ์ที่โลหิตจางHorseมีIsUnicornคุณสมบัติและคุณสมบัติบางอย่างที่UnicornStuffมีความยาวแตรอยู่ (เมื่อปรับขนาดสำหรับผู้ขับขี่ / แววที่กล่าวถึงในคำถามของคุณ)
moarboilerplate

38

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

horses.OfType<Unicorn>().Average(u => u.HornLength)ส่วนตัวผมต้องการเพียงแค่ไปกับ มันเป็นการแสดงออกถึงความตั้งใจของรหัสอย่างชัดเจนซึ่งมักเป็นสิ่งที่สำคัญที่สุดเนื่องจากมีใครบางคนกำลังจะต้องรักษามันไว้ในภายหลัง


โปรดยกโทษให้ฉันถ้าไวยากรณ์แลมบ์ดาของฉันไม่ถูกต้อง ฉันไม่ได้เป็น C # coder มากและฉันไม่สามารถเก็บรายละเอียดที่เป็นความลับเช่นนี้ได้ มันควรจะชัดเจนว่าฉันหมายถึงอะไร
Mason Wheeler

1
ไม่ต้องกังวลปัญหานี้จะได้รับการแก้ไขเมื่อรายการมีเพียงUnicorns อยู่แล้ว (สำหรับระเบียนที่คุณสามารถละเว้นreturn)
moarboilerplate

4
นี่คือคำตอบที่ฉันจะไปหากฉันต้องการแก้ปัญหาอย่างรวดเร็ว แต่ไม่ใช่คำตอบถ้าฉันต้องการ refactor รหัสให้เป็นไปได้มากขึ้น
Andy

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

1
@DavidGrinberg จะเกิดอะไรขึ้นถ้าการเขียนวิธีการที่สะอาดและอ่านได้นี้หมายความว่าคุณจะต้องใช้โครงสร้างการสืบทอดที่ก่อนหน้านี้ไม่มีอยู่
moarboilerplate

9

ไม่มีอะไรผิดปกติใน. NET กับ:

var unicorn = animal as Unicorn;
if(unicorn != null)
{
    sum += unicorn.HornLength;
    count++;
}

ใช้เทียบเท่า Linq ก็ดีเช่นกัน:

var averageUnicornHornLength = animals
    .OfType<Unicorn>()
    .Select(x => x.HornLength)
    .Average();

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

var averageHornedAnimalHornLength = animals
    .OfType<IHornedAnimal>()
    .Select(x => x.HornLength)
    .Average();

โปรดทราบว่าเมื่อใช้ Linq, Average(และMinและMax) จะโยนข้อยกเว้นถ้านับได้ว่างเปล่าและประเภท T ไม่เป็นโมฆะ นั่นเป็นเพราะค่าเฉลี่ยไม่ได้กำหนดจริงๆ (0/0) ดังนั้นคุณต้องการบางสิ่งเช่นนี้จริงๆ:

var hornedAnimals = animals
    .OfType<IHornedAnimal>()
    .ToList();
if(hornedAnimals.Count > 0)
{
    var averageHornLengthOfHornedAnimals = hornedAnimals
        .Average(x => x.HornLength);
}
else
{
    // deal with it in your own way...
}

แก้ไข

ฉันคิดว่าต้องเพิ่ม ... หนึ่งในเหตุผลที่คำถามแบบนี้ไม่เหมาะกับโปรแกรมเมอร์เชิงวัตถุคือมันถือว่าเราใช้คลาสและวัตถุเพื่อทำแบบจำลองโครงสร้างข้อมูล แนวคิดดั้งเดิมของ Smalltalk-esque object oriented คือการจัดโครงสร้างโปรแกรมของคุณจากโมดูลที่ถูกสร้างเป็นวัตถุและให้บริการแก่คุณเมื่อคุณส่งข้อความถึงพวกเขา ความจริงที่ว่าเรายังสามารถใช้คลาสและวัตถุเพื่อทำแบบจำลองโครงสร้างข้อมูลเป็นผลข้างเคียง (มีประโยชน์) แต่เป็นสองสิ่งที่แตกต่างกัน ฉันไม่คิดว่าสิ่งหลังควรได้รับการพิจารณาว่าเป็นโปรแกรมเชิงวัตถุเนื่องจากคุณสามารถทำสิ่งเดียวกันกับ a structได้ แต่มันจะไม่สวย

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

ในทางกลับกันหากคุณ (mis-) ใช้ความคิดคลาส / วัตถุ / อินเทอร์เฟซเพื่อสร้างโครงสร้างข้อมูลหรือแบบจำลองข้อมูลฉันเองก็ไม่เห็นปัญหากับการใช้แนวคิดแบบ is-a อย่างเต็มที่ หากคุณกำหนดว่ายูนิคอร์นเป็นประเภทย่อยของม้าและมันสมเหตุสมผลในโดเมนของคุณอย่างสมบูรณ์จากนั้นไปข้างหน้าอย่างแน่นอนและค้นหาม้าในฝูงของคุณเพื่อค้นหายูนิคอร์น ท้ายที่สุดในกรณีเช่นนี้เรามักจะพยายามสร้างภาษาเฉพาะโดเมนเพื่อแสดงวิธีแก้ปัญหาที่เราต้องแก้ให้ดีขึ้น ในแง่นั้นไม่มีอะไรผิดปกติกับ.OfType<Unicorn>()ฯลฯ

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


7
คุณรู้อยู่แล้วว่าanimal เป็นUnicorn ; เพิ่งแคสต์แทนที่จะใช้asหรืออาจใช้งานได้ดีขึ้นas แล้วตรวจสอบหาค่าว่าง
Philip Kendall

3

แต่ความจริงที่ว่าในที่สุดคุณก็สิ้นสุดการซักถามชนิดของวัตถุที่รันไทม์ไม่ตรงกับฉัน

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

ระบุว่ามันสมเหตุสมผลที่จะใช้การสนับสนุนภาษาของคุณเพื่อทำสิ่งนี้ ใน. NET คุณจะใช้typeofเป็นตัวอย่าง

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

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

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


ในบริบทดั้งเดิมif horn > 0เป็นวิธีแก้ปัญหานี้ในตอนแรก จากนั้นปัญหาที่มักเกิดขึ้นคือเมื่อคุณต้องการตรวจสอบผู้ขับขี่และแสงแวววาวและhorn > 0ถูกฝังอยู่ทั่วสถานที่ในรหัสที่ไม่เกี่ยวข้อง นอกจากนี้ม้ารองคลาสหลังจากความจริงมักจะเป็นข้อเสนอที่แพงที่สุดดังนั้นฉันมักจะไม่อยากทำถ้าพวกเขายังคงเขียนด้วยกันในตอนท้ายของ refactor ดังนั้นมันจึงกลายเป็น "ตัวเลือกที่น่าเกลียด"
moarboilerplate

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

3

เนื่องจากคำถามนั้นมีfunctional-programmingแท็กเราสามารถใช้ผลรวมเพื่อสะท้อนให้เห็นถึงสองรสชาติของม้าและการจับคู่รูปแบบเพื่อแก้ปัญหาระหว่างพวกเขา ตัวอย่างเช่นใน F #:

type Equine =
| Horse
| Unicorn of hornLength: float

module equines =

  let averageHornLength (equines : Equine list) =
    equines 
    |> List.choose (fun x -> 
      match x with
      | Unicorn u -> Some(u)
      | _ -> None)
    |> List.average

let herd = [ Horse ; Horse ; Unicorn(35.0) ; Horse ; Unicorn(50.0) ]

printfn "Average horn length in herd : %f" (equines.averageHornLength herd) // prints 42.5

เหนือ OOP, FP มีข้อได้เปรียบของการแยกข้อมูล / ฟังก์ชั่นซึ่งอาจช่วยให้คุณประหยัดจาก "ความรู้สึกผิดที่ผิด" (การไม่ยุติธรรม) ของการละเมิดระดับของสิ่งที่เป็นนามธรรม

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


2

รูปแบบสั้น ๆ ของคำตอบเดียวกันท้ายต้องอ่านหนังสือหรือบทความบนเว็บ

รูปแบบผู้เข้าชม

ปัญหามีส่วนผสมของม้าและยูนิคอร์น (การละเมิดหลักการทดแทน Liskov เป็นปัญหาที่พบบ่อยในรหัสฐานแบบดั้งเดิม)

เพิ่มวิธีการให้กับม้าและคลาสย่อยทั้งหมด

Horse.visit(EquineVisitor v)

ส่วนต่อประสานผู้เยี่ยมชมที่มีลักษณะคล้ายม้านี้ใน java / c #

interface EquineVisitor {
  void visitHorse(Horse z);
  void visitUnicorn(Unicorn z);
}

Unicorn.visit(EquineVisitor v){
   v.visitUnicorn(this);
}

Horse.visit(EquineVisitor v){
   v.visitHorse(this);
}

ในการวัดแตรเขาเขียนตอนนี้ ....

class HornMeasurer implements EquineVistor {
    void visitHorse(Horse h){} // ignore horses
    void visitUnicorn(Unicorn u){
         double len = u.getHornLength();
         totalLength+=len;
         unicornCount++;
    }

    double getAverageLength(){
          return totalLength/unicornCount;
    }

    double totalLength=0;
    int unicornCount=0;
}

รูปแบบของผู้เข้าชมถูกวิพากษ์วิจารณ์ว่าทำให้การปรับโครงสร้างและการเติบโตยากขึ้น

คำตอบสั้น ๆ : ใช้รูปแบบการออกแบบผู้เข้าชมเพื่อรับการจัดส่งสองครั้ง

ดูhttps://en.wikipedia.org/wiki/Visitor_pattern

ดูhttp://c2.com/cgi/wiki?VisitorPattern สำหรับการสนทนาของผู้เยี่ยมชม

ดูรูปแบบการออกแบบโดย Gamma และคณะ


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

0

สมมติว่าในยูนิคอร์นในสถาปัตยกรรมของคุณเป็นสายพันธุ์ย่อยของม้าและคุณได้พบกับสถานที่ที่คุณจะได้รับคอลเล็กชั่นHorseที่พวกเขาอาจจะเป็นUnicorns ฉันเองจะไปด้วยวิธีแรก ( .OfType<Unicorn>()...) เพราะมันเป็นวิธีที่ตรงไปตรงมามากที่สุด . สำหรับใครก็ตามที่เข้ามาในภายหลัง (รวมถึงตัวคุณเองใน 3 เดือน) จะเห็นได้ชัดว่าคุณกำลังพยายามทำอะไรให้สำเร็จด้วยรหัสนั้น: เลือกยูนิคอร์นจากม้า

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

foreach (var horse in horses)
{
    try
    {
        var length = horse.MeasureHorn();
        //...
    }
    catch (NoHornException e)
    {
        continue;
    }
}

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

ให้บอกว่าคุณไปเส้นทางค่าเวทมนตร์สำหรับการตรวจสอบเขาบนม้า ดังนั้นตอนนี้ชั้นเรียนของคุณจะมีหน้าตาดังนี้:

class Horse
{
    public double MeasureHorn() { return -1; }
    //...
}

class Unicorn : Horse
{
    public override double MeasureHorn { return _hornLength; }
    //...
}

ตอนนี้Horseชั้นเรียนของคุณต้องรู้เกี่ยวกับUnicornชั้นเรียนและมีวิธีพิเศษในการจัดการกับสิ่งที่ไม่สนใจ ตอนนี้คิดว่าคุณยังมีPegasusและZebras Horseสืบทอดจากที่ ตอนนี้Horseต้องมีFlyวิธีการเช่นเดียวกับMeasureWings, CountStripesฯลฯ และแล้วUnicornชั้นจะได้รับวิธีการเหล่านี้มากเกินไป ตอนนี้ชั้นเรียนของคุณทุกคนต้องรู้จักกันและคุณได้ทำให้ชั้นเรียนสกปรกด้วยวิธีการมากมายที่ไม่ควรอยู่ที่นั่นเพียงเพื่อหลีกเลี่ยงการถามระบบประเภท "นี่คือยูนิคอร์นหรือไม่"

ดังนั้นการเพิ่มบางสิ่งลงไปHorseเพื่อบอกว่ามีบางสิ่งUnicornและจัดการกับการวัดฮอร์นทั้งหมดหรือไม่ ทีนี้คุณต้องตรวจสอบการมีอยู่ของวัตถุนี้เพื่อที่จะรู้ว่ามีบางสิ่งเป็นยูนิคอร์นหรือไม่ นอกจากนี้ยังทำให้น้ำเล็กน้อยในตอนนี้คุณอาจจะมีList<Horse> unicornsที่เก็บยูนิคอร์นทั้งหมดจริงๆ แต่ระบบชนิดและตัวดีบักไม่สามารถบอกคุณได้อย่างง่ายดาย "แต่ฉันรู้ว่ามันคือยูนิคอร์นทั้งหมด" คุณพูด "ชื่อก็พูดอย่างนั้น" ถ้ามีชื่ออะไรไม่ดีล่ะ หรือว่าคุณเขียนอะไรบางอย่างโดยมีข้อสันนิษฐานว่ามันจะเป็นยูนิคอร์นจริงๆ แต่หลังจากนั้นความต้องการก็เปลี่ยนไปและตอนนี้มันอาจจะมีเพกาซีผสมอยู่ด้วย? (เพราะไม่มีอะไรแบบนี้เกิดขึ้นโดยเฉพาะอย่างยิ่งในซอฟต์แวร์แบบดั้งเดิม / การประชดประชัน) ตอนนี้ระบบการพิมพ์จะทำให้ pegasi ของคุณมีความสุขด้วยยูนิคอร์นของคุณ หากตัวแปรของคุณได้รับการประกาศเป็นList<Unicorn>คอมไพเลอร์ (หรือสภาพแวดล้อมรันไทม์) จะมีความเหมาะสมถ้าคุณพยายามที่จะผสมใน pegasi หรือม้า

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

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


ข้อยกเว้นเงียบไม่ดีแน่นอน - ข้อเสนอของฉันคือการตรวจสอบที่จะเป็นif(horse.IsUnicorn) horse.MeasureHorn();และจะไม่มีการจับข้อยกเว้น - พวกเขาจะถูกเรียกใช้เมื่อ!horse.IsUnicornและคุณอยู่ในบริบทของยูนิคอร์นหรือในMeasureHornที่ไม่ใช่ยูนิคอร์น ด้วยวิธีนี้เมื่อมีการโยนข้อยกเว้นคุณจะไม่ปิดบังข้อผิดพลาดมันจะระเบิดอย่างสมบูรณ์และเป็นสัญญาณบางอย่างที่ต้องแก้ไข เห็นได้ชัดว่ามันเหมาะสำหรับบางสถานการณ์เท่านั้น แต่เป็นการใช้งานที่ไม่ได้ใช้ข้อยกเว้นในการกำหนดเส้นทางการดำเนินการ
moarboilerplate

0

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

การเขียนโปรแกรมเชิงวัตถุนั้นพึ่งพาความคิดของความสัมพันธ์ IS-A ค่อนข้างหนักมันมีเนื้อหาที่หนักแน่นเกินกว่าที่จะนำไปสู่แนวคิดที่มีชื่อเสียงสองประการ:

แต่ฉันคิดว่ามีวิธีการเขียนโปรแกรมที่ใช้งานได้มากกว่าเพื่อดูความสัมพันธ์ IS-A ที่อาจไม่มีปัญหาเหล่านี้ ก่อนอื่นเราต้องการสร้างแบบจำลองม้าและยูนิคอร์นในโปรแกรมของเราดังนั้นเราจะมี a Horseและa Unicorntype ค่าของประเภทเหล่านี้คืออะไร? ฉันจะบอกว่า:

  1. ค่าของประเภทเหล่านี้เป็นตัวแทนหรือคำอธิบายของม้าและยูนิคอร์น (ตามลำดับ);
  2. พวกเขาเป็นตัวแทนschematizedหรือคำอธิบาย - พวกเขาไม่ได้ฟรีฟอร์มพวกเขาสร้างขึ้นตามกฎที่เข้มงวดมาก

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

  1. มีฟังก์ชั่นทั้งหมดที่แปลงCircleคำอธิบายวงกลม (schematized วงกลม) ให้เป็นEllipse(คำอธิบายประเภทอื่น) ที่อธิบายถึงวงกลมเดียวกัน
  2. มีความเป็นฟังก์ชั่นบางส่วนที่ใช้เวลาและถ้าอธิบายวงกลมผลตอบแทนที่สอดคล้องกันEllipseCircle

ดังนั้นในแง่การเขียนโปรแกรมเชิงฟังก์ชันUnicornประเภทของคุณไม่จำเป็นต้องเป็นประเภทย่อยHorseเลยคุณเพียงแค่ต้องการการดำเนินการเช่นนี้:

-- Convert any unicorn-description of into a horse-description that
-- describes the same unicorns.
toHorse :: Unicorn -> Horse

-- If the horse described by the given horse-description is a unicorn,
-- then return a unicorn-description of that unicorn, otherwise return
-- nothing.
toUnicorn :: Horse -> Maybe Unicorn

และtoUnicornต้องเป็นสิ่งที่ตรงกันข้ามกับtoHorse:

toUnicorn (toHorse x) = Just x

Maybeประเภทของ Haskell เป็นสิ่งที่ภาษาอื่นเรียกว่า "ตัวเลือก" ประเภท ตัวอย่างเช่นOptional<Unicorn>ประเภทJava 8 เป็นได้Unicornหรือไม่มีอะไร โปรดทราบว่าทางเลือกสองทางของคุณ ได้แก่ การยกเว้นหรือส่งคืน "ค่าเริ่มต้นหรือค่าเวทย์มนตร์" - คล้ายกับประเภทตัวเลือกมาก

ดังนั้นสิ่งที่ฉันทำที่นี่คือสร้างแนวคิด IS-A ขึ้นมาใหม่ในแง่ของประเภทและฟังก์ชั่นโดยไม่ต้องใช้ชนิดย่อยหรือการสืบทอด สิ่งที่ฉันจะเอาไปจากนี้คือ:

  1. แบบจำลองของคุณต้องมีHorseชนิด
  2. Horseประเภทความต้องการในการเข้ารหัสข้อมูลเพียงพอที่จะตรวจสอบอย่างไม่น่าสงสัยว่าค่าใด ๆ อธิบายยูนิคอร์น;
  3. การดำเนินการบางอย่างของHorseประเภทต้องเปิดเผยข้อมูลนั้นเพื่อให้ลูกค้าประเภทสามารถสังเกตได้ว่าการให้Horseเป็นยูนิคอร์น;
  4. ลูกค้าHorseประเภทนี้จะต้องใช้การดำเนินการหลังเหล่านี้ที่รันไทม์เพื่อแยกแยะระหว่างยูนิคอร์นและม้า

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

ในการเขียนโปรแกรมเชิงวัตถุวิธีที่คุ้นเคยในการทำสิ่งนี้มีดังต่อไปนี้:

  • มีHorseประเภท;
  • มีUnicornเป็นชนิดย่อยของHorse;
  • ใช้การสะท้อนกลับชนิดรันไทม์เป็นการดำเนินการที่ลูกค้าเข้าถึงได้ซึ่งระบุว่าได้รับมาHorseหรือUnicornไม่

นี่เป็นจุดอ่อนที่ยิ่งใหญ่เมื่อคุณดูจากมุม "สิ่งของกับคำอธิบาย" ที่ฉันนำเสนอไว้ด้านบน:

  • ถ้าคุณมีHorseอินสแตนซ์ที่อธิบายยูนิคอร์น แต่ไม่ใช่Unicornอินสแตนซ์ล่ะ?

กลับไปที่จุดเริ่มต้นนี่คือสิ่งที่ฉันคิดว่าเป็นส่วนที่น่ากลัวจริงๆเกี่ยวกับการใช้ subtyping และ downcasts สำหรับการสร้างแบบจำลองความสัมพันธ์ IS-A นี้ - ไม่ใช่ความจริงที่ว่าคุณต้องทำการตรวจสอบรันไทม์ เหยียดหยามพิมพ์บิตถามHorseไม่ว่าจะเป็นUnicornตัวอย่างที่ไม่ตรงกันกับการถามHorseไม่ว่าจะเป็นยูนิคอร์น (ไม่ว่าจะเป็นHorse-description ม้าที่ยังเป็นยูนิคอร์น) ไม่เว้นเสียแต่ว่าโปรแกรมของคุณจะมีความยาวมาก ๆ เพื่อห่อหุ้มโค้ดที่สร้างขึ้นHorsesเพื่อให้ทุกครั้งที่ไคลเอ็นต์พยายามสร้างสิ่งHorseที่อธิบายถึงยูนิคอร์นUnicornคลาสจะถูกสร้างอินสแตนซ์ จากประสบการณ์ของฉันโปรแกรมเมอร์ไม่ค่อยทำสิ่งนี้อย่างระมัดระวัง

ดังนั้นฉันจะไปกับวิธีการที่มีการดำเนินการที่ชัดเจนและไม่ดาวน์สตรีมที่แปลงHorses เป็นUnicorns นี่อาจเป็นวิธีการของHorseประเภท:

interface Horse {
    // ...
    Optional<Unicorn> toUnicorn();
}

... หรืออาจเป็นวัตถุภายนอก ("วัตถุแยกต่างหากของคุณบนม้าที่บอกคุณว่าม้านั้นเป็นยูนิคอร์นหรือไม่"):

class HorseToUnicornCoercion {
    Optional<Unicorn> convert(Horse horse) {
       // ...
    }
}

ทางเลือกระหว่างเหล่านี้เป็นเรื่องของวิธีการที่โปรแกรมของคุณจะจัดในทั้งสองกรณีคุณมีเทียบเท่าของฉันHorse -> Maybe Unicornการดำเนินงานจากข้างต้นคุณก็บรรจุภัณฑ์ในรูปแบบที่แตกต่างกัน (ที่เป็นที่ยอมรับจะมีผลกระทบต่อเนื่องกับสิ่งที่ดำเนินงานHorseความต้องการประเภท เพื่อเปิดเผยให้กับลูกค้า)


-1

ความคิดเห็นของ OP ในคำตอบอีกข้อหนึ่งทำให้ฉันคิดว่า

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

ผมคิดว่าเราต้องการข้อมูลเพิ่มเติม คำตอบอาจขึ้นอยู่กับหลายสิ่ง:

  • สิ่งอำนวยความสะดวกด้านภาษาของเรา เช่นฉันอาจเข้าหานี้แตกต่างกันในทับทิมและจาวาสคริปต์และ Java
  • แนวความคิดของตัวเอง: อะไรคือม้าและสิ่งที่เป็นยูนิคอร์น? ข้อมูลใดที่เชื่อมโยงกับแต่ละรายการ พวกมันเหมือนกันทุกประการยกเว้นเขาหรือมีความแตกต่างอื่น ๆ
  • เราใช้มันอย่างอื่นนอกเหนือจากการหาค่าเฉลี่ยความยาวฮอร์นอย่างไร? แล้วฝูงสัตว์ล่ะ? บางทีเราควรทำตัวแบบพวกมันด้วยเหรอ? เราใช้ที่อื่นหรือไม่ herd.averageHornLength()ดูเหมือนจะตรงกับรูปแบบแนวคิดของเรา
  • วัตถุของม้าและยูนิคอร์นถูกสร้างขึ้นมาได้อย่างไร? การเปลี่ยนรหัสนั้นขึ้นอยู่ภายในขอบเขตของการปรับโครงสร้างใหม่ของเราหรือไม่

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

OP ให้ฉันรู้ว่าฉันยังคงเข้าใจผิด ...


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

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

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

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

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

-2

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


ฉันเห็นด้วยกับสิ่งนี้ Mason Wheeler มีทางออกที่ดีในคำตอบของเขาเช่นกัน แต่ถ้าคุณต้องการแยกยูนิคอร์นด้วยเหตุผลต่าง ๆ ในที่ต่าง ๆ รหัสของคุณจะมีhorses.ofType<Unicorn>...โครงสร้างมากมาย มีGetUnicornsฟังก์ชั่นจะเป็นหนึ่งซับ แต่มันจะทนต่อการเปลี่ยนแปลงความสัมพันธ์ม้า / ยูนิคอร์นจากมุมมองของผู้โทร
Shaz

@ Ryan ถ้าคุณกลับมาIEnumerable<Horse>ถึงแม้ว่าเกณฑ์ยูนิคอร์นของคุณจะอยู่ในที่เดียวมันก็ถูกห่อหุ้มดังนั้นผู้โทรของคุณต้องตั้งสมมติฐานเกี่ยวกับสาเหตุที่พวกเขาต้องการยูนิคอร์น (ฉันจะได้ซุปหอยในวันนี้ ไม่ได้หมายความว่าฉันจะเอามันพรุ่งนี้โดยทำสิ่งเดียวกัน) Horseนอกจากนี้คุณต้องเปิดเผยค่าเริ่มต้นสำหรับฮอร์นในการ หากUnicornเป็นประเภทของตัวเองคุณต้องสร้างประเภทใหม่และบำรุงรักษาการแมปประเภทซึ่งสามารถแนะนำค่าใช้จ่าย
moarboilerplate

1
@ moarboilerplate: เราพิจารณาทุกอย่างที่สนับสนุนโซลูชัน ส่วนความงามคือมันเป็นอิสระจากรายละเอียดการดำเนินการใด ๆ ของยูนิคอร์น ไม่ว่าคุณจะเลือกปฏิบัติตามข้อมูลสมาชิกชั้นเรียนหรือช่วงเวลาของวัน (ม้าเหล่านั้นอาจกลายเป็นยูนิคอร์นในเวลาเที่ยงคืนหากดวงจันทร์เหมาะสมสำหรับทุกสิ่งที่ฉันรู้) การแก้ปัญหาหมายถึงส่วนต่อประสานยังคงเหมือนเดิม
Martin Maat
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.