Home > Articles > Programming > Windows Programming

This chapter is from the book

Intent

Provide an abstract container used to pass parameters to any objects within a framework. This will also serve to package discrete parameters into a single and more efficient data-marshaling package.

Problem

Whether you are working with Web services or any publicly available business function, eliminating unnecessary data traffic is important. Most server-based services will take a variety of parameters to perform any one business function. Variables anywhere from strings to bytes to arrays will need to be passed to these business services, and they should be passed in the most efficient manner. Most object-oriented designs call for some form of encapsulation. This may be data objects providing “accessors” (getters) and “mutators” (setters) used to interact with the business data. Interaction with these objects occurs through “hydration” using mutators or through “extraction” using accessors. This type of multiple round trip get/set interaction is fine when an object is local or when the interaction is simple. Multiple gets and sets across the network would not be good. In general, where this scenario falls short is when the objects involved are separated by some form of boundary.

Boundaries come in many forms. There are network boundaries, process boundaries, domain boundaries (.NET), and storage boundaries (I/O), etc. The problem is that as a developer, interacting with objects using several round trips to set and get data can become a problem—a performance problem. This is especially apparent when calling business objects across network boundaries where each get or set call adds a significant performance hit to the overall transaction. Aside from being wasteful in network usage, it also forces the server object to maintain state in between accessor/mutator invocations. In some cases, holding state may be necessary but this should be used carefully.

An option in avoiding multiple round trips during object interaction is to pass all parameters into the method at once. For some designs, this is perfectly fine. In those cases, the parameter list may include only one or two data elements that you must pass to an object. Most of time, however, this is not sufficient. One or two parameters can quickly become three, four, five, and many more. Maintaining business methods with long parameter lists (although done) is not recommended. This is where the Abstract Packet comes into play. It is simply a container for those parameters. It is a generic container with the ability to hold as many parameters as are necessary to facilitate any business object, as long as that business can receive that packet's data type (Figure 4.9). This also simplifies the signature of most business methods because now a business method can be typed with a single parameter. This also applies to return values. The return value can be of the same type, as long as that type is generic enough to contain data that will be returned from any business function.

04fig09.gifFigure 4.9. Abstract Packet implementation class diagram.

Forces

Use the Abstract Packet pattern when:

  • Web services will be used that contain more than two or three parameters.

  • Business functions need to contain a standard signature contract to isolate future changes.

  • Parameter types change frequently for business methods.

  • Working with services crossing expensive boundaries (process, network, etc.).

Structure

04fig10.gifFigure 4.10. Abstract Packet generic class diagram.

Consequences

  1. Improves the parameter-passing efficiency. When all parameters are bundled into one object, the developer will have more control on how to marshal those parameters. This includes any serialization that may take place. This also provides a controlled means of retrieving or unpackaging those parameters within the containing object.

  2. Provides a container in which to build dynamic parameter sets. For those scenarios where the set of parameters can vary frequently, the Abstract Packet provides the container in which to build such a facility. In our example, the Packet class simply aggregates an already dynamic data type in the form of an ADO.NET DataSet object. Using the DataSet data member, the Packet can take any shape and contain any data. As long as the business method that receives this packet knows how to interact with it, the packet can be used throughout the framework. The packet then can contain any data and be passed to each tier of the framework without implementing specific behavior for each representation of the packet. Only the business method that must directly interact with the packet's data must know what data elements it requires. For example, at this stage, the business method would call the packet's GetData() and request specific fields from the packet. The packet, in turn, delegates the lookup to the aggregated DataSet. To the rest of the system, this is just a generic packet.

    Look Ahead

    Another option to this pattern is to bind a “type-strong” Data Service object that will be a child of a DataSet and, thus, can also be bound to the packet when the packet is built or translated. This new option provides a type-strong DataSet that any business method wishing to interact with the packet can use instead of using the packet's delegation methods. Using a type-strong DataSet is one way to avoid boxing/unboxing and can improve performance. Not to mention that it provides a much friendlier development environment for Visual Studio .NET users, especially those who love Intellisense. Using a type-strong DataSet will be fully discussed in the Chapter 5.

  3. Eliminates the binding of business methods to technology-specific data types, such as those in ADO.NET (DataSet). This simply avoids forcing business methods from including ADO.NET types in their signatures and provides another level of abstraction.

  4. Hides the implementation details of the aggregated inner type (a DataSet, in this case). Business methods, even those directly interacting with the data, do not require any of the details for manipulating types such as a DataSet. Those services directly interacting with the packet can simply use the methods provided by the packet. Methods such as GetData() require only parameters such as a field name to retrieve the underlying information. Keep in mind that a DataSet does not have to be bound to an actual database; a field name can be just a name of a column from the DataSet that could have been generated dynamically. As mentioned earlier in the Look Ahead sidebar, there is also another means of interaction (see Chapter 5).

