14 Feb. Design Pattern in OO ABL – Part 2 of 3
Design Patterns are conceptual work, the idea has been known to the public ever since the publication of the book 'Design Patterns: Elements of Reusable Object-Oriented Software', in 1995.
This article looks at the pattern 'Lazy Loading' and 'Adapter' in the context of OpenEdge Object Oriented ABL (OOABL).
This is the second part of our three-part series on Design Patterns. The first article is available here, it covers the pattern 'Builder', 'Singleton' and 'Multiton'. The third part will focus on 'Abstract Fabric' and 'Proxy'.
PATTERN 4 – LAZY LOADING (CREATIONAL PATTERN)
Often, data and objects may be used or not, depending on the program flow. Lazy Loading delays the load until the moment, an object (or data) will be used. Lazy Loading is something you may have used in OpenEdge for years, without knowing it is a pattern ;)
Lazy loading has four main variants:
- Lazy Initialization – Delay getting data for a field until it is needed
- Virtual proxy – Delay instantiating an object until it is needed. This may also increase security, as the proxy must not expose all methods and properties.
- Ghost – Here an object will held only partial information (e.g. id of record). When record data are accessed, missing data will be loaded. Ghost makes start up faster.
- Value Holder – Object with a get value method, getting data on demand from real object.
Popular Lazy Loading scenarios:
- Access aggregated data
- Load images / data when they appear in view (infinite scroll)
- Tab widget is selected
- Initialize a service (ESB, log system, rpc…) when first used
In this OpenEdge example (Lazy Initialization) we calculate the invoice summary of the last three month for a customer.
CLASS cMyInvoice:
...
DEFINE PUBLIC PROPERTY iInvSum AS INTEGER NO-UNDO INITIAL ? PRIVATE SET.
PUBLIC GET:
IF iInvSum = ? THEN DO:
DEFINE VARIABLE iCn AS INTEGER NO-UNDO.
iCN = THIS-OBJECT:iCustNum.
//loop through invoices of customer
//accumulate invoices ...
END.
RETURN iInvSum.
END GET.
...
END CLASS.
- Delaying or avoiding work
- Starting screen faster
- Extracting code
- May increase overall calls to DB
- May show inconsistent data
- (cause some parts are loaded later, first loaded parts may not refreshed)
In OpenEdge daily work Lazy Loading is most useful for service level objects (logging, ESB) as well as giving the user fast screen load and 'infinite scroll' (data browse, tabs). When using kind of 'Business Objects' (collecting data on AppServer and sending a bunch of data via DataSet or Temp-Table) delayed load of data can be too expensive. Later calls are time consuming.
PATTERN 5 – ADAPTER (STRUCTURAL PATTERN)
The adapter is often used in a project, when something changed and you need to make the existing system running with a new subsystem. Examples for this are new scanning hardware, switching from Google Maps to OpenStreetMap, switching from Sonic ESB to Apache ESB, using Business Entities from multiple vendors...
- Allowing change of subsystems/libraries
- Reusing objects
- Creating abstract application tiers (e.g. use adapter for Business Entities)
- Adopting 3rd party objects
- Making usage simpler (more convenient)
- (hide parts of API or adapt to project style. Look also for facade pattern.)
- More code
- Small run-time overhead
The OO feature of interfaces is a static feature but it allows to use various objects without dynamic code. This diagram shows classes included in a use case, where the client can use Google Maps or OpenStreetMap.
Level 1 Interface class: It defines every method of the adapters.
INTERFACE pattern_example.adapter2.IMaps:
METHOD PUBLIC CHARACTER GetAddress (dLat AS DECIMAL, dLng AS DECIMAL).
//...other method declarations
END INTERFACE.
Level 2 – Adapters: Next level are the adapters, in this case the adapter to Google.
CLASS pattern_example.adapter2.GoogleMapsAdapter IMPLEMENTS IMaps:
CONSTRUCTOR PUBLIC GoogleMapsAdapter ():
// Create session connect to Google Maps.
END CONSTRUCTOR.
METHOD PUBLIC CHARACTER GetAddress(INPUT dLat AS DEC, INPUT dLng AS DEC):
// Call method in Google Maps...
RETURN "The address found by GOOGLE".
END METHOD.
// ...other
END CLASS.
Level 3 – The client: The constructor loads one of the adapters (Google, OSM). The call (mGetAdrFromCoord) will call the method in the adapter, it does not matter which one.
CONSTRUCTOR PUBLIC MyGeoApi ( INPUT cSearchTool AS CHARACTER ):
DEFINE VARIABLE oMapApi AS pattern_example.adapter2.IMaps.
CASE cSearchTool:
WHEN "OSM" THEN DO:
oMapApi = NEW OpenMapsAdapter().
END.
// Google is default
OTHERWISE DO:
oMapApi = NEW GoogleMapsAdapter().
END.
END.
END CONSTRUCTOR.
METHOD PUBLIC CHARACTER mGetAdrFromCoord (dLat AS DECIMAL, dLng AS DECIMAL):
RETURN oMapApi:GetAddress(dLat, dLng).
END METHOD.
It is easy to see that multiple adapters can exist, hiding real calls and call interfaces.
The idea of an adapter is very common. It is a practical approach to simplify API/interface access. It was used in OpenEdge prior to OO, but OO makes it much more elegant.