Vous êtes sur la page 1sur 24

NutriShopper High-Level Design Phase IV

2012-04-20 Team Diaspora Alok Gupta Kevin Hopps Dan Maas David Ward

Design Overview
Package nutrishopper.controller
Contains classes that handle input event from the Nutrishopper user interface, often via a registered mobile or web handler and converts the event into an appropriate user action, understandable for the model.

UserProfileController
Define methods to handle application users actions for creating, viewing, updating and removing health profiles. Health profile encapsulates dietary preferences of the user defining nutrition criteria that is used for evaluating a food item while shopping. addProfile(User, HealthProfile) Handles users action to add a new health profile for the given user.

viewProfile(User):List
Handles users action to provide a list of health profiles associated with the user.

updateProfile(User, HealthProfile)
Handles users action to save changes made to health profile.

removeProfile(User, HealthProfile)
Handles users action to deletes their health profile from the Nutrishopper database.

ItemController
Define methods to handle user events like food item lookup (via barcode scan or manual entry for produce), view items from previous lookups and select a food item from the list of food items returned after lookup. viewLookupHistory():LookupHistory Provides a list of previously looked up food items. upcLookup(upcImage): List<FoodItem> Handles the UPC scan event for looking up a food item and provides a list of food items matching the UPC label. manualLookup(String name): List<FoodItem> Looks up a food item by name and provides a list of food items matching the user provided name. selectItemFromLookupList(FoodItem): FoodItem Handles the user selection of a food item from the list of food items displayed to the user after initial lookup. selectItemFromHistoryList(FoodItem): FoodItem Handles the user selection of a food item from the list of food items displayed to the user after initial lookup

ShoppingCartController
Define methods to handle user actions to add, discard and view a selected food item to/from the shopping cart. addToCart(FoodItem):boolean Process user action to add a food item to the shopping cart. The method returns a boolean indicating id the action was successfully executed.

discard(FoodItem):boolean
Process user action to discard a food item from the shopping cart. The method returns a boolean indicating id the action was successfully executed. viewCurrentCart():ShoppingCart Fetches the current cart data for displaying the summary to the user.

Package nutrishopper.fooddata
Contains classes/entities representing all the information associated with food items.

FoodItem
Value entity representing a food item.

FoodItemCategory
Value entity representing a category for a group of related food items.

Nutrient
Value entity representing a particular nutrient (e.g. carbohydrates)

Ingredient
Value entity representing a particular ingredient (e.g. peanuts)

NutrientAmountPair
Value entity pairing nutrient and amount per serving for a given food item.

FoodItemRepository
Interface advertising the ability to reconstitute stored Food ltems from a database.

getItemByUPC(String upc):List<FoodItem> Retrieves the persisted food item in database by upc string. getItemByCriteria(String name):List<FoodItem> Retrieves the persisted food item in database by name .

FoodItemRepositoryChain
Implementation of the FoodltemRepository interface, which aggregates a series of FoodltemRepositories that it searches in sequence given a query, returning the first Foodltem that one of its children returns. This class manifests the chain of responsibility pattern.

StoreFoodItemRepository
Implementation of the FoodltemRepository interface which presides over the retailer's private database.

GenericFoodItemrepository
Implementation of the FoodltemRepository interface which represents global database.

Package nutrishopper.storedata
Contains entities representing all data associated with stores selling one or more food items.

Promotion

Entity tied to a Store in association with one or more Foodltems, usually offering a discount or other enticement to purchase.

Store
Entity that creates associations with specific Food Items, including Promotions and Locations. A Store has a particular location on a map.

PromotionData
Entity encapsulating the promotion data in context of a given food item..

StoreData
Entity encapsulating the store data in context of a given food item.

Coupling and Cohesion of nutrishopper.storedata


These classes are closely related, making the cohesion of this package high. The

Package nutrishopper.shoppingcart
Contains classes representing the shopping cart (aggregation of user selected food items while shopping).

ShoppingCart
Entity representing physical shopping cart, holds food items.

add(FoodItem):boolean Adds user selected food item. remove(FoodItem):boolean Removes user selected food item. remove(index):boolean Removes a food item by index from the underlying aggregation.

