DPSD - Project 1.1

Requirements

Part A

In this assignment you will continue to build a Pizza Configuration API. Please do write up notes in your Lessons Learned from the prior version. You may want to add/adjust them as you receive feedback from Project 1.0 and work on this next iteration.

To get you started for the lessons learned based on your first version of your Project 1, I've given you some areas to consider:

Now on to the next version of Pizza Configuration!

I would like you to expand your proof of concept, so we will update our code for pizza configurations using interfaces and abstract classes, also add a custom exception handler to enhance your design.

Note: Do you know what an API is? Please look this up and be prepared to discuss in class.

For expanding your proof of concept please consider the following requirements; we will expand your existing design with these options:

Your Deliverable:

Design and code classes for these requirements and write a driver program to exercise your API and test the exception handler. Test your code adequately.

Concepts you will need to know.

Plan of Attack - Part A

Refactoring refers to making changes in your design and code after it is written to better meet requirements that have changed or to better implement a good design. It can be a simple as changing attribute names, or completely re-organizing your code. You may have changes you need to make from Project 1.0 in addition to the ones listed below.

Use your IDE and please complete this step before approaching this unit.

  1. Change your PizzaConfig and OptionSet classes to use the ArrayList class instead of basic arrays.
  2. There is another opportunity in this iteration of Project 1 to use another collection class - where is it?? Once you figure that out, code it to use a LinkedHashMap.

Step 1: Writing API Design and Construction

Here is what you are being asked to do:

  1. You must build an API for the pizza configuration classes. This API will be used to create a Pizzeria.
  2. Your API must use a custom exception handling mechanism to make your module more reliable.
Here are some of the main the considerations for developing your API:
  1. You will to use Interfaces and abstract classes to implement your API.
  2. You have a model package (this is a component) that has the pizza-configuration classes. This component represents the data used by your your API and is called "the Model" in this write-up. Each top-level pizza-configuration object contains an list of OptionSet objects, which have their respective Options.
  3. The methods in OptionSet and Option class protected. Only your PizzaConfig class and appropriate methods in that class are public.
  4. We are now trying to provide a set of methods that act as a API for accessing the data and associated functionality in the model package.
  5. In this case, your "users" are the programmer creating a pizzeria, and the person who sets up the configuration for the pizzeria; no one is able to order pizza (yet!).
  6. Now let's decide on methods that should be exposed in the Interface. For your implementation you will implement these six methods (or ones that are very similar):

    1. void createPizzeria(String pizzeriaName, ArrayList pizzaConfig)
      • This method will create the pizzeria with the pizza configuration(s) created by your list of Test objects. If you did not implement a hierarchy of Test cases, you will need to do this now.

        Note that this method does not return the object. Question: how will you handle duplicates?

        Note: this is a temporary method only to give you the ability to add a PizzaConfig until we properly implement the file-reading part.

    2. void configurePizzeria(String filename);
      • Given a text file name, this method will build an instance of your pizzeria configuration. This method does not return the object. For now, make this a stub (a printline() will suffice); you will implement file I/O in the next version of your API.

    3. void printPizzeria(String pizzeriaName);
      • This function searches the Model and prints the properties of a given pizzeria. In essence this would print the menu for the pizzaria, but for now just show the data; since this is a POC (proff-of-concept), there is no need to print in a pretty way.
    4. void updateOptionSetName(String pizzeriaName, String optionSetname, String newName);
      • This function searches the Model for the entry specified by the model name for a given OptionSet and sets the name of OptionSet to newName.
    5. void updateBasePrice(String pizzeriaName, double newPrice);
      • This function searches the Model for the entry specified by the pizzeria name and updates the base price to the value specified by newPrice.

    6. void updateOptionPrice(String pizzeriaName, String optionName, String Option, double newPrice);
      • This function searches the Model for the entry specified by the pizzeria name and updates the Option in the given OptionSet to have the price specified by newPrice.
