Mujtaba's design study

From CSSEMediaWiki
(Difference between revisions)
Jump to: navigation, search
(The Request Handler Class)
 
(7 intermediate revisions by one user not shown)
Line 34: Line 34:
 
The system that we talked about above is called ‘’’HORIZON’’’ and it operated by the Jet Propulsion Laboratory (JPL) of NASA. It appears that it is currently the only system of its kind in the world and I assume it is used worldwide by all astronomers and space agencies around the world.  
 
The system that we talked about above is called ‘’’HORIZON’’’ and it operated by the Jet Propulsion Laboratory (JPL) of NASA. It appears that it is currently the only system of its kind in the world and I assume it is used worldwide by all astronomers and space agencies around the world.  
 
They provide ‘’’two’’’ ways of querying the system; a full featured ‘’’telnet service’’’ (full featured in terms of the options provided and possible ways of querying the system) and simple ‘’’web interface’’’ one. Moreover, they also provide large files containing the data in raw format. Those files are released continuously and only contain data for specific period of time (e.g. from 1990-2020) and are meant to be used by professionals who want to extract the data and report it in their own way. The files also come in different standards.
 
They provide ‘’’two’’’ ways of querying the system; a full featured ‘’’telnet service’’’ (full featured in terms of the options provided and possible ways of querying the system) and simple ‘’’web interface’’’ one. Moreover, they also provide large files containing the data in raw format. Those files are released continuously and only contain data for specific period of time (e.g. from 1990-2020) and are meant to be used by professionals who want to extract the data and report it in their own way. The files also come in different standards.
 
 
=== Definitions===
 
'''Ephemeris:'''
 
 
'''Coordinate System:'''
 
 
'''Step & Step Identifier:'''
 
  
  
Line 55: Line 47:
  
 
===The Request Handler Class===
 
===The Request Handler Class===
This class is '''a singleton''' one and it is the main class that clients would be interacting with. It has a single method called '''sendReq''' that a client use to send a request and receive a response. The client supplies an instance of '''EphServiceAdapter''' object and an instance of '''Request''' object and receives an instance of '''Response''' object.
+
This class is '''a Singleton''' one and it is the main class that clients would be interacting with. It has a single method called '''sendReq''' that a client use to send a request and receive a response. The client supplies an instance of a concrete '''EphemerisProvider''' object and an instance of a concrete '''Request''' object and receives an instance of '''Response''' object. It is not essential for our problem domain that the RequestHandler be a '''Singleton''' but we preferred to make it so since it makes more sense that one object is handling all the requests and it saves resources since the same object is reused instead of creating a new object for each request. If performance becomes an issue then this could be fixed in the implementation of the sendReq method using threading and delegation. The singleton requirement could also be removed if necessary without our design being affected at all.  
  
The '''Program to the interface not the implementation''' maxim has been followed strictly here. The two parameters of '''sendReq''' and the response are all of abstract types. This is important in our desing since we want to make sure that this singleton class can keep up with future changes and will work for any type of Request, Response, and EphServiceAdapter.
+
The '''Program to the interface not the implementation''' maxim has been followed strictly here. The two parameters of '''sendReq''' and the response are all of abstract types. This is important in our desing since we want to make sure that this singleton class can keep up with future changes and will work for any type of Request, Response, and EphemerisProvider.
  
 
The '''Liskov Substitution Principle (Design by Contract)''' has also been followed here in implementing the '''sendReq''' method. The implementation of this method looks like the following:
 
The '''Liskov Substitution Principle (Design by Contract)''' has also been followed here in implementing the '''sendReq''' method. The implementation of this method looks like the following:
  
  public Response sendReq(EphServiceAdapter serv, Request req)  
+
  public Response sendReq(EphemerisProvider serv, Request req)  
 
  {  
 
  {  
 
   Response res = serv.getEphemerides(req);   
 
   Response res = serv.getEphemerides(req);   
Line 67: Line 59:
 
  }
 
  }
  