ShoppingCartRepository
An interface advertising the ability to reconstitute stored shopping carts, including the Food items that were in them.

saveShoppingCart(ShoppingCart):boolean Persist a given shopping cart to the underlying database. getShoppingCart(id):ShoppingCart Retrieve a shopping cart by id.

ShoppingCartFactory
Creates a new ShoppingCart ensuring proper set up of invariants.

createShoppingCart():ShoppingCart Creates a new instance of ShoppingCart.

Package nutrishopper.lookup
Contains classes related to food item lookup.

LookUpManager
Domain service responsible for managing food item lookup history.

addToLookupHistory(FoodItem):boolean Adds the given food item to the look up history for the user in current context. getLookupHistory():LookupHistory Retrieves the look up history for the user in current context.

ItemManager
Domain service entity responsible for orchestrating food item lookup.

findItemByCriteria(String name):List<FoodItem> Finds the food item by name. resolve(upcImage):String upc Resolves scanned barcode image to a UPC string. findItemByUPC(String upc):List<FoodItem> Finds the food item by upc string.

LookUpHistory
Domain entity representing the Item look up history.

addFoodItem(FoodItem) Adds the food item looked and selected by the user to the underlying collection.. getFoodItemCollection:List<FoodItem> Returns the underlying collection containing the food items looked up and selected by the user.

BarcodeResolver
Domain service responsible for resolving the scanned upc label image.

resolve(upcImage):String upc Converts the upcImage to upc label.

Package nutrishopper.evaluation
EvaluationManager
Domain service responsible for evaluating the quality of the food item selected by the user, in context of their health profile rules.

evaluateItem(FoodItem):FoodItem Evaluates the quality of the selected food item in light of users health profile.

ItemEvaluationStrategyFactory
Factory class encapsulating the logic for choosing the right implementation of ItemEvaluationStrategy.

createItemEvaluationStrategy(healthProfile):ItemEvaluationStrategy. Creates an instance of the (chooses right implementation based on configuration) ItemEvaluationStrategy.

ItemEvaluationStrategy
Interface to abstract the strategy for evaluating a food item.

evaluateFoodItem(HealthProfile, FoodItem):ItemEvaluationResult Evaluates the selected FoodItem in context of the current users HealthProfile rule and nutrition criteria. This method returns ItemEvaluationResult which encapsulates the criteria that passed and the overall score indicating the quality of the food item.

WeightedItemEvaluationStrategy
Implementation of ItemEvaluationStrategy responsible for evaluating the food item quality based on the total importance (or weights) of the matched criteria.

ItemEvaluationResult
Entity representing the food item evaluation results.

getQualityIndicator():int Returns the quality indicator based on the criteria that matched the user selected food items and ingredients. getMatchedCriteria(); List<Criteria> Returns the quality indicator based on the criteria that matched the user selected food items and ingredients.

Package nutrishopper.evaluation.criteria
Contains classes representing evaluation rules and criteria.

NutritionRule
Entity encapsulating food item evaluation criteria configured by the user for assessing the quality of the scanned or selected food item

getNutritionCriteria():List Returns the list of user configured nutrition criteria.

Criterion
Interface for abstracting evaluation criterion. Defines contracts for EvaluationManager when evaluating a food item.

NutritionCriterion
Represents criteria and importance factor for a given Nutrient.

IngredientCriterion
Represents criteria and importance factor for a given Ingredient.

Package nutrishopper.user
Contains classes related to application user account and profile management.

User
Holds important information about registered users.

getHealthProfiles(): List

Retrieves a list of HealthProfiles associated with the user. getActiveHealthProfile(): HealthProfile Retrieves the active HealthProfile associated with the user. A user could define multiple HealthProfiles but only one can be active for a given shopping experience. All FoodItems will be evaluated based on the active HealthProfile. setHealthProfile(HealthProfile) Associates a given HealthProfile with the user.

UserRepository
Interface advertising the ability to reconstitute persisteded Users

UserFactory
Creates Users from nothing" The marketing and sales departments love this object.

Package nutrishopper.profile
HealthProfile
This is a concrete class that is used by the user to setup varies profiles they wish to use (such as Diabetic and/or Weight Loss profiles).

