allmhuran Guest
|
Posted: Sun Oct 26, 2008 4:46 am Post subject: Design involving composition of inherited members. |
|
|
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.
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.
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.
I thought a good pattern would be to have a Procedure class. Each
specific API call would be inherited from the base Procedure class.
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.
OK, so instead let's say I don't put the request and response in the
base class Procedure, and instead declare a request and response of
the appropriate type specifically in each class derived from
procedure. The problem now is that I haven't created a declarative
guarantee that any class derived from Procedure will declare members
derived from APIRequest and APIResponse. Assumption: A good design
would have that guarantee, so this is a bad design.
Now, serialization and deserialization both require that the
serializer know the type of object being transformed. For
serialization either of the above designs is OK, since the request
member has been instantiated and the dot net method .getType() can be
called on an instantiated class. But deserialization is problematic:
the response member is not instantiated before deserialize is called,
deserialize instantiates the class. I am therefore finding it
difficult to design a nice, generic "Transmit" or "Execute" method: I
cant tell it to deserialize into a type of APIResponse, since the
actual type will be one derived from APIResponse, with different
members and therefore a different deserialization.
Any input would be appreciated. I can bang out several ways of doing
this which will actually work, but I'm a sucker for elegance and they
all seem to have some amount of ugliness and kludgery. |
|
H. S. Lahman Guest
|
Posted: Sun Oct 26, 2008 10:46 pm Post subject: Re: Design involving composition of inherited members. |
|
|
Responding to allmhuran...
| Quote: | 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...
| Quote: | 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.
| Quote: |
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.
| Quote: |
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.
| Quote: |
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@pathfindermda.com
Pathfinder Solutions
http://www.pathfindermda.com
blog: http://pathfinderpeople.blogs.com/hslahman
"Model-Based Translation: The Next Step in Agile Development". Email
info@pathfindermda.com for your copy.
Pathfinder is hiring:
http://www.pathfindermda.com/about_us/careers_pos3.php.
(888)OOA-PATH |
|