พารามิเตอร์ทางเลือกหรือตัวสร้างที่มากเกินไป


28

ฉันกำลังดำเนินการDelegateCommandและเมื่อฉันกำลังจะใช้ตัวสร้างฉันจะมาพร้อมกับสองตัวเลือกการออกแบบต่อไปนี้:

1: มีตัวสร้างโอเวอร์โหลดหลายตัว

public DelegateCommand(Action<T> execute) : this(execute, null) { }

public DelegateCommand(Action<T> execute, Func<T, bool> canExecute)
{
    this.execute = execute;
    this.canExecute = canExecute;
}

2: มีคอนสตรัคเตอร์เดียวเท่านั้นที่มีพารามิเตอร์เป็นตัวเลือก

public DelegateCommand(Action<T> execute, Func<T, bool> canExecute = null)
{
    this.execute = execute;
    this.canExecute = canExecute;
}

ฉันไม่รู้ว่าจะใช้อันไหนเพราะฉันไม่รู้ว่าข้อดี / ข้อเสียที่เป็นไปได้มาจากวิธีใดวิธีหนึ่งจากสองวิธีที่เสนอ ทั้งสองสามารถถูกเรียกเช่นนี้:

var command = new DelegateCommand(this.myExecute);
var command2 = new DelegateCommand(this.myExecute, this.myCanExecute);

ใครบางคนช่วยชี้ฉันในทิศทางที่ถูกต้องและให้ข้อเสนอแนะ?


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

1
ตัวสร้างแบบคงที่ (เหมือนBitmap.FromFile) ก็เป็นตัวเลือกเช่นกัน
BlueRaja - Danny Pflughoeft

3
เก็บกฎการวิเคราะห์รหัสCA1026: ค่าเริ่มต้นไม่ควรใช้ในใจ
Dan Lyons

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

2
@Voo คอมไพเลอร์สำหรับ Openedge | Progress 10.2B จะข้ามไป ค่อนข้างมากฆ่ากลยุทธ์การเปลี่ยนแปลงวิธีการไม่ทำลายของเรา; แต่เราจำเป็นต้องใช้การโหลดมากเกินไปเพื่อให้ได้ผลลัพธ์เดียวกัน
Bret

คำตอบ:


24

ฉันชอบคอนสตรัคเตอร์หลายตัวมากกว่าค่าเริ่มต้นและโดยส่วนตัวแล้วฉันไม่ชอบตัวอย่างคอนสตรัคเตอร์สองรายการของคุณ

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

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

ซึ่งหมายความว่ามันจะสะอาดและแยกออกจากกันได้ดีกว่าหากใช้วิธีนี้:

public DelegateCommand(Action<T> execute) : this(execute, _ => true) { }

public DelegateCommand(Action<T> execute, Func<T, bool> canExecute)
{
    this.execute = execute ?? throw new ArgumentNullException(..);
    this.canExecute = canExecute ?? throw new ArgumentNullException(..);
}

สังเกตเห็นการ_ => trueส่งผ่านไปยังตัวสร้างหลักที่ตอนนี้ยังตรวจสอบพารามิเตอร์ทั้งหมดnullและไม่สนใจค่าเริ่มต้นใด ๆ


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


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


12
ในทางกลับกันการสร้างพารามิเตอร์มากเกินไปเป็นทางเลือกจะทำให้เกิดความสับสน ...หากพูดตามจริงแล้วการมีพารามิเตอร์มากเกินไป
Andy

1
@DavidPacker ฉันเห็นด้วยกับคุณ แต่จำนวนพารามิเตอร์ที่มากเกินไปเป็นอีกเรื่องหนึ่งฉันคิดว่า ;-)
t3chb0t

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

1
ตัวอย่างเช่นสมมติว่าฉันมีคลาสที่จัดรูปแบบสตริงซึ่งฉันสามารถเลือกสร้างด้วยCultureInfoวัตถุได้ ฉันต้องการ API ที่อนุญาตให้CultureInfoพารามิเตอร์ถูกส่งผ่านพารามิเตอร์เพิ่มเติม แต่ยืนยันว่าหากมีการระบุไว้ก็ไม่ควรเป็นnullเช่นนั้น วิธีนั้นnullค่าที่เกิดขึ้นโดยอุบัติเหตุจะไม่ถูกตีความผิด ๆ ว่าเป็นความหมาย "ไม่มี"
Gary McGill

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

17

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

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


3

ฉันคิดว่ามีสองกรณีที่แตกต่างกันซึ่งแต่ละวิธีสามารถส่องแสง

ประการแรกเมื่อเรามีคอนสตรัคเตอร์เรียบง่าย (ซึ่งตามปกติแล้วในประสบการณ์ของฉัน) ฉันจะพิจารณาข้อโต้แย้งเพิ่มเติมเพื่อส่องแสง

  1. พวกเขาย่อโค้ดที่ต้องเขียน (และยังต้องอ่านด้วย)
  2. คุณสามารถมั่นใจได้ว่าเอกสารอยู่ในที่เดียวและไม่ซ้ำ (ซึ่งไม่ดีเพราะมันเปิดขึ้นในพื้นที่พิเศษที่อาจล้าสมัย)
  3. หากคุณมีอาร์กิวเมนต์ที่เป็นตัวเลือกมากมายคุณสามารถหลีกเลี่ยงการรวมชุดของตัวสร้างที่สับสน Heck แม้จะมีเพียง 2 อาร์กิวเมนต์ที่เป็นตัวเลือก (ไม่เกี่ยวข้องกัน) หากคุณต้องการ Constructor ที่มีการโอเวอร์โหลดแยกกันคุณจะต้องมี Constructor 4 ตัว (เวอร์ชันที่ไม่มีส่วนใดส่วนหนึ่งที่มีทั้งสองและ Version ที่มีทั้งคู่) เห็นได้ชัดว่ามันไม่ได้ปรับขนาดได้ดี

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


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