getNutritionRule() used to retrieve a NutritionRule getAvoidFoodItems():List used to get a list of FoodItems that need to be avoided getIgnoreFoodItems():List used to get a list of FoodItems that can be ignored isActive():boolean the Active flag so the user can get his/her current profile

HealthProfileCategory
This is a class used to simply categorize our health profiles. We allow multiple categories to be applied to each profile.

categoryName:string categoryDescription:string

Coupling and Cohesion Among Packages


Each of the packages, except the nutrishopper.controller package, consists of small numbers of classes which are closely related to one another, making cohesion high. The nutrishopper.controller package contains all the controller classes, which are not related to each other at all, making the cohesion of this package low. The coupling among the packages centers about the FoodItem class, which permeates the design. In addition, the amount per serving, which is associated with a Nutrient, and the minimum and maximum amounts in the NutritionCriterion, must be expressed in the same units (e.g. grams). This also represents a significant coupling.

Design Narrative
We have created class and sequence diagrams that shows object/method interactions for the NutriShopper application. Below, we briefly discuss the main functional areas of our design: what they are, how they work, and room for future extension points. 1. Item scanning and lookup Users can add items to their cart in one of two ways: they can scan an items bar code, or they can look up an item to add via a list. Both cases evaluate the goodness of a food item (see number 4 below) and ask the user if they really want to add it to their shopping cart. When a user has entered scan mode, their phone is continuously waiting for a barcode image to resolve. Both 1d and 2d barcodes will be able to be resolved, although we have not shown the details of that in our design. When a barcode is resolved, the application will freeze on it and immediately call ItemController.upcLookup(). This creates an ItemManager and calls the method Resolve on the object. The ItemManager then uses BarCodeResolver to resolve the image and return a unique upc string. This string is then used to retrieve a list of FoodItem(s) (i.e. List<FoodItems>) from the FoodItemRepositoryChain. This repository is implemented as FoodItemRepositoryChain, which sequentially searches the store-specific database for the item, and then searches the generic food item database. We assume that there might be more than one FoodItem that is returned. After the list of items is returned we will display the list in the UI to allow the end user to choose the correct item. When the item is chosen from the UI (via the selectItemFromLookupList(FoodItem)) ItemController.evaluate(FoodItem) is called and then in turn calls the EvaluationManager.evaluate(FoodItem). This evaluates the goodness of the item, and is described in part 3 below. Adding the item to the cart is accomplished with ShoppingCartController and then the ShoppingCart object. Most of the same steps apply for looking an item up manually, except that the item is retrieved by name from the FoodItemRepositoryChain, and there is no barcode to resolve. This process begins when the user clicks a Manual Lookup button (which triggers an onButtonEvent(ManualLookup)) and selects their item from a databasedriven list of non-UPC products, like corn or chicken. The store-specific database will be searched first here as well. A total list is compiled and presented to the UI. Once the item is selected from the list the same process is followed from above to have the item evaluated and added to the shopping cart. In both cases, the user is presented with this goodness and asked if they want to add it to their shopping cart. This is denoted by the ConfirmItemAfterLookup button in the UI layer. Whether or not they add it, it is stored in the item lookup history, which allows the user to re-examine previous lookups via ItemController.viewLookupHistory(). The store-specific lookup is merely stubbed out in our design. This will be implemented in a future design iteration, and will easily extend off of the existing design. Store-

