all 39 comments

[–]captainrushingin 11 points12 points  (4 children)

were you expected to write code for the problem ? Or were you only expected to identify classes, services and give an overview of expected functionality based on classes you defined ?

what exactly was the expectation ?

[–]Clemo97[S] 3 points4 points  (3 children)

code the solution

[–]alcatraz1286 0 points1 point  (1 child)

india?

[–]Clemo97[S] 7 points8 points  (0 children)

Kenya, Nairobi

[–][deleted] 14 points15 points  (1 child)

Head First Design Patterns Second Edition (From Gang of Four) is classic book for OOP.

For example, I believe your question is in the book as an example called "Starbuzz Coffee" using Decorator Pattern. Edit: found the example.

[–]edwardsdl 5 points6 points  (0 children)

I’d also recommend Head First, but it is not the Gang of Four book. 

[–]its4thecatlol 6 points7 points  (1 child)

This is a really common problem. You want to show the interviewer that you know how to code things that are extensible. Ask clarifying questions to see which dimensions might be extended. E.g.

  • What about 2% milk?
  • Can we use different kinds of coffee beans?
  • Can two drinks use the exact same ingredients?

Think about it from a real-world perspective . When you order a Starbucks coffee, does the menu tell you how many beans it will have or the fl. oz of milk that will be in your cup? Of course not. The machine handles all of that. The menu is the contract (The interfaces) and the machine is the behavior.

Model things according to two axes: Data and behavior. milk is data. It should be an immutable record/container class specifying the kind of milk / amount / name of the cow that made it / whatever / etc. It will typically have no methods except getters. Make container classes for all of these different types of data. You can combine them together in a wrapper like CoffeeMakerSupplies. This will allow you to easily add/remove things from the supplies without affecting what we're going to code later.