Hence, any concrete of Request would be substitutable here without requiring any change in our '''sendReq''' method. The same applies to EphServiceAdapter & Response, as well.
+
Hence, any concrete of Request would be substitutable here without requiring any change in our '''sendReq''' method. The same applies to EphemerisProvider & Response, as well.
  
 
The '''sendReq''' method has also been declared '''final''' to ensure that future subclassed can't break this contract. It's also declared as '''synchronized''' to ensure it is multi-thread safe.
 
The '''sendReq''' method has also been declared '''final''' to ensure that future subclassed can't break this contract. It's also declared as '''synchronized''' to ensure it is multi-thread safe.
  
 +
===The Ephemeris Provider Interface===
 +
This interface plays a key role in the design. We make a good use of '''iheritance''' and '''polymorphsim''' here to hide the different possible ways of aquiring the ephemeris data and exposing instead a common interface (the getEphemerides method).
  
===The Ephemeris Service Adapter Interface===
+
It worths mentioning that one might sense the flavor of the '''Adapter Pattern''' here. This is because the "EphemerisProvider" interface could be thought of as being the '''Target''' interface  that the '''Client''' (RequestHandler) is interacting with, while each realization of EphemerisProvider plays the role of an '''Adapter''' that adapts the interface of the '''Adaptee''' (the JPLTelnetService or the FileService) to the target interface. Both of the "FileService" and the "JPLTelnetService" have their own way of aquiring the ephemeris data so the '''FileEphProvider''' & the '''JPLTelnetEphProvider''' are adapting that to our target interface. The thing that deviates from the Standard Adapter Pattern is that I'm not showing that each actual Adapter is having an instance of their Adaptees. This is because in my case here, the JPLTelService or FileService are not necessarily always Java Objects that I can declare a type for, they can essentially be anything like a database, a file, or a web service. The client providing the FileEphProvider for example, is responsible for implementing the actual process of accessing that service. All we care for is that our contract method '''getEphemerides''' takes our Request object and get back to us with our Response object.  
This interface plays a key role in the design. The '''Adapter Pattern''' is used here to enable us to have a standard reliable interface to our client (the RequestHandler class) for accessing what are rather different services with different interfaces.
+
 
+
To put this in context, in the UML diagram below, the "JPL Telnet Service" is an '''Adaptee''', the "JPLTelnetServiceAdapter" is an '''Adapter''', the "EphServiceAdapter" is the '''Target Interface''', while the "RequestHandler" is the client.  
+
  
 
Again, this interface class is itself also following both the '''Program to the interface not the implementation''' and the '''LSP''' princibles.
 
Again, this interface class is itself also following both the '''Program to the interface not the implementation''' and the '''LSP''' princibles.
  
 +
====A lurking Command Pattern?====
 +
There is a sensation here that this could have been implemented as a Command Pattern but personally I wouldn't do it because the command pattern is useful when the requests are meant to be stored in some kind of objects or queued/stored  and executed at different times but both cases don't apply in our domain problem here. Plus, in the command pattern we have two agents playing; the client who is responsible for  supplying the command as an object, and second, the invoker who is responsible for issuing or initiating the execution of the command. But in our case here, the client is doing both jobs.
 +
 +
In fact, one could argue that our implementation is extremely close to a command pattern, except that the client is doing both jobs of suppling the Command object & initiating the execution process (calling the sendReq method). In that case you could think of the "EphemerisProvider" as the '''Command Interface''' and each realization of it as a '''Concrete Command''' which each has an instance of a '''Receiver''' (e.g. the JPL Telnet Service). The "RequestHandler" class would be the '''Invoker'''.
 +
 +
===The Coordinate Abstract Class===
 +
It is important to explain the purpose of this class first before discussing its desing. The main purpose here is find a proper way to store the coordinate values of celestial bodies locations in the sky. We have already talked about the complexities associated here in the Complexities Section above, mainly point number 4. Will go through them in more details now.
 +
 +
*In Astronomy several different coordinat systems exist that are used to specify the location of celestial bodies. Not all of those systems use the same number of components, some use two components and others use three components, like (x,y) and (x,y,z). I decided to be open and extensible as to the number of components possible as it is not surprising that a new system (or possibly an already existing one) is using four components. Further, each system has different names for each component, for example the most common one uses '''RA''' and '''DEC''' (short for Right Ascension & Declination) as the names of its components. For this reason I used two arrays; one called '''componentNames''' of type String to keep the names and another called '''componentValues''' which is of type Douple and which keeps the actual numerical values. Of course I could have created a new Class called '''Component''' with a String field and a Double field. However, I intentionally refrained from doing that because I have a '''convertCoord''' method that converts convert coordinate components from one system to another using mathematical algorithms and it is much easier and more appropriate to pass to that method an array with all the coordinates instead of having to extract them one by one from each object.
 +
 +
*Each Coordinate system  can use any of three popular Reference Frames when calculating the coordinate values. Hence, I'm keeping those reference frames' names as Enums to make them better managable.
 +
 +
*It is important to understand that the '''FileService''' or '''JPLTelnetService''' will usually give us a celestial body's coordinate in one specific system as simple component values. The concrete '''EphemerisProvider''' is responsible for creating the Coordinate object and populating its components. The first Coordinate constructor is provided to be used in this situation, when the first Coordinate object is initially created for any celestial body.
 +
If clients wish several different versions of Coordinate objects (each using a different system) then the EphemerisProvider should use the second Coordinate constructr (which uses no parameters) to create a new coordinate. It can then use a '''CoordinateConverter''' with the appropriate '''CoordConvertStrategy''' to convert the coordinate components to the desired system before populating them in the Coordinate object.
 +
 +
*Another point here is that some coordinate systems can have more than one form reporting the component values. For example, sometimes the values can be reported in (hours, mins, seconds) of an arc, while at other times they are reported in Degrees. For this reason we are supplying the format method which uses a similar design implementation to the '''convert''' method in the '''Quantity''' Class which is a variation of the '''Strategy Pattern'''. Please see the Quantity Class for more discussion on this design. The actual implementation of the format method would look like this:
 +
format(formatter){
 +
  componentsValues= formatter.changeFormat(componentsValues);
 +
}
 +
 +
 +
=====Different Coordinate Design=====
 +
It worths mentioning here that my design for the '''Coordinate''' class has gone through many many many refinements until I felt got  extremely irritated. I initially designed it as a '''Strategy Pattern''' then realized it was flawed since I was mixing '''More than one Responsibility''' which are the creation of the Coordinate object and the conversion from one system to another.
 +
Later after grinding and milling, I designed it as a '''Factory Method''' to allow the creation of different Coordinate objects. But again I realized my '''different''' coordinate objects are not really different except that I was pressing hard for incorporating the conversion process as a method in each different type of coordinate. So they were not really different except in that method. Again I apparently was running into violating the '''Single Responsibility''' principle. So finally I got it right and separated the conversion responsibility from the creation of the object. Here is the last flawed design (the Factory Method) one.[[Different Design]]
 +
 +
 +
 +
 +
===The Abstract Request Class===
 +
This class is fairly simple. Its main purpose is to keep the request information. The client constructs this objects and hands it to the RequestHandler using the '''sendReq''' method. The '''EphemerisEnum''' array is important because sometimes the user might want to specifiy exactly what ephemeris info he wants, like only the Coordinate and distance. He can use this array to specify the names of the specifc ephemeris info desired. This array however is optional as the JPL Horizon system can also accept a general request in which case all supported ephemeris info would be returned.
 +
 +
The StepIdentifier is to specify the period between each set of ephemerides. This is because ephemerides for a given celestial body are usually given for a continous several different periods separated by a time gap.
 +
 +
This class has been designed as abstract in order to allow other classes and methods to follow the '''Dependency Inversion Principle'''. The same is true for the Response class in this regard.
 +
 +
 +
 +
===The Abstract  Response Class===
 +
This class's purpose is to keep the response information which is coming from an '''EphemerisProvider''' object. The raw String field keeps the actual raw response for logging purposes if desired (violating YAGNI?). The service name is kept in order to keep track of which service this Response was coming from. The Ephemeris field is obvious.
  
===The Request Abstract Class===
 
  
===The Response Abstract Interface===
 
  
 
===The Ephemeris Abstract Class===
 
===The Ephemeris Abstract Class===
 +
This class is simply a data object and it should have setters and getters for all fields but I didn't show them since that would clutter the diagram. Each Ephemeris object could contain one or more Coordinate object (different systems) and hence the array coordinate.
  
===The Coordinate Abstract Class===
 
  
===The Convertible Abstract Class===
 
  
===The IdateTime Abstract class===
+
===The Quantity Class===
 +
This class design has been inspired by the '''Quantity Pattern''' from Fowler's "Observation & Measurment" analysis patterns. The intent here is to provide a good mechanism of storing measurment values and at the same time providing a nice way to convert the value from one unit to another.
  
 +
My design here is slightly different from Fowler's in that I'm using just one method called '''convert''' that takes an IUnitConverterStrategy object that knows how to convert to the target desired unit. For example, a MilesPerSecondStrategy would work by first checking the unit string variable and depending on its value, the right conversion algorithm would be carried out. The implementation of the convert method in the Quantity class ensures that the unit string is properly updated.
 +
Fowler's Quantity pattern uses a '''Unit''' type instead of String and lets the Quantity class have several methods each for converting the quantity to a specific unit. For example in Fowler's design when considering a length Quantity you would have a convertTo(Centimeters) and convertTo(Meters) and so on. He also introduces a more sphisticated way to deal with compound units.
  
 +
I didn't choose to use Fowler's exact design as it introduces complexities that are not necessary in the problem I'm dealig with. His design also means having large number of methods, each for converting to a specific unit. Moreover, I believe his design doesn't follow the '''OCP Principle''' as whenever you have a new Unit introduced, you would have to change your Quantity class to add a new method that convert your quantity to that Unit.
 +
I believe my approach by having  a single convert method that can be used to convert to any unit is more aesthetically appealing.
  
==Patterns Used==
+
It worths mentioning that both approaches however have a '''Switch Statements Smells''' problem. But apparently this is necessary as it is an avoidable that at some stage one has to check the 'from' unit in order to do the proper conversion. While one might argue that this could be solved with polymorphism, after close inspection, it can be seen that this would even introduces more complexity! Consider the case for compound units!
  
 +
===Strategy Pattern?===
 +
Even though this certainly is not a standard '''Strategy Pattern''', I still like to think of it as a variation of Strategy Pattern. The reason for this is that the intent of using the '''IUnitConverterStrategy''' interface for our convert method is extremely similar to the intent of the standard Strategy Pattern; which is to introduce interchangeable behavior. The standard pattern uses composition to achieve that while I use parameter passing.
 +
The implementation of the convert method is as follows:
 +
convert(IUnitConverterStrategy : converter){
 +
   converter.convert(this);
 +
  unit=converter.getUnit()
 +
}
  
==Maxims Followed==
+
The toString method simply returns the concatenation of the amount and unit variables.
  
  
==Maxims Violated==
 
  
  
== Initial OO Design ==
 
  
Here is my initial design up to now. I think it is quite simple now, but this is just the beginning :) This is right now only an overview of how I imagine the api to be. I expect to actually implement The JPL's Telnet Service Adapter (or maybe the CD-ROM?) so that should also have its own design!!
+
== The UML Class Diagram ==
  
Please feel free to add any comments below or any questions. Comments and ideas will be greatly appreciated.
+
Here is the final Class diagram! It has gone through lots and lots of iterations and refinements and I'm certainly quite proud of it. I know it has areas that could be improved but given the time allowed, I consider it a good design :)
 +
[[Image:JEAFinal.jpg]]
  
'''Class Diagram- Phase 1 (2 August, 2010) '''
 
[[Image:JEA1.jpg]]
 
  
 +
==Critique==
 +
*While I'm very happy of this final version of my design, still undoubtedly there are some areas that could probably be designed better. The Coordinate class could most probably be improved with more thought given to it but as it is now, it at least achieves exactly what I wanted it to, which is to support multiple number of components and along with its Converter Strategy Class it allows any coordinate to be converted from one system to another.
  
== Final Design ==
 
  
Here is a draft version of what should be the final design. It has gone through lots and lots of iterations and refinements and is now as such I don't expect any major changes to it.  
+
*Another point is that I just noticed now that the Ephemeris field in the Response Class might probably need to be an array in order to support the stepping feature of the Request. In this case The Ephemeris Abstract Class would need to be modified such that fields that are not variable are stored only once.
A detailed write up and explanations should follow soon along with some diagrams of older versions.
+
[[Image:JEA11.jpg]]
+
  
 +
==Code==
  
= Comments =
+
Here is the code of my design. I used the Java round trip feature to generate it so it has no implementation yet at all. But at least it shows that my design structure is not flawed.
  Please add your comments here!!
+
[[Media:code.zip]]
= Questions =
+
  Please add your questions here!!
+

Latest revision as of 03:59, 22 October 2010

Contents

About the Project

Java Ephemerides API (JEA) is a project to design a java API library that can be used to connect to any ephemeris data repository and query it for information about celestial bodies.

Problem Demystified

It is important to understand the problem I'm trying to solve here before one can proceed with trying to understand the OO design below. Basically the main problem here is that we want to know information about celestial bodies. Celestial bodies include all the known planets, stars, comets, asteroids, and even man-made satellites. The kind of information we are interesting in is called Ephemerides or Ephemeris (as singular form) and it basically refers to such information as: the celestial body's location in the sky, brightness, rise time, set time, velocity, distance, temperature, pressure, number of satellites, radius, orbital period, gravity, .. etc. As an OO designer you might want to think of those as simply properties of celestial bodies, but astronomers would of course not agree with the term ‘properties’ as it blows away that sense of scientific pomposity from the word ‘’’ephemeris’’’.

Shortcut

--Those properties or ephemerides mentioned above are stored and maintained by NASA. They provide a system called HORIZON that can be used to query the ephemeris of any celestial body the user wants. They also provide large files that contain this information. There are several ways of hooking into those information repositories and querying them for data. The OO design below basically tries to provide a Java framework that could be used for dealing with ephemerides regardless of the type and source of the repository. It could be used to hook into NASA’s system or any other system providing ephemeris info.

--You can now jump to the design description section, but if you run into any mystery then blame only yourself.


Complexities

As you might already have guessed, there is various numbers of complexities in reporting those ephemerides information. I list these complexities below:

  • 1-Not all properties are applicable to each celestial body. For example, pressure is not applicable for a man-made satellite.
  • 2-While some properties are static like number of satellites and radius, many of them are actually variable ones like location, rise and set time, distance... etc
  • 3-Many of those properties are actually not absolute data points but are relative ones depending on the observer’s location. For instance, Mars location in the sky for someone standing on earth is different from its location for someone who is in space stations like the ISS. To complicate things further, Mars location in the sky would actually differ for observers depending on their exact location (longitude & latitude) on the observing site. It’s location for an observer in Christchurch is different from that for an observer in London. Similarly, distance and velocity are also relative to the observer’s location.
  • 4-Many properties can be reported in various numbers of formats or standards. The most complicated property (or ephemeris) is actually the location. A celestial body’s location in the sky is reported in what’s called a coordinate system and there are different numbers of coordinate systems in use. The two most popular ones are ‘’’Right Ascension-Declination’’’ shortened as ‘’’(RA-DEC)’’’ and ‘’’Azimuth-Elevation’’’ shortened as ‘’’(AZ-EL)’’’. Each coordinate system can have more than one format of reporting, for example RA-DEC can be reported in degrees or in minutes and hours of an arc. The same coordinate system can also be reported according to different standard reference points known as Reference Frames. The ‘’’important’’’ thing to know here however is that once we have the location of a body’s location in a certain coordinate system, then we can use mathematical functions to convert that coordinate system to any other coordinate system we need.
  • 5-Some properties are measured ones and astronomers actually re-measure them from time to time for accuracy reasons. Some (like a body’s location) are also interpolated using known mathematical functions.


Reporting the Ephemerides

For the reasons above, astronomers used to have long tables that list those ephemerides for the major planets for several months ahead. Today, NASA provides a ‘’’Repository’’’ that astronomers could hook into and query the ephemeris of any celestial body they want. ‘’’To query the repository you would usually have to specify the celestial body, the date, time, and observer’s location and then the system would provide you with all known ephemeris info about that celestial body for the date, time you provided’’’. Of course, this is only the basic and most common query; the system still provides various advanced ways of queries and options to specify.


JPL’s HORIZON SYSTEM

The system that we talked about above is called ‘’’HORIZON’’’ and it operated by the Jet Propulsion Laboratory (JPL) of NASA. It appears that it is currently the only system of its kind in the world and I assume it is used worldwide by all astronomers and space agencies around the world. They provide ‘’’two’’’ ways of querying the system; a full featured ‘’’telnet service’’’ (full featured in terms of the options provided and possible ways of querying the system) and simple ‘’’web interface’’’ one. Moreover, they also provide large files containing the data in raw format. Those files are released continuously and only contain data for specific period of time (e.g. from 1990-2020) and are meant to be used by professionals who want to extract the data and report it in their own way. The files also come in different standards.


Users

The API will obviously be most useful for astronomers and amateur sky observers. They can use it to exactly locate and pinpoint any celestial object of interest in the sky. Nowadays observers are well equipped with electronic and computerized telescopes that can automatically navigate to any object in the sky with the press of a button. All of that is done by internally looking up the object's ephemeris data in the device's internal database. In fact, any kind of spacecraft can't navigate in space without being equipped with some means to calculate ephemerides information.

Still, the users of this API are not limited to astronomers only. Many scientific disciplines do require such ephemerides information, especially those of the Sun's and Moon's since they are close to our planet and impose various effects on our planet that different scientific disciplines might be interested in. For example, a scientist studying how the Sun's position in the sky help birds finding their way during long distance migration, will find such a tool invaluable.


Description of the Design

The Request Handler Class

This class is a Singleton one and it is the main class that clients would be interacting with. It has a single method called sendReq that a client use to send a request and receive a response. The client supplies an instance of a concrete EphemerisProvider object and an instance of a concrete Request object and receives an instance of Response object. It is not essential for our problem domain that the RequestHandler be a Singleton but we preferred to make it so since it makes more sense that one object is handling all the requests and it saves resources since the same object is reused instead of creating a new object for each request. If performance becomes an issue then this could be fixed in the implementation of the sendReq method using threading and delegation. The singleton requirement could also be removed if necessary without our design being affected at all.

The Program to the interface not the implementation maxim has been followed strictly here. The two parameters of sendReq and the response are all of abstract types. This is important in our desing since we want to make sure that this singleton class can keep up with future changes and will work for any type of Request, Response, and EphemerisProvider.

The Liskov Substitution Principle (Design by Contract) has also been followed here in implementing the sendReq method. The implementation of this method looks like the following:

public Response sendReq(EphemerisProvider serv, Request req) 
{ 
  Response res = serv.getEphemerides(req);  
  return res;
}

Hence, any concrete of Request would be substitutable here without requiring any change in our sendReq method. The same applies to EphemerisProvider & Response, as well.

The sendReq method has also been declared final to ensure that future subclassed can't break this contract. It's also declared as synchronized to ensure it is multi-thread safe.

The Ephemeris Provider Interface

This interface plays a key role in the design. We make a good use of iheritance and polymorphsim here to hide the different possible ways of aquiring the ephemeris data and exposing instead a common interface (the getEphemerides method).

It worths mentioning that one might sense the flavor of the Adapter Pattern here. This is because the "EphemerisProvider" interface could be thought of as being the Target interface that the Client (RequestHandler) is interacting with, while each realization of EphemerisProvider plays the role of an Adapter that adapts the interface of the Adaptee (the JPLTelnetService or the FileService) to the target interface. Both of the "FileService" and the "JPLTelnetService" have their own way of aquiring the ephemeris data so the FileEphProvider & the JPLTelnetEphProvider are adapting that to our target interface. The thing that deviates from the Standard Adapter Pattern is that I'm not showing that each actual Adapter is having an instance of their Adaptees. This is because in my case here, the JPLTelService or FileService are not necessarily always Java Objects that I can declare a type for, they can essentially be anything like a database, a file, or a web service. The client providing the FileEphProvider for example, is responsible for implementing the actual process of accessing that service. All we care for is that our contract method getEphemerides takes our Request object and get back to us with our Response object.

Again, this interface class is itself also following both the Program to the interface not the implementation and the LSP princibles.

A lurking Command Pattern?

There is a sensation here that this could have been implemented as a Command Pattern but personally I wouldn't do it because the command pattern is useful when the requests are meant to be stored in some kind of objects or queued/stored and executed at different times but both cases don't apply in our domain problem here. Plus, in the command pattern we have two agents playing; the client who is responsible for supplying the command as an object, and second, the invoker who is responsible for issuing or initiating the execution of the command. But in our case here, the client is doing both jobs.

In fact, one could argue that our implementation is extremely close to a command pattern, except that the client is doing both jobs of suppling the Command object & initiating the execution process (calling the sendReq method). In that case you could think of the "EphemerisProvider" as the Command Interface and each realization of it as a Concrete Command which each has an instance of a Receiver (e.g. the JPL Telnet Service). The "RequestHandler" class would be the Invoker.

The Coordinate Abstract Class

It is important to explain the purpose of this class first before discussing its desing. The main purpose here is find a proper way to store the coordinate values of celestial bodies locations in the sky. We have already talked about the complexities associated here in the Complexities Section above, mainly point number 4. Will go through them in more details now.

  • In Astronomy several different coordinat systems exist that are used to specify the location of celestial bodies. Not all of those systems use the same number of components, some use two components and others use three components, like (x,y) and (x,y,z). I decided to be open and extensible as to the number of components possible as it is not surprising that a new system (or possibly an already existing one) is using four components. Further, each system has different names for each component, for example the most common one uses RA and DEC (short for Right Ascension & Declination) as the names of its components. For this reason I used two arrays; one called componentNames of type String to keep the names and another called componentValues which is of type Douple and which keeps the actual numerical values. Of course I could have created a new Class called Component with a String field and a Double field. However, I intentionally refrained from doing that because I have a convertCoord method that converts convert coordinate components from one system to another using mathematical algorithms and it is much easier and more appropriate to pass to that method an array with all the coordinates instead of having to extract them one by one from each object.
  • Each Coordinate system can use any of three popular Reference Frames when calculating the coordinate values. Hence, I'm keeping those reference frames' names as Enums to make them better managable.
  • It is important to understand that the FileService or JPLTelnetService will usually give us a celestial body's coordinate in one specific system as simple component values. The concrete EphemerisProvider is responsible for creating the Coordinate object and populating its components. The first Coordinate constructor is provided to be used in this situation, when the first Coordinate object is initially created for any celestial body.

If clients wish several different versions of Coordinate objects (each using a different system) then the EphemerisProvider should use the second Coordinate constructr (which uses no parameters) to create a new coordinate. It can then use a CoordinateConverter with the appropriate CoordConvertStrategy to convert the coordinate components to the desired system before populating them in the Coordinate object.

  • Another point here is that some coordinate systems can have more than one form reporting the component values. For example, sometimes the values can be reported in (hours, mins, seconds) of an arc, while at other times they are reported in Degrees. For this reason we are supplying the format method which uses a similar design implementation to the convert method in the Quantity Class which is a variation of the Strategy Pattern. Please see the Quantity Class for more discussion on this design. The actual implementation of the format method would look like this:
format(formatter){
 componentsValues= formatter.changeFormat(componentsValues);
}


Different Coordinate Design

It worths mentioning here that my design for the Coordinate class has gone through many many many refinements until I felt got extremely irritated. I initially designed it as a Strategy Pattern then realized it was flawed since I was mixing More than one Responsibility which are the creation of the Coordinate object and the conversion from one system to another. Later after grinding and milling, I designed it as a Factory Method to allow the creation of different Coordinate objects. But again I realized my different coordinate objects are not really different except that I was pressing hard for incorporating the conversion process as a method in each different type of coordinate. So they were not really different except in that method. Again I apparently was running into violating the Single Responsibility principle. So finally I got it right and separated the conversion responsibility from the creation of the object. Here is the last flawed design (the Factory Method) one.Different Design



The Abstract Request Class

This class is fairly simple. Its main purpose is to keep the request information. The client constructs this objects and hands it to the RequestHandler using the sendReq method. The EphemerisEnum array is important because sometimes the user might want to specifiy exactly what ephemeris info he wants, like only the Coordinate and distance. He can use this array to specify the names of the specifc ephemeris info desired. This array however is optional as the JPL Horizon system can also accept a general request in which case all supported ephemeris info would be returned.

The StepIdentifier is to specify the period between each set of ephemerides. This is because ephemerides for a given celestial body are usually given for a continous several different periods separated by a time gap.

This class has been designed as abstract in order to allow other classes and methods to follow the Dependency Inversion Principle. The same is true for the Response class in this regard.


The Abstract Response Class

This class's purpose is to keep the response information which is coming from an EphemerisProvider object. The raw String field keeps the actual raw response for logging purposes if desired (violating YAGNI?). The service name is kept in order to keep track of which service this Response was coming from. The Ephemeris field is obvious.


The Ephemeris Abstract Class

This class is simply a data object and it should have setters and getters for all fields but I didn't show them since that would clutter the diagram. Each Ephemeris object could contain one or more Coordinate object (different systems) and hence the array coordinate.


The Quantity Class

This class design has been inspired by the Quantity Pattern from Fowler's "Observation & Measurment" analysis patterns. The intent here is to provide a good mechanism of storing measurment values and at the same time providing a nice way to convert the value from one unit to another.

My design here is slightly different from Fowler's in that I'm using just one method called convert that takes an IUnitConverterStrategy object that knows how to convert to the target desired unit. For example, a MilesPerSecondStrategy would work by first checking the unit string variable and depending on its value, the right conversion algorithm would be carried out. The implementation of the convert method in the Quantity class ensures that the unit string is properly updated. Fowler's Quantity pattern uses a Unit type instead of String and lets the Quantity class have several methods each for converting the quantity to a specific unit. For example in Fowler's design when considering a length Quantity you would have a convertTo(Centimeters) and convertTo(Meters) and so on. He also introduces a more sphisticated way to deal with compound units.

I didn't choose to use Fowler's exact design as it introduces complexities that are not necessary in the problem I'm dealig with. His design also means having large number of methods, each for converting to a specific unit. Moreover, I believe his design doesn't follow the OCP Principle as whenever you have a new Unit introduced, you would have to change your Quantity class to add a new method that convert your quantity to that Unit. I believe my approach by having a single convert method that can be used to convert to any unit is more aesthetically appealing.

It worths mentioning that both approaches however have a Switch Statements Smells problem. But apparently this is necessary as it is an avoidable that at some stage one has to check the 'from' unit in order to do the proper conversion. While one might argue that this could be solved with polymorphism, after close inspection, it can be seen that this would even introduces more complexity! Consider the case for compound units!

Strategy Pattern?

Even though this certainly is not a standard Strategy Pattern, I still like to think of it as a variation of Strategy Pattern. The reason for this is that the intent of using the IUnitConverterStrategy interface for our convert method is extremely similar to the intent of the standard Strategy Pattern; which is to introduce interchangeable behavior. The standard pattern uses composition to achieve that while I use parameter passing. The implementation of the convert method is as follows:

convert(IUnitConverterStrategy : converter){ 
  converter.convert(this); 
  unit=converter.getUnit()
}

The toString method simply returns the concatenation of the amount and unit variables.



The UML Class Diagram

Here is the final Class diagram! It has gone through lots and lots of iterations and refinements and I'm certainly quite proud of it. I know it has areas that could be improved but given the time allowed, I consider it a good design :) JEAFinal.jpg


Critique

  • While I'm very happy of this final version of my design, still undoubtedly there are some areas that could probably be designed better. The Coordinate class could most probably be improved with more thought given to it but as it is now, it at least achieves exactly what I wanted it to, which is to support multiple number of components and along with its Converter Strategy Class it allows any coordinate to be converted from one system to another.


  • Another point is that I just noticed now that the Ephemeris field in the Response Class might probably need to be an array in order to support the stepping feature of the Request. In this case The Ephemeris Abstract Class would need to be modified such that fields that are not variable are stored only once.

Code

Here is the code of my design. I used the Java round trip feature to generate it so it has no implementation yet at all. But at least it shows that my design structure is not flawed. Media:code.zip

Personal tools