การออกแบบวิธีการที่เกี่ยวข้องกับฐานข้อมูลซึ่งผลตอบแทนที่ดีกว่า: จริง / เท็จหรือแถวได้รับผลกระทบ


10

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

พิจารณารหัสที่ส่งคืนint:

A.1

public int myLowerLevelMethod(int id) {
    ...
    int affectedRows = myOrm.deleteById(id)
    ...

    return affectedRows;
}

การใช้งาน:

A.2

public void myOtherMethod() {
    ...
    int affectedRows = myLowerLevelMethod(id)

    if(affectedRows > 0) {
        // Success
    } else {
        // Fail
    }
}

เปรียบเทียบกับการใช้บูลีน:

B.1

public boolean myLowerLevelMethod(int id) {
    ...
    int affectedRows = myOrm.deleteById(id)
    ...

    return affectedRows > 0;
}

การใช้งาน:

B.2

public void myOtherMethod() {
    ...
    boolean isSuccess = myLowerLevelMethod(id)

    if(isSuccess) {
        // Success
    } else {
        // Fail
    }
}

อันไหนดี (A หรือ B) ดีกว่า? หรือข้อดี / ข้อเสียของแต่ละคน?


ใน "A.2" ของคุณ หากศูนย์แถวได้รับผลกระทบเหตุใดจึงเป็นความล้มเหลวถ้าศูนย์แถวต้องได้รับผลกระทบ กล่าวอีกนัยหนึ่งหากไม่มีข้อผิดพลาดฐานข้อมูลเหตุใดจึงล้มเหลว
Jaydee

5
มีความแตกต่างทางความหมายระหว่าง "ไม่ประสบความสำเร็จ" และ "ศูนย์แถวรับผลกระทบ" หรือไม่? เช่นเมื่อลบคำสั่งซื้อทั้งหมดของลูกค้าจะมีความแตกต่างระหว่าง "ไม่มีลูกค้า" และ "ลูกค้าไม่มีคำสั่งซื้อ"
Cephalopod

คุณพิจารณาการลบโดยไม่คาดหมายกับแถวศูนย์หรือไม่ ในกรณีที่โยน
usr

@ Arian ฉันคิดว่านั่นเป็นคำถามที่แท้จริงสำหรับฉัน ฉันคิดว่าฉันเลือก B เพราะด้วยรหัสของฉันตอนนี้มีการตรวจสอบ 0 ในบางสถานที่และ -1 สำหรับคนอื่น ๆ
Hoang Tran

คำตอบ:


18

ตัวเลือกอื่นคือการส่งคืนวัตถุผลลัพธ์แทนชนิดพื้นฐาน ตัวอย่างเช่น:

OperationResult deleteResult = myOrm.deleteById(id);

if (deleteResult.isSuccess()) {
    // ....
}

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

if (deleteResult.isSuccess()) {
    System.out.println("rows deleted: " + deleteResult.rowsAffected() );
}

การออกแบบนี้ช่วยให้ระบบของคุณเติบโตและรวมฟังก์ชันใหม่ (รู้เกี่ยวกับแถวที่ได้รับผลกระทบ) โดยไม่ต้องแก้ไขโค้ดที่มีอยู่


2
+1 "การออกแบบนี้ช่วยให้ระบบของคุณเติบโตและรวมฟังก์ชันการทำงานใหม่ (รู้เกี่ยวกับแถวที่ได้รับผลกระทบ) โดยไม่ต้องแก้ไขโค้ดที่มีอยู่" นี่เป็นวิธีการคิดที่ถูกต้องเกี่ยวกับหมวดหมู่ของคำถามนี้
InformedA

ฉันทำสิ่งที่คล้ายกันในโครงการเก่าของฉัน ฉันมีคำขอ wrapper และผลลัพธ์วัตถุ ทั้งสองใช้ Compositions เพื่อรวมรายละเอียดเพิ่มเติม และทั้งสองมีข้อมูลพื้นฐานในนั้นเช่นกัน ในกรณีนี้วัตถุผลลัพธ์มีรหัสสถานะและเขตข้อมูล msg
แจ้งเมื่อ

โดยเฉพาะอย่างยิ่งสำหรับระบบที่ใช้ ORM ฉันจะเรียกวิธีปฏิบัติที่ดีที่สุดนี้ ORM ที่เป็นปัญหาอาจมีประเภทออบเจกต์ผลลัพธ์ดังกล่าวด้วยเช่นกัน!
Brian S

เพียงแค่ดูตัวอย่าง OPs ทั้งหมดที่ฉันนึกได้ก็คือ deleteById จะส่งคืน 2 ในบางจุด :) ดังนั้นเป็นประเภทที่กำหนดเองแน่นอนใช่
deadsven

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