Participants

  • Packet (Same name as implementation)— This is the Abstract Packet itself. This class acts as a form of “flyweight” in that its job is to contain data that can be shared efficiently with the rest of the system. It will act as a container of both extrinsic (passed-in) and intrinsic (static) data used by the rest of the system to route a request and perform an action.

  • Packet Translator (same)— This includes any packet translation that constructs a packet using an overloaded method called Translate(). The Translator constructs the destination object and maps the appropriate values into the new data object. This construction and translation logic is business-specific. The goal is to simplify and abstract this logic. The client does not know or care how the construction or translation takes place or which Translate method to call. The client simply invokes Translate(), and the overloaded method takes care of invoking the appropriate method based on the type passed. Refer to the Packet Translator section later in this chapter for details.

  • DataSet (same)— This is a standard ADO.NET DataSet object. This can represent any data schema, whether it is based on a persistent data model or not. This becomes the actual data container with a callable wrapper. This wrapper is the Packet class. The packet holds descriptive data elements to identify the packet, which can then be used for routing or other logic. Any other generic container such as an ArrayList, object[], etc., can be used as well.

  • ProductDataSet (CreditCardDS)— This is the business-specific data services class (data access object). This is a classic data services object that directly represents a view on a database or other persistent set. This class is strongly typed to the specific data elements of a particular business service or database. It inherits from a DataSet to gain any ADO.NET features, such as serialization and XML support, to allow it initialize or extract the data once it is hydrated.

Implementation

The Abstract Packet was implemented primarily to aggregate a DataSet. In fact, the DataSet type in .NET can be used as an Abstract Packet with and of itself. For a technology backgrounder on ADO.NET and DataSets in particular, please refer to Chapter 5. Those already familiar with DataSets will understand that a DataSet is a generic object that can hold just about any data representation in memory. A DataSet can be dynamically built and hydrated from a database or, as is the case in this example, be hydrated from an XSD schema. The beauty of our Abstract Packet example is the fact that it does not “reinvent the wheel.” The Packet class does not try to duplicate functionality that a DataSet already provides. It simply delegates to it and acts as an aggregator of an existing Dataset. The other data members of the Packet class are simply used to identify the packet for use by the architecture as this packet gets passed from service to service.

To build a packet, one must first have a DataSet object. In our example, a Web service receives and returns the DataSet type. When a DataSet is passed into our Web service, it instantiates and builds an appropriate Packet object. The building step can become complex and, therefore, should be farmed out to another service, such as a Packet Translator (covered later in this book). The primary step in building a packet is simply to set the DataSet as a data member of the packet. This is done using the Data property of the packet. The remaining properties of the packet are optional. More properties can be added to the packet as needed by the business requirements. The point is that the DataSet, now a member of the packet, still contains most of the data. When data needs to be extracted from a packet, its GetData methods or indexers can then be called, which delegates to the DataSet. The Packet class can now become the primary parameter passed to all business methods. This is similar to the functionality of an Adapter Pattern (GoF).

