The next stop on our voyage through the Design Patterns galaxy takes us to the Flyweight design pattern , another of the design patterns found in the GoF catalog. We will find this design pattern useful in optimizing components.
Imagine you live in Procedureton and work in Objectropolis, with about 20 kilometers distance separating the two towns. Now imagine your commute between work and home is facilitated by a roadway with a lane dedicated exclusively to your use; no other commuters can use your lane. This would be a boon to those of us who have ever dealt with delays due to heavy traffic or anxiety due to aggressive commuting behaviors. We could arrive and leave work at any time we choose without regard to the commuting habits of others. No longer would we suffer the stress of a tailgating commuter encroaching upon us from behind and creating a dangerous situation.
Now imagine all the residents of Procedureton also work in Objectropolis, and each one of them also has their own dedicated lane of roadway for their exclusive use. The benefits of such an arrangement would be many, but unfortunately this is completely impractical. Aside from the cost to build such a roadway, which would be prohibitive, it would also allocate all the land between the two towns for the sole purpose of surface transportation between them.
In the real world, we are more pragmatic with such matters. We avoid the exorbitant cost and wasteful use of land resources toward building and maintaining such a utopian arrangement by sharing, with all other commuters, a single roadway with perhaps only one lane dedicated for travel in each direction, and, as a consequence, enduring some inconvenience once in a while.
This same concept applies to programming. Some objects require the use of other objects to perform services. Though conceivable for one object to “own” another object, thus restricting it to servicing only the needs of the owning object, we can reap the benefits of improved performance and reduced resource consumption by having fewer of these service objects shared amongst all the objects requiring their services.
Sharing Resources
The Flyweight design pattern reduces the number of objects required for processing by arranging for many objects to share some of the other objects they require instead of each one having its own objects.
The Flyweight design pattern is categorized by GoF with a structural purpose and an object scope. The intent behind this design pattern is the following:
Use sharing to support large numbers of fine-grained objects efficiently. 1
The definitive word of the intent is sharing. In order to facilitate sharing, a shared object needs to be designed in such a way that eliminates any attribute values that would prevent it from being used by more than one other object. Attribute values that can be shared become known as intrinsic data and can reside in the shared object because the value is relevant to all sharing objects. Those values that cannot be shared constitute extrinsic data and must be passed to the shared object by each sharing object with each call for services.
Flyweight makes use of these participants2 working in collaboration with each other:
Flyweight: Declares an interface through which flyweights can receive and act on extrinsic state.
ConcreteFlyweight: Implements the Flyweight interface and adds storage for intrinsic state, if any. A ConcreteFlyweight object must be shareable. Any state it stores must be intrinsic; that is, it must be independent of the ConcreteFlyweight object's context.
UnsharedConcreteFlyweight: Not all Flyweight subclasses need to be shared. The Flyweight interface enables sharing; it doesn't enforce it. It's common for UnsharedConcreteFlyweight objects to have ConcreteFlyweight objects as children at some level in the flyweight object structure.
FlyweightFactory: Creates and manages flyweight objects. Ensures that flyweights are shared properly. When a client requests a flyweight, the FlyweightFactory object supplies an existing instance or creates one, if none exists.
Client: Maintains a reference to flyweight(s) . Computes or stores the extrinsic state of flyweight(s).
The UML class diagram for the Flyweight design pattern is shown in Figure 24-1.
Figure 24-1. UML class diagram for the Flyweight design pattern
Notice that the FlyweightFactory participant retains a pool of instances of ConcreteFlyweight and UnsharedConcreteFlyweight participants, each of which uses the interface provided by the Flyweight participant. The implementation for its getFlyweight method will determine whether or not an instance of a flyweight by the requested key value already exists, and if one does not exist, will create a new flyweight instance using that key value and add it to the flyweight pool , returning to the caller the new or existing flyweight instance.
Notice also that the ConcreteFlyweight participants retain attributes representing intrinsic state, representing information that can be shared amongst all its users, but that it accepts extrinsic state information through the method signature of the public operation method, requiring all users to provide any information that cannot be shared by all its users.
The UML class diagram for the Flyweight design pattern describing the practical means by which to find the best route for commuting between Procedureton and Objectropolis is shown in Figure 24-2.
Figure 24-2. UML class diagram for the Flyweight design pattern applied to a best route scenario
Although we may have many buses simultaneously moving passengers between Procedureton and Objectropolis, each bus does not require its own unique bestRouteIdentifier object. We need a maximum of only two instances of the bestRouteIdentifier class, which could be shared by all of the buses: one describing the best route to Procedureton and the other describing the best route to Objectropolis.
Flyweight in ABAP
Here is how we might implement the Flyweight design pattern UML class diagram illustrated in Figure 24-2 into functioning ABAP code. First, shown in Listing 24-1, is the definition of the vehicle class. We do not show the full definition and implementation of this class since that level of information is not pertinent to the operation of the Flyweight pattern, instead merely indicating that its definition would precede the definition of other classes that will reference it.
Listing 24-1. Class vehicle
class vehicle definition.
o
o
endclass.
Listing 24-2 shows the abstract class best_route_identifier, playing the role of the Flyweight participant and defining an abstract method follow_route and accepting an instance of vehicle to follow the route.
Listing 24-2. Abstract class best_route_identifier
class best_route_identifier definition abstract.
public section.
methods : follow_route abstract
importing route_follower type ref to vehicle.
endclass.
Notice the absence of any attributes that would prevent instances of this class from being shared amongst multiple vehicles.
Listing 24-3 shows two subclasses, best_route_to_procedureton and best_route_to_objectropolis, inheriting from class best_route_identifier, each playing the role of the ConcreteFlyweight participant. Each one provides an implementation for the abstract follow_route method, inherited from the superclass, providing the best route to its respective destination.
Listing 24-3. Subclasses Inheriting from best_route_identifier
class best_route_to_procedureton definition
inheriting from best_route_identifier.
public section.
methods : follow_route redefinition.
endclass.
class best_route_to_procedureton implementation.
method follow_route.
statements go here indicating best route to Procedureton
endmethod.
endclass.
class best_route_to_objectropolis definition
inhering from best_route_identifier.
public section.
methods : follow_route redefinition.
endclass.
class best_route_to_objectropolis implementation.
method follow_route.
statements go here indicating best route to Objectropolis
endmethod.
endclass.
Listing 24-4 shows the static route_identifier_factory class with its public static method get_best_route. This class plays the role of the FlyweightFactory participant. Notice that its implementation of this public method searches for an existing entry in the best_route_identifier_pool, and if one is not found for the desired destination, an object of type best_route_identifier is created and is used to populate a new entry in the best_route_identifier_pool.
Listing 24-4. Static Class best_route_identifier_factory
class best_route_identifier_factory definition abstract final.
public section.
class-methods: get_best_route
importing desired_destination
type string
exporting best_route
type ref to best_route_identifier.
private section.
types : begin of route_identifier_entry
, destination type string
, best_route type ref to best_route_identifier
, end of route_identifier_entry
.
class-data : best_route_identifier_pool
type standard table of route_identifier_entry.
class-methods: create_best_route_instance
importing destination
type string
exporting best_route
type ref to best_route_identifier.
endclass.
class best_route_identifier_factory definition.
method get_best_route.
data : route_identifier
like line of best_route_identifier_pool.
read table best_route_identifier_pool
into route_identifier
with key destination = desired_destination.
if route_identifier-destination is initial.
route_identifier-destination = desired_destination.
call method create_best_route_instance
exporting destination = desired_destination
importing best_route = route_identifier-best_route.
append route_identifier
to best_route_identifier_pool.
endif.
best_route = route_identifier-best_route.
endmethod.
method create_best_route_instance.
statements go here to create new instance of best_route_identifier
subclass to facilitate travel to the specified destination
endmethod.
endclass.
Notice the definition of the pool of flyweights holding various instances of best_route_identifiers, each one accompanied by the destination for which the flyweight instance represents the best route. This means that vehicle instances that would make a request to best_route_identifier_factory to get the best route to its destination will all share the same set of best_route_identifiers; none of them are associated with any specific vehicle instance.
Listing 24-5 shows the definition of the bus class , playing the role of the Client participant, using the best_route_identifier_factory.
Listing 24-5. Class bus
class bus definition inheriting from vehicle.
public section.
methods : move_passengers.
private_section
methods : load_passengers.
endclass.
class bus implementation.
method move_passengers.
data : fastest_path type ref to best_route_identifier.
call method me->load_passengers.
call method best_route_identifier_factory=>get_best_route
exporting desired_destination = 'Objectropolis'.
importing best_route = fastest_path.
call method fastest_path->follow_route( me ).
endmethod.
method load_passengers.
statements go here indicating how to load passengers
endmethod.
endclass.
Notice that method move_passengers of the bus class invokes method get_best_route of static class best_route_identifier_factory, passing the name of the destination and receiving an instance of best_route_identifier holding the best path to that destination. It then invokes method follow_route of the instance best_route_identifier, passing itself via the me current instance reference to the bus. Any vehicle instances that call method get_best_route of class best_route_identifier_factory using the same destination will receive a reference to the very same instance of best_route_identifier as all other vehicle instances making this call.
Summary
In this chapter, we learned about intrinsic and extrinsic information, and that we can reduce the number of objects required in a design if we can create classes that retain only intrinsic information, leaving it to users of the class to provide whatever extrinsic information might be necessary. This pattern provides us with another way in which we can share objects among many users, utilize resources more efficiently, reduce storage consumption, and optimize the performance of the components we require.
Flyweight Exercises
Refer to Chapter 21 of the functional and technical requirements documentation (see Appendix B) for the accompanying ABAP exercise programs associated with this chapter. Take a break from reading the book at this point to reinforce what you have read by changing and executing the corresponding exercise programs. The exercise programs associated with this chapter are those in the 314 series: ZOOT314A through ZOOT314C.