TexasSwede
texasswede@gmail.com
  • About this blog
  • My Website
  • My Resume
  • XML Export Tool
  • Photos

How to write better code in Domino Designer – Part 3

Posted on February 22, 2013 by Karl-Henry Martinsson Posted in Lotusscript, Notes/Domino, Programming 2 Comments

Welcome to the third part of this series of articles about how to write better code for the Notes/Domino platform. In part 1 I wrote about creating your forms in a way so you get an instant idea of how they work (what is hidden, computed-for-display fields, etc) and in part 2 the subject had moved to Lotusscript, more specifically variable names and comments.

As already mentioned in that last article (as well as in some of the comments), breaking out code into functions is a great way to make code easier to read as well as dramatically easier to maintain. That is what I will focus on in this article.

 

Functions

In Lotusscript, the functions are declared the same way as in Visual Basic:

Function YourFunctionName(argument As DataType) As ReturnDataType
    YourFunctionName = ReturnValue
End Function

You can have any number of arguments, from none to many. However, it is often suggested to keep the number of arguments to a minimum. I try to not use more than three arguments, unless there is no way around it. If you need to send a lot of information into a function, use an array or a custom data type instead. Then it is easier to change the arguments later, without changing the signature of the function. I have occasionally seen that cause issues in nested script libraries. Also, with many arguments the function declaration will probably not fit on your screen, unless you wrap the line. I try to always keep code visible in the editor, as it is easy to miss things when you have to scroll sideways to view code.

So let’s take a look at one of the code samples in my last article, the one where we call names.nsf to get the email address for the current user. Let’s make this a function (like Kenneth Axi suggests in his comment), but we will make it a more generic function, returning the email address for any user present in address book, not just current user.

Here is the original code:

key = session.CommonUsername
Set view = db.GetView("People")
Set doc = view.GetDocumentByKey(key)
emailaddress = doc.GetItemValue("InternetAddress")

Below is how I would write the function. We will talk more about error handling later, but you want to make sure to always check return values before you use them. The function takes one argument, the name of the user we want the email address for, and returns the email address, or blank if no user/address was found. The username must be in the same format as the names are being displayed in the view we doing the lookup in.

Function getEmailAddress(username As String) As String
    Dim personname As NotesName 
    Dim nabdb As NotesDatabase
    Dim nabview As NotesView
    Dim persondoc As NotesDocument

    If username = "" Then
        MsgBox "Username is empty. Exiting.",,"No Value"
        Exit Function
    End If
    '*** Create NotesName object and get CommonName part.
    '*** We do this so username argument can be in any format.
    Set personname = New NotesName(username)

    '*** Use Domino Directory on main server
    Set nabdb = New NotesDatabase("Domino1/MyDomain","names.nsf")
    If nabdb Is Nothing Then
        MsgBox "Could not open names.nsf. Exiting.",,"Error"
        Exit Function
    End If

    Set nabview = nabdb.GetView("PeopleByFirstName")
    If nabview Is Nothing Then
        MsgBox "Could not find view 'PeopleByFirstName'. Exiting.",,"Error"
        Exit Function
    End If

    Set persondoc = nabview.GetDocumentByKey(personname.Common)
    If persondoc Is Nothing Then	
        getEmailAddress = ""	  ' Did not find person, return blank value
    End If
    getEmailAddress = persondoc.GetItemValue("InternetAddress")(0)
End Function

You may wonder why I am creating a NotesName object and not just using the name passed as argument directly? That will allow me to get the Common Name of the name, no matter in what format it is passed. You should always check the values of any arguments passed to your functions to make sure you can handle them and that they contain valid data. In this case I make sure the username is not empty, but for a string containing a date I would use the IsDate() function to test that the value is a valid date-time.

Now we can call the function like this:

MsgBox getEmailAddress("Karl-Henry Martinsson")
MsgBox getEmailAddress("Karl-Henry Martinsson/MyDomain")
MsgBox getEmailAddress("CN=Karl-Henry Martinsson/O=MyDomain")

