Narinder:
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
949-336-2902
On Thu, Jun 20, 2019 at 7:48 AM Narinder Chandi via 4D_Tech <
[email protected]> wrote:
> I'm wondering, who out there is still actively developing using selector
> methods?
>
> For a long time now (way back since v6, perhaps even before that...) I
> have tended to encapsulate all of my code into Project Methods such that
> Form and Object level methods consist of nothing more than a Case...End
> Case block of Form Events that then then call an appropriate Project
> Method. We all pretty much work at some time or another with legacy code
> that usually has no conventions whatsoever. Any new code I add to such a
> codebase will follow the selector method convention.
>
> Do you still adhere to the selector method philosophy or have you moved
> onto an alternative 4D programming style? If so, can you describe it and
> why you have found it a better approach than selector methods? It's easy to
> stick with old ways and not adapt to better approaches so I'm interested to
> learn and explore better techniques within 4D.
>
> One of the reasons I am asking this question is that many years ago there
> was the wonderful QuickCode Pro plugin written by Aparajita which sadly
> ceased working with 4D v6, I think. As I recall, prior to that Aparajita
> had privileged access to the 4D source code to enable QCP to work its magic
> but at some point that access was rescinded and QCP sadly reached EOL. QCP
> was perfect for navigating selector methods using the Case tool as this
> technique often results in large methods.
>
> With 4D 2003, I started to write my own QCP toolbar replacement using
> native 4D, at the time using the DynamicStructure plugin (that too has
> disappeared now I think) and some other tricks to read and parse method
> source code. With newer versions of 4D some new possibilities have opened
> up and I am considering re-visiting this project in theory without needing
> to rely on any external plugins. Does anybody already have something like
> this that that they have written for their own use? Would anybody be
> interested in such a 4D component?
>
> Thanks for your thoughts and input.
>
> Regards,
>
> Narinder Chandi,
> ToolBox Systems Ltd.
> --
>
>
>
> **********************************************************************
> 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]
> **********************************************************************
**********************************************************************
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]
**********************************************************************