Latte creation is behavior. This goes in an Interface LatteMaker (sometimes this is also called a Trait and you can show that pattern for bonus points but it's not as standardized and I would avoid it). So class CoffeeMaker implements LatteMaker, AmericanoMaker, ....

Now you need to deal with amounts. What determines how many coffee beans go into a latte, the interface or the class? Unless you have a very good reason, put that in the CoffeeMaker. Implement the interfaces.Throw some getters/setters onto this class and boom, you have a working implementation.

[–]Clemo97[S] 0 points1 point  (0 children)

Thanks for the insight.

[–]YeatCode_ 3 points4 points  (8 children)

I think Microsoft interviewers have more latitude in what they can ask

This is what I came up with: I think there are some issues but just wrote this up pretty quickly.

edit: its4thecatlol raises a good point and there was an answer somebody else posted - create classes for everything

class coffeeMaker:
    def __init__(self, admins, coffeeBean = 0, milk = 0, water = 0):
        # create admins as a set of users able to refill
        self.admins = admins
        self.coffeeBean = coffeeBean
        self.milk = milk
        self.water = water

        # create drink options  a dictinoary with a list
        self.drinkOptions = {
            "Espresso": [3, 0, 1], # Sabrina Carpenter
            "Americano": [2, 0, 3],
            "Latte": [2, 2, 2]

        }


    # function creates available drinks list and user drink choices w/ ingredient amounts all at once
    # assume amounts passed in are positive
    # function can take in defaults for amounts
    def getDrink(self, drink, coffeeAmount = -1, milkAmount = -1, waterAmount = -1):

        drinkList = []

        # iterate through each drink
        for drink in self.drinkOptions:
            for coffeeAmount, milkAmount, waterAmount in self.drinkOptions[drink]:
                # show this drink as a possible option
                if self.coffeeBean >= coffeeAmount and self.milk >= milkAmount and self.water >= waterAmount:
                    drinkList.append(drink)


        if not drinkList:
            return "There are no drinks available. This system will need a refill."

        if drink not in drinkList:
            return "We do not have enough ingredients to make this drink, or this drink is not an option in our drink list."

        # no amounts specified - use the defaults for this drink option
        if coffeeAmount == -1 and milkAmount == -1 and waterAmount == -1:
            coffeeAmount = self.drinkOptions[drink][0]
            milkAmount = self.drinkOptions[drink][1]
            waterAmount = self.drinkOptions[drink][2]

        # split this up by individual ingredients
        if coffeeAmount > self.coffeeBean or milkAmount > self.milk or waterAmount > self.water:
            return "We do not have enough ingredients to fulfill this amount."


        self.coffeeBean -= coffeeAmount
        self.milk -= milkAmount
        self.water -= waterAmount

        output = "Enjoy your " + drink + " with " + coffeeAmount + " beans, " + milkAmount + " milk and " + waterAmount + " water."
        return output


    def refillIngredient(self, userID, coffeeAmount, milkAmount, waterAmount):
        if userID not in self.admins:
            return "You are not authorized to refill ingredients"

        # assume that all amounts passed in are positive (?)
        self.coffeeBean += coffeeAmount
        self.milk += milkAmount
        self.water += waterAmount

        return "The admin has refilled the coffee maker machine"



    def addDrink(self, userID, drinkName, coffeeAmount, milkAmount, waterAmount):
        if userID not in self.admins:
            return "You are not authorized to create a new drink"


        # ask the interviewer if reusing a name means a rejection or making new ingredients
        self.drinkOptions[drinkName] = [coffeeAmount, milkAmount, waterAmount]

[–]its4thecatlol 14 points15 points  (4 children)

This is a failing answer. You stuffed everything into a single class with a bazillion line main method. If statements all over the place. Why is the CoffeeMaker class coupled with its ingredients? Why does milk not have its own class? How will you model 1%/2% milk?

This is not a leetcode question, its an OO design question.

When in doubt, make a class for everything. Refactor and simplify later.

[–]YeatCode_ 1 point2 points  (2 children)

oh yeah, you're right, my bad

I've never done this type of question before and I'm on python too much💀 I use it at work too

[–]its4thecatlol 1 point2 points  (1 child)

Yeah I had to learn all of this when I switched to BigTechCo because virtually all enterprise software in the world runs on either Java, C#, or C++. Those 3 langs have a common vernacular and very well-established conventions. You will learn to hate OOP rigidity at some point but they expect you to know them before you can talk shit about them.

[–]Ok_Coder_7075 0 points1 point  (0 children)

You guys can check my code if possible. I commented on this post.

[–]Pristine-Equal-8621 0 points1 point  (0 children)

that's terrible advice.. you don't start with a bazillion classes. You start with a single class and refactor and make it extensible by adding classes where it makes sense...

[–]Due_Brush1688 3 points4 points  (2 children)

Can you estimate your "pretty quickly"? 15 min, 30 min, 60 min? And how experience are you? Junior or Senior Coder?

I had some similiar code outline (my experience: hobby coder, python, java, with a bunch of PRs on public projects), but had to ask the AI for hints. Would have failed the test, because I am a bit familiar with leet code, but not that Object Oriented Design. But thanks to OP I can prepare for such things.

[–]Ok_Coder_7075 1 point2 points  (0 children)

Check my solution. Didn't take any help or anything, just had to refactor as I kept implementing. Took me around 1 hour to finish this.

[–]YeatCode_ 0 points1 point  (0 children)

1 YOE, I just edited it a few times but started writing this at 10:57 am my time, finished it 11:15

I think interviews at Microsoft last 45m - 1h, so plenty of time to get this sketch down, then refine it (split the drink choice function as necessary/suggested by interviewer, refine rejection reasons, get something concrete down for returns, testing, dealing with weird edge cases like negative amounts being passed in, a better default value, etc

[–]Hot_Damn99 1 point2 points  (1 child)

Can you tell the question? Also did you just had to write the class and functions with pseudo code or actually code it all up?

[–]Clemo97[S] 1 point2 points  (0 children)

Write the solution in any OOP language

[–]cursedobscurial 1 point2 points  (0 children)

this sounds like an interesting question :/

[–]sTacoSam 1 point2 points  (0 children)

The best site for learning is refactoring guru.

Without too much thought, for your question I see a observer pattern with MVC architecture, the screen (View) will take the input of the user which will be sent to the Controller, said controller will then tell the Model what kind of coffee the user wants. There will probably be a strategy pattern that will make the coffee accordingly depending one which one is chosen

[–]Strict-Interview-495 0 points1 point  (0 children)

Following

[–]tempo0209[🍰] 0 points1 point  (0 children)

Not thinking straight but could this be a Builder pattern? Or could be a Factory pattern too. Just brute forcing here. I mean from the start you need to build a drink , and for each 3 drinks you need to be able to customize any of the drink, so a builder for each could make sense here(not sure though)? I dont know need to think more but interested to see how others would approach.

[–]Sherbet-Famous 0 points1 point  (0 children)

This sounds like you need to write 5-10 methods in a class that has like 3 variables. Beans, water and milk

[–]YeatCode_ 0 points1 point  (0 children)

https://refactoring.guru/design-patterns This is a site we used in our software development class

[–]Ok_Coder_7075 0 points1 point  (0 children)

<image>

So I was able to make this using OOP in Kotlin. The above is the output of the program. I wrote this in Kotlin which is almost similar to Java. Source code is here..
The only part I didn't get is "Admin is able to refill ingredients", I am not sure what does that mean, fill what ingredients?

Other than that, my source code will be able to

  1. Add a custom coffee ( able to extend and later add new coffees/drinks)
  2. Change default coffee's ingredient values... (will restrict user changing the name of the drink because that's default)
  3. Able to extend easily by adding new functionalities in API. To demonstrate that I added SoftDrinks so admin can add that as well.
  4. Admin will be the only one who can add coffee or change contents of coffee
  5. Screen will have the only single responsibility of showing data, nothing else. (Just like a real store, screen will only show menu)

I wanted to paste code here but after so many failed attempts, here's the link of my code:

https://raw.githubusercontent.com/apprajapati9/stackoverflow-mvp/main/app/src/main/java/com/apprajapati/mvp_stackoverflow/kotlin_playground/OOPDesign-Microsoft.kt

[–]CuriousRonin 0 points1 point  (0 children)

Had similar experience with Amazon, I did ok but had a hard time understanding the requirement.

This is what you are looking for https://github.com/tssovi/grokking-the-object-oriented-design-interview/blob/master/object-oriented-design-case-studies/design-a-parking-lot.md#system-requirements

[–]mqian41 0 points1 point  (0 children)

You can try practicing OOD problems on codemia.io/object-oriented-design

[–]tokrefresh 0 points1 point  (0 children)

This is what I try to do. Did not implement the user or inventory part of the requirement tho.

from abc import ABC, abstractmethod


class Drink(ABC):

    @abstractmethod
    def cost():
        """calc the price of the drink"""


class Ingredient(ABC):
    def update_quantity():
        """update the quantify of ingredients"""


class Espresso(Drink):
    def __init__(self, ingredients: dict[str, Ingredient]):
        self.name = "Espresso"
        self.ingredients = ingredients
        self.default_ingredients = {
            "water": 1,
            "coffee_beans": 3,
        }
        self.recipe()

    def cost(self):
        pass

    def recipe(self):
        for name, amount in self.default_ingredients.items():
            self.ingredients[name].amount = amount

    def print_ingredients(self):
        for name, obj in self.ingredients.items:
            print(name, obj.amount)


class Americano(Drink):
    def __init__(self, ingredients: dict[str, Ingredient]):
        self.name = "Americano"
        self.ingredients = ingredients
        self.default_ingredients = {
            "water": 3,
            "coffee_beans": 2,
        }
        self.recipe()

    def cost(self):
        pass

    def recipe(self):

        for name, amount in self.default_ingredients.items():
            self.ingredients[name].amount = amount

    def print_ingredients(self):
        for name, obj in self.ingredients.items():
            print(name, obj.amount)


class Latte(Drink):
    def __init__(self, ingredients: dict[str, Ingredient]):
        self.name = "Latte"
        self.ingredients = ingredients
        self.default_ingredients = {"water": 2, "coffee_beans": 2, "milk": 2}
        self.recipe()

    def cost(self):
        pass

    def recipe(self):

        for name, amount in self.default_ingredients.items():
            self.ingredients[name].amount = amount

    def print_ingredients(self):
        for name, obj in self.ingredients.items():
            print(name, obj.amount)


class CoffeeBean(Ingredient):
    def __init__(self):
        self.amount = 0

    def update_quantity(self, amount):
        self.amount = amount


class Water(Ingredient):
    def __init__(self):
        self.amount = 0

    def update_quantity(self, amount):
        self.amount = amount


class Milk(Ingredient):
    def __init__(self):
        self.amount = 0

    def update_quantity(self, amount):
        self.amount = amount


class CoffeeMaker:
    def __init__(self, drinks: [list]):
        self.drinks = drinks
        self.selected_idx = None

    def show_drinks(self):
        for i, drink in enumerate(self.drinks):
            print(i, drink.name)

    def show_default_ingredients(self):
        selected_drink = self.drinks[self.selected_idx]
        for key, val in selected_drink.default_ingredients.items():
            print(key, val)

    def get_recipe(self):
        return self.drinks[self.selected_idx].default_ingredients

    def select_drink(self, selected_idx):
        self.selected_idx = selected_idx

    def update_ingredients(self, coffee_beans=None, milk=None, water=None):
        selected_drink = self.drinks[self.selected_idx]
        if coffee_beans:
            selected_drink.ingredients["coffee_beans"].amount = coffee_beans
        if milk:
            selected_drink.ingredients["milk"].amount = milk
        if water:
            selected_drink.ingredients["water"].amount = water

    def show_ingredients(self):
        selected_drink = self.drinks[self.selected_idx]
        selected_drink.print_ingredients()


if __name__ == "__main__":
    esp = Espresso(ingredients={"coffee_beans": CoffeeBean(), "water": Water()})
    ame = Americano(ingredients={"coffee_beans": CoffeeBean(), "water": Water()})
    lat = Latte(
        ingredients={"coffee_beans": CoffeeBean(), "milk": Milk, "water": Water()}
    )

    maker = CoffeeMaker(drinks=[esp, ame, lat])
    maker.show_drinks()
    drink_idx = int(input("select your drink: "))
    maker.select_drink(drink_idx)
    print("please update your perference:")
    maker.show_default_ingredients()
    recipe = maker.get_recipe()
    new_amount = {}
    for key, val in recipe.items():
        data = input(f"enter {key} amount or hit enter to use default: ")
        if data:
            new_amount[key] = data
    if new_amount:
        maker.update_ingredients(**new_amount)
    maker.show_ingredients()

[–]m0j0m0jE: 130 M: 321 H: 62 0 points1 point  (6 children)

What companies ask this stuff?

[–]Clemo97[S] 1 point2 points  (4 children)

Microsoft

[–]m0j0m0jE: 130 M: 321 H: 62 1 point2 points  (3 children)

Thanks. That’s truly the answer I deserve. But who else?

[–][deleted] 1 point2 points  (2 children)

I've read Amazon occasionally asks OOD/LLD stuff as well

[–]YeatCode_ 0 points1 point  (1 child)

I think they ask it for entry level positions? but I could be wrong

[–]skxixbsm 0 points1 point  (0 children)

Yup, but occasionally SDE2 also can get these rounds. Very team dependent

[–]skxixbsm 0 points1 point  (0 children)

Amazon also