Showing posts with label Best Practices. Show all posts
Showing posts with label Best Practices. Show all posts

Wednesday, July 30, 2014

"Private" App Class Members

I was reading Lee Greffin's post More Fun with Application Packages -- Instances and stumbled across this quote from PeopleBooks:

A private instance variable is private to the class, not just to the object instance. For example, consider a linked-list class where one instance needs to update the pointer in another instance.

What exactly does that mean? I did some testing to try and figure it out. Here is what I came up with:

  1. It is still an instance variable which means each in-memory object created from the App Class blue print has its own memory placeholder for each instance member.
  2. Instances of other classes can't interact with private instance members.
  3. Instances of the exact same class CAN interact with private members of a different instance.
  4. Private instance members differ from static members in other languages because they don't all share the same pointer (pointer, reference, whatever).

I thought it was worth proving so here is my sample. It is based on the example suggested in PeopleBooks:

For example, consider a linked-list class where one instance needs to update the pointer in another instance.

The linked list is just an item with a pointer to the next item (forward only). A program using it keeps a pointer to the "head" and then calls next() to iterate over the list. It is a very common pattern so I will forgo further explanation. Here is a quick implementation (in the App Package JJM_COLLECTIONS):

class ListItem
   method ListItem(&data As any);
   method linkTo(&item As JJM_COLLECTIONS:ListItem);
   method next() Returns JJM_COLLECTIONS:ListItem;
   method getData() Returns any;
private
   instance JJM_COLLECTIONS:ListItem &nextItem_;
   instance any &data_;
end-class;

method ListItem
   /+ &data as Any +/
   %This.data_ = &data;
end-method;

method linkTo
   /+ &item as JJM_COLLECTIONS:ListItem +/
   &item.nextItem_ = %This;
end-method;

method next
   /+ Returns JJM_COLLECTIONS:ListItem +/
   Return %This.nextItem_;
end-method;

method getData
   /+ Returns Any +/
   Return %This.data_;
end-method;

Notice the linkTo method sets the value of the private instance member of a remote instance (its parameter), NOT the local instance. This is what is meant by private to the class, not private to the instance. Each instance has its own &nextItem_ instance member and other instances of the exact same class can manipulate it. Here is the test case I used to test the remote manipulation implementation:

import TTS_UNITTEST:TestBase;
import JJM_COLLECTIONS:ListItem;

class TestListItem extends TTS_UNITTEST:TestBase
   method TestListItem();
   method Run();
end-class;

method TestListItem
   %Super = create TTS_UNITTEST:TestBase("TestListItem");
end-method;

method Run
   /+ Extends/implements TTS_UNITTEST:TestBase.Run +/
   Local JJM_COLLECTIONS:ListItem &item1 =
      create JJM_COLLECTIONS:ListItem("Item 1");
   Local JJM_COLLECTIONS:ListItem &item2 =
      create JJM_COLLECTIONS:ListItem("Item 2");
   
   &item2.linkTo(&item1);
   
   %This.AssertStringsEqual(&item1.next().getData(), "Item 2",
      "The next item is not Item 2");
   %This.Msg(&item1.next().getData());
end-method;

The way it is written requires you to create the second item and then call the second item's linkTo method to associate it with the head (or previous) element.

Now, just because you CAN manipulate a private instance member from a remote instance doesn't mean you SHOULD. Doing so seems to violate encapsulation. You could accomplish the same thing by reversing the linkTo method. What if we flipped this around so you created the second item, but called the first item's linkTo? It is really the first item we want to manipulate in a forward only list (now, if it were a multi-direction list perhaps we would want to manipulate the &prevItem_ member?). Here is what the linkTo method would look like:

method linkTo
   /+ &item as JJM_COLLECTIONS:ListItem +/
   %This.nextItem_ = &item;
end-method;

Now what if we wanted a forward AND reverse linked list? Here is where maybe the ability to manipulate siblings starts to seem a little more reasonable (I still think there is a better way, but humor me):

class ListItem
   method ListItem(&data As any);
   method linkTo(&item As JJM_COLLECTIONS:ListItem);
   method next() Returns JJM_COLLECTIONS:ListItem;
   method prev() Returns JJM_COLLECTIONS:ListItem;
   method remove() Returns JJM_COLLECTIONS:ListItem;
   method getData() Returns any;
private
   instance JJM_COLLECTIONS:ListItem &nextItem_;
   instance JJM_COLLECTIONS:ListItem &prevItem_;
   instance any &data_;
end-class;

method ListItem
   /+ &data as Any +/
   %This.data_ = &data;
end-method;