specific items will be able to contain things like aisle, store ID, and other store-specific data of interest to users. Another piece of functionality that we stubbed out is viewing promotions for items in the shopping cart. Stores will contain promotions, each of which will pertain to a list of products. Users will be able to view promotions and take action based on the promotions they find. 2. User profile and nutritional rules setup. User health profile setup addresses the use cases where the user views, enters and updates data related to their health condition and configures criteria for each of the desired nutrient (say entering minimum and maximum thresholds per serving for carbohydrate along with an importance factor). The application uses these criteria to evaluate the quality of a scanned food item. The UI provides forms and action triggers to view and collect user's profile data and submits it to the "UserProfileController" application layer component (part of the nutrishopper.controller package). The controller processes the UI data by mapping it to the domain objects (i.e. User, HealthProfile, NutritionRule, etc.). The controller defines methods for adding, viewing and updating user's health profiles. The domain is centered around the User object which sits at the root of the aggregate comprising of the HealthProfile, NutritionRule, NutritionCriteria, etc. objects (refer to Profile Setup class diagram for the full list). User class aggregates one to many HealthProfile objects. The user may want the ability to setup different profiles for different reasons in their lifestyle (i.e. maybe a Diabetic, however, they may also want a weight loss profile as well). It provides methods to look up and save the associated HealthProfile objects along with fetching the active HealthProfile object. The active HealthProfile is the one that gets used for evaluating the scanned food items. HealthProfile class defines the profile details and provides reference to the associated NutritionRule which encapsulates the nutrient criteria configured for a given user. It also aggregates one to many FoodItemCategory objects configured by the user to avoid or even have it listed as important. The class provides methods to look up and save NutritionRule as well as HealthProfile data. NutritionRule class holds the good, average and poor quality threshold importance as configured by the user. It aggregates one to many NutritionCriterion and IngredientCriterion objects defining constraints at the nutrient and ingredient level.

The class provides methods to save and look up NutritionRule, NutritionCriterion, and IngredientCriterion objects. NutritionCriterion class holds user's preference (minimum and maximum amount per serving) for each nutrient along with a importance factor as set by the user. The IngredientCriterion class holds the users preference for whether or not the ingredient should be allowed or not for their respective purchases. It also holds the level of importance that ingredient has on the system. The Nutrient and Ingredient classes are used to hold the specific information about those items. The user configures the FoodItems they wish to avoid via the FoodItemCategory Objects collection and with this list of FoodItems we are able to match them up with the Nutrient and Ingredient classes. The Nutrient and Ingredient classes are tied to the FoodItem (that is used to avoid in the users health profile) via the NutrientAmountPair class. We use this class to assign the amounts that need to be capped or maintained with a given Nutrient or Ingredient. The UserRepository class acts as the data access implementation for the "User" aggregate defining methods to look up and save the User aggregate data to the underlying data source. UserFactory encapsulates the User creation logic establishing invariance for the "User" aggregate.

3. Item health evaluation Once the ItemController finds a scanned food item (for the user in context) it invokes the EvaluationManager to evaluate the quality in light of the rule and criteria associated with the user. During startup, the ItemEvaluationStrategyFactory is instantiated. When it is asked to return an ItemEvaluationStrategy, it does so based on configuration information. At this stage in our design, we have only one concrete ItemEvaluationStrategy, but the design allows for many. The EvaluationManager will ask the ItemEvaluationStrategyFactory for an ItemEvaluationStrategy and hold onto the reference for repeated use. The EvaluationManager will evaluate a FoodItem using the active HealthProfile associated with the current User. The EvaluationManager uses an object implementing the ItemEvaluationStrategy interface to accomplish this. Once the ItemEvaluationStrategy returns its EvaluationResult, the EvaluationResult is added to the FoodItem for use by other components in the application. WeighteItemEvaluationStrategy implements the ItemEvaluationStrategy interface and is currently our only implementation of that interface. Future extensions could implement the interface in different ways, and the ItemEvaluationStrategyFactory would return

different implementations based on the configuration information. WeightedItemEvaluationStrategy evaluates each food FoodItem's nutrient label value against the criterion (maximum and minimum threshold) configured for that nutrient and based on whether the nutrient value falls within the range it builds a list of matched nutrition criterion and adds the criterion importance to the total matched importance. The evaluation results are encapsulated in ItemEvaluationResult class. The class holds a reference to the list of matched NutritionCriteria, the NutritionRule associated with the HealthProfile in context and the totalMatchedImportance (represents the summation of the importance associated with each nutrition criteria that was successfully matched with FoodItem in context).

Design Scope
For this design, we only implemented a subset of the use cases we outlines in our project requirements document. For reference, we have created designs for use cases 1-9 and 16. We focused on implementing a thorough design for the core business functions that we deemed most important to the user and the business. If we have done our design correctly, we will be able to add further functionality as an extension of our current design. Assumptions Our generic food item database contains both upc-specific products, like Campbells Chicken Noodle Soup Classic and generic products like Romaine Lettuce. The same is true of the store-specific database.

