| Register | FAQ | Calendar | Search | Today's Posts | Mark Forums Read |
|
#1
| |||
| |||
| 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. |
|
#2
| |||
| |||
| 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@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 |
![]() |
| Thread Tools | |
| Display Modes | |
In an effort to better serve ads to our visitors, cookies are used on objectmix.com. For more information, check out our Privacy Policy.