A DataSet could have been passed instead, but using a Packet class provides another level of abstraction. This abstraction will safeguard those methods from change and provide a high-level interface to those services that may not need to know how to manipulate a DataSet directly. The DataSet can be as simple as representing a single table or as complex as representing an entire database with constraints and all. By using a DataSet, all data can be treated as though directly contained within an actual database. This is true even if the DataSet is strictly represented in memory. Within the Packet class, methods can be designed to manipulate the DataSet in any way it sees fit. One caveat to this particular implementation, however, is the fact that the Packet class does not contain any type-specific methods. For example, each overloaded SetData() method takes an object as one of its parameters. Although this facilitates setting any data type of any field in the DataSet, this also introduces what .NET refers to as boxing. It is recommended that for performance-intensive implementations, type-specific methods should be created to avoid this side effect.

Technology Backgrounder—Boxing/Unboxing

Those already familiar with details behind value types, reference types, and the process of boxing/unboxing can skip this section. For those wanting more information, read on.

In the .NET CLR, you have two general types: value types and reference types. Value and reference are similar in that they both are objects. In fact, everything in the CLR is an object. Even value types are objects in that they have the System.ValueType as a parent class, which has System.Object as its parent. Each primitive type is represented by an equivalent class. For example, the primitive types of int and long in C# both alias the System.Int32 and System.Int64 classes, respectively, both of which have System.ValueType as parent. Other value types include structs and enumerations (enums). If it inherits from System.ValueType, it is treated as a value type in the CLR.

Value types are handled a bit differently than reference types in that they are passed by value. Passing by value means that a copy of the value is made prior to calling the function. For most value types, the cost of making this copy is small and usually outweighs the performance issues that arise when dealing with reference types. Value types represent a value that is allocated on the stack. They are never null and must contain data. Any custom value type can be created simply by deriving from System.ValueType. When creating your own value types, however, keep in mind that a value type is sealed, meaning that no one else can derive from your new type.

Reference types are based on the heap and can contain null values. They include types such as classes, interfaces, and pointers. These types are passed by reference, meaning that when passed, the address of the object (or pointer) is passed into the function. No copy is made. Unlike value types, when you make a change, the original value is changed, as well, because you are now dealing with a pointer. Reference types can be used when output parameters are required or when a type consumes a significant chunk of memory (remember that structs are value types, and they can grow quite large). However, they also must be managed by the CLR. This means that they must be kept track of and garbage collected. This also will add a performance penalty. Value types should be used wherever possible to improve performance and to conserve memory. If your object consumes a lot of memory, a reference type should be used, bearing in mind that any destruction or finalization of your type is going to be nondeterministic.

Once you understand the technical differences of how value types and reference types are treated, you will understand how unboxing and boxing work. Values types can become reference types, and the opposite is true as well. This can be forced or this can be automatic. The CLR will automatically convert a value type into a reference type whenever needed. This is called boxing. Boxing refers to converting a stack-allocated value into a heap-based reference type. An example of this would the following:

int nFoo = 1;// nFoo is a value type
object oBar = nFoo;     // oBar is a reference type of type
// System.Object

Here, a box is created and the value of nFoo is copied into it. To translate, heap space is allocated, and the value of nFoo is copied into that memory space and now must be temporarily managed. When a value is boxed, you receive an object upon which methods can be called, just like any other System.Object type (e.g., ToString(), Equals(), etc.). The reverse of this process is called unboxing, which is the just the opposite. A heap-based object is converted into its equivalent stack-based value type, such as:

int nFoo = (int)oBar;// oBar is a reference type

Unboxing and boxing, although convenient, can also become a small performance bottleneck and should be used with care. For methods that will be called extremely often, as will our Packet data object, using a System.Object type as a parameter where value types will be expected should anticipate a low performance. This is due to boxing. Methods such as these can be changed to support a System.ValueType but you must also create methods to except other types, including strings (which, by the way, are not value types).

Most of the methods and indexers defined in this class delegate to the Data property. The Data property simply returns the m_dsRawData member variable of this class (which is the DataSet we are wrapping). The Packet class uses this property to delegate most of the calls to the wrapped DataSet to return data, set data, and so on. This uses the DataSet for the heavy lifting. Wrapping the DataSet in this aspect gives the Abstract Packet its “Adapter” qualities, allowing it to be passed to all business services that accept a Packet data type. Listing 4.8 contains code for a typical Abstract Packet implementation.