Beautiful, isn’t it?

 

How to name functions

This brings us to the naming convention for functions. For functions, I use CamelCase, as that makes it much easier to see what the function does. I have to admit, I am not always consistent with get and set, I sometimes capitalize the first letter, sometimes not. Using CamelCase for functions and lowercase for variables makes it easier for me to read my code later, but you should do what you are comfortable with, and what works for you.

The actual name of the function should describe what it does, without being too long or too short. A well named function will by itself document what it does when you see it in your code later. The function we just created above could have been called getEmail() or getEmailAddressForUserFromCentralServer(). The first is a little too short — you don’t know if it is getting a mail address or an actual email document– while the second is too long.

 

Script Libraries

Consider creating script libraries where you store commonly used functions. I have a number of script libraries, containing different sets of functions, and when I need certain functionality in a new application, I just copy one or more of them into that application. I name the script library in a way so I know what functions are in each one of them:

I group functions into separate script libraries, making it easy to copy just certain functionality to a new application.

There are many ways to name your script libraries, and I know some developers who use very complicated elaborate naming systems, where they can see at a glance if the script library uses back-end or front-end functions/classes, etc. I prefer a slightly simpler approach.

So let’s look at a real life code example, from one of my production applications. This is an agent, called from a browser, that generates some charts and other information, based on the URL parameters passed to the agent.

Functions - Live Code

Thanks to the descriptive function names, hardly any comments are needed. The only comment in this code segment clarifies why we are doing what we do. Obviously the code is much more complex than what you see above, but it will give you an idea of how you can make your code easy to review and maintain.

 

In the next article I will talk about classes and object oriented Lotusscript.

« How to write better code in Domino Designer – Part 2
New graphics card »

2 thoughts on “How to write better code in Domino Designer – Part 3”

  1. Andre Guirard says:
    February 23, 2013 at 06:57

    So you’re suggesting that if you had a function whose signature was:

    Function Snoot(xpos As Long, ypos As Long, etc, etc, etc) As Double

    that it might be better to write it as:

    Type Position
    xpos As Long
    ypos As Long
    End Type

    Function Snoot(pos As Position, etc, etc, etc) As Double

    so that (let’s say) if you add a zpos later, you have just one place to do it (the type definition). And you say this is to avoid problems in nested script libraries.

    I assume the problems you’re talking about are the type that can be solved by recompiling all LotusScript. But I don’t see how the above change would help with this. If type Position is defined in a different library than Snoot, changing the type definition requires a recompile just as much as changing the argument list. The difference is that with individual arguments, you’re also forced to manually update the calling code, every place the function is called. But here’s the thing. You _need_ to update the calling code anyway. With the individual arguments, you know it, because you get compile errors.

    So if you add a “zpos” to Position, then everyplace we’re calling Snoot, there’s an extra question to be answered – what’s the value we want for zpos? If zpos is a separate argument, the compiler forces you to answer this question — so you have to think about how to update each call. But if you use a Position type as your argument, then some code like this:

    Dim pos As position
    pos.xpos = 54
    pos.ypos = 23
    tmp = snoot(pos, …)

    still compiles, but the zpos value is zero. Was this the value you would’ve wanted to pass in this case? Maybe not; maybe you’ve just introduced a bug.

    I think the problem here is that using a type or class to group arguments is a half-measure that just makes the problem worse. To me, long parameter lists are a sign that the code is not sufficiently object-oriented. The function, in other words, should be a method of a class which will have several of those parameters as members. I understand if this is more than you want to cover in one blog post, but I don’t think the recommendation you made is helpful by itself.

    These days, when I write LotusScript code, my default assumption is that I’m writing classes. I only use individual functions when I have a library of functions that I would have liked to define as static class members of a utility class — which LotusScript syntax doesn’t support.

    Reply
    • Karl-Henry Martinsson says:
      February 24, 2013 at 01:22

      Then I learned something new. :-)
      I was told in the past that the reason I had to recompile all my Lotusscript code (sometimes even multiple times) was because the signature changed. I would normally not declare my type definition in another script library, but in the same as I have my function that uses it.

      In your example above, obviously I would have to modify the function Snoot() to handle the (possibly) missing/empty pos.zpos. I also need to check the values, but I don’t have to update every place I call the function, I just have to assign a value to the optional argument when I need it. This method gives me a way to mimic overloading, by having optional arguments in the type definition. A typical example would be a custom data type for person data:

      Type PersonData
      firstname As String
      lastname As String
      address As String
      city As String
      state As String
      zip As String
      End Type

      Function CreatePersonDocument( pdata As PersonData)

      I then later add a string containing email address to this type definition. In the function CreatePersonDocument() I have to check the value of pdata.emailaddress to see if it contains a value, If it is blank/empty I will not write the contents of that variable into the document.
      OK, I know that I could still write the empty value into the document, and the end result would be the same. It’s just an example. Could been age As Integer instead, and if the value is 0 not writing it into the document.

      Yes, you are correct, if one does not check the values passed to the function, you may introduce bugs.

      I will cover classes very soon in this series of articles, I am writing pretty much only classes these days, except for quick one-time export functions and similar.

      Reply

