TEAM LICENSES: Save money and learn new skills through a Hacking with Swift+ team license >>

Component of components

Forums > Swift

Hi all,

I am curious to know how you would translate my challenge into code - I am a (very) new developer so please have patience with me :)

Currently, I am working on a small tool to support product managers in estimating the cost of their products, with the following logic:

  • Product A is made of several components (Components 1, 2, and 3)
  • Component 1 is made from Raw material X and Y
  • Component 2 is made from raw material Z
  • Component 3 is made from combining component 1 and 2

I am currently working with the two classes Product, Component, and Raw material. However, I have difficulty in understanding how to make a component from other components. Could you help kick-start my mental challenge to get past my brain-block? :)

Thank you.

   

@blokhus has a brainblok:

I am currently working with the two THREE classes Product, Component, and Raw material.
I have difficulty in understanding how to make a component from other components.
Could you help kick-start my mental challenge to get past my brain-block? :)

From some of your past posts, it looks like you've worked your way through the 100 Days of SwiftUI course?

Data Modeling

You describe a classic parent-child relationship in your product-component-material use case. That is, a product is made from one or more components, your components are made from one or more materials.

In a Swift data model you'd represent the product as a class with parameters that describe a single product.

class LunchBasket {
    let name:      String     // Fancy name for the basket, Valentine for Three, Pre-game Celebration
    let cost:      Double     // How many euros will this set us back?
    let isNutFree: Bool       // Is this basket free of nuts?

    // basket contents is an array of edibles that you put in the basket
    var  contents: [Edible]  // stuff your basket with goodies
}

class Edible {
    let name:      String  // Fancy name for something in the basket. Frommage du Jour, Sweets, etc
    let kCalories: Double  // How much damage to consumer's waist line?
    let photo:     Image   // Some representation
    // ... snip...  add more here...
}

Note the contents of the LunchBasket may be empty!

This is just a template. But I think you can figure this out?

If you need a refresher, please review the Moonshot project. It implements one-to-many relationships with astronauts. One mission has three astronauts. Review the Mission struct to see how that was implemented.

Components made from Material, or other Components

This is tricky. What do components and materials have in common? Think about that and then review @twoStraw's lessons on class inheritance. Perhaps you can have both the components and the material inherit from a common class? They may implement some common parameters, perhaps some required methods. The key difference, of course, is that the Components class may consist of other Components!

Then consider how your LunchBasket struct might implement an array of the parent class. I think there were some exercises with cats and dogs inheriting from a common Animal class? Go back to your Checkpoint 7 code in Playgrounds. Can you create an array containing Persian, Lion, Corgi, and Poodle? They are all subclasses of Animal.

See -> Cats and Dogs

Keep Coding!

Please return here and share you Product, Component, and Material models with us! Also, keep asking questions.

   

Paste this simple example into Playgrounds and noodle over the logic.

enum FoodType { case protein, fat, carbohydrate }

// High level. General abstraction of properties for
// any type of food component. Basic building blok.
class GenericComponent: CustomStringConvertible {
    var name:       String
    var kCalories:  Int

    init(name: String = "", kCalories: Int = 0) {
        self.name       = name
        self.kCalories  = kCalories
    }
    var description: String { "\(name) has \(kCalories) KCal"}
}

// Inherit from GenericComponent, but add a new parameter
class SimpleFoodElement: GenericComponent {
    var  foodClass: FoodType
    init(name: String = "", foodClass: FoodType = .protein) {
        self.foodClass = foodClass
        super.init(name: name)
    }
    override var description: String {"\(name) (\(foodClass)) has \(kCalories) KCal"}
}

// A sandwich spread may be a mix of mayo and catsup, with paprika
class ComplexFoodElement: GenericComponent {
    private(set) var ingredients = [SimpleFoodElement]()
    // Make a complex food element from one or more SimpleFoodElements
    public func addIngredient(subIngredient: SimpleFoodElement) { ingredients.append(subIngredient) }

    override var description: String {
        guard !ingredients.isEmpty else { return "no ingredients" }
        return "\(name)\n" + ingredients.map { ".." + $0.description }.joined(separator: "\n")
    }
}

var πŸ₯“            = SimpleFoodElement(name: "bacon",        foodClass: .protein)
πŸ₯“.kCalories      = 800

var πŸ₯¬            = SimpleFoodElement(name: "lettuce",      foodClass: .carbohydrate)
πŸ₯¬.kCalories      = 0
//
var catsup        = SimpleFoodElement(name: "catsup",       foodClass: .carbohydrate)
catsup.kCalories  = 1000

var mayo          = SimpleFoodElement(name: "M&S Mayo",     foodClass: .fat)
mayo.kCalories    = 700

var 🍞            = SimpleFoodElement(name: "Wonder Bread", foodClass: .carbohydrate)
🍞.kCalories      = 600
//
// Spread made from three SingleFoodElements
var 🍯            = ComplexFoodElement(name: "BLT sauce")
🍯.addIngredient(subIngredient: mayo   )
🍯.addIngredient(subIngredient: catsup )
🍯.addIngredient(subIngredient: SimpleFoodElement(name: "paprika", foodClass: .carbohydrate))

print("\n---------- Ingredients ------------")
print( πŸ₯“   )  // Simple
print( πŸ₯¬   )  // Simple
print( 🍞   )  // Simple
print( 🍯   )  // Complex

class Sandwich: GenericComponent {
    // A sandwich may have one, or more sub-ingredients
    // Notice the type of the array. It's an array of GenericComponents!
    private (set) var ingredients =  [GenericComponent]()

    // add something that is a generic component
    // SingleFoodElement or ComplexFoodElement
    public func addIngredient( _ ingredient: GenericComponent ) { ingredients.append(ingredient) }

    override var description: String { 
        guard !ingredients.isEmpty else { return "no ingredients"}
        return ingredients.map { $0.description }.joined(separator: "\n") }
}
//
let πŸ₯ͺ = Sandwich(name: "BLT")  // Let's make a sandwich
// Add ingredients
πŸ₯ͺ.addIngredient( πŸ₯“     ) // Simple
πŸ₯ͺ.addIngredient( πŸ₯¬     ) // Simple
πŸ₯ͺ.addIngredient( 🍞     ) // Simple
πŸ₯ͺ.addIngredient( 🍯     ) // Complex!
//
print("\n---------- \(πŸ₯ͺ.name) ------------")
print( πŸ₯ͺ  )
//
// Homework!
// Add another complex food type to the sandwich.
// Add tomatoes for a proper BLT!
//

Keep Coding

As a homework assignement, please add tomatoes to the sandwich. For extra credit, add another complex food item to the sandwich. Let us know how you solved your problem.

Share your knowledge.

   

Hacking with Swift is sponsored by Blaze.

SPONSORED Still waiting on your CI build? Speed it up ~3x with Blaze - change one line, pay less, keep your existing GitHub workflows. First 25 HWS readers to use code HACKING at checkout get 50% off the first year. Try it now for free!

Reserve your spot now

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.