Listing 4.8 Typical Abstract Packet implementation.

    public class Packet : IDisposable
    {
          private DataTableCollection m_oData = null;
          private DataTable m_dtMeta = null;

          private DataSet m_dsRawData = null;

          private string m_sType;
          private string m_sService;
          private string m_sAction;

          private PacketTranslator m_oTranslator = null;

          public Packet()
          {
             RawData = new DataSet();
             Translator = new PacketTranslator();
          }

          public Packet(PacketTranslator oTranslator) : this()
          {
             m_oTranslator = oTranslator;
          }

          public static bool operator == (Packet p1, Packet p2)
          {
             if ((object)p1 == null && (object)p2 == null)
                return true;
             if ((object)p1 != null && (object)p2 == null)
                return false;
             else
                return p1.Equals((Packet)p2);
          }

          public static bool operator != (Packet p1, Packet p2)
          {
             if ((object)p1 == null && (object)p2 == null)
                return false;
             if ((object)p1 != null && (object)p2 == null)
                return true;
             else
                return !p1.Equals((Packet)p2);
          }

          public DataSet RawData
          {
             get { return m_dsRawData; }
             set { m_dsRawData = value; }
          }

          public DataTableCollection Data
          {
             get { return m_oData; }
             set { m_oData = value; }
          }

          public DataTable Meta
          {
             get { return m_dtMeta; }
             set { m_dtMeta = value; }
          }

          public string Type
          {
             get { return m_sType; }
             set { m_sType = value; }
          }

          public string Action
          {
             get { return m_sAction; }
             set { m_sAction = value; }
          }

          public string Service
          {
             get { return m_sService; }
             set { m_sService = value; }
          }

          public PMPacketTranslator Translator
          {
             get { return m_oTranslator; }
             set { m_oTranslator = value; }
          }

          public string TransId
          {
             get { return m_sTransId; }
             set { m_sTransId = value; }
          }

          // assume first table in collection
          public object GetData(string sColumn)
          {
             DataRow dr = null;
             object oReturn = null;

             if (Data[0].Rows.Count > 0)
             {
                dr = Data[0].Rows[0];
                oReturn = dr[sColumn];
                if (oReturn == System.DBNull.Value)
                   oReturn = null;
             }
             return oReturn;
          }

          public object GetData(string sTable, string sCol)
          {
             return GetData(sTable, sCol, 0);
          }

          public object GetData(string sTable, string sCol, int nRow)
          {
             DataRow dr = null;
             object oReturn = null;

             if (Data[sTable].Rows.Count > 0)
             {
                dr = Data[sTable].Rows[nRow];
                oReturn = dr[sCol];

                if (oReturn == System.DBNull.Value)
                   oReturn = null;
             }
             return oReturn;
          }

          public object[] GetRowData(string sTable, int nRow)
          {
             object[] oRowArray = null;

             if (Data[sTable].Rows.Count > 0)
             {
             oRowArray = Data[sTable].Rows[nRow].ItemArray;
             }

             return oRowArray;
          }

public void SetRow(string sTable, int nRow, object[] oaRow)
          {
             Data[sTable].Rows[nRow].ItemArray = oaRow;
          }

          public void SetData(string sCol, object oVal)
          {
             DataRow dr = null;

             if (Data[0].Rows.Count > 0)
             {
             dr = Data[0].Rows[0];
             dr[sCol] = oVal;
             }
          }

public void SetData(string sTable, string sCol, object oVal)
          {
             SetData(sTable, sCol, 0, oVal);
          }

public void SetData(string sTable, string sCol, int nRow, object oValue)
          {
             DataRow dr = null;

             if (Data[sTable].Rows.Count > 0)
             {
                dr = Data[sTable].Rows[nRow];
                dr[sColumn] = oValue;
             }
          }

          public string this[string sColumn]
          {
             get
             {
                return Convert.ToString(GetData(sColumn));
             }
             set
             {
                SetData(sColumn, value);
             }
          }

          public string this[string sTable, string sColumn]
          {
             get
             {
return Convert.ToString(GetData(sTable, sColumn));
             }
             set
             {
                SetData(sTable, sColumn, value);
             }
          }

          public string this[string sTable, string sColumn, int nRow]
          {
             get
             {
                return Convert.ToString(GetData(sTable, sColumn, nRow));
             }
             set
             {
                SetData(sTable, sColumn, nRow, value);
             }
          }

          public static string SafeCastString(object oValue)
          {
             string sReturn;

             if (oValue != null)
                sReturn = (string)oValue;
             else
                sReturn = "";

             return sReturn;
          }

          public static decimal SafeCastDecimal(object oValue)
          {
             decimal dReturn;

             if (oValue != null)
                dReturn = (decimal)oValue;
             else
                dReturn = (decimal)0.0;

             return dReturn;
          }

          ...

          public void Dispose()
          {
             RawData.Dispose();
          }

    }
}