method linkTo
   /+ &item as JJM_COLLECTIONS:ListItem +/
   REM ** manipulate previous sibling;
   &item.nextItem_ = %This;
   %This.prevItem_ = &item;
end-method;

method next
   /+ Returns JJM_COLLECTIONS:ListItem +/
   Return %This.nextItem_;
end-method;

method prev
   /+ Returns JJM_COLLECTIONS:ListItem +/
   Return %This.prevItem_;
end-method;

method remove
   /+ Returns JJM_COLLECTIONS:ListItem +/
   %This.nextItem_.linkTo(%This.prevItem_);
   REM ** Or manipulate both siblings;
   REM %This.prevItem_.nextItem_ = %This.nextItem_;
   REM %This.nextItem_.prevItem_ = %This.prevItem_;
   Return %This.prevItem_;
end-method;

method getData
   /+ Returns Any +/
   Return %This.data_;
end-method;

And here is the final test case

import TTS_UNITTEST:TestBase;
import JJM_COLLECTIONS:ListItem;

class TestListItem extends TTS_UNITTEST:TestBase
   method TestListItem();
   method Run();
end-class;

method TestListItem
   %Super = create TTS_UNITTEST:TestBase("TestListItem");
end-method;

method Run
   /+ Extends/implements TTS_UNITTEST:TestBase.Run +/
   Local JJM_COLLECTIONS:ListItem &item1 =
      create JJM_COLLECTIONS:ListItem("Item 1");
   Local JJM_COLLECTIONS:ListItem &item2 =
      create JJM_COLLECTIONS:ListItem("Item 2");
   Local JJM_COLLECTIONS:ListItem &item3 =
      create JJM_COLLECTIONS:ListItem("Item 3");
   
   &item2.linkTo(&item1);
   
   %This.AssertStringsEqual(&item1.next().getData(), "Item 2",
      "Test 1 failed. The next item is not Item 2");
   %This.AssertStringsEqual(&item2.prev().getData(), "Item 1",
      "Test 2 failed. The prev item is not Item 1");
   
   &item3.linkTo(&item2);
   %This.AssertStringsEqual(&item1.next().next().getData(), "Item 3",
      "Test 3 failed. The next.next item is not Item 3");
   %This.AssertStringsEqual(&item1.next().next().prev().getData(), "Item 2",
      "Test 4 failed. The prev item is not Item 2");
   
   Local JJM_COLLECTIONS:ListItem &temp = &item2.remove();
   %This.AssertStringsEqual(&item1.next().getData(), "Item 3",
      "Test 5 failed. The next item is not Item 3");
   %This.AssertStringsEqual(&item1.next().prev().getData(), "Item 1",
      "Test 6 failed. The prev item is not Item 1");
   
end-method;

I hope that helps clear up some of the confusion around the term "private" as it relates to Application Classes.

Wednesday, July 16, 2014

PeopleCode Coding Discipline

Java, JavaScript, C++, C Sharp, Objective C, Groovy... what do these languages have in common? Yes, curly braces, but besides that... actually, there are a lot of similarities between these languages. Throw Visual Basic, Perl, Python, or any other well-known language into the mix and the number of similarities drops significantly. Setting semantics and syntax aside, a common attribute of all well-known languages is standards and best practices. Some of those best practices (such as coding style) differ by language. For example, bash scripts can either look like, uh... bash scripts or they can look like c-style logic statements. Obviously, bash best practices prefer you make bash code look like bash code. Other standards are personal: do you prefer real tabs or spaces? How many spaces does your tab consume? Do you put curly braces on a new line?

How does all of this fit into PeopleCode? Forget about code formatting preferences. Application Designer has its own code formatting ideas. But there are other best practices that can help you write better code with fewer defects (fewer defects = better code). By following best practices your code will be easier to read, you will be more productive, and your users will be happier because you deliver better solutions faster.

Even though best practices usually result in code that is more efficient to process, that isn't really the point. Computers can interpret just about anything. Compilers and interpreters are really good at eliminating useless words and resolving seemingly incomprehensible logic. I love Martin Fowler's quote, "Any fool can write code that a computer can understand. Good programmers write code that humans can understand." Best practices are really about writing code that humans can easily comprehend. For example, avoid complex logic (including double negatives, or any negative logic, for that matter), keep your method and function code short, etc. If you write some code, leave it for a night, and then come back the next day and either need to read lots of comments to figure it out or spend a few minutes "remembering" what that code does, then the code is probably too complex. The problem with complex code is that it is easily misinterpreted by humans. Another problem with complex code is we actually ignore it when trying to resolve problems. We know it takes time to digest complex code, so we avoid it, preferring to test simple code first. Why waste time trying to understand complex code if it might be functioning properly?

