Re: Design involving composition of inherited members.



Responding to allmhuran...

Problem outline:
I am designing classes to work with an API. The API operates via XML
transmitted via http. Each API call involves sending a request and
receiving a response. There are several different types of call, each
with a different set of parameters which can be included with the
request. Each type of request returns a specific set of response
values in a well defined structure specific to that request.

Not a good idea. Presumably the API is hiding some complex behavior implementation (as opposed to simple data store access). The synchronous return of a value makes the API caller dependent on what that behavior does. For example, one cannot unit test the caller without having a correctly working implementation of whatever the API is hiding. (Stubbing for unit test to return the right values is just self-delusion; one is testing the test harness, not the caller.)

When the API behavior is done, it should send the results back to the client in a separate message. That allows the client to process the results separately from the processing that creates the API inputs. IOW, one wants a variation on callbacks. [Generally one uses APIs at the subsystem level. You might find the "Application Partitioning" category on my blog of interest for this sort of thing, particularly the interface model.]

Nonetheless, assuming the API architecture is a fait accompli...

For example, a request to get the details of an invoice takes a single
parameter (invoiceId) and returns fields related to the invoice (date,
amount, etc). A request to list the clients on the system takes a set
of optional filters, and returns an XML formatted set of clients and
their associated field values.

OK, you have a basic message format of {messageID, <input data packet>; returns <output data packet>}. The data packets may be XML strings or some other data structure and that will depend on messageID. Presumably the API is implemented as a Facade with a different method for each methodID.


There is one final gotcha: I would like to use XML Serialization (just
to get to know it). In dot NET, an XML document can be deserialized
into a class, where the class members are structured in such a way as
to match the XML structure. The deserialization process instantiates
the object. The key point being that we do not instantiate the object
and then pass it on as a receptacle.

Just because something is there does not mean it is a good idea to use it. B-) I rarely use XML and I don't know anything about the Serialization facility. But the phrase "members are structured ... to match the XML structure" scares the hell out of me. Individual classes aren't XML structures so it seems to me one would have to define attributes as nested data structures to emulate that. That would be a major no-no from an OOA/D perspective because object attributes are supposed to be scalar ADTs. I would expect something more along the lines of multiple objects in a Composite pattern to emulate an XML hierarchy. [Hierarchy itself is antithetical to OOA/D since the OO paradigm strives mightily to remove the sorts of hierarchical dependencies one had with functional decomposition.]

So my advice would be: Don't Do That! B-) Render unto OOA/D the things that are OO and render unto RAD processing the things that are XML. IOW, solve the customer problem with the right objects and then worry about how to pass data across subsystem (presumably distributed if one is using XML at all) boundaries.

However, given that you want to use it, I don't see a problem. The serialization applies to encoding/decoding the data packets. If the data packets' semantics are unique to each messageID, then each API method would know which particular serialization to use (i.e., it would the type of the particular data packet to serialize). Similarly, whatever API client context is invoking a particular API method will know what type of serialization will be needed to encode the data packets because it already understands the context rules of invoking the right API method.


I thought a good pattern would be to have a Procedure class. Each
specific API call would be inherited from the base Procedure class.

Uh-oh. This sounds like a function as a first class object. Another OOA/D no-no.


A Procedure class would contain an APIRequest class and an APIResponse
class to hold the values to be serialized and deserialized. For each
individual request and response, with their specific fields, I would
derive from the APIRequest and APIResponse base class. Since every
Procedure must have both a request and a response, I would add these
as members to the base Procedure class, with the types being the base
request and response classes.

abstract class Procedure {
APIRequest request;
APIResponse response;
}

The first problem with this is that if I execute a call and populate a
class derived from APIResponse (eg, ClientListResponse), I would have
to explicitly cast procedure.response to
(ClientListResponse)procedure.response before being able to access the
members. Assumption: Explicit casting would not be necessary in a good
design, so this is a bad design.

