Douglas,

Thanks. Plenty of food for thought in your post too!

Regards,
 
Narinder Chandi,
ToolBox Systems Ltd.
-- 
 
    Kirk has explained how the “form controller” approach works and it is
    essentially the same as selector methods. In the past, I avoided that
    approach, as I’ll touch on below, but have embraced it fully, starting with
    V11.
    
    IIRC, that approach to programming arose in the late 90’s. My impression at
    the time was that it was part of the movement in the 4D community to try to
    bring OO coding principles into 4D and I saw that as trying to bludgeon a
    square peg into a round hole. It is unfortunate that 4D, to this day, is
    not an OO language but I hold out hope. Another motivation for that
    selector codes was that 4D method names were limited to 15 characters. That
    was a significant impediment and I accepted that selector methods were a
    way to get around that. My dislike for selector methods was that they
    violated the “a method should have one clearly defined purpose” dictum and,
    if you looked code quality using the McCabe score, the decision count was
    sky high.
    
    In practical terms, it was very hard to track down what routines were
    calling what other routines because the “Find in design” feature in 4D was
    glacially slow in those versions.    Another issue I ran into was that code
    was so intertwined that it could be very hard to decipher. I referred to
    that approach as “loopy swoopy” and, actually had one incident where I was
    called upon to fix a bug in a body of code that had been written by the
    previous programmer and, after studying it for some hours, had to admit to
    the client that I needed to rewrite the code because I could not figure out
    how the code worked. That was a very painful experience (bruised ego!) and
    it reinforced my position against using that approach.
    
    With the release of V11, 4D’s Find in design feature was vastly improved,
    fortunately so it became much easier to find where routines are used. By
    2007, I had also been programmatically creating wrapper routines for Object
    Tools objects for about a decade. That allowed me to move away from process
    variables and debugging code was  vastly simplified - I could put a break
    point in a setter or a getter, run the code, and the debugger would pop
    open. With those improvements, I was able to now use the form controller
    approach and I embraced it fully.
    
    Kirk did a good presentation at the last Summit and I’d advise you to get a
    copy of it from him. My approach differs somewhat from how Kirk handles
    things. IIRC, Kirk’s code reacts to form events. I’ve taken a different
    approach which completely avoids coupling. This code pattern, reacting to
    the appropriate events, is used in each active object on the form;
    
    C_LONGINT($formEvent_L)
    $formEvent_L:=Form event
    
    Case of
    : ($formEvent_L=On Data Change)
    PROC_FormEventValues_Assign (OBJ_GPBN ("$h_")->)
    MARKUP_FC (OBJ_GPBN ("$h_")->)
    End case
    
    
    PROC_FormEventValues_Assign contains this code:
    
    PROC_FocusObjectPtr_Set ($h_;OBJ_FocusPtr_Return )
    PROC_FocusObjectName_Set ($h_;OBJ_FocusName_Return )
    
    PROC_CurrentObjectPtr_Set ($h_;OBJ_CurrentPtr_Return )
    PROC_CurrentObjectName_Set ($h_;OBJ_CurrentName_Return )
    
    PROC_FormEvent_Set ($h_;Form event)
    
    PROC_gTablePtr_Set ($h_;gTablePtr_Get )
    
    PROC_CurrentFormPage_Set ($h_;FORM Get current page)
    
     PROC_CurrentFormTable_Set ($h_;Current form table)
    
     PROC_FunctionName_Set ($h_;”")
    
    PROC_ObjectState_Set ($h_;”")
    
    PROC_FormEventValues_Assign
    
    The OBJ_methods are wrappers for the underlying 4D function and gTablePtr
    is a tell that this is a Foundation-based system.
    This code is using an Object Tools object but newer code uses a C_Object.
    
    
    I use that code in every object method rather than the form method because
    I ran into issues with how the tab control behaves under certain
    conditions. My approach requires more code but it does give me absolute
    control over form activity and, as I mentioned, helps decouple my _FC.
    
    
    The object is the only parameter passed to the form controller and it uses
    Case statement and code as shown in this (cleaned up) sample code:
    
    C_LONGINT($h_;$1)
    $h_:=$1
    
    
      //C_POINTER($currentObject_P)
      //$currentObject_P:=PROC_CurrentObjectPtr_Get ($h_)
    
    C_TEXT($currentObjectName_T)
    $currentObjectName_T:=PROC_CurrentObjectName_Get ($h_)
    
    C_POINTER($focusObject_P)
    $focusObject_P:=PROC_FocusObjectPtr_Get ($h_)
    
    C_LONGINT($formEvent_L)
    $formEvent_L:=PROC_FormEvent_Get ($h_)
    
    Case of
    : ($formEvent_L=On Activate)
      //NOP
    
    : ($formEvent_L=On Deactivate)
      //NOP
    
    : ($formEvent_L=On Mouse Enter)
    
    C_TEX
    Case of
    : (OBJ_GPBN ($currentObjectName_T)=(->[Proposals]Bill_And_Hold))
    
    If (Not(USER_CanSetBillAndHold_Get ($h_)))
    PROPOSAL_ToolTip_OM (OBJ_GPBN ($currentObjectName_T);"")
    End if
    
    : (PROP_IsSelAddDeleteButton (0;<>pNil;$currentObjectName_T))
    
    If ([Proposals]Bill_And_Hold)
    PROPOSAL_ToolTip_OM (OBJ_GPBN ($currentObjectName_T);"")
    End if
    
    : ($currentObjectName_T="PROP_Selected_OptnDiscountRateT")
    OBJ_GPBN ("PROP_Selected_OptnDiscountRateT")->:="Options Discount:
    "+String([Proposals]F030_OptionsDiscountRate*100)+"%"
    
    End case
    
    $h_:=PROC_FormEventValues_Clear ($h_)
    
    : ($formEvent_L=On Mouse Leave)
    Case of
    : ($currentObjectName_T="PROP_Selected_OptnDiscountRateT")
    OBJ_GPBN ("PROP_Selected_OptnDiscountRateT")->:=""
    End case
    
    $h_:=PROC_FormEventValues_Clear ($h_)
    
    Else
    
    Case of
    : ($formEvent_L=On Clicked)
    
    Case of
    : ($focusObject_P=(OBJ_GPBN ("PREF_ShowLineNumberscb")))
    PrefsPut ("PREF_ShowLineNumberscb";String(OBJ_GPBN
    ("PREF_ShowLineNumberscb")->=1))
    
    : ($focusObject_P=(->bAccept))
    gDirtyRec_Set (True)
    
    : ($currentObjectName_T="@installBy@")
    
    
    : ($formEvent_L=On Outside Call)
    
    
    : ($formEvent_L=On Unload)
    
    
    : ($formEvent_L=On Load)
    
    
    End case
    
    End case
    
    End case
    
    
    In order to reduce coupling, there are very few process variables and the
    vast majority of the data is passed in the Object. That’s even easier with
    C_Object and dotted notation.
    
    Working with this approach, I do wish that QCP was still available but, as
    you’ve detailed, it’s no longer available. It its stead, I’ve come to learn
    to use the Find function and to use Ctrl/Cmd-L to navigate through the
    code.
    
    This approach does result in large methods which would be catastrophic if a
    method were to be corrupted. That seems to be a thing of the past,
    fortunately, but that situation can be avoided by frequent backups or using
    source code repository.
    
    If you were an advocate of the selector method approach, you’ll take to the
    form controller. As Kirk mentions, it’s a very useful approach and I’ve
    overcome my conceptual concerns by viewing the FC approach as a being a
    flexible dispatcher method so I’ve convinced myself that I’m not violating
    the “a method should have one clearly defined purpose” guideline.
    
    --
    Douglas von Roeder



**********************************************************************
4D Internet Users Group (4D iNUG)
Archive:  http://lists.4d.com/archives.html
Options: https://lists.4d.com/mailman/options/4d_tech
Unsub:  mailto:[email protected]
**********************************************************************

Reply via email to