Related Patterns

  • Value Object (Alur, Crupi, Malks)

  • Adapter (GoF)

  • Composite (GoF)

InformIT Promotional Mailings & Special Offers

I would like to receive exclusive offers and hear about products from InformIT and its family of brands. I can unsubscribe at any time.

Overview


Pearson Education, Inc., 221 River Street, Hoboken, New Jersey 07030, (Pearson) presents this site to provide information about products and services that can be purchased through this site.

This privacy notice provides an overview of our commitment to privacy and describes how we collect, protect, use and share personal information collected through this site. Please note that other Pearson websites and online products and services have their own separate privacy policies.

Collection and Use of Information


To conduct business and deliver products and services, Pearson collects and uses personal information in several ways in connection with this site, including:

Questions and Inquiries

For inquiries and questions, we collect the inquiry or question, together with name, contact details (email address, phone number and mailing address) and any other additional information voluntarily submitted to us through a Contact Us form or an email. We use this information to address the inquiry and respond to the question.

Online Store

For orders and purchases placed through our online store on this site, we collect order details, name, institution name and address (if applicable), email address, phone number, shipping and billing addresses, credit/debit card information, shipping options and any instructions. We use this information to complete transactions, fulfill orders, communicate with individuals placing orders or visiting the online store, and for related purposes.

Surveys

Pearson may offer opportunities to provide feedback or participate in surveys, including surveys evaluating Pearson products, services or sites. Participation is voluntary. Pearson collects information requested in the survey questions and uses the information to evaluate, support, maintain and improve products, services or sites, develop new products and services, conduct educational research and for other purposes specified in the survey.

Contests and Drawings

Occasionally, we may sponsor a contest or drawing. Participation is optional. Pearson collects name, contact information and other information specified on the entry form for the contest or drawing to conduct the contest or drawing. Pearson may collect additional personal information from the winners of a contest or drawing in order to award the prize and for tax reporting purposes, as required by law.

Newsletters

If you have elected to receive email newsletters or promotional mailings and special offers but want to unsubscribe, simply email information@informit.com.

Service Announcements

On rare occasions it is necessary to send out a strictly service related announcement. For instance, if our service is temporarily suspended for maintenance we might send users an email. Generally, users may not opt-out of these communications, though they can deactivate their account information. However, these communications are not promotional in nature.

Customer Service

We communicate with users on a regular basis to provide requested services and in regard to issues relating to their account we reply via email or phone in accordance with the users' wishes when a user submits their information through our Contact Us form.

Other Collection and Use of Information


Application and System Logs

Pearson automatically collects log data to help ensure the delivery, availability and security of this site. Log data may include technical information about how a user or visitor connected to this site, such as browser type, type of computer/device, operating system, internet service provider and IP address. We use this information for support purposes and to monitor the health of the site, identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents and appropriately scale computing resources.

Web Analytics

Pearson may use third party web trend analytical services, including Google Analytics, to collect visitor information, such as IP addresses, browser types, referring pages, pages visited and time spent on a particular site. While these analytical services collect and report information on an anonymous basis, they may use cookies to gather web trend information. The information gathered may enable Pearson (but not the third party web trend services) to link information with application and system log data. Pearson uses this information for system administration and to identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents, appropriately scale computing resources and otherwise support and deliver this site and its services.