Here are a few considerations for structuring the code:
  1. It is important for us to NOT expose the pizza-configuration instance(s). The user of the API (a programmer and/or person setting up configurations) creates pizzerias through the API and all the required coding functionality is provided through the API.
  2. So how do we organize the code so we can encapsulate access to pizza configurations?
    1. First, structure your project to create a new package called wrapper.
    2. Add two interfaces in your wrapper package:
      1. One called CreatePizzeria with the createPizzeria(), configurePizzeria(), and printPizzeria() methods in it.
      2. Another one called UpdatePizzeria with the updateOptionSetName(), updateBasePrice(), and updateOptionPrice() methods in it.
    3. Add a new abstract class called ProxyPizzerias in the wrapper package. This class will contain all the implementations of any method declared in the interfaces. Note that our ProxyPizzerias class is both an Adaptor and Proxy (more with this in later versions).
      package wrapper;
      
      import model.*;
      
      public abstract class ProxyPizzerias
      {
      private PizzaConfig configs; 
      
      // All interface methods are implemented here
      
      }
      }
      
      This class contains all the instances of PizzaConfig. The variable configs can be used for handling all operations on a pizza-configuration as needed by the interfaces.
    4. So this raises a question - if we are defining all methods declared in interfaces in the abstract class ProxyPizzerias then why is ProxyPizzerias not implementing the interfaces directly? Instead, it is using inheritance for this purpose: we want ProxyPizzerias to be a "hidden" container, containing all the data and then we will expose an empty class for usage of our API. In order to do this, we will add a new class called PizzeriaConfigAPI in the wrapper package that will have no lines of code but will always look like this.
      package wrapper;
      public class PizzeriaConfigAPI
      extends ProxyPizzerias implements CreatePizzeria, UpdatePizzeria
      {
      
      }
      So whenever a new interface has to be added you can simply update the PizzeriaConfigAPI declaration and write all methods in the abstract class called ProxyPizzerias. In a nutshell we have encapsulated the access to the pizza-config instances through the API and also hidden the code (artificially) in the abstract class.
    5. Next write a driver to test each of the methods.
      1. Instantiate a PizzeriaConfigAPI object using a CreatePizzeria reference and instantiate another using an UpdatePizzeria reference.
      2. Are you able to create and print the pizza-config objects through the CreatePizzeria interface?
      3. Are you able to update one of OptionSet's name and update an Option price for the pizza-config instance created in the previous step?
      4. If you follow the exact design specified above, you will not be able to update the same Pizzeria, as the object in ProxyPizzerias is an instance variable and not declared as a static object. In other words when you create an instance of PizzeriaConfigAPI (child of ProxyPizzerias), a new pizza-config is created. Think about it - this API needs to provide access to all pizza configs in existence. So, to work properly, it requires the variable configs to be static so it can be shared between references to the API.
      5. At the same time, your calling code cannot "know" that the internal representation is static. Make sure your design and implementation follows this basic tenet of encapsulation.
      6. This is also and example of what is called "separation of concerns", meaning that at each point in the designed classes, one class does not know or care about the internals of another. We will build on this in subsequent versions of our PizzeriaConfigAPI.
      7. Update your code to use static and see how it responds.
      8. Make sure to use use polymorphism with your PizzeriaConfigAPI rather than instantiating it multiple times. In other words, create and use references of the the parent classes CreatePizzeria and UpdatePizzeria in order to test that you have the right design. IMPORTANT - this requires that your design be proper, or it will not work, which is why it is a good test. We will look for this during evalation of your code.
      9. This is similar to a design pattern called a "Singleton". After you are finished with the assignment, write up a short explanation how you might update your design to implement the Model API as a Singleton and if this is a better idea (why or why not?) Email this to me before the lecture following the due-date.

    Step 2: Defensive mechanisms to make software more self-healing

    Our next step to start on a custom exception handler to deal with issues in runtime. This code will be in a package called exceptions.

    By "self-healing" we mean your API code will throw exceptions, catch them, and the exceptions themselves will at least attempt to fix the issue in order to continue execution.

    Your Exception classes and handlers at a minimum should handle and "fix" at least 5 exceptions. Here are some possible examples:

    1. When validated, the base-price is missing. Note: what does this imply about your design?
    2. When validated, the name is missing. Note: what does this also imply about your design?
    3. Trying to add an OptionSet that already exists.
    4. Trying to add an Option to an OptionSet when it already exists.

    Note: you will extend this to handle errors relating to reading in pizza configurations from your data-file in the next iteration.

    Your custom Exception classes should have a mechanism to allow/disallow logging. For logging, you need to investigate how to properly implement Logging in Java.

After you have finished the basic design for your exceptions, investigate the Factory design pattern and use this pattern to create the exceptions that implement self-healing code for your API.

Plan of Attack - Part B

Note, there is a major design flaw in the system as designed above.

Hint: Reminder: Part A indicates there is an opportunity to use a LinkedHashMap. What is it?

Note that to loop through elements in a LinkedHashMap you should use an Iterator. For the ArrayList, you may use any method of access you wish.


Additional notes from our in-class discussions:

Yes, the major design flaw is that the names are plural (Pizzerias and configs), but the intance variable is declared to be an single PizzaConfig. This will not work - you need to design this version of the code to handle multiple Pizzerias, not just one.

Finalized Technical Requirement - your Model (set of pizza-configs) should be saved using LinkedHashMap. The set of OptionSet in each Model and respective Options should be saved in an ArrayList.


There is still one major flaw in the design - can you spot it?

We will discuss again during class.


Submitting your work:

Please review your work against this checklist before submission:


Program Specifications / Correctness
  1. No errors, program always works correctly and meets the specification(s).
  2. The API can be reused as a whole and methods are designed to be reuseable.
  3. A custom Exception Handler class hierarchy has been implemented. It handles a minimum of 5 exceptions and can be extended for handling file I/O errors in the next iteration.
  4. The custom Exception Handler uses polymorphism and follows the Factory Pattern for object creation and exception handling.
  5. Ability to log and configuring the logging is added to your custom Exception classes.
  6. The API follows the basic design as specified in the requirements.
  7. Interfaces and Abstract Classes are utilized to add extensibility.
  8. The Interfaces are used to expose the Model (PizzaConfigs, OptionSet and Option) and the functionality is meaningful (means the methods in the interface are well thought out/useful in context of application).
  9. Collections: a LinkedHashMap is used for storing the proper elements in this new version of the API. The OptionSet and Option objects are each store in an ArrayList.

Readability
  1. No errors, code is clean, understandable, and well-organized.
  2. Code has been packaged and authored based on Java Coding Standards.

Documentation
  1. The documentation is well written and clearly explains what the code is accomplishing and how.
  2. Class Diagram is provided and reviewed before coding.
  3. Final Class Diagram is provided.
  4. Test results are fully documented.

Code Efficiency
  1. No errors, code uses the best approach in every case.
  2. The code is efficient in both memory and speed, without sacrificing readability and understanding.