12

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

โปรแกรมเมอร์จะไม่ตำหนิคุณเพราะเขา / เธอต้องเขียนสิ่งนี้เพื่อตรวจสอบว่ามีการเปลี่ยนแปลงบางอย่างระหว่างการทำงานหรือไม่:

if(affectedRows > 0) {
    // success
} else {
    // fail
}

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

BTW: หากโดย "ล้มเหลว" คุณหมายถึงข้อผิดพลาดในการค้นหาประโยค (ในกรณีที่จำนวนแถวที่ได้รับผลกระทบเป็น 0 อย่างเห็นได้ชัด) จากนั้นการโยนข้อยกเว้นจะเหมาะสมกว่า


4
-1 เนื่องจากเหตุผลที่คุณให้ การให้ข้อมูลเพิ่มเติมไม่ใช่ความคิดที่ดีเสมอไป บ่อยครั้งที่การออกแบบที่ดีกว่านั้นจะนำไปสู่การสื่อสารกับผู้โทรในสิ่งที่ต้องการและไม่มีอะไรเพิ่มเติม
Arseni Mourzenko

1
@MainMa ไม่มีสิ่งใดในวิธีการสร้างวิธีการโอเวอร์โหลด นอกจากนี้ในภาษาดั้งเดิม (เช่นตระกูล C) สามารถใช้จำนวนแถวในตรรกะได้โดยตรง (0 เป็นเท็จสิ่งอื่นใดเป็นจริง)
PTwr

@ PTwr: ดังนั้นเพื่อรับมือกับความจริงที่ว่าวิธีการส่งคืนข้อมูลมากเกินไปคุณแนะนำให้สร้างเกิน? ดูเหมือนจะไม่ถูกต้อง สำหรับ "ศูนย์เป็นเท็จไม่ใช่ไม่ใช่ศูนย์จริง" นี่ไม่ใช่ประเด็นของความคิดเห็นของฉันและเป็นการปฏิบัติที่ไม่ดีในบางภาษา
Arseni Mourzenko

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

4
คุณควรคืนจำนวนแถวที่ได้รับผลกระทบที่นี่เสมอ !!! เนื่องจากคุณไม่สามารถทราบได้ว่าจำนวนแถว = 0 เป็นความล้มเหลวหรือหากจำนวนแถว = 3 เป็นความสำเร็จ หากใครบางคนต้องการที่จะแทรก 3 แถวและมีเพียง 2 แทรกคุณจะกลับมาจริง แต่มันไม่ถูกต้อง! และถ้ามีคนต้องการที่จะupdate t set category = 'default' where category IS NULLได้รับผลกระทบ 0 แถวจะประสบความสำเร็จเพราะตอนนี้ไม่มีรายการที่ไม่มีหมวดหมู่แม้ว่าจะไม่มีผลกระทบต่อแถว!
Falco

10

ฉันจะไม่แนะนำใด ๆ ของพวกเขา ให้ส่งคืนสิ่งใด (โมฆะ) ในความสำเร็จแทนและส่งข้อยกเว้นเมื่อล้มเหลว

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

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

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


4
-1 ทำไมคุณไม่ส่งคืนสิ่งที่คุณสามารถคืนบางสิ่งบางอย่างโดยไม่มีผลข้างเคียงเชิงลบที่ให้บริบทการดำเนินงานพิเศษ
FreeAsInBeer

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

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

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

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

2

"นี่ดีกว่านี้เหรอ?" ไม่ใช่คำถามที่มีประโยชน์เมื่อสองทางเลือกไม่ทำสิ่งเดียวกัน

หากคุณจำเป็นต้องรู้จำนวนแถวที่ได้รับผลกระทบคุณต้องใช้เวอร์ชัน A หากคุณไม่จำเป็นต้องทำคุณสามารถใช้เวอร์ชัน B - ได้ แต่ข้อดีที่คุณอาจได้รับในแง่ของความพยายามในการเขียนรหัสน้อยลงได้หายไปแล้วตั้งแต่ คุณมีปัญหาในการโพสต์ทั้งสองเวอร์ชันไปยังฟอรัมออนไลน์!

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


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

1

สองในหลักการที่สำคัญที่สุดในการออกแบบซอฟแวร์การบำรุงรักษาเป็นKISSและYAGNI

  • KISS : ทำให้ง่ายงี่เง่า
  • YAGNI : คุณไม่ต้องการมันหรอก

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

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

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

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


-1

ทั้งแถวหรือสถานะข้อผิดพลาด

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

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

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