User:Chen Qing
(New page: = Design Study = Under construction .....) |
(→Source Code) |
||
(17 intermediate revisions by one user not shown) | |||
Line 1: | Line 1: | ||
+ | Hello everyone, welcome to my home page. I am a 2008 cosc427 student. | ||
+ | |||
= Design Study = | = Design Study = | ||
− | + | Recently I have built a “quick and dirty” MS Access based VBA application for a friend. The application is for helping a small printing company to maintain their job records, organize their job flow and providing some reporting function etc. I thought this is a good case to exercises what we have learned through the cosc427 lectures (and also past courses), so I reworked on this application and will build it within an object-oriented environment. This application also connects to a backend database, for the purpose of this design study, I will only consider the basic structure for the business logic layer. | |
+ | |||
+ | === Main Design Decisions === | ||
+ | |||
+ | The design can be break into three major parts: print product family, print job & job workflow and print company structure itself. I will analyse the design separately in the following section. | ||
+ | |||
+ | ==== Print Product Family ==== | ||
+ | |||
+ | A good point for start is to model the actual print products produced by the company. At present, there are two main types of print products, one is single sheet/page product, and the other is multiple sheet/page products. The example we chosen to model here are book, business card, NCR booklet and pad. The type of the print product is limited by the actual hardware (printing machine) and employee skill sets. All of the print products are share some common properties and also has some unique properties. The design for this part is illustrated in Figure 1. ''(Note: for the readability of the UML class diagram, some of the method parameters and return types are not shown.)'' | ||
+ | |||
+ | [[image:PrintProduction_Products.jpg|thumb|800px|centre|'''Figure 1: Print Product Overview]] | ||
+ | |||
+ | A common interface for print product (abstract class PrintProduct) is defined. This interface contains all the properties which a print product could contain (and which can be produced by this company). The [[Decorator]] pattern is used here. By defining all properties in the interface, the child implementation class can choose to enrich the properties which are relevant to it. In the PrintProduct abstract class, only the common properties for all the print products are implemented. A second level abstract class MultiPagePrintProduct is introduced to implement the common properties for all the children of the class, this helps to avoid some code duplication down at the child concrete classes. | ||
+ | |||
+ | In order to handle the creation of these concrete print product classes, the [[Abstract Factory]] and [[Factory Method]] pattern are used here. These patterns can provide client a unified interface for create those concrete print product. In the actual implementation, the createPrintProduct method will take all the properties required by a PrintProduct, and depends on which particular concrete factory object the client is using, it will only initialize those properties which are valid towards the concrete print product. | ||
+ | |||
+ | An Enumeration PrintProductType is later on introduced. This covers all the current print product types. If customer decide to add new print product type, for instance, map, the changes need to do is just introduce a new print product type, a new concrete print product class and a new factory class associated with it. The print product interface (abstract class PrintProduct) will not be changed very often as explained earlier (machine limitation, employee skill sets etc.), if there is a change for the interface, it is also a sign that there is a couple more other things might be affected. In that situation, reworking on the interface is a much better idea. | ||
+ | |||
+ | An argument was raised when I was working on this design; it is whether the print product interface should be considered as too "fat". A notice here is for simple print product such as business card, it has to carry a lot of extra properties when it is used with this interface. In the actual implementation, it is possible for client to use a property which does not belong to business card. Different from the first design attempt, which basically code up a no-op function for such properties, in the new design an unsupported operation exception has been introduced to couple with this problem. I think a trade off has to be made here for using a uniformed print product interface. | ||
+ | |||
+ | As the result of this “fat” interface, the creation of each concrete print product object also needs to fill a lot of parameters in order to initialize a concrete print product properly. The decision for this version of the design is introduce "fat" constructors and only initialize appropriate properties by each individual concrete factory object. An alternative of this is to only initialize the common properties for all the current products, then ask client code to initialize the other necessary properties for a field. The danger for this approach is that assumption of client fully initialize a concrete product object properly has to be made, which might not always be the case. The [[Facade]] pattern might be applied here to guide/organize all the product creation, but there still need a way to pass in those required parameters at some stage. Since there isn’t a clear win of this approach, I stayed with the existing design. | ||
+ | |||
+ | ===== Used Design Patterns ===== | ||
+ | |||
+ | * [[Decorator]] | ||
+ | * [[Factory Method]] | ||
+ | * [[Abstract Factory]] | ||
+ | |||
+ | ===== Applied Design Principles ===== | ||
+ | |||
+ | * [[Dependency inversion principle]] The PrintProduct abstract class acts as the interface for all the concrete print products for client object. | ||
+ | * [[Open closed principle]] PrintProduct interface can be extended with new concrete print product class | ||
+ | |||
+ | ===== Violdated Design Principles ===== | ||
+ | |||
+ | * [[Fat interfaces]] | ||
+ | * Method with large number of parameters | ||
+ | |||
+ | ==== Print Job and Print Job Workflow ==== | ||
+ | |||
+ | [[image:PrintProduction_PrintJob.jpg|thumb|800px|centre|'''Figure 2: Print Job Overview]] | ||
+ | |||
+ | As you might guessed already, the print company has a job queue which records all the jobs in the company. These set ups are modelled as class PrintJob and JobQueue. For each print job they had, there would be a number of print product associated with it. An ArrayList is used to store the products for a job in the implementation, the [[Iterator]] is used to iterator through the print products list. | ||
+ | |||
+ | For each job, it also has a state field to record which stage of the job is in currently. Typical stages here are quote, design, press (printing), finishing and dispatch. Since there is a requirement to calculate the cost for these states, and the calculation algorithm for each state is different, the [[State]] pattern is used here. Another benefit with applying the [[State]] pattern here is it also leaves the possibility for adding new state / drop existing state as the company’s business procedure evolves. The state transition for a job is handled by JobStateHandler interface. Currently, there are two state transition patterns existed for the company, one is for jobs which require external designer to do the design stage, another is for normal print jobs. The state transition is also seen as a frequent change candidate, so the [[Strategy]] pattern is used here. A JobStateHandler is bind to the job itself, and the concrete handler for each job object will direct the job to follow the right state transition path. | ||
+ | |||
+ | One unsolved problem with this part of the design is the code duplication for get quote price in QuoteState and cost method for other state classes. In theory, the quote should be close to the sum of actual costs which will occur later in each state. Currently, the same algorithm is shared between quote state and other states, as the result, the code duplication occurs. Some other design tricks might be used here for improving this, but I do notice a fact that in the finished system, the quote calculation might not have the same access to a large number of fields which other production state could access. Furthermore, it is wise to say we do not want to use the exactly same algorithm for calculating the quote and the cost for each stage. The cost for each stage is aimed at more accurate actual production cost, and the algorithm for quote should not require a lot of detailed information. | ||
+ | |||
+ | ===== Used Design Patterns ===== | ||
+ | |||
+ | * [[State]] | ||
+ | * [[Strategy]] | ||
+ | * [[Iterator]] | ||
+ | |||
+ | ===== Applied Design Principles ===== | ||
+ | |||
+ | * [[Dependency inversion principle]] The State interface provide a common interface for client code to interact with any concrete state class. | ||
+ | * [[Open closed principle]] State and JobStateHanlder enables new state and state handler to be added easily. | ||
+ | |||
+ | ===== Violdated Design Principles ===== | ||
+ | |||
+ | * [[Duplicate Code Smell]] | ||
+ | |||
+ | |||
+ | ==== Print Company Structure ==== | ||
+ | |||
+ | [[image:PrintProduction_PrintCompany.jpg|thumb|800px|centre|'''Figure 3: Print Company Structure]] | ||
+ | |||
+ | As any typical company, the print company consist of a number of employees, departments, customers and in this case external contractors. A Person class is introduced to handle some of the common parts between employees and external contractors. The person class is also used for contactor person in Customer class. The design of the Person class here is more about reduce code duplication rather than providing a common interface, hence Person class is a concrete class in the actual implementation. | ||
+ | |||
+ | As mentioned earlier, there is a JobQueue in the company to hold all the jobs it has. A number of jobs are stored in there. The actual implementation for this job queue is done with ArrayList, and [[Iterator]] is used here again. Departments and external contractors need to watch this job queue closely all the time. The [[Observer]] pattern is used here with JobQueue as subject, Department and ExternalContractor are used as the observer. Every time job queue changes, departments and external contractors will be noticed and they can re-fetch a job queue from the company. As this system now is still in development stage, the performance hasn’t been a problem for frequent job queue changes. Based on the knowledge of customer, there will be no frequent job queue changes at this stage, but it would be a potential problem in future if the work load for this application increased sharply. The alternative approach for this is to set up a timer and refresh job queue by a pre-set time frequency. This design will lose the real time watching function for the application so the original design is used, but this part of the design still need to be watched closely if there is a performance problem in future. | ||
+ | |||
+ | For security reasons, we do not want external contractor to see the same job queue as departments could do. However, there is also no need for maintaining a different or sub job queue for this purpose alone. The [[Proxy]] pattern is used (access control proxy) here to control the access for departments and external contractors. As a result, external contractor will not be able to invoke the getQueue method aimed at internal employees. | ||
+ | |||
+ | Lastly, we only need one instance of print company, which is the owner object for everything else in the application, so the [[Singleton]] pattern is used here. The [[Singleton]] is not directly applied to JobQueue since the implementation ensures print company will initialize the unique job queue and other object will get the job queue through the print company. | ||
+ | |||
+ | ===== Used Design Patterns ===== | ||
+ | |||
+ | * [[Observer]] | ||
+ | * [[Proxy]] The access control proxy is used here, the implementation is done with Java InvocationHandler. | ||
+ | * [[Singleton]] | ||
+ | * [[Iterator]] | ||
+ | |||
+ | ===== Applied Design Principles ===== | ||
+ | |||
+ | |||
+ | ===== Violdated Design Principles ===== | ||
+ | |||
+ | * [[Avoid concrete base class]] The Person class is a concrete base class for Employee and ExternalContractor. | ||
+ | * [[Law of demeter]] JobQeueu is retrieved from PrintCompany when used by client classes. This is used for ensuring the uniqueness of the JobQueue in the application without using the [[Singleton]] pattern for both PrintCompany and JobQueue. | ||
+ | |||
+ | |||
+ | ==== Dropped the Composite Pattern Used by the First Attempt ==== | ||
+ | |||
+ | [[image:JobAndProduct.jpg|thumb|800px|centre|'''Figure 4: Old Design (First Attempt, Dropped)]] | ||
+ | |||
+ | In the first attempt, the [[Composite]] pattern was used for the print product family. The reason for using it at that time is for given a consistent interface for client code to use, i.e. define the interface with PrintProduct abstract class. Later analysis found that what it is really needed is actually a [[Decorator]] pattern. This is provided as we had the PrintProduct interface already. The actual print product object only provides more behaviours to the defined functionality in the interface. A more direct reason for dropping the [[Composite]] design is due to the design I had did not conforms to a part and component relationship, furthermore, the composition brought in by [[Composite]] (component node) does not bring more benefit to the application, for keeping things simple, the [[Decorator]] is used here. | ||
+ | |||
+ | === Implementation Notes === | ||
+ | |||
+ | Different from the original vba code based application, the design is coded in Java 1.5. Generic is not used for the final submission due to a UML tool limitation. Some of the interesting topic encountered during the development are organized below. | ||
+ | |||
+ | ==== Thread Safe Singleton Creation ==== | ||
+ | |||
+ | A thread safe getInstance() method for [[Singleton]] pattern used with PrintCompany class is applied in the implementation. An interesting read can be found from | ||
+ | |||
+ | http://www-128.ibm.com/developerworks/java/library/j-dcl.html | ||
+ | |||
+ | === Source Code === | ||
+ | |||
+ | Please see the attached zip file for the source. [[Media:PrintProduction.zip]] | ||
+ | |||
+ | [[image:PrintProduction.jpg|thumb|800px|centre|'''Figure 5: Print Product Overview]] | ||
+ | |||
+ | === Future Work === | ||
+ | |||
+ | As this project is still evolving, more work on the design might be added. The next big task for this project is to design the data access layer strcuture. As most of the typical business applications, database interaction is one of the core part of the application. I will keep working on this over next month. In the meanwhile, I will seeking for better solution for the duplicate code problem this application had with the quote calculation. |
Latest revision as of 03:58, 1 October 2008
Hello everyone, welcome to my home page. I am a 2008 cosc427 student.
Contents |
Design Study
Recently I have built a “quick and dirty” MS Access based VBA application for a friend. The application is for helping a small printing company to maintain their job records, organize their job flow and providing some reporting function etc. I thought this is a good case to exercises what we have learned through the cosc427 lectures (and also past courses), so I reworked on this application and will build it within an object-oriented environment. This application also connects to a backend database, for the purpose of this design study, I will only consider the basic structure for the business logic layer.
Main Design Decisions
The design can be break into three major parts: print product family, print job & job workflow and print company structure itself. I will analyse the design separately in the following section.
Print Product Family
A good point for start is to model the actual print products produced by the company. At present, there are two main types of print products, one is single sheet/page product, and the other is multiple sheet/page products. The example we chosen to model here are book, business card, NCR booklet and pad. The type of the print product is limited by the actual hardware (printing machine) and employee skill sets. All of the print products are share some common properties and also has some unique properties. The design for this part is illustrated in Figure 1. (Note: for the readability of the UML class diagram, some of the method parameters and return types are not shown.)
A common interface for print product (abstract class PrintProduct) is defined. This interface contains all the properties which a print product could contain (and which can be produced by this company). The Decorator pattern is used here. By defining all properties in the interface, the child implementation class can choose to enrich the properties which are relevant to it. In the PrintProduct abstract class, only the common properties for all the print products are implemented. A second level abstract class MultiPagePrintProduct is introduced to implement the common properties for all the children of the class, this helps to avoid some code duplication down at the child concrete classes.
In order to handle the creation of these concrete print product classes, the Abstract Factory and Factory Method pattern are used here. These patterns can provide client a unified interface for create those concrete print product. In the actual implementation, the createPrintProduct method will take all the properties required by a PrintProduct, and depends on which particular concrete factory object the client is using, it will only initialize those properties which are valid towards the concrete print product.
An Enumeration PrintProductType is later on introduced. This covers all the current print product types. If customer decide to add new print product type, for instance, map, the changes need to do is just introduce a new print product type, a new concrete print product class and a new factory class associated with it. The print product interface (abstract class PrintProduct) will not be changed very often as explained earlier (machine limitation, employee skill sets etc.), if there is a change for the interface, it is also a sign that there is a couple more other things might be affected. In that situation, reworking on the interface is a much better idea.
An argument was raised when I was working on this design; it is whether the print product interface should be considered as too "fat". A notice here is for simple print product such as business card, it has to carry a lot of extra properties when it is used with this interface. In the actual implementation, it is possible for client to use a property which does not belong to business card. Different from the first design attempt, which basically code up a no-op function for such properties, in the new design an unsupported operation exception has been introduced to couple with this problem. I think a trade off has to be made here for using a uniformed print product interface.
As the result of this “fat” interface, the creation of each concrete print product object also needs to fill a lot of parameters in order to initialize a concrete print product properly. The decision for this version of the design is introduce "fat" constructors and only initialize appropriate properties by each individual concrete factory object. An alternative of this is to only initialize the common properties for all the current products, then ask client code to initialize the other necessary properties for a field. The danger for this approach is that assumption of client fully initialize a concrete product object properly has to be made, which might not always be the case. The Facade pattern might be applied here to guide/organize all the product creation, but there still need a way to pass in those required parameters at some stage. Since there isn’t a clear win of this approach, I stayed with the existing design.
Used Design Patterns
Applied Design Principles
- Dependency inversion principle The PrintProduct abstract class acts as the interface for all the concrete print products for client object.
- Open closed principle PrintProduct interface can be extended with new concrete print product class
Violdated Design Principles
- Fat interfaces
- Method with large number of parameters
Print Job and Print Job Workflow
As you might guessed already, the print company has a job queue which records all the jobs in the company. These set ups are modelled as class PrintJob and JobQueue. For each print job they had, there would be a number of print product associated with it. An ArrayList is used to store the products for a job in the implementation, the Iterator is used to iterator through the print products list.
For each job, it also has a state field to record which stage of the job is in currently. Typical stages here are quote, design, press (printing), finishing and dispatch. Since there is a requirement to calculate the cost for these states, and the calculation algorithm for each state is different, the State pattern is used here. Another benefit with applying the State pattern here is it also leaves the possibility for adding new state / drop existing state as the company’s business procedure evolves. The state transition for a job is handled by JobStateHandler interface. Currently, there are two state transition patterns existed for the company, one is for jobs which require external designer to do the design stage, another is for normal print jobs. The state transition is also seen as a frequent change candidate, so the Strategy pattern is used here. A JobStateHandler is bind to the job itself, and the concrete handler for each job object will direct the job to follow the right state transition path.
One unsolved problem with this part of the design is the code duplication for get quote price in QuoteState and cost method for other state classes. In theory, the quote should be close to the sum of actual costs which will occur later in each state. Currently, the same algorithm is shared between quote state and other states, as the result, the code duplication occurs. Some other design tricks might be used here for improving this, but I do notice a fact that in the finished system, the quote calculation might not have the same access to a large number of fields which other production state could access. Furthermore, it is wise to say we do not want to use the exactly same algorithm for calculating the quote and the cost for each stage. The cost for each stage is aimed at more accurate actual production cost, and the algorithm for quote should not require a lot of detailed information.
Used Design Patterns
Applied Design Principles
- Dependency inversion principle The State interface provide a common interface for client code to interact with any concrete state class.
- Open closed principle State and JobStateHanlder enables new state and state handler to be added easily.
Violdated Design Principles
Print Company Structure
As any typical company, the print company consist of a number of employees, departments, customers and in this case external contractors. A Person class is introduced to handle some of the common parts between employees and external contractors. The person class is also used for contactor person in Customer class. The design of the Person class here is more about reduce code duplication rather than providing a common interface, hence Person class is a concrete class in the actual implementation.
As mentioned earlier, there is a JobQueue in the company to hold all the jobs it has. A number of jobs are stored in there. The actual implementation for this job queue is done with ArrayList, and Iterator is used here again. Departments and external contractors need to watch this job queue closely all the time. The Observer pattern is used here with JobQueue as subject, Department and ExternalContractor are used as the observer. Every time job queue changes, departments and external contractors will be noticed and they can re-fetch a job queue from the company. As this system now is still in development stage, the performance hasn’t been a problem for frequent job queue changes. Based on the knowledge of customer, there will be no frequent job queue changes at this stage, but it would be a potential problem in future if the work load for this application increased sharply. The alternative approach for this is to set up a timer and refresh job queue by a pre-set time frequency. This design will lose the real time watching function for the application so the original design is used, but this part of the design still need to be watched closely if there is a performance problem in future.
For security reasons, we do not want external contractor to see the same job queue as departments could do. However, there is also no need for maintaining a different or sub job queue for this purpose alone. The Proxy pattern is used (access control proxy) here to control the access for departments and external contractors. As a result, external contractor will not be able to invoke the getQueue method aimed at internal employees.
Lastly, we only need one instance of print company, which is the owner object for everything else in the application, so the Singleton pattern is used here. The Singleton is not directly applied to JobQueue since the implementation ensures print company will initialize the unique job queue and other object will get the job queue through the print company.
Used Design Patterns
- Observer
- Proxy The access control proxy is used here, the implementation is done with Java InvocationHandler.
- Singleton
- Iterator
Applied Design Principles
Violdated Design Principles
- Avoid concrete base class The Person class is a concrete base class for Employee and ExternalContractor.
- Law of demeter JobQeueu is retrieved from PrintCompany when used by client classes. This is used for ensuring the uniqueness of the JobQueue in the application without using the Singleton pattern for both PrintCompany and JobQueue.
Dropped the Composite Pattern Used by the First Attempt
In the first attempt, the Composite pattern was used for the print product family. The reason for using it at that time is for given a consistent interface for client code to use, i.e. define the interface with PrintProduct abstract class. Later analysis found that what it is really needed is actually a Decorator pattern. This is provided as we had the PrintProduct interface already. The actual print product object only provides more behaviours to the defined functionality in the interface. A more direct reason for dropping the Composite design is due to the design I had did not conforms to a part and component relationship, furthermore, the composition brought in by Composite (component node) does not bring more benefit to the application, for keeping things simple, the Decorator is used here.
Implementation Notes
Different from the original vba code based application, the design is coded in Java 1.5. Generic is not used for the final submission due to a UML tool limitation. Some of the interesting topic encountered during the development are organized below.
Thread Safe Singleton Creation
A thread safe getInstance() method for Singleton pattern used with PrintCompany class is applied in the implementation. An interesting read can be found from
http://www-128.ibm.com/developerworks/java/library/j-dcl.html
Source Code
Please see the attached zip file for the source. Media:PrintProduction.zip
Future Work
As this project is still evolving, more work on the design might be added. The next big task for this project is to design the data access layer strcuture. As most of the typical business applications, database interaction is one of the core part of the application. I will keep working on this over next month. In the meanwhile, I will seeking for better solution for the duplicate code problem this application had with the quote calculation.