IBM's implementation of OOP within Lotus Notes is far from perfect. In fact, it would be very easy to assume that IBM do not see OOP as being a target for Lotus Notes developers. One of the areas in which an Object Oriented Programmer must overcome some additional obstacles is when it becomes necessary to add business logic to either a subform (LotusScript) or a custom control (SSJS). While it is possible to expose a Subform on its own via a DialogBox it is more typical that a subform or custom control is exposed as a component within a Form or XPage. In Part 3 of this series I discussed ways to wire a class to a form or an XPage. But what about Subforms and Custom Controls? These components do not provide direct access to the variables defined in their parent. Nor are the events fired by these components accessible from the parent object. . Subforms Does the subform get used to display data from one core object (document type)? If the answer is yes then you can create a new variable global to the subform that instantiates another instance of the class defined on the parent form. Then you can wire actions/events to that variable. Theoretically this is far from perfect. From a practical perspective this usually provides most (if not all) of what is needed. Note: Depending on the version of Lotus Notes you are using you may encounter compile issues with this approach. On occasions I have been forced to make the variable declaration local to each action just to get the LS code to compile. . If your subform is more generic and could potentially be included on many forms representing different document types then it starts to get more complicated. In this scenario you may need a base class that defines the business logic for this subform (and any other subforms that serve the same group of document types). Then you can instantiate an instance of that class on your subform(s). The parent form will now (most likely) be using a superclass that extends the base class with additional logic specific to that document type. In a way this actually improves the design of your application. The fact that a subform can be shared by multiple objects is really an indication that there is a group of Fields (properties) and Actions (methods) that are common to a group of objects (Forms).
Custom Controls A well designed XPage application is almost always going to include the use of Custom Controls. So this scenario is potentially a constant issue for OOP XPage development. The good news is that it is possible to pass an object to a custom control via property. To do this you need to define a property of the type java.lang.Object. This is available as "Object" under the list of Complex Types that can be selected when specifying the "Type" of a property. So if your XPage has a Controller variables and several Model variables (e.g. two documents on an XPage or One Document and a View) you can pass these variables to each Custom Control by specifying these variable as the values for one or more properties. To access a function in my controller I can make a SSJS call to compositeData.controller.functionName() (assuming the property name assigned is "controller"). This offers a lot of advantages over the Subform approach as we are sharing the same variable (passed by reference) allowing us to communicate (and signal) between the parent XPage and the underlying custom controls. In later blogs I hope to provide more details about how this capability can be exploited to do things I can only dream about using Forms/Subforms.
OOP
Series
|
Bewertungen
0
|
Test Driven Development has been made popular by agile programming methodologies such as Extreme Programming and SCRUM. TDD involves starting the development cycle by first building automated test scripts that define the requirements of the code. The developer then develops new code, or refactors existing code that is designed to pass the tests. . Process
- Translate requirements into Classes/Properties/Methods: Each requirement can be translated into a series of one or more properties/methods that must be performed against core objects (databases, views, documents). Assign a name to each property/method and create a shell for that property/method in your Script library.
- Translate requirements into use cases: Reinterpret the requirements a second time by considering the scenarios each property/method should handle. Create test scripts that test each of these scenarios.
- Create test data: Where needed, create test data (documents) that will facilitate the testing of scenarios. Forms used during prototyping usually act as an excellent way to create test data.
- Code: Add the code necessary to implement each property/method.
- Test Code: When coding is completed execute the appropriate unit tests to see if all the tests pass. If they do, then the code is complete. If the test fails then make changes to the code to address the issues and retest.
. Advantages Test Driven Development brings a lot to the development cycle:-
- Developing tests first forces a developer to think through the details of requirements before they start writing code. The developer then has a clearer understanding of the requirements which usually leads to a higher quality of code being developed in the first round of development.
- In traditional development methodologies it is often only during testing (Unit Testing, Integration Testing, System testing etc.) that scenarios are discovered that were neither outlined in the requirements and/or written into the code. Making changes at this late stage is expensive often leading to revised requirements, additional coding, new tests, and another round of testing. TDD increases the chances that the need for clarifications will be detected early on in the cycle before development begins.
- If you are working with a separate QA or UAT team it is possible to review the tests scripts with those teams to ensure that everyone is in agreement about the scenarios that must be tested. A consensus can be reached before the code is developed rather than responding to a defect raised in QA/UAT. Where necessary, requirements can be clarified reducing the rework for both coding and the development/execution of test scripts.
- Code developed using TDD has a greater chance of following the well known KISS principal (Keep It Simple Stupid) leading to simpler designs. The developer needs to only develop their code to the point where it passes the tests. This tends to reduce the time/effort involved in writing code that is simply not needed by the application.
- TDD adds a degree of certainty to the development cycle. In traditional development lifecycles it can be difficult to measure if development is on track/budget. Writing code is one thing. Writing code that works is another. Until the first round of testing is done it is often difficult to determine how much time may need to be spent to address issues found in testing. With TDD it is possible to produce burn-rate charts that track the total number of tests and the number of tests passed. These can be extrapolated to determine if a project is ahead/behind schedule.
Note: If the above approach is taken with Class Notes applications (LotusScript), then the task of migrating an application to XPages becomes much simpler. The test scenarios and data should remain largely unchanged. The test scripts will need to be migrated from LotusScript to SSJS/JavaScript but the changes should largely be syntax changes . In Part 14 we will return to our Notes Application Catalog and demosntrate how we can apply Test Driven Development.
|
Bewertungen
0
|
One aspect of OOP I have often struggled with when doing Notes development is the validation process. In theory the validation of an object forms part of of the Business Logic Layer (BLL). i.e. The logic for validating a document's content should always be the same if you if you a displaying a form on a Notes client or a Web browser. It should be the same if it is a Domino Web Form or an XPage. It should be the same if I edit/add records via a Notes Form or via Inview Edit. It should be the same if it is displayed on a wide screen monitor or a small PDA screen. It should also be the same if you are running an agent to import data from another source. The challenge for classic Notes development is that Lotus Notes makes it so very easy to integrate validation into the presentation layer (PL) of a form by adding validation formula directly to a field on a form. It's not perfect, as I get one error message for each field validation each and every time validation is triggered. I must also remember to duplicate the logic should there be more than one way a document can be edited. Despite this I have often found myself yielding to the RAD model of Lotus Notes development and placing my field validations on the form this way. When you are building an application just for the Notes client and only one form is ever used to display documents for a core object the practical often rules over the theory of good design and OOP. I do however wish Lotus Notes did a better job of supporting OOP by allowing me another way to specify validation logic as part of a class so that I could define it once as part of my BLL and use it in many places. (PL) as needed. . With the advent of XPages things got both better and worse at the same time. To IBM's credit they have significantly expanded the ease with which validation rules can be added to an XPage control. It now requires the mere checking of a box to indicate that a control must have a value entered. Additional validators have been added that allow me to indicate a specific length range for a control's value, match a pattern etc. I can even build my own custom validators for complicated validation logic. There are also two error controls that integrate with these validators. One allows me to display validation errors for a single field and the other allows me to consolidate these validation errors in a single place. This is all great for building an application quickly. But I would argue strongly that this approach also encourages developers to build applications with bad architecture. Note: This is not the only aspect of XPages that irks me as encouraging bad application architecture, but that's another story..... . It took me many years to crack this nut. - (yes I am slow!) but there are solutions. Ways in which the validation aspects of an application can be implemented that preserve the concepts of OOP and n-tier architecture and at the same time allow a Notes application developer using LotusScript and/or JavaScript/SSJS to remain productive. It is even possible to take some of the great concepts found in Xpage validators and error controls and implement them as part of Notes Classic applications. . As is often the case with OOP, the way to constantly accelerate your Notes development is to separate out application specific logic from generic functionality. Build reusable components for the generic functionality once and then include them in every application as needed. In my example I am drawing from my ∙dominoFramework in which I have a DominoDocument class defined to add additional capabilities to the NotesDocument class (things I wish IBM would add). In this class I have taken many of the XPage validators and implemented them as LotusScript giving my DominoDocument class methods such as validateConstraint(), validateDateTimeRange(), validateDoubleRange(), validateLength(), and validateRequired(). Note: I could take the same approach to implement this concept using JavaScript, SSJS, or Java. . The following is a sample of the code needed to support the validateRequired() method:-
'/** ' * Validate a required field has a value ' * @param FieldName The name of the field being validated ' * @param Message The message to be displayed if the validation fails ' * @return True = Field is considered to have a value ' * @unittest 001.011.060 ' */ Function validateRequired(FieldName As String, Message As String) As Boolean Dim Item As Variant Try: On Error GoTo Catch If (iDocument.HasItem(FieldName$)) Then Set Item = iDocument.GetFirstItem(FieldName$) Select Case Item.Type Case Text: If (Item.Text = "") Then Call recordValidationError(FieldName$, Message$) Else validateRequired = True Case RICHTEXT: If (Item.GetUnformattedText() = "" And IsEmpty(Item.EmbeddedObjects) And (Item.ValueLength = 88 Or Item.ValueLength = 104)) Then Call recordValidationError(FieldName$, Message$) Else validateRequired = True End If End Select Else Call recordValidationError(FieldName$, "Error: Unable to locate field " + FieldName$) End If Exit Function Catch: Stop Call recordValidationError(FieldName$, "Error " + Error$ + " validating field " + FieldName$) Exit Function End Function
. To integrate validation with the presentation layer I add two special (temporary) fields to a document. $ErrorFields contains a list of the fields in which a validation (or other) error has been detected. $ErrorMessages contains a list of the error messages that should be displayed. The validators manage the contents of these fields and using a special subform I can display a consolidated list of validation errors in a single place on all my forms without the need to write any new code. I am now free to add the application specific validation logic as a validate() method within each of my core object classes. . Returning to our Notes Application Catalog example I can add validation for each of our core objects as the classes I have built extend the DominoDocument class and hence have the validator methods available to me. For our Application object we may simply wish to ensure that a Title is always provided for our application. Following our MVP model, a validate() method contains the BLL for validating any Application object.
'/** ' * Validate document placing ' * @return True = no validation errors detected ' * @unittest 010.001.002 ' */ Function validate() As Boolean Call DominoDocument..validate() Call ValidateRequired("Title","Please provide a title") validate = Not Me.HasValidationErrors End Function . The second part of our MVP is a domain controller (Presenter) specifically for a Notes Form. In this case a onQuerySave() controller that responds to the Form QuerySave event that:-
- Causes the save to be terminated if there are validation errors;
- Moves the Form's cursor to the first field containing an error, and
- Triggers a refresh of hide-when formula allowing all our validation error messages to be displayed.
'/** ' * Form Event: QuerySave - Validate document ' * @param Source Reference to the NotesUIDocument generating this event ' * @unittest Manual ' */ Sub onQuerySave(Source As NotesUIDocument, Continue As Variant) Try: On Error GoTo Catch Continue = Me.validate If (Not Continue) Then Call Source.GotoField(Me.ErrorFields.toArray()(0)) Call Source.RefreshHideFormulas() End If Exit Sub Catch: Stop Resume Next End Sub . Note: The call to DominoDocument..validate() initializes our $ErrorFields and $ErrorMessages fields. . The end result of this approach is a Lotus Notes form that pretty much mimics the validation behavior of XPages, but in a way that follows the sound software engineering principals of OOP and n-tier architecture.
. Note: By implementing the above approach into Notes Classic applications you are taking yet another step in future proofing your applications. The validation logic is no longer spread across a wide range of fields on any number of forms in your application, it is consolidated into a single method within each of your core object classes. The code can be easily located and, should you ever decide to migrate your code to XPages, the task becomes a relatively simple one to translate your LotusScript validate() method into a comparable SSJS method. .
OOP Series Part 1 - Wiring A View To A Class
Part 2 - Responding To An InviewEdit Event
Part 3 - Wiring A Form Or XPage To A Class
Part 4 - Building a Notes Application Catalog
Part 5 - Defining Classes For Domain Catalog
Part 6 - Creating An Automated Test Plan
Part 7 - Creating Automated Test Scripts
Part 8 - Why Prototyping Still Matters
Part 9 - Automating The generation Of Classes
Part 10 - The Constructor
Part 11 - How I Destroy The MVP/MVC Model
Never EVER Buy A HP Computer
|
Bewertungen
0
|
When reading about OOP and n-tier architecture you will often see discussion given to the Model-View-Controller (MVC)and/or Model-View-Presenter (MVP) design patterns. No matter how many times I read the material on the subject I am never sure I understand 100% the way these patterns are supposed to be implemented. Even when using programming languages such as C# it always seem to me that the resulting code is way more complicated than I would like and there is an excessive amount of duplication produced. So I have developed my own unique approach for LotusScript that adheres to many of the core concepts behind these design patterns without having anywhere near the same level of complexity and/or duplication. I am sure OOP purists can quickly pick hole in my approach, but I have found my own unique solution seems to be fairly effective at designing Notes applications that remain RAD, relatively simple and still have a solid architecture. . To me, one of the most important drivers of MVC/MVP is the need within an n-tier architecture for a lower tier to have absolutely no knowledge about any of the tiers that lie above. This means, when I am writing my Business Logic Layer I have no knowledge about the client on which the application is running, Whether it is Notes client or Web client, XPages or Classic Notes, 30" Widescreen monitor or a small mobile screen. Of course, the fact it is being written in LotusScript might eliminate a few of my choices! . Business Logic Layer (Model) My Business logic layer must consist of a collection of properties and methods that have no direct interaction with the user. No use of NotesUI classes, no print or messagebox statements etc. They receive input via parameters and pass back results via parameters and/or storing information in domain data (e.g. document fields). In theory, the same business logic can be used by any number of presentation layers (remember there are even ways to get an XPage to execute LotusScript code). . Presentation Layer (View) At the "top" of our n-tier application lies the Presentation Layer. Forms, Views, Pages, XPages etc. These design elements display information on the screen and then act as an event engine to invoke lower tiers. Each of our presentation design elements contain their own unique set of events. The opening or closing of a form/page, clicking of a button or action, the entering, exiting, or changing of a field etc. When these event occur we must have a way in which we can interact with the business logic layer. Some implementations of MVC/MVP allow the View to make calls directly to the Model. Some model require that the View always go via a Controller/Presenter. All models support the View talking to the model via the Presenter/Controller. My approach for this is as follows:-
- If the event being responded to requires an action that can be executed by simply calling a method in the BLL then I wire the event directly to that method.
- If the event being responded to is complex in nature and/or requires additional interaction with the user I wire the event to a special Supervising Controller method.
- Controllers can be modularized in such a way that controller can call other controllers as well as make calls the the BLL.
- Controllers are part of the presentation layer and can interact with the user. Because of this fact it is necessary to create separate controllers for each type of client you are supporting (e.g. Notes client versus Web client).
- Rather than creating separate classes for Model, View, and Controller, I place all the code in a single class. I structure the class so that the BLL properties/methods are together in one section of the class code and the controller methods in another section. I also use a naming standard that makes it clear what might be a BLL method and what may be a controller. This makes even more sense with modern Notes development because this is LotusScript code and the chances are high that I am developing this for the Notes client only. i.e. Because of the practical limits of developing XPage applications with anything other than SSJS I will rarely find myself needing multiple controllers within in LotusScript.
. An Example So let's return to our Notes Application catalog to see the above in practice. Having scanned our Domain Catalog (or physical server) we will have collected a list of databases located on our server(s). Some of these databases will be system databases and/or mail/PIM databases that are supported by Administrators. So we need a process to "exclude" these databases. In this application I decided to do this by assigning a status field to manage the workflow of our applications. We will have two status "System" and "Mail" which are used to denote non-application databases and exclude them from a number of views.
At the BLL level we assign a new status by changing the Status field to the designated value as well as recording the date/time of the status change and the name of the person changing the status in separate fields.:-
'/** ' * Assign a new status to the document, recording details of who made the change ' * @param NewStatus The new status to be assigned ' * @unittest ' */ Function assignStatus(NewStatus As String) As Void Select Case NewStatus Case "Discovered","Mail","System": If (Me.Status$ <> NewStatus$) Then Me.Status$ = NewStatus$ Set Me.LastUpdatedBy = New NotesName(Session.Username) Set Me.LastUpdated = New NotesDateTime(Now) End If Case Else ' Status Not recognized, so do nothing End Select End Function
. Note that the above logic does not interact with the user in any way or make any assumptions about in what context it is being invoked. At the presentation layer we will have two ways of invoking this logic. From a view we might allow all selected documents to be assigned a status via a view action. At the form level (e.g. when a document is opened) we may allow this one document to be assigned via a form action. . For the view action we need to assign each and every selected document as per the BLL above. We also need to ensure each document is saved. We may also choose to refresh the view to display the udpated status as well as de-selecting the selected documents in the view. Much of this logic is specific to a Notes view being displayed via a Note client and hence is implemented as part of the controller.
'/** ' * Request to change status of selected documents in view ' * @param NewStatus The new status to be assigne dto selected documents ' * @unittest ' */ Sub OnStatusAssignment(NewStatus As String) Dim Database As DominoCatalogDatabase Dim DatabaseDoc As NotesDocument Dim SelectedDocs As NotesDocumentCollection Set SelectedDocs = Me.SelectedDocuments.toDocumentCollection() If (SelectedDocs.Count = 0) Then Exit Sub ' Update status for all selected documents Set DatabaseDoc = SelectedDocs.getFirstDocument() While (Not DatabaseDoc Is Nothing) Set Database = New DominoCatalogDatabase(DatabaseDoc) Call Database.assignStatus(NewStatus$) Call Database.Save(False,False,True) Set DatabaseDoc = SelectedDocs.getNextDocument(DatabaseDoc) Wend ' Refresh UI View If (Not UIW.CurrentView Is Nothing) Then Call UIW.ViewRefresh() Call UIW.CurrentView.DeselectAll() End If End Sub
. The form action invokes the same BLL but the interaction is a little different. We are now processing a single document which is already open in the Notes client. We may decide that if the document is being edited we will not execute the save, providing the opportunity for the user to exit the document without being saved. If the document is open in read mode we do not have this option so we force the change to be saved as part of the event.
'/** ' * Assign a new status to the database record ' * @param newStatus The New status to be assigned ' * @unittest ' */ Function onStatusAssignment(NewStatus As String) As Void Call Me.assignStatus(NewStatus$) If (Not iUIDocument.Editmode) Then Call Me.Save(False,False,True) End Function
. In the presentation layer we have a view action which simply invokes our View Controller and we have a form action that invokes our Form Controller. These actions need to know nothing about how the action gets executed. The code in our presentation is therefore never more than a single line to invoke the relevant controller or BLL method. In some cases, such as Form/View events, we can even trap the events as part of our class requiring no code to be added to our form/view.
Sub Click(Source As Button) Call CatDatabase.onStatusAssignment("System") End Sub
. In Part 12 of this series we will look at the issue of form/page validation at look at ways we can add some of the concepts found in XPage validators to Classic Notes Forms via LotusScript code. .
|
Bewertungen
0
|
In Part 9 of this series I shared a few ideas about how it is possible to automate the creation of a shell for a LotusScript class by including property definitions based upon a Form definition or the fields contained in an existing Notes document. Properties are perhaps the most frequent and least complex components for a class so automating this task is a big win for Notes developers. It frees up time to focus on the all important business logic. In today's article I wish to look at the constructor, and again look at how we can simplify/automate this important class component. . In most Notes applications the activity is centered around processing documents. Most of the classes we define are based upon documents of a particular type (e.g. Form). In our Lotus Notes Application catalog we have three core objects identified (Application, Database, and Replica). These core objects have classes which extend the NotesDocument and NotesUIDocument classes. Not only will we need to wire our forms to these classes, but the methods we create will often need to access another document that is related to the current document. The constructor is used to instantiate an instance of a specific class. For Notes Document-centric classes it therefore pays to have a wide range of options for how you application can indicate exactly which document it is that they need to process. . The DominoDocument Constructor One of the first classes I wrote when I started doing Notes development using OOP was a class to extend the NotesDocument class. This I called DominoDocument. One of the limitations of LotusScript as an OOP language is that it does not support method overloads. So I can only have one constructor for each class. Another limitation of LotusScript is that method parameters cannot be declared as optional. So to work within the limitations of LotusScript I typically adopt a pattern of declaring a single parameter for my class constructors and declaring it to be of type Variant. This allows me to create pseudo constructors within my single constructor by conducting a check on the TypeName of the parameter passed. Why is this important? Applications often have different contexts from which they need to instantiate a document-centered class.:-
- I may already have an instantiated NotesDocument or NotesUIDocument I wish to extend
- I may have a ViewEntry that points to the document
- I may wish to access the currently opened document (whatever that happens to be)
- I may know the NoteID, UNID, or even NotesURL of the document
- or I may have some form of key that is specific to documents of this type that I need to look-up to locate the document
The implementation may vary a little from one application to another but generally the same code can be used to handle all these scenarios. With LotusScript the Constructor has one characteristic that differentiates it from other methods. If a class extends another base class then the constructor of the base class is executed first and then the constructor for the extended (super) class is executed and so on up the chain. For standard methods only the outermost method with a matching name is executed. By basing all document-centric classes from a base class (DominoDocument) it is possible to develop and place most of the generic constructor code in the base class. Typically the only code the super class requires is to deal with the application specific logic of finding a document based upon a key value. . The following is the constructor for my DominoDocument Class
' C O N S T R U C T O R _______________________________________________________________________________ '/** ' * Constructor ' * @param Source A reference to the NotesDocument being extended by this class ' * NotesDocument ' * NotesUIDocument ' * NotesViewEntry ' * NotesID (String) ' * DocumentUNID (String) ' * NotesURL (String) ' * Key (String or String Array) - Function getBykey will be invoked to locate document in a designated view using provided key ' * @unittest 001.011.051 ' */ Sub New(Source As Variant) Select Case Typename(Source) Case "NOTESDOCUMENT": Set iDocument = Source Case "NOTESUIDOCUMENT": Set iUIDocument = Source If (Source.Document Is Nothing) Then On Event PostOpen From Source Call OnPostOpen Else Set iDocument = Source.Document Case "NOTESVIEWENTRY": Set iDocument = Source.Document Case "STRING": Call getByKey(Cstr(Source)) Case "STRING( )": Call getByKey(Source) End Select End Sub . The class's getByKey() method deals with the scenarios of a NotesID, UNID, or NotesURL.
'/** ' * Locates an existing document based upon the document's key (Invoked by constructor only) ' * Note: By default the "current" database is assumed. Used DominoDatabase.getDocument() to access documents by key in another database ' * @param Source Key that can be used to locate the existing document (NoteID, UNID, Notes URL or HTTP URL) ' * @unittest 001.011.051 ' */ Private Function getByKey(Source As Variant) As Boolean Dim NotesObject As Variant On Error Resume Next ' Prevent error if key is not a a valid NotesURL Select Case Typename(Source) Case "STRING" If (Ucase(Left(Source,6)) = "NOTES:" Or Ucase(Left(Source,5)) = "HTTP:") Then Set NotesObject = Session.Resolve(Source) If (NotesObject Isa "NotesDocument") Then Set iDocument = NotesObject getByKey = True Exit Function End If End If Select Case Len(Source) Case 1,2,3,4,5,6,7,8: ' Possible NoteID in LS format. Check if the string is a hex number If (Math.hexToDecimal(CStr(Source)) > 0) Then Set iDocument = DB.GetDocumentByID(Source) Case 9: Set iDocument = DB.GetDocumentByID(Left(Source,8) + Right(Source,8)) ' Possible Note ID in @Language format Case 32 : Set iDocument = DB.GetDocumentByUNID(Source) ' Possible UNID End Select End Select If (Not iDocument is Nothing) Then If (Not iDocument.IsValid) Then Set iDocument = Nothing Else getByKey = True ' Valid UNID returns an invalid document if UNID not found End If Exit Function End Function . So when it comes time to create my constructor for a new class such as our Database Class it is rare that any code is needed in the constructor itself. That is all handled by the DominoDocument constructor. I do need to create my own version of the getDocumentByKey method. In the example below you will see that I use the statement Call DominoDocument..getByKey(Source) to invoke the underlying DominoDocument.getBykey() method first. If that fails to find a document based upon Notes ID, UNID, or NotesURL then I will do a lookup into a specific view in my application to find a match on the first sorted column (key). Simply omit this call if you wish to only look up documents based upon an application key.
'/** ' * Locate an existing document based upon the provided key value ' * @param Source Key that can be used to locate a database document (ReplicaID) ' * @return True = document was found ' * @unittest ' */ Private Function getByKey(Source As Variant) As Boolean Dim DatabaseList As NotesView Call DominoDocument..getByKey(Source) If (Not iDocument Is Nothing) Then Exit Function Set DatabaseList = DB.GetView(FRAMEWORK∙VIEW∙DATABASES) If (DatabaseList Is Nothing) Then Exit Function Set iDocument = DatabaseList.Getdocumentbykey(Source,True) getByKey = Not iDocument is Nothing End Function . One of the things I like most about OOP is its support for the concept of write a solution once and use it many times. Yes, you would be surprised how often you can exclaim... "There's A Module For That" . In Part 11 of this series I will progress to creating some of the application specific methods needed for our Notes Application catalog.
|
Bewertungen
0
|
In Part 8 of this series I discussed the use of prototyping as a way of interacting with our users to get a good feel for the overall requirements of our applications. Having completed the prototyping we now have three forms, one for each of our core objects (Application, database, Replica). In implementing an n-tier architecture, the Forms combined with the Views represent to major part of the presentation layer for our Notes Application catalog. The next step in the process is to build the classes into which we will add all our application logic. . Because we have already built our forms, we have already defined many of the components we will need in building our new classes. Our class will require properties for our Domain data. These largely represent the fields on our form (excluding computed for display). We can either review the form definition and create the property code manually one property at a time or we might consider automating the process. I have previously mention the property generator tool available in my .Domino Framework 1.0 project on OpenNTF as a way of building the code for each property. But it is possible to go one step further and build an entire class definition using either the Form definition or by taking an example document and inspecting the fields stored inside the document. To get the greatest amount of information about our form you will most likely need to use DXL. This will allow you to identify:-
- The name of each field
- The type of field (e.g. to eliminate computed for display fields)
- The type of data stored in the field (determines the data type of the property)
- Whether or not the field allows multiple values (important in defining the property)
- Description and field hint (can be used to document each property)
. So by taking one of our existing forms such as this:-
. And passing this through a code generation tool, we can produce a Class Definition such as the following even before we have had to write a single line of code ourselves. As with Unit Testing, OOP offers many great opportunities for automation.
'/** ' * Class Database ' * ' * @author Peter Presnell ' * @version 1.0.0 ' * @since 1.0.0 ' */ Class Database As DominoDocument
' C O N S T R U C T O R ___________________________________________________________________________________ '/** ' * Constructor ' * @param Source Reference to NotesDocument being extended by this class ' * @unittest ' */ Sub New(Source As Variant)
End Sub
' P R O P E R T I E S______________________________________________________________________________________
'/** ' * Status ' * @unittest ' */ Property Get Status As String If (iDocument Is Nothing) Then Exit Property If (iDocument.HasItem("Status")) Then Status$ = iDocument.GetItemValue("Status")(0) End Property Property Set Status As String If (iDocument Is Nothing) Then Exit Property Call iDocument.ReplaceItemValue("Status",Status$) End Property '/** ' * A brief description of the application ' * @unittest ' */ Property Get Abstract As String If (iDocument Is Nothing) Then Exit Property If (iDocument.HasItem("Abstract")) Then Abstract$ = iDocument.GetItemValue("Abstract")(0) End Property Property Set Abstract As String If (iDocument Is Nothing) Then Exit Property Call iDocument.ReplaceItemValue("Abstract",Abstract$) End Property '/** ' * Production, QA, Development etc. ' * @unittest ' */ Property Get DBEnvironment As String If (iDocument Is Nothing) Then Exit Property If (iDocument.HasItem("DBEnvironment")) Then DBEnvironment$ = iDocument.GetItemValue("DBEnvironment")(0) End Property Property Set DBEnvironment As String
|