Requirements Changes (not related to change cases) We added an additional use case for storing lookup history. This functionality stores all items that the user looked up, whether or not they added them to their cart. That way, a user can go back and re-examine a previously searched item and reconsider if they want to add it to their cart. Assessment of Design Quality With Regard to Risks One of our stated risks was duplication of development efforts due to development on multiple platforms. The design clearly separates UI from the rest of the components, which mitigates this risk. Another of our stated risks was items not existing in the global database. Our design accounts for this by virtue of manual entry. One of our stated risks was the possibility of poor signal inside a store. The design allows us to make updates to a database, which will be helpful in case the mobile app crashes or the phone dies, but does not guard against disconnection. With Regard to Quality Criteria

Extensibility and Adaptability - we wanted to be able to allow stores to add their own products, promotions and item locations, and our design accommodates this. We also want to allow for the development of new algorithms for evaluating the quality of a food item. The ItemEvaluationStrategy does a fairly nice job of handling this. However, one weakness in the design is the dependence of this interface on the HealthProfile. An alternative for managing this might be to remove the HealthProfile as a parameter, leaving FoodItem as the sole parameter, and to embed HealthProfile in a particular implementation, or family of implementations, of ItemEvaluationStrategy. We also wanted to abstract the item lookup functionality in such a way that other methods of looking up an item could be created without impacting the processes that allow users to take action on the looked up item. For instance, in the future we could implement an image recognition lookup that uses an algorithm to look up an item based on what it looks like. The item would then still be looked up by name and returned to the user via existing mechanisms. One use case that we did not implement for this design phase contains two different scenarios: getting a suggested shopping cart based on previous carts, and getting suggested items based on what is in the current cart. We realized that our use case is very vague, and needs to be further analyzed before designs are created. That being said, we believe our design will be able to easily handle these extensions, which will use our existing item lookup and item evaluation facilities to show the user suggested carts and items. Responsiveness/Availability - the only thing in the design which could impact this goal would be database latency. We have not addressed this in the design, though it is possible the same solution for alleviating the poor signal risk would work for this potential problem as well.

Glossary account - the set of stored information for a given user ID application - a term given to the generation of arguments and confusion among members of the design team bar code - a scannable image on a food item that uniquely identifies it within a particular grocery store chain of responsibility - a design pattern consisting of a set of processing objects which represent a series of steps to be taken in the processing of some command object. controller - a software component that interacts with components in the user interface and translates requests into actions in the domain layer coupling - the degree to which changes in one software component will require changes in another dietary preferences - another name for a health profile domain layer - a grouping of software components that represent entities in the domain of the need being addressed by the software factory - an entity whose purpose is to create certain aggregates and objects food item - an item which may be purchased in the store health profile - the collective criteria by which a food item is evaluated ingredient - listed on the label of a food item layer - a grouping of software components, often represented by a horizontal section of the software class diagram, indicating a hierarchy of dependence, with higher layers depending only on lower layers login - the act of authenticating a particular user ID and retrieving his account information mobile application - the Nurtishopper program which runs on a mobile device nutrient - may be an ingredient, but more likely listed in the nutrition information on the label of a food item package - a group of software components that are closely related and perhaps more tightly coupled to each other than to software components outside the package repository - an entity that can store certain aggregates and objects for reconstitution at a later time. shopping cart - the physical cart in the store; the conceptual container used by the mobile application upc - see bar code user interface layer - a grouping of software components, the purpose of which is to interact directly with the user

