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.

Tuesday, July 29, 2014

List of PeopleSoft Blogs

It has been amazing to watch the exponential growth in the number of PeopleSoft community bloggers. It seems that most of them have links to other PeopleSoft blogs, but where is the master list of PeopleSoft blogs? Here is my attempt to create one. Don't see your blog on the list? Add a comment and I'll review your blog. If your blog is education oriented, then I will put it in the list... and probably delete your comment (that way you don't have to feel awkward about self promotion). There are some PeopleSoft related blogs that I really wanted to include, but they just weren't educational (more marketing than education). I suppose some could say that the Oracle blogs I included were primarily marketing focused. That is true. I included them, however, because those product release announcements are so valuable.
I have not read all of these blogs. I can't, don't, and won't attest to the quality of the content in those blogs. Each reader should evaluate the content of these blog posts before implementing suggestions identified in these blogs and their corresponding comments.