Today's Quest Newsletter contained a link to 10 Bad Coding Practices That Wreck Software Development Projects. These are language agnostic practices that we can easily apply to PeopleSoft development.

If I were to summarize Coding best practices, I think I would do it like this: two.sentenc.es. Now, arguably, short does not equal comprehensible. There are programmers that err on the terse side because it is clever. This is true, often short code is clever. It is also hard to read. Most of us, however, err the other way. E. F. Schumacher said, "Any fool can make things bigger, more complex, and more violent. It takes a touch of genius — and a lot of courage — to move in the opposite direction." Schumacher died in 1977, so this problem is not new.

Computer programming is about communication. As programmers we have two audiences:

  • Computers (which can interpret anything -- even complex stuff)
  • Humans (who have a limited attention span, distractions, and a preference for simplicity)

Here is why I think discipline and best practices are critical for good PeopleCode:

We use PeopleCode to create business rules, but PeopleCode is NOT a business rules language. PeopleCode is a Metadata manipulation language. (Note: this is purely my opinion)

Here is why I believe PeopleCode is for metadata, not business rules: PeopleCode only has Metadata objects: records, fields, SQL, components, menus, etc. These are all metadata. These are the low level API's we use to write business logic. Consider the following PeopleCode:

Local record &rec = CreateRecord(Record.PSOPRDEFN);
Local field &descr;

&rec.SelectByKey("jimsoprid");
&descr = &rec.GetField(Field.OPRDEFNDESC);

&descr.Value = "Jim Marion";

&rec.Update();

This code implements business logic, but does so by manipulating metadata objects. PeopleCode metadata objects are building blocks for business logic. If we were to rewrite this using a business logic language, it would probably look something like this:

Local User &u = GetUser("jimsoprid");

&u.descr = "Jim Marion";
&u.Update();

And this is why discipline and best practices are SO important for PeopleCode developers: We are trying to speak business logic with a metadata vocabulary. We start with a communication deficit. It is like trying to teach advanced weaving using an automobile mechanics vocabulary. The two subjects have different vocabularies. But if you combine the words correctly, you can communicate the same meaning.

Tuesday, September 07, 2010

URL Administration (Nodes and URL Definitions)

I use URL's quite extensively for Ajax and other non-Integration Broker integrations (NEVER HARD CODE URL'S!). PeopleTools provides a couple of ways to store URL information. The most well-known of these features is the URL definition (PeopleTools > Utilities > Administration > URLs). URL Definitions are great for relative URL's, but I'm not fond of storing fully qualified URL's in this manner. Here is why...

Imagine having 5 URL's that point to different resources on the same server and then one day the server's host name changes. Because of this change I have to modify 5 URL definitions. What if I forget one?

An alternative is to store the base URL in a node definition and then the relative portion of the URL in a URL definition. Creating a fully qualified URL in this manner requires concatenating two definitions: the node URI and the URL definition. Next Question: How do I access these Meta-data objects from PeopleCode? Most of us are familiar with the GetURL PeopleCode function, but what mechanism does PeopleCode offer for retrieving a node's Content and Portal URI?

A PeopleSoft instance's node definitions are accessible through the %Session object. The Session object contains a method named GetNodes which returns a collection of the instance's node definitions. A call to the collections FindItemByName method returns a reference to a single node, which, of course, has properties of its own. Putting this all together, returning the Portal URI of a node named UCM would require PeopleCode that looks something like:

Local string &serverUrl = %Session.GetNodes().ItemByName("UCM").PortalURI;

By centralizing the base portion of the URL in a node definition, we save some administration overhead.

Sunday, August 02, 2009

Using jEdit to Edit PeopleSoft Files

A couple of years ago, Chris Heller wrote a great article on editing PeopleSoft files with the Notepad++ text editor. Since I prefer jEdit over Notepad++ (my jEdit fascination dates back to my Java programming roots and, more recently, the jEdit Ruby plugin). I have a collection of custom jEdit syntax files in my box.net jEdit shared folder. In this folder, you will find DMS and PeopleCode syntax files (unlike Notepad++, jEdit includes SQR syntax files out of the box). Feel free to download and use these files. Caveat: these files are far from complete. I add to them on an as-needed basis. If you find errors and/or omissions, please leave a comment here, and I'll do my best to update my shared syntax files. See the jEdit help file for how to add these syntax files to jEdit (hint: jEdit calls syntax files "Edit Modes").

By the way, jEdit has a Code2HTML plugin. I use the syntax files in my shared folder along with the Code2HTML plugin to generate the syntax highlighting used on this blog.