Leave a comment Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Stack Exchange

profile for Karl-Henry Martinsson on Stack Exchange, a network of free, community-driven Q&A sites

Recent Posts

  • Domino 14 is now available
  • Domino 14 Early Access Program
  • Announced: Engage 2024
  • Integrate Node-RED with Notes and Domino
  • Notes and Domino v12 is here!

Recent Comments

  • Theo Heselmans on Announced: Engage 2024
  • Lotus Script Multi-thread Message Box [SOLVED] – Wanted Solution on ProgressBar class for Lotusscript
  • Viet Nguyen on Keep up with COVID-19 though Domino!
  • Viet Nguyen on Keep up with COVID-19 though Domino!
  • Mark Sullivan on Looking for a HP calculator? Look no further!

My Pages

  • How to write better code in Notes

Archives

  • December 2023 (1)
  • October 2023 (2)
  • September 2023 (1)
  • June 2021 (1)
  • April 2021 (2)
  • March 2021 (1)
  • August 2020 (3)
  • July 2020 (2)
  • April 2020 (2)
  • March 2020 (1)
  • December 2019 (2)
  • September 2019 (1)
  • August 2019 (2)
  • July 2019 (2)
  • June 2019 (3)
  • April 2019 (2)
  • December 2018 (1)
  • November 2018 (1)
  • October 2018 (5)
  • August 2018 (2)
  • July 2018 (3)
  • June 2018 (2)
  • May 2018 (1)
  • April 2018 (2)
  • March 2018 (1)
  • February 2018 (2)
  • January 2018 (4)
  • December 2017 (3)
  • November 2017 (2)
  • October 2017 (2)
  • September 2017 (1)
  • August 2017 (2)
  • July 2017 (6)
  • May 2017 (4)
  • February 2017 (1)
  • January 2017 (2)
  • December 2016 (2)
  • October 2016 (3)
  • September 2016 (4)
  • August 2016 (1)
  • July 2016 (2)
  • June 2016 (2)
  • May 2016 (3)
  • April 2016 (1)
  • March 2016 (4)
  • February 2016 (2)
  • January 2016 (4)
  • December 2015 (3)
  • November 2015 (2)
  • October 2015 (1)
  • September 2015 (2)
  • August 2015 (1)
  • July 2015 (5)
  • June 2015 (2)
  • April 2015 (2)
  • March 2015 (3)
  • February 2015 (2)
  • January 2015 (10)
  • December 2014 (1)
  • November 2014 (3)
  • October 2014 (3)
  • September 2014 (13)
  • August 2014 (6)
  • July 2014 (5)
  • May 2014 (3)
  • March 2014 (2)
  • January 2014 (10)
  • December 2013 (5)
  • November 2013 (2)
  • October 2013 (5)
  • September 2013 (4)
  • August 2013 (7)
  • July 2013 (3)
  • June 2013 (1)
  • May 2013 (4)
  • April 2013 (7)
  • March 2013 (8)
  • February 2013 (9)
  • January 2013 (5)
  • December 2012 (7)
  • November 2012 (13)
  • October 2012 (10)
  • September 2012 (2)
  • August 2012 (1)
  • July 2012 (1)
  • June 2012 (3)
  • May 2012 (11)
  • April 2012 (3)
  • March 2012 (2)
  • February 2012 (5)
  • January 2012 (14)
  • December 2011 (4)
  • November 2011 (7)
  • October 2011 (8)
  • August 2011 (4)
  • July 2011 (1)
  • June 2011 (2)
  • May 2011 (4)
  • April 2011 (4)
  • March 2011 (7)
  • February 2011 (5)
  • January 2011 (17)
  • December 2010 (9)
  • November 2010 (21)
  • October 2010 (4)
  • September 2010 (2)
  • July 2010 (3)
  • June 2010 (2)
  • May 2010 (3)
  • April 2010 (8)
  • March 2010 (3)
  • January 2010 (5)
  • November 2009 (4)
  • October 2009 (7)
  • September 2009 (1)
  • August 2009 (7)
  • July 2009 (1)
  • June 2009 (4)
  • May 2009 (1)
  • April 2009 (1)
  • February 2009 (1)
  • January 2009 (3)
  • December 2008 (1)
  • November 2008 (1)
  • October 2008 (7)
  • September 2008 (7)
  • August 2008 (6)
  • July 2008 (5)
  • June 2008 (2)
  • May 2008 (5)
  • April 2008 (4)
  • March 2008 (11)
  • February 2008 (10)
  • January 2008 (8)