First, there is nothing inherently wrong with casting. The full code generators that process UML OOA models use them all the time. The tricky part of using casts is being sure one is casting the right thing. (When code generators use casts it is based on deterministic analysis of the model's relationships.)

However, if you are confident the cast is safe and will continue to be safe after any likely maintenance, there is nothing wrong with using a cast. For example, if you have an explicit messageID in hand that ALWAYS maps 1:1 to a specific data packet type, it would be quite safe to use a cast for the message data packet. That's because the fundamental architecture of the design and the problem space ensures 1:1 mapping.

[Caveat. I am talking about casting when one already knows what the type is from the context. Downcasting to determine the type is an entirely different beast and should be avoided. The need for downcasting invariably means that the relationships in the OOA/D model were incorrectly formed.]

Second, this seems overly elaborate for the problem. Let's say you have a C++ Facade for the API that looks like:

class APIFacade
{
public:
CustomerResponse* getCustomer (CustomerID id);
ClientListResponse* getClientList (FilterList* filters);
...
}

getClientList can decode filters in a type specific manner because only one possible input data packet is allowed for that messageID. Similarly, the client has to know that only one type of input data packet can be used for that messageID.

If FilterList is really an XML string, the method still knows exactly what type of XML decoding is necessary so it can invoke the serializing facility properly. OO abstraction allows one to utilize a specific type like FilterList for that parsing even though the actual data is always an XML string regardless of semantics. IOW, one uses the type system to map the context semantics to XML.

The only place where I would see a problem would be if one raised the API's level of abstraction once more step:

class APIFacade
{
public:
Response* procedure (RequestID id, Request* request);
}

so that there was only one API method. Presumably RequestID has a data domain of {CUSTOMER, CLIENT_LIST, ...}. Note the not-so-subtle mapping to your Procedure. B-)

Now one does need to do some casting within the method based on the value of id. [In a true Facade it would more likely simply re-displatch the message to a particular object that understands the RequestID context based on a jump table. That would avoid a monolithic switch statement method doing all the processing.]

The onus is now on the caller to provide the right RequestID with the Request. But it has to encode the right Request data anyway, so that should be fairly foolproof. (In fact, it is quite likely that the caller already has some decision variable it uses to pick the right encoding, so one could provide a lookup table between that decision variable and the RequestID.)

The real point here, vis a vis your desire to use XML serialization, is that the serialization is an implementation mechanism for encoding/decoding XML from/to objects. As such it is hidden from the basic API structure in the method implementations (think: factory object for message data packets).



--
There is nothing wrong with me that could
not be cured by a capful of Drano.

H. S. Lahman
hsl@xxxxxxxxxxxxxxxxx
Pathfinder Solutions
http://www.pathfindermda.com
blog: http://pathfinderpeople.blogs.com/hslahman
"Model-Based Translation: The Next Step in Agile Development". Email
info@xxxxxxxxxxxxxxxxx for your copy.
Pathfinder is hiring: http://www.pathfindermda.com/about_us/careers_pos3.php.
(888)OOA-PATH
.



Relevant Pages

  • constructing an (xml) request properly for http post to an api
    ... would like help in constructing the (xml) request properly ... if one scrolls to the bottom of the emaillabs api overview ...
    (comp.lang.ruby)
  • Re: IIS returns status code 200 even if XML is invalid
    ... ASMX requests are handled by ASP.NET ... when there is a problem with an XML being sent to the server ... The request is not being handled by the "designated method" which ... That HTTP 1.1 200 OK return code means the the HTTP request/connection ...
    (microsoft.public.dotnet.framework.webservices)
  • Re: FAQ 3.19 How can I make my CGI script more efficient?
    ... you know you'll need as well (to thwart off overhead for future AUTOLOAD ... When using the mod_perl API, it's a lot easier to write test cases too, just ... implement the required portions of the API as a "dummy request". ... I just wish perl would "come back" to where it used to be, ...
    (comp.lang.perl.misc)
  • Re: D3 Connectivity Demos Download
    ... 2002 IIRC and I dumped the sample VB API project shortly after then due to ... originally based on an ASCII protocol with a standard command set. ... grouped attributes of 3 values each for the client code to pick apart. ... It's not really different than calling an XML shipping API that contains ...
    (comp.databases.pick)
  • Re: FAQ/FAQ notes site makeover
    ... VK suggested and XML procedure. ... Randy Webb wrote: ... easy to add a script-driven request generator with each topic having ... possibly that FAQ topics will not accomodate all and every accumulated ...
    (comp.lang.javascript)