2012-10-07 10 views
6

विचार करें कि मेरे पास gen_fsm के साथ लागू एक एफएसएम है। कुछ राज्य नाम में कुछ ईवेंट के लिए मुझे डेटाबेस में डेटा लिखना चाहिए और परिणामस्वरूप उपयोगकर्ता को जवाब देना चाहिए। तो निम्नलिखित StateName एक समारोह का प्रतिनिधित्व करती है:ओटीपी के सिद्धांत। अभ्यास में कार्यात्मक और गैर-कार्यात्मक कोड कैसे अलग करें?

statename(Event, _From, StateData) when Event=save_data-> 
    case my_db_module:write(StateData#state.data) of 
     ok -> {stop, normal, ok, StateData}; 
     _ -> {reply, database_error, statename, StateData) 
    end. 

जहां my_db_module: लिखने गैर कार्यात्मक कोड वास्तविक डेटाबेस लिखने को लागू करने का एक हिस्सा है।

मुझे इस कोड के साथ दो प्रमुख समस्याएं दिखाई देती हैं: पहला, एफएसएम की एक शुद्ध कार्यात्मक अवधारणा गैर-कार्यात्मक कोड के हिस्से से मिश्रित होती है, यह एफएसएम की इकाई परीक्षण को असंभव बनाता है। दूसरा, एफएसएम को लागू करने वाला एक मॉड्यूल my_db_module के विशेष कार्यान्वयन पर निर्भरता है।

मेरी राय में, दो समाधान संभव हो रहे हैं:

  1. लागू my_db_module: कुछ प्रक्रिया से निपटने के डेटाबेस के लिए एक अतुल्यकालिक संदेश भेजने के रूप में write_async, statename में जवाब न दें, StateData में से बचाने के लिए, wait_for_db_answer करने के लिए स्विच और एक हैंडल_इनोफ़ो में एक संदेश के रूप में डीबी प्रबंधन प्रक्रिया से परिणाम का इंतजार करें।

    statename(Event, From, StateData) when Event=save_data-> 
        my_db_module:write_async(StateData#state.data), 
        NewStateData=StateData#state{from=From}, 
        {next_state,wait_for_db_answer,NewStateData} 
    
    handle_info({db, Result}, wait_for_db_answer, StateData) -> 
        case Result of 
         ok -> gen_fsm:reply(State#state.from, ok), 
           {stop, normal, ok, State}; 
         _ -> gen_fsm:reply(State#state.from, database_error), 
           {reply, database_error, statename, StateData) 
        end. 
    

    ऐसे कार्यान्वयन के लाभ वास्तविक डेटाबेस को छूए बिना यूनिट मॉड्यूल से मनमाना संदेश भेजने की संभावना है। समाधान संभावित दौड़ स्थितियों से पीड़ित है, अगर डीबी पहले जवाब देता है, कि एफएसएम राज्य या अन्य प्रक्रिया बदलता है तो एफएसएम को save_data भेजता है।

  2. दौरान एक कॉलबैक फ़ंक्शन, लिखा प्रयोग करें init/1 StateData में:

    init([Callback]) -> 
    {ok, statename, #state{callback=Callback}}. 
    
    statename(Event, _From, StateData) when Event=save_data-> 
        case StateData#state.callback(StateData#state.data) of 
         ok -> {stop, normal, ok, StateData}; 
          _ -> {reply, database_error, statename, StateData) 
    end. 
    

    यह समाधान दौड़ की स्थिति से ग्रस्त नहीं है, लेकिन अगर FSM कई कॉलबैक का उपयोग करता है यह वास्तव में कोड अभिभूत। यद्यपि वास्तविक फ़ंक्शन कॉलबैक में बदलना यूनिट परीक्षण संभव बनाता है, यह कार्यात्मक कोड अलगाव की समस्या को हल नहीं करता है।

मैं इस समाधान के सभी के साथ सहज नहीं हूँ। क्या इस समस्या को शुद्ध ओटीपी/एरलांग तरीके से संभालने के लिए कुछ नुस्खा है? ओटीपी और यूनिट के सिद्धांतों को कम करने की मेरी समस्या हो सकती है।

उत्तर

2

इसे हल करने का एक तरीका डेटाबेस मॉड्यूल की निर्भरता इंजेक्शन के माध्यम से है।

आप के रूप

-record(state, { ..., db_mod }). 

अपने राज्य रिकॉर्ड को परिभाषित और अब आप gen_server की init/1 पर db_mod इंजेक्षन कर सकते हैं:

statename(save_data, _From, 
      #state { db_mod = DBMod, data = Data } = StateData) -> 
    case DBMod:write(Data) of 
    ok -> {stop, normal, ok, StateData}; 
    _ -> {reply, database_error, statename, StateData) 
    end. 
: जब हम अपने कोड है

init([]) -> 
    {ok, DBMod} = application:get_env(my_app, db_mod), 
    ... 
    {ok, #state { ..., db_mod = DBMod }}. 

तो

हमारे पास किसी अन्य मॉड्यूल के साथ परीक्षण करते समय डेटाबेस मॉड्यूल को ओवरराइड करने की क्षमता है। एक स्टब इंजेक्शन अब बहुत आसान है और आप फिट बैठकर डेटाबेस कोड प्रतिनिधित्व को बदल सकते हैं।

एक और विकल्प meck जैसे टूल का उपयोग करने के लिए डेटाबेस मॉड्यूल को नकल करने के लिए उपयोग करना है, लेकिन मैं आमतौर पर इसे कॉन्फ़िगर करने योग्य बनाना पसंद करता हूं।

सामान्य रूप से, मैं अपने मॉड्यूल में जटिल कोड को विभाजित करता हूं ताकि इसे अलग से परीक्षण किया जा सके। मैं शायद ही कभी अन्य मॉड्यूल के अधिक यूनिट परीक्षण करता हूं और ऐसे हिस्सों में त्रुटियों को संभालने के लिए बड़े पैमाने पर एकीकरण परीक्षण पसंद करता हूं। कॉमन टेस्ट, प्रोपेर, ट्राइक और एरलांग क्विक चेक पर एक नज़र डालें (उत्तरार्द्ध खुला स्रोत नहीं है, न ही पूर्ण संस्करण मुक्त है)।

संबंधित मुद्दे