Categories

  • AppDev (10)
  • Blogging (11)
    • WordPress (5)
  • Design (5)
    • Graphics (1)
    • UI/UX (2)
  • Featured (5)
  • Financial (2)
  • Food (5)
    • Baking (3)
    • Cooking (3)
  • Generic (11)
  • History (5)
  • Hobbies (10)
    • LEGO (4)
    • Photography (4)
  • Humor (1)
  • IBM/Lotus (178)
    • #Domino2025 (14)
    • #DominoForever (8)
    • #IBMChampion (46)
    • Administration (7)
    • Cloud (7)
    • CollabSphere (9)
    • Community (49)
    • Connect (33)
    • ConnectED (12)
    • Connections (3)
    • HCL (15)
    • HCL Master (1)
    • IBM Think (1)
    • Lotusphere (46)
    • MWLUG (25)
    • Notes/Domino (99)
      • Domino 11 (7)
    • Sametime (8)
    • Verse (14)
    • Volt (3)
    • Watson (6)
  • Life (8)
  • Microsoft (7)
    • .NET (2)
    • C# (1)
    • Visual Studio (1)
  • Movies (3)
  • Old Blog Post (259)
  • Personal (23)
  • Programming (84)
    • App Modernization (11)
    • Formula (4)
    • Lotusscript (47)
    • NetSuite (4)
      • SuiteScript (3)
    • node.js (4)
    • XPages (4)
  • Reviews (9)
  • Sci-Fi (4)
  • Software (24)
    • Flight Simulator (2)
    • Games (4)
    • Open Source (2)
    • Utilities (6)
  • Technology (37)
    • Aviation (3)
    • Calculators (2)
    • Computers (6)
    • Gadgets (7)
    • Mobile Phones (7)
    • Science (3)
    • Tablets (2)
  • Travel (7)
    • Europe (1)
    • Texas (2)
    • United States (1)
  • Uncategorized (16)
  • Web Development (50)
    • Frameworks (23)
      • Bootstrap (14)
    • HTML/CSS (12)
    • Javascript (32)
      • jQuery (23)

Administration

  • Log in
  • Entries feed
  • Comments feed
  • WordPress.org

Tracking

Creeper
MediaCreeper
  • Family Pictures
© TexasSwede 2008-2014