Process Narrative
For phase 4 of our design process, we began by talking through use cases, tracing the flow of responsibility through our class diagram and as we pointed at the objects represented in it. This was helpful in revealing problems in our design. Working to fix the problem we had discovered led to some spirited conversations. We are four people with strong opinions. There are positive and negative aspects to conversations like this. On the negative side, they can be unpleasant and frustrating. On the positive side, we learn quite a bit and the design is better for them. Only rarely did one person or faction have to give up just to go along. Usually our desire to resolve the issues led to a consensus. Another benefit to wrestling with the issue through to consensus was that, because the design was improved, subsequent discussions and use cases were easier to map out as the design gelled. One problem that destined us for argument was the fact that we had conflicting assumptions but did not realize it. Three of us assumed that the application running on the mobile device was thin and that the bulk of the domain classes functionality was running on a server, while one of us assumed that the application running on the mobile device consisted of most of the functionality, with only the lowest level data-serving functions residing on the server. This difference in assumptions arose out of our respective experiential backgrounds. Our discussions had a few recurring themes: 1. How to bind some additional data to an existing class or object 2. When to make a class concrete vs. abstract/extensible An example of item 1 was the issue of how to combine a FoodItem with its evaluation result. We start with a FoodItem, essentially a value object that comes from a database (via a repository, of course). The evaluation result is produced by a potentially complex algorithm that measures the FoodItem against preselected health requirements. Subsequent operations typically require access to both the FoodItem and its evaluation result. We considered creating an independent data structure that contained both the FoodItem and the evaluation result. We also considered using inheritance to extend the data structure of FoodItem, but since no behavior was being added or modified, that seemed like a poor solution. We also considered the fact that we had one or two other optional pieces of information that may also be associated with the FoodItem later, so we decided to add fields to the FoodItem to hold the evaluation result and these other optional items. As an example of item 2, we wrestled with the FoodItemRepository. We knew that there could potentially be both a global database and a store-specific database to search, so we could either make the FoodItemRepository an interface with an abstract lookup method, or we could make it a concrete object that always searched a sequence of one or more databases. In this case, because the caller of the lookup never needs to know the details, and we may want to change the implementation, we opted to make it an interface, backed by a concrete class that implemented the chain of responsibility pattern to implement the search.

In all, we spent about 40 person-hours on this work. The bulk of our time for phase 4 was spent in discussion on Skype, jointly working in LucidChart on the diagrams. Some of the written work was done jointly and some was done individually. Process Narrative from previous phases For our design process, we started out by looking at our use cases, and then designing class diagrams from those use cases. We discussed some design ideas and reviewed a couple of use cases. We decided on a layered organization, 3-5 layers, but had some disagreements about what to name the topmost layers, what to name the items in the layers, and what functionality should be in which layer. We decided to think more about it individually, and each draw preliminary class diagrams with the intent of discussing them and choosing one to move forward with. From there, we discussed and assigned different portions of our diagrams to different team members. We found this to be of mixed benefit. It allowed us to quickly create class diagrams. However, our class diagrams went through multiple refactoring iterations and it ended up taking us a long time to get them right. We also initially ended up duplicated a lot of work, where multiple team members ended up designing the same components at the same time. We have done very well listening to each other and explaining the above concepts to each other, which has helped us flesh out our design and achieve a level of consistency across different team members diagrams. It might have been helpful to start by doing CRC cards or some other strategy for assembling our objects and how they should relate to each other. From there we could have assigned different responsibilities to different team members without members working on the same areas. We continue to struggle with what the term application means. We come from different design backgrounds, and each of us has a different concept of what each layer should be responsible for, and how the layers should interact with each other. Most of us come from a web application background, and think in terms of a client-server separation. However, the other way to think about this is in terms of the application as one whole unit, where the application is defined as the UI and the domain objects that it interacts with. Yet another way is to talk about the application as being an entity in the application layer, which has the responsibility for coordinating activities among other entities to accomplish the users goal. We also have spent a lot of time understanding what domain objects we should have, and how they are supposed to relate to each other. Also we have spent a lot of time implementing factories and repositories for the appropriate objects. We have had discussions about which objects should be interfaces and which should not be, and decided that most should be interfaces, and the further into the domain you go the more interfaces you should find. We have also found that making sure our diagrams align with each other is a challenge - especially to use the same ubiquitous language, and ensuring our components could talk to each other.

We also have not gotten to a number of our use cases for a few different reasons: 1. we did not have these use cases sufficiently fleshed out 2. these use cases were not part of the core domain 3. we ran out of time All told, we focused on the core aspects of our design first, and when we found those to be sufficiently challenging, we found that it was a better investment to spend most of our time on those core aspects. We have decided to finalize our class and sequence diagrams by Tuesday, and then spend the remaining days consolidating our diagrams, writing our narratives, creating a high level (non-UML) diagram of our application, and creating our glossary.

Vous aimerez peut-être aussi