การจัดการข้อยกเว้นที่ทราบถึงขั้นตอนการดำเนินการ

แก้ไข:

สำหรับบุคคลที่สนใจวิธีที่สะอาดกว่าในการดำเนินการนั้น ลองดูที่ คำตอบ< /ก>.


ในงานของฉัน ฉันมักจะต้องใช้ API ที่สร้างจากภายนอกเพื่อเข้าถึงระบบระยะไกล ตัวอย่างเช่นในการสร้างคำขอและส่งไปยังระบบระยะไกล:

   #include "external_lib.h"
   void SendRequest(UserRequest user_request)
   {
       try
       {
           external_lib::Request my_request;
           my_request.SetPrice(user_request.price);
           my_request.SetVolume(user_request.quantity);
           my_request.SetVisibleVolume(user_request.quantity);
           my_request.SetReference(user_request.instrument);
           my_request.SetUserID(user_request.user_name);
           my_request.SetUserPassword(user_request.user_name);
           // Meny other member affectations ...
       }
       catch(external_lib::out_of_range_error& e)
       {
           // Price , volume ????
       }
       catch(external_lib::error_t& e)
       {
           // Here I need to tell the user what was going wrong
       }
   }

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

ไม่ว่าอะไรก็ตาม ข้อยกเว้นของ lib ภายนอก (ส่วนใหญ่) ไม่เคยระบุว่าฟิลด์ใดเป็นแหล่งที่มาของการยกเลิกคำขอ

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

ความพยายามครั้งแรกของฉันในการจัดการข้อยกเว้นเหล่านั้นคือการ "รวม" คลาสคำขอด้วยของฉัน จากนั้นตัวเซ็ตเตอร์แต่ละตัวจะถูกห่อด้วยวิธีการที่ทำสิ่งเดียวเท่านั้น: บล็อก try/catch catch block จากนั้นจะส่งข้อยกเว้นใหม่ของฉัน: my_out_of_range_volume หรือ my_out_of_range_price ขึ้นอยู่กับผู้ตั้งค่า ตัวอย่างเช่น SetVolume() จะถูกห่อด้วยวิธีนี้:

My_Request::SetVolume(const int volume) 
{
    try
    {
        m_Request.SetVolume(volume);
    }
    catch(external_lib::out_range_error& e)
    {
        throw my_out_of_range_volume(volume, e);
    }
}

คุณคิดอย่างไรกับมัน? คุณคิดอย่างไรเกี่ยวกับข้อยกเว้นในการจัดการค่าใช้จ่ายที่มันบอกเป็นนัย? ... :/

คำถามเปิดอยู่ ฉันต้องการแนวคิดใหม่เพื่อกำจัดข้อจำกัดของ lib!


person yves Baumes    schedule 04.04.2009    source แหล่งที่มา


คำตอบ (5)


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

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

person John Ellinwood    schedule 04.04.2009
comment
ไลบรารีการสะท้อนกลับเป็นจุดที่ดีและใกล้เคียงกับข้อเสนอของนีล(?) เกี่ยวกับข้อมูลสแต็ก: เป็นจุดที่ดีจริงๆ แม้ว่าฉันจะไม่สามารถแก้ไข lib ที่ฉันใช้ได้ แต่คุณรู้วิธีรับข้อมูลนี้เมื่อตรวจพบข้อยกเว้นที่ส่งกลับมาหาฉันหรือไม่ - person yves Baumes; 04.04.2009

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

แต่แทนที่จะส่งข้อยกเว้นกลับไปยังผู้ใช้ของฉัน ฉันจะใช้รหัสข้อผิดพลาด บางสิ่งเช่นนี้:

class MyRequest
{
    enum RequestErrorCode
    {
        PRICE_OUT_OF_LIMIT,
        VOLUME_OUT_OF_LIMIT,
        ...
        ...
        ...
    };

    bool SetPrice(const int price , RequestErrorCode& ErrorCode_out);

    ...

private:

    external_lib::Request mRequest;


};

