OOAD Fundamentals PDF

Document Details

TimeHonoredHeliodor9919

Uploaded by TimeHonoredHeliodor9919

McLaughlin, Pollica and West

Tags

object-oriented analysis and design ooad guitar store software design

Summary

This document provides an example of Object-Oriented Analysis and Design (OOAD) applied to a guitar store inventory system. It details the design and analysis of creating an inventory system for a guitar store.

Full Transcript

# OOAD Fundamentals ## Page 1 ## OOAD Fundamentals Dave and Helen's Guitar Store Example from Head First Object-Oriented Analysis and Design by McLaughlin, Pollica and West ## Page 2 ## The Design Problem Dave and Helen Chapin recently retired to Lethbridge from Calgary. As they want to stay...

# OOAD Fundamentals ## Page 1 ## OOAD Fundamentals Dave and Helen's Guitar Store Example from Head First Object-Oriented Analysis and Design by McLaughlin, Pollica and West ## Page 2 ## The Design Problem Dave and Helen Chapin recently retired to Lethbridge from Calgary. As they want to stay active in their retirement and they love music, they bought a small local guitar shop to keep them busy. The previous owner of the shop used paper for all of the inventory records. One of the first things that Dave and Helen decided to do was to upgrade the inventory system to a computerized one. They have hired your team to create their inventory system for them. ## Page 3 ## THE GUITAR STORE Version 1.0 ## Page 4 ## Initial Design | | Guitar | Inventory | |---|---|---| | - | serialNumber: string | - guitars: vector<Guitar*> | | - | price: double | + addGuitar(string, double, string, string, string, string) | | - | builder: string | + getGuitar(string): Guitar* | | - | model: string | + removeGuitar(string) | | - | frontWood: string | + search(Guitar*): Guitar* | | - | backWood: string | | | + | Guitar(string, double, ) | | | + | getSerialNum(): string | | | + | getPrice(): double | | | + | getBuilder(): string| | | + | getModel(): string | | | + | getFrontWood(): string | | | + | getBackWood(): string | | ## Page 5 ## Initial Design ```cpp Guitar* Inventory::search (Guitar* wanted) { for(Guitar* g: guitars) { std::string builder = g->getBuilder(); if(builder.compare (wanted->getBuilder()) != 0){ continue; } std::string model = g->getModel(); if(model.compare (wanted->getModel()) != 0){ continue; } std::string backwood = g->getBackWood(); if(backwood.compare(wanted->getBackWood()) != 0){ continue; } std::string frontWood = g->getFrontWood(); if(frontWood.compare(wanted->getFrontWood()) != 0){ continue; } return g; } return nullptr; } ``` ## Page 6 ## Problem #1 ### Entering the guitar Enter the serial number: FS3450204059138 Enter the price: $1499.95 Enter the builder: Fender Enter the model: Stratocaster Enter the top wood: Alder Enter the bottom wood: Alder ### Searching for the guitar Enter the builder: fender Enter the model: Stratocaster Enter the top wood: Alder Enter the bottom wood: Alder The guitar is not in stock. ## Page 7 ## What Happened? - String comparison is case-sensitive - No error checking (not going to focus on this) ```cpp Guitar* Inventory::search (Guitar* wanted) { for(Guitar* g: guitars) { std::string builder = g->getBuilder(); if(builder.compare (wanted->getBuilder()) != 0){ continue; } std::string model = g->getModel(); if(model.compare (wanted->getModel()) != 0){ continue; } std::string backwood = g->getBackWood(); if(backwood.compare(wanted->getBackWood()) != 0){ continue; } std::string frontWood = g->getFrontWood(); if(frontWood.compare(wanted->getFrontWood()) != 0){ continue; } return g; } return nullptr; } ``` ## Page 8 ## How to Fix? - **Good**: Put strings into a "canonical" case ```cpp transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return toupper(c); }); ``` - **Better**: Use constants to prevent data entry errors ```cpp const string FENDER = "Fender" ``` - **Best**: Get rid of strings - use enumerations (enum) ```cpp enum Builder {FENDER, GIBSON, TAYLOR, ...}; ``` ## Page 9 ## Problem #2 - search() only returns one guitar - the first one found. - What if multiple guitars match? ```cpp Guitar* Inventory::search (Guitar* wanted) { for(Guitar* g: guitars) { std::string builder = g->getBuilder(); if(builder.compare (wanted->getBuilder()) != 0){ continue; } std::string model = g->getModel(); if(model.compare (wanted->getModel()) != 0){ continue; } std::string backwood = g->getBackWood(); if(backwood.compare(wanted->getBackWood()) != 0){ continue; } std::string frontWood = g->getFrontWood(); if(frontWood.compare(wanted->getFrontWood()) != 0){ continue; } return g; } return nullptr; } ``` ## Page 10 ## How to fix? - Return a collection. ```cpp vector<Guitar*> search() { vector<Guitar*> matches; for( ... ) { if( ) { matches.push_back(g); } } return matches; } ``` ## Page 11 ## Problem #3 - Have to create a Guitar to find a Guitar? ```cpp Guitar* Inventory::search (Guitar* wanted) { for(Guitar* g : guitars) { std::string builder = g->getBuilder(); if(builder.compare(wanted->getBuilder()) != 0){ continue; } std::string model = g->getModel(); if(model.compare (wanted->getModel()) != 0){ continue; } } std::string backWood = g->getBackWood(); if(backwood.compare(wanted->getBackWood()) != 0){ continue; } std::string frontWood = g->getFrontWood(); if(frontWood.compare(wanted->getFrontWood()) != 0){ continue; } return g; } ``` ## Page 12 ## Problem #3 - Have to create a Guitar to find a Guitar? ```cpp Guitar* Inventory::search (Guitar* wanted) { for(Guitar*g: guitars) { std::string builder = g->getBuilder(); if(builder.compare(wanted->getBuilder()) != 0){ continue; } std::string model = g->getModel(); if(model.compare (wanted->getModel()) != 0){ continue; } std::string backwood = g->getBackWood(); if(backwood.compare(wanted->getBackWood()) != 0){ continue; } std::string frontWood = g->getFrontWood(); if(frontWood.compare(wanted->getFrontWood()) != 0){ continue; } return g; } return nullptr; } ``` ## Page 13 ## How to fix? - In general, passing in values to a method (or constructor) that are not used indicates a design problem. - This "code smell" indicates that a class had two (or more) responsibilities in the design. - Guitar represents: - An inventory item. - A description of an inventory item. - Classes should do one thing and do it well! ## Page 14 ## Use Encapsulation - Encapsulate the properties that the customer is looking for into a different class. | | Guitar | Inventory | GuitarSpec | |---|---|---|---| | - | serialNumber: string | - guitars: vector<Guitar*> | - builder: Builder | | - | price: double | + addGuitar(string, double, GuitarSpec*) | - model: string | | - | spec: GuitarSpec* | + getGuitar(string): Guitar* | - frontWood: Wood | | + | Guitar(string, double, GuitarSpec*) | + removeGuitar(string) | - backWood: Wood | | + | getSerialNum(): string | + search(Guitar Spec*): Guitar* | - numStrings: int | | + | getPrice(): double | | + getBuilder(): Builder | | + | getSpec(): GuitarSpec* | | + getModel(): string | | | | | + getFrontWood(): Wood | | | | | + getBackWood(): Wood | | | | | + getNumStrings(): int | | | | | | ## Page 15 - Helen and Dave have started selling 12-string guitars. - What changes need to be made to the design? | | Guitar | Inventory | GuitarSpec | |---|---|---|---| | - | serialNumber: string | - guitars: vector<Guitar*> | - builder: Builder | | - | price: double | + addGuitar(string, double, GuitarSpec*) | - model: string | | - | spec: GuitarSpec* | + getGuitar(string): Guitar* | - frontWood: Wood | | + | Guitar(string, double, GuitarSpec*) | + removeGuitar(string) | - backWood: Wood | | + | getSerialNum(): string | + search(GuitarSpec*): Guitar* | - numStrings: int | | + | getPrice(): double | | + getBuilder(): Builder | | + | getSpec(): GuitarSpec* | | + getModel(): string | | | | | + getFrontWood(): Wood | | | | | + getBackWood(): Wood | | | | | + getNumStrings: int | - **Update search()** - **Add an attribute and getter** ## Page 16 ## Guitar Store v.1.1 - 2/3 of classes had to change! - Changes are easy, but that is due to the small size of the software - Adding features should not (ideally) result in significant changes. ## Page 17 ## Why did we have to make changes to Inventory? - Inventory.search() knows a lot about the details of InstrumentSpec. ```cpp std::vector<Guitar*> Inventory::search (GuitarSpec* wanted) { std::vector<Guitar*> inStock; for (Guitar*g: guitars) { GuitarSpec* spec = g->getSpec(); GuitarSpec:: Builder builder = spec->getBuilder(); if(wanted->getBuilder() != GuitarSpec:: Any && builder != wanted->getBuilder()) { continue; } std::string model = spec->getModel(); if(wanted->getModel() != && model != wanted->getModel()) { continue; } GuitarSpec:: Wood backWood = spec->getBackWood(); if(wanted->getBackWood() != GuitarSpec:: DontCare && backWood != wanted->getBackWood()) { continue; } } } ``` ## Page 18 ## Principle - Methods that use data should be as close to the data as possible. ## Page 19 - Have search delegate matching to GuitarSpec | | Guitar | Inventory | GuitarSpec | |---|---|---|---| | - | serialNumber: string | - guitars: vector<Guitar*> | - builder: Builder | | - | price: double | + addGuitar(string, double, GuitarSpec*) | - model: string | | - | spec: GuitarSpec* | + getGuitar(string): Guitar* | - frontWood: Wood | | + | Guitar(string, double, GuitarSpec*) | + removeGuitar(string) | - backWood: Wood | | + | getSerialNum(): string | + search (GuitarSpec*): Guitar* | - numStrings: int | | + | getPrice(): double | | + getBuilder(): Builder | | + | getSpec(): GuitarSpec* | | + getModel(): string | | | | | + getFrontWood(): Wood | | | | | + getBackWood(): Wood | | | | | + matches(GuitarSpec*) | ## Page 20 ## Change in Guitar attributes localized to one class! - Code in Inventory is much simpler! ```cpp std::vector<Guitar*> Inventory::search (GuitarSpec* wanted) { std::vector<Guitar*> inStock; for (Guitar* g: guitars) { GuitarSpec* spec = g->getSpec(); if(spec->matches (wanted)){ inStock.push_back(g); } } return inStock; } ``` ```cpp bool GuitarSpec:: matches (Guitar Spec* wanted) { if(wanted->getBuilder() != GuitarSpec:: Any && builder != wanted->getBuilder()) { return false; } if (wanted->getModel() != && model != wanted->getModel()) { return false; } if(wanted->getBackWood() != GuitarSpec:: DontCare && backWood != wanted->getBackWood()) { return false; } if(wanted->getFrontWood() != GuitarSpec:: Dont Care && frontWood != wanted->getFrontWood()) { return false; } if(wanted->getNumStrings() != 0 && numStrings != wanted->getNumStrings()) { return false; } return true; } ``` ## Page 21 - Helen and Dave have started selling mandolins. - What changes need to be made to the design? | | Guitar | Inventory | GuitarSpec | |---|---|---|---| | - | serialNumber: string | - guitars: vector<Guitar*> | - builder: Builder | | - | price: double | + addGuitar(string, double, GuitarSpec*) | - model: string | | - | spec: GuitarSpec* | + getGuitar(string): Guitar* | - frontWood: Wood | | + | Guitar(string, double, GuitarSpec*) | + removeGuitar(string) | - backWood: Wood | | + | getSerialNum(): string | + search(GuitarSpec*): Guitar* | - numStrings: int | | + | getPrice(): double | | + getBuilder(): Builder| | + | getSpec(): GuitarSpec* | | + getModel(): string | | | | | + getFrontWood(): Wood | | | | | + getBackWood(): Wood | | | | | + getNumStrings(): int | | | | | | - Mandolins are like guitars, but they only have four strings, and there are three different styles. ## Page 22 - Add methods to Inventory for Mandolin | | Guitar | Inventory | Mandolin | |---|---|---|---| | - | serialNumber: string | - guitars: vector<Guitar*> | - serialNumber: string | | - | price: double | - mandolins: vector<Mandolin*> | - price: double | | - | spec: GuitarSpec* | + addGuitar(string, double, GuitarSpec*) | - spec: MandolinSpec* | | + | Guitar(string, double, GuitarSpec*) | + getGuitar(string): Guitar* | + Mandolin(string, double, MandolinSpec*) | | + | getSerialNum(): string | + removeGuitar(string) | + getSerialNum(): string | | + | getPrice(): double | + search(GuitarSpec*): Guitar* | + getPrice(): double | | + | getSpec(): GuitarSpec* | + addMandolin(string, double, MandolinSpec*) | + getSpec(): MandolinSpec* | | | | + getMandolin(string): Mandolin* | | | | | + removeMandolin(string) | | | | | + search(Mandolin Spec*): Mandolin* | | - Lots of repetitive changes! - **Add a Mandolin class** - **Add a MandolinSpec class** ## Page 23 - How to fix it? | | Guitar | Inventory | Mandolin | |---|---|---|---| | - | serialNumber: string | - guitars: vector<Guitar*> | - serialNumber: string | | - | price: double | - mandolins: vector<Mandolin*> | - price: double | | - | spec: GuitarSpec* | + addGuitar(string, double, GuitarSpec*) | - spec: MandolinSpec* | | + | Guitar(string, double, GuitarSpec*) | + getGuitar(string): Guitar* | + Mandolin(string, double, MandolinSpec*) | | + | getSerialNum(): string | + removeGuitar(string) | + getSerialNum(): string | | + | getPrice(): double | + search(GuitarSpec*): Guitar* | + getPrice(): double | | + | getSpec(): GuitarSpec* | + addMandolin(string, double, MandolinSpec*) | + getSpec(): MandolinSpec* | | | | + getMandolin(string): Mandolin* | | | | | + removeMandolin(string) | | | | | + search(Mandolin Spec*): Mandolin* | | | | GuitarSpec | MandolinSpec | |---|---|---| | - | builder: Builder | - builder: Builder | | - | model: string | - model: string | | - | frontWood: Wood | - frontWood: Wood | | - | backWood: Wood | - backWood: Wood | | - | numStrings: int | - type: Style | | + | getBuilder(): Builder | + getBuilder(): Builder | | + | getModel(): string | + getModel(): string | | + | getFrontWood(): Wood | + getFrontWood(): Wood | | + | getBackWood(): Wood | + getBackWood(): Wood | | + | getNumStrings(): int | + getStyle(): Style | | + | matches(GuitarSpec*) | + matches(MandolinSpec*) | ## Page 24 ## Principle - Abstract what is common. - Encapsulate what varies. ## Page 25 - Add an Instrument base class that contains attributes/methods common to Guitar and Mandolin. | | Instrument | Inventory | InstrumentSpec | GuitarSpec | MandolinSpec | |---|---|---|---|---|---| | - | serialNumber: string | - instruments: vector<Instrument*> | - builder: Builder | - numStrings: int | - style: Style | | - | price: double | + addInstrument(string, double, InstrumentSpec*) | - model: string | + getNumStrings(): int | + getStyle(): Style | | - | spec: InstrumentSpec* | + getInstrument(string): Instrument* | - frontWood: Wood | | | | + | Instrument(string, double, InstrumentSpec*) | + removeInstrument(string) | - backWood: Wood | | | | + | getSerialNum(): string | + search(GuitarSpec*): Guitar* | + getBuilder(): Builder | | | | + | getPrice(): double | + search(MandolinSpec*): Mandolin* | + getModel(): string | | | | + | getSpec(): InstrumentSpec* | | + getFrontWood(): Wood | | | | | | | + getBottomWood() : Wood | | | - **Update Inventory to use the new classes.** - **Add an InstrumentSpec base class that contains attributes/methods common to GuitarSpec and MandolinSpec** ## Page 26 - Made a lot of changes to the design, but there is much less repetition of code. - There are still problems and things we can improve! | | Instrument | Inventory | InstrumentSpec | GuitarSpec | MandolinSpec | |---|---|---|---|---|---| | | | | - model: string | - numStrings: int | - style: Style | | | | | | + getNumStrings(): int | + getStyle(): Style | | | | | + getBottomWood() : Wood | | | ## Page 27 - What are these classes contributing? | | Instrument | Inventory | InstrumentSpec | GuitarSpec | MandolinSpec | |---|---|---|---|---|---| | | | - instruments: vector<Instrument*> | builder: Builder | numStrings: int | style: Style | | | + addInstrument(string, double, InstrumentSpec*) | | model: string | + getNumStrings(): int | + getStyle(): Style | | | + getInstrument(string): Instrument* | | frontWood: Wood | | | | | + removeInstrument(string) | | backWood: Wood | | | | | + search(GuitarSpec*): Guitar* | | + getBuilder(): Builder | | | | | + search(Mandolin Spec*): Mandolin* | | + getModel(): string | | | | | | | + getFrontWood(): Wood | | | | | | | + getBottomWood(): Wood | | | - Why two? - Can simply this with a data structure ## Page 28 ## Classes need a reason to exist - If a class is not "pulling its weight", then it needs to be removed. - As we change a design, classes often become shadows of their former selves. - It can be hard to eliminate something you spent time on! ## Page 29 ## Map Data Structure - A map is a collection that stores [key, value] pairs - A.K.A. Dictionary, Associative Array | Key | Value | |---|---| | "topWood" | "Alder" | | "backWood" | "Maple" | | "numStrings" | 6 | | "builder" | "Fender" | | "model" | "Stratocaster" | - InstrumentSpec::properties - Declaring - map<type, type> - e.g. map<string, string> properties; - Operations - Add/Insert: - properties[“wood”] = “Alder”; - Get: - string madeOf = properties[“wood”]; - string madeOf = properties.at(“wood”); ## Page 30 ## Principle - Program to the interface, not the implementation. ## Page 31 ## Programming to the implementation (concrete classes), not the interface (base/abstract class). | | Instrument | Inventory | InstrumentSpec | GuitarSpec | MandolinSpec | |---|---|---|---|---|---| | | | - instruments: vector<Instrument*> | builder: Builder | - numStrings: int | - style: Style | | | + addInstrument(string, double, InstrumentSpec*) | | model: string | + getNumStrings(): int | + getStyle(): Style | | | + getInstrument(string): Instrument* | | frontWood: Wood | | | | | + removeInstrument(string) | | backWood: Wood | | | | | + search(GuitarSpec*): Guitar* | | + getBuilder(): Builder | | | | | + search(Mandolin Spec*): Mandolin* | | + getModel(): string | | | | | | | + getFrontWood(): Wood | | | | | | | + getBottomWood(): Wood | | | ## Page 32 - Programming to the interface. - Got rid of the "dead" classes: - Guitar - Mandolin | | Instrument | Inventory | InstrumentSpec | |---|---|---|---| | - | serialNumber: string | - instruments: vector<Instrument*> | - properties: map <string, string> | | - | price: double | + addInstrument(string, double, InstrumentSpec*) | + getProperty (string): Object* | | - | spec: InstrumentSpec* | + getInstrument(string): Instrument* | + getProperties(): map <string, Object*> | | + | Instrument(string, double, InstrumentSpec*) | + removeInstrument(string) | + matches(InstrumentSpec*) | | | | + search(InstrumentSpec*): Instrument* | | - Replaced *Spec classes with a map. ## Page 33 - Made a lot of changes (again!) to the design, but the design is simpler and there is much less code. | | Instrument | Inventory | InstrumentSpec | |---|---|---|---| | | | - instruments: vector<Instrument*> | - properties: map <string, string> | | | | + addInstrun | + getProperty (string): Object | | | | + getInstrum | + getProperties(): map <string, Object*> | | | | + removelns | + matches(InstrumentSpec*) | | | | + search(Ins | | ## Page 34 ## THE KEY PRINCIPLES ## Page 35 ## Classes Should Only Have One Responsibility - In the initial implementation of the guitar store, the design violated this in two ways: 1. The Guitar class served as both a representation of a guitar in the inventory AND a description of the guitar for searching. This was fixed by encapsulation - creating another class (GuitarSpec) that had the responsibility of describing a guitar. 2. Inventory.search() handled all of the comparisons of attributes, leading to an inflexible method when new attributes were added to the Guitar class. This was fixed using delegation by creating a matches() method in GuitarSpec that Inventory.search() calls. ## Page 36 ## Abstract what is common, encapsulate what varies. - To support the store selling mandolins, we need to create a Mandolin and MandolinSpec class. However, both of these classes were very similar to the Guitar and GuitarSpec class. We abstracted the common attributes into a superclass (Instrument and InstrumentSpec) and encapsulated the variations in subclasses (numString for GuitarSpec and type for MandolinSpec) - As the InstrumentSpec classes only contained attributes, these attributes could be encapsulated using a data structure that supports key-value pairs (i.e. a map) ## Page 37 ## Program to an interface not an implementation - In Instrument, search() had as a parameter the specific *Spec class for the type of instrument for which we were looking. This led to having to create multiple search(). Instead of programming to the concreate *Spec classes, we changed search to take the abstract InstrumentSpec class and use polymorphism to call the correct matches() method. Then Inventory can search for any instrument, even ones not created yet, without having to change. ## Page 38 ## Do Not Repeat Yourself - This principle is a driving force behind the other principles: 1. **Classes having a single responsibility** means that changes need to be made in only one place (usually) in the code. Otherwise, you find yourself making scattered changes throughout the code that are often the same change. 2. **Abstract what is common, encapsulate what varies** what we do to not repeat ourselves 3. **Programming to an interface, not the implementation ** means that methods can depend on generalizations. Otherwise, we end up writing the same code where the only difference is the class being used. ## Page 39 ## CAT CLINIC - Design Problem #1 ## Page 40 ## Northmere Veterinary Clinic - Background - Dr. Brigoakes is the owner and operator of a local veterinary clinic. As she is a cat lover, she only treats animals of the feline persuasion. Recently, Dr. Brigoakes hired her neighbour's nephew, a student at the University of Lethbridge, to implement a simple client-management system for her veterinary clinic. Unfortunately, the nephew had only taken up to CPSC 2620, and had yet to learn effective software design. The current design of the client-management system is the following: ## Page 41 ## Design & UML | | Clients | Client | Cat | |---|---|---|---| | - | clients: vector<Client*> | - phone: int | - name: string | | + | addClient(Client) | - name: string | - breed: string | | + | findClient(Client*) | - cats: vector<Cat*> | - colour: string | | + | getClient(int): Client* | + findCat(Cat*): Cat* | - marking: string | | | | + getName() | - birthYear: int | | | | + getPhoneNumber() | + getName(): string | | | | + getCat(string name) : Cat* | + getBreed(): string | | | | | + getColour(): string | | | | | + getMarking(): string | | | | | + getBirthYear: int | ## Page 42 ## Your Task - Dr. Brigoakes has recently taken on a partner, Dr. Wisner, who treats dogs as well as cats. On the basis of a recommendation from a good friend (not her neighbour!), she has hired your CPSC 2720 project team to re-implement the system to manage dogs as well as cats. She tells you that in the past she has had problems with the existing system, such as not being able to find client records when a name is spelt wrong. - On your own create a new UML class diagram that shows the design for your system which follows the design principles discussed in class such as: - Code to an interface, not an implementation. - Abstract what is common, encapsulate what varies. - Classes should have only one reason to change. - Delegate functionality to the class that has the information. - **Test your design.** Once your team has settled on a new design for the system, test it by seeing what changes need to be made if the clinic starts treating a new type of animal, such as rabbits. ## Page 43 ## A Revised Design | | ClientRecords | ClientSpec | Client | Pet | Breed | Colour | PetSpec | Cat | Dog | Cat Markings | Dog Markings | Markings | |---|---|---|---|---|---|---|---|---|---|---|---|---| | - | clients: List<Client> | - firstname: string | - firstname: string | - name: string | | | - markings: Markings | - markings: CatMarkings | - markings: DogMarkings | | | <interface> | | - | pets: List<Pet> | - lastname: string | - lastname: string | - age: int | | | - name: string | | | | | + matches(): bool | | + | findClient(in client: ClientSpec): List<Client> | - id: int | - phonenumber: int | - breed: Breed | | | - age: int | | | | | | | + | findPet(in pet: PetSpec): List<Pet> | | - pets: List<Pet> | - colour: Colour | | | - breed: Breed | | | | | | | | | | + addPet(in pet: Pet) | - owner: Client | | | | | | | | | | | | | + matches(in client: ClientSpec): bool | + matches(in spec: PetSpec): bool | | | | | | | | ## Page 44 ## Dealing with Rabbits | | ClientRecords | ClientSpec | Client | Pet | Breed | Colour | PetSpec | Cat | Dog | Cat Markings | Dog Markings | Markings | RabbitMarkings | |---|---|---|---|---|---|---|---|---|---|---|---|---|---| | - | clients: List<Client> | - firstname: string | - firstname: string | - name: string | | | - markings: Markings | - markings: CatMarkings | - markings: DogMarkings | | | <interface> | | | - | pets: List<Pet> | - lastname: string | - lastname: string | - age: int | | | - name: string | | | | | + matches(): bool | | | + | findClient(in client: ClientSpec): List<Client> | - id: int | - phonenumber: int | - breed: Breed | | | - age: int | | | | | | | | + | findPet(in pet: PetSpec): List<Pet> | | - pets: List<Pet> | - colour: Colour | | | - breed: Breed | | | | | | | | | | | + addPet(in pet: Pet) | - owner: Client | | | | | | | | | | | | | | + matches(in client: ClientSpec): bool | + matches(in spec: PetSpec): bool | | | | | | | | | - **Change is localized (flexible and maintainable)** ## Page 45 ## ICE CREAM STORE - Design Problem #2 ## Page 46 - What changes are needed to follow the primary design principles? | | DessertCounter | Topping | Syrup | IceCream | Vanilla | Chocolate | Peppermint | MintChocolateChip | Cherries | Nuts | Whipped Cream | HotFudge | Caramel | Sundae | Cone | |---|---|---|---|---|---|---|---|---|---|---|---|---|---|---| | + | +orderCone(in icecream: IceCream [*], in topping: Topping[*]): Cone | - description: string | - ingrdiants: string[*] | | | | | | +serve() | +serve() | +serve() | +serve() | +serve() | - iceCream: IceCream [*] | - icecream: IceCream [*] | | + | +orderSundae(in icecream: IceCream [*], in topping: Topping[*], in syrup: Syrup[*]): Sundae | + getDescription(): string | + getIngredients(): string[*] | | | | | | | | | | | - syrups: Syrup[*] | - toppings: Topping[*] | | + | +addTopping(in cone: Cone, in topping: Topping): Cone | + serve() | + serve() | | | | | | | | | | | - toppings: Topping[*] | - toppings: Topping[*] | | + | +addTopping(in sundae: Sundae, in topping: Topping): Sundae | | | | | | | | | | | | | + addiceCream(in icecream: IceCream) | + addScoop(in icecream: IceCream) | | + | | | | | | | | | | | | | | + addSyrup(in syrup: Syrup) | | | + | | | | | | | | | | | | | | + addTopping(in topping: Topping) | + addTopping(in topping: Topping) | | + | | | | | | | | | | | | | | + serve() | + serve() | ## Page 47 ## Program to

Use Quizgecko on...
Browser
Browser