Cookies and Related Technologies

This site uses cookies and similar technologies to personalize content, measure traffic patterns, control security, track use and access of information on this site, and provide interest-based messages and advertising. Users can manage and block the use of cookies through their browser. Disabling or blocking certain cookies may limit the functionality of this site.

Do Not Track

This site currently does not respond to Do Not Track signals.

Security


Pearson uses appropriate physical, administrative and technical security measures to protect personal information from unauthorized access, use and disclosure.

Children


This site is not directed to children under the age of 13.

Marketing


Pearson may send or direct marketing communications to users, provided that

  • Pearson will not use personal information collected or processed as a K-12 school service provider for the purpose of directed or targeted advertising.
  • Such marketing is consistent with applicable law and Pearson's legal obligations.
  • Pearson will not knowingly direct or send marketing communications to an individual who has expressed a preference not to receive marketing.
  • Where required by applicable law, express or implied consent to marketing exists and has not been withdrawn.

Pearson may provide personal information to a third party service provider on a restricted basis to provide marketing solely on behalf of Pearson or an affiliate or customer for whom Pearson is a service provider. Marketing preferences may be changed at any time.

Correcting/Updating Personal Information


If a user's personally identifiable information changes (such as your postal address or email address), we provide a way to correct or update that user's personal data provided to us. This can be done on the Account page. If a user no longer desires our service and desires to delete his or her account, please contact us at customer-service@informit.com and we will process the deletion of a user's account.

Choice/Opt-out


Users can always make an informed choice as to whether they should proceed with certain services offered by InformIT. If you choose to remove yourself from our mailing list(s) simply visit the following page and uncheck any communication you no longer want to receive: www.informit.com/u.aspx.

Sale of Personal Information


Pearson does not rent or sell personal information in exchange for any payment of money.

While Pearson does not sell personal information, as defined in Nevada law, Nevada residents may email a request for no sale of their personal information to NevadaDesignatedRequest@pearson.com.

Supplemental Privacy Statement for California Residents


California residents should read our Supplemental privacy statement for California residents in conjunction with this Privacy Notice. The Supplemental privacy statement for California residents explains Pearson's commitment to comply with California law and applies to personal information of California residents collected in connection with this site and the Services.

Sharing and Disclosure


Pearson may disclose personal information, as follows:

  • As required by law.
  • With the consent of the individual (or their parent, if the individual is a minor)
  • In response to a subpoena, court order or legal process, to the extent permitted or required by law
  • To protect the security and safety of individuals, data, assets and systems, consistent with applicable law
  • In connection the sale, joint venture or other transfer of some or all of its company or assets, subject to the provisions of this Privacy Notice
  • To investigate or address actual or suspected fraud or other illegal activities
  • To exercise its legal rights, including enforcement of the Terms of Use for this site or another contract
  • To affiliated Pearson companies and other companies and organizations who perform work for Pearson and are obligated to protect the privacy of personal information consistent with this Privacy Notice
  • To a school, organization, company or government agency, where Pearson collects or processes the personal information in a school setting or on behalf of such organization, company or government agency.

Links


This web site contains links to other sites. Please be aware that we are not responsible for the privacy practices of such other sites. We encourage our users to be aware when they leave our site and to read the privacy statements of each and every web site that collects Personal Information. This privacy statement applies solely to information collected by this web site.

Requests and Contact


Please contact us about this Privacy Notice or if you have any requests or questions relating to the privacy of your personal information.

Changes to this Privacy Notice


We may revise this Privacy Notice through an updated posting. We will identify the effective date of the revision in the posting. Often, updates are made to provide greater clarity or to comply with changes in regulatory requirements. If the updates involve material changes to the collection, protection, use or disclosure of Personal Information, Pearson will provide notice of the change through a conspicuous notice on this site or other appropriate way. Continued use of the site after the effective date of a posted revision evidences acceptance. Please contact us if you have questions or concerns about the Privacy Notice or any objection to any revisions.

Last Update: November 17, 2020