bool MyRequest::SetPrice(const int price , RequestErrorCode& ErrorCode_out)
{
    bool bReturn = true;
    try
    {

        bReturn = mRequest.SetPrice(price);
    }
    catch(external_lib::out_of_range_error& e)
    {

        ErrorCode_out = PRICE_OUT_OF_LIMIT;
        bReturn = false;
    }
    return bReturn;
}



      bool SendRequest(UserRequest user_request)
{
    MyRequest my_request;
    MyRequest::RequestErrorCode anErrorCode;
    bool bReturn = my_request.SetPrice(user_request.price, anErrorCode);
    if( false == bReturn)
    {
        //Get the error code and process
        //ex:PRICE_OUT_OF_LIMIT
    }
}
person aJ.    schedule 04.04.2009
comment
ฉันสงสัยว่าจะทำอย่างนั้นแทนที่จะลองบล็อกสองครั้ง / ซ้อนกันตามที่วิธีแก้ปัญหาของฉันทำ แต่การเช็คคืนจะดำเนินการทุกครั้งและมีค่าใช้จ่ายเล็กน้อย เหตุใดจึงไม่อาศัยข้อยกเว้น lib ภายนอกจึงบังคับให้ฉันใช้มัน และมันได้รับการทำความสะอาดโค้ดคุณว่าไหม? - person yves Baumes; 04.04.2009
comment
จริงๆ แล้ว แทนที่จะสร้างโค้ด ให้สร้างเป็นสตริงหรือคลาส เมื่อมีข้อผิดพลาด เมธอด MySetFunc จะเพิ่มข้อผิดพลาดต่อท้ายสตริง จากนั้นคุณเพียงแค่ต้องตรวจสอบว่าสตริงมีบางอย่างต่อท้ายหรือไม่ ด้วยรหัส มันจะถูกเขียนทับในการโทรแต่ละครั้ง และคุณต้องใช้ ifs หลังจากการโทรทุกครั้ง - person jmucchiello; 04.04.2009
comment
@yves Baumes ความตั้งใจของฉันคือการหลีกเลี่ยงการโยนข้อยกเว้นอื่น นอกจากนี้ จำเป็นต้องตรวจสอบโค้ดส่งคืนเฉพาะในกรณีที่เกิดความล้มเหลว - person aJ.; 04.04.2009

ฉันคิดว่าในกรณีนี้ฉันอาจจะกล้ามาโคร บางอย่างเช่น (ไม่ได้ทดสอบ, ละเว้นแบ็กสแลช):

#define SET( ins, setfun, value, msg )                           
  try {
    ins.setfun( value );
  }
  catch( external::error & )  {
    throw my_explanation( msg, value );
  }

และใช้งานอยู่:

Instrument i;
SET( i, SetExpiry, "01-01-2010", "Invalid expiry date" );
SET( i, SetPeriod, 6, "Period out of range" );

คุณได้รับความคิด

person Community    schedule 04.04.2009
comment
นั่นเป็นจุดดี นั่นคือสิ่งที่ฉันได้นำไปใช้จริง ;-) - person yves Baumes; 04.04.2009

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

อย่างไรก็ตาม ทางเลือกอื่นสำหรับคำแนะนำของ Neil อาจใช้ boost::lambda หากคุณต้องการหลีกเลี่ยงมาโคร

person Lars    schedule 04.04.2009
comment
ใช่ คุณพูดถูก โดยปกติแล้วในระบบที่เราเขียน เราจะหลีกเลี่ยงข้อยกเว้น แต่ที่นี่ lib ถูกสร้างขึ้นอย่างที่มันเป็น... ดังนั้นฉันต้องเปิดใช้งานข้อยกเว้นคือโปรแกรมของฉัน และจัดการกับมันในที่สุด ขอบคุณสำหรับ boost::lambda tips ฉันจะมองให้ลึกยิ่งขึ้น! - person yves Baumes; 04.04.2009

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

ปัญหาสำคัญในการตรวจสอบแต่ละขั้นตอนคือ ในระบบเรียลไทม์ คุณอาจมีเวลาแฝงมากเกินไป

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

person dirkgently    schedule 04.04.2009
comment
เกี่ยวกับโซลูชันตัวนับ คุณพูดถูก แต่สิ่งที่ฉันไม่ชอบคือความจริงที่ต้องดูแลรักษาตัวนับที่ไม่สามารถใช้งานได้เมื่อคำขอนี้สำเร็จ แต่ถึงกระนั้นค่าใช้จ่ายก็ไม่มากเมื่อเทียบกับทุกสิ่งรอบตัว ขอบคุณ! - person yves Baumes; 04.04.2009
comment
เกี่ยวกับการตรวจสอบลอจิก (การตรวจสอบล่วงหน้าชนิดหนึ่ง) สภาพที่แท้จริงจะพัฒนาตามเวลาจริง (เช่น ช่วงราคา) และฉันต้องพึ่งพาไลบรารีนี้ (การสื่อสารกับระบบระยะไกล AFAIK) เพื่อไม่ให้มีการลบลวง (คำขอถูกปฏิเสธในขณะที่อาจใช้ได้) - person yves Baumes; 04.04.2009
comment
ฉันเห็นได้ว่าคุณมาจากไหน ฉันหมายถึง เฉพาะ การตรวจสอบที่สามารถทำได้อย่างน่าเชื่อถือในส่วนของผู้ใช้เท่านั้นที่ควรทำที่นั่น สำหรับคนอื่น ๆ คุณต้องพูดคุยกับเซิร์ฟเวอร์ของคุณ เช่น: ผู้ใช้อาจมีบัญชีขนาดเล็กและไม่สามารถวาง สั่งซื้อมูลค่า x$ นี่คือค่าคงที่ - person dirkgently; 04.04.2009
comment
อีกสิ่งหนึ่งที่ต้องมีการตรวจสอบคือ คุณจะต้องมีการซิงโครไนซ์ออนไลน์/ออฟไลน์ (และการอัปเดตข้อมูลผู้ใช้เป็นระยะ) - person dirkgently; 04.04.2009