Created
November 29, 2023 12:23
-
-
Save 270ajay/a32987d30537e363d10254df76116278 to your computer and use it in GitHub Desktop.
Code Refactoring: Taken from Derek Banas's Refactoring Playlist on YouTube
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class _FootballPlayer: | |
def __init__( | |
self, | |
passer_rating: int, | |
rushing_yard: int, | |
receiving_yard: int, | |
total_tackles: int, | |
interceptions: int, | |
avg_punt: int, | |
avg_kick_off_return: int, | |
avg_punt_return: int, | |
): | |
self.passer_rating = passer_rating | |
self.rushing_yard = rushing_yard | |
self.receiving_yard = receiving_yard | |
self.total_tackles = total_tackles | |
self.interceptions = interceptions | |
self.avg_punt = avg_punt | |
self.avg_kick_off_return = avg_kick_off_return | |
self.avg_punt_return = avg_punt_return | |
def create_quarter_back_football_player( | |
passer_rating: int, rushing_yards: int | |
) -> _FootballPlayer: | |
return _FootballPlayer(passer_rating, rushing_yards, 0, 0, 0, 0, 0, 0) | |
def create_running_back_football_player(rushing_yards: int) -> _FootballPlayer: | |
return _FootballPlayer(0, rushing_yards, 0, 0, 0, 0, 0, 0) | |
if __name__ == "__main__": | |
print("------- Old way ----------") | |
aaron_qb = _FootballPlayer(102, 100, 0, 0, 0, 0, 0, 0) | |
james_rb = _FootballPlayer(0, 32, 0, 0, 0, 0, 0, 0) | |
print("Passer rating of aaron:", aaron_qb.passer_rating) | |
print("Rushing yards of james:", james_rb.rushing_yard) | |
print("------- New way ----------") | |
new_aaron_qb = create_quarter_back_football_player(102, 100) | |
new_james_rb = create_running_back_football_player(32) | |
print("Passer rating of aaron:", new_aaron_qb.passer_rating) | |
print("Rushing yards of james:", new_james_rb.rushing_yard) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from abc import ABC, abstractmethod | |
class Sandwich: | |
def __init__(self): | |
self._bread: str = "" | |
self._vegetables: str = "" | |
self._meat: str = "" | |
self._cheese: str = "" | |
self._condiments: str = "" | |
def get_bread(self) -> str: | |
return self._bread | |
def get_vegetables(self) -> str: | |
return self._vegetables | |
def get_meat(self) -> str: | |
return self._meat | |
def get_cheese(self) -> str: | |
return self._cheese | |
def get_condiments(self) -> str: | |
return self._condiments | |
def set_bread(self, bread: str) -> None: | |
self._bread = bread | |
def set_vegetables(self, vegetables: str) -> None: | |
self._vegetables = vegetables | |
def set_meat(self, meat: str) -> None: | |
self._meat = meat | |
def set_cheese(self, cheese: str) -> None: | |
self._cheese = cheese | |
def set_condiments(self, condiments: str) -> None: | |
self._condiments = condiments | |
def __str__(self): | |
return ( | |
f"{self.get_bread()} {self.get_vegetables()} {self.get_meat()} " | |
f"{self.get_cheese()} {self.get_condiments()} " | |
) | |
class SandwichBuilder(ABC): | |
def __init__(self): | |
self._sandwich: Sandwich = None | |
def get_sandwich(self) -> Sandwich: | |
return self._sandwich | |
def make_sandwich(self) -> None: | |
self._sandwich = Sandwich() | |
@abstractmethod | |
def build_bread(self) -> None: | |
pass | |
@abstractmethod | |
def build_vegetables(self) -> None: | |
pass | |
@abstractmethod | |
def build_meat(self) -> None: | |
pass | |
@abstractmethod | |
def build_cheese(self) -> None: | |
pass | |
@abstractmethod | |
def build_condiments(self) -> None: | |
pass | |
class BaconLettuceTomatoBuilder(SandwichBuilder): | |
def build_bread(self) -> None: | |
self._sandwich.set_bread("White bread") | |
def build_vegetables(self) -> None: | |
self._sandwich.set_vegetables("Lettuce Tomato") | |
def build_meat(self) -> None: | |
self._sandwich.set_meat("Bacon") | |
def build_cheese(self) -> None: | |
self._sandwich.set_cheese("") | |
def build_condiments(self) -> None: | |
self._sandwich.set_condiments("Mayonnaise") | |
class SandwichArtist: | |
def __init__(self): | |
self._sandwich_builder = None | |
def set_sandwich_builder(self, sandwich_builder: SandwichBuilder): | |
self._sandwich_builder = sandwich_builder | |
def get_sandwich(self) -> Sandwich: | |
return self._sandwich_builder.get_sandwich() | |
def take_sandwich_order(self) -> None: | |
self._sandwich_builder.make_sandwich() | |
self._sandwich_builder.build_bread() | |
self._sandwich_builder.build_vegetables() | |
self._sandwich_builder.build_meat() | |
self._sandwich_builder.build_cheese() | |
self._sandwich_builder.build_condiments() | |
if __name__ == "__main__": | |
paul = SandwichArtist() | |
paul.set_sandwich_builder(BaconLettuceTomatoBuilder()) | |
paul.take_sandwich_order() | |
print(paul.get_sandwich()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Item: | |
def __init__(self, item_name: str): | |
self._item_info: dict[str, str] = {} | |
self._item_name = item_name | |
self._children: list[Item] = [] | |
def get_item_name(self) -> str: | |
return self._item_name | |
def set_item_name(self, item_name: str) -> None: | |
self._item_name = item_name | |
def add(self, child_node: "Item") -> None: | |
self._children.append(child_node) | |
def add_item_information(self, info_name: str, info: str) -> None: | |
self._item_info[info_name] = info | |
def get_item_information(self, info_name: str) -> str: | |
return self._item_info[info_name] | |
def __str__(self): | |
strings_list = [f"\n{self._item_name} "] | |
if self._item_info: | |
strings_list.append(self.display_product_info()) | |
for child_node in self._children: | |
strings_list.append(str(child_node)) | |
return "".join(strings_list) | |
def display_product_info(self) -> str: | |
product_info = "" | |
for info_name, info in self._item_info.items(): | |
product_info += f"{info_name}: {info} " | |
return product_info | |
class ItemBuilder: | |
def __init__(self, root_name: str): | |
self._items: list[Item] = [] | |
self._root: Item = Item(root_name) | |
self._current: Item = self._root | |
self._parent: Item = self._root | |
self.add_item(self._root) | |
self._root.add_item_information("Parent", self._parent.get_item_name()) | |
def add_item_information(self, name: str, value: str) -> None: | |
self._current.add_item_information(name, value) | |
def add_child(self, child_name: str) -> None: | |
child_node = Item(child_name) | |
self.add_item(child_node) | |
self._current.add(child_node) | |
self._parent = self._current | |
self._current = child_node | |
child_node.add_item_information("Parent", self._parent.get_item_name()) | |
def add_sibling(self, sibling_name: str) -> None: | |
sibling_node = Item(sibling_name) | |
self.add_item(sibling_node) | |
self._parent.add(sibling_node) | |
self._current = sibling_node | |
sibling_node.add_item_information("Parent", self._parent.get_item_name()) | |
def add_item(self, item: Item) -> None: | |
self._items.append(item) | |
def __str__(self): | |
return str(self._root) | |
def display_all_items(self) -> None: | |
for item in self._items: | |
print(f"{item.get_item_name()}: {item.display_product_info()}") | |
def edit_this_item(self, item_name: str) -> None: | |
for item in self._items: | |
if item.get_item_name() == item_name: | |
self._current = item | |
self.set_item_parent(self._current.get_item_information("Parent")) | |
return | |
def set_item_parent(self, parent_item_name: str) -> None: | |
for item in self._items: | |
if item.get_item_name() == parent_item_name: | |
self._parent = item | |
return | |
def get_item_by_name(self, item_name_to_get: str) -> Item: | |
item_to_return = None | |
for item in self._items: | |
if item.get_item_name() == item_name_to_get: | |
item_to_return = item | |
break | |
return item_to_return | |
if __name__ == "__main__": | |
products = ItemBuilder("Products") | |
products.add_child("Produce") | |
products.add_child("Orange") | |
products.add_item_information("Price", "$1.00") | |
products.add_item_information("Stock", "100") | |
products.add_sibling("Apple") | |
products.add_sibling("Grapes") | |
products.edit_this_item("Products") | |
products.add_child("Cereal") | |
products.add_child("Special K") | |
products.add_item_information("Price", "$4.00") | |
products.add_sibling("Raisin Bran") | |
products.add_item_information("Price", "$4.00") | |
products.add_sibling("Fiber One") | |
products.add_item_information("Price", "$4.00") | |
products.display_all_items() | |
print(f"{products.get_item_by_name('Cereal')}") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from abc import ABC, abstractmethod | |
class Customer: | |
def __init__(self, age: int, is_man: bool, bill: float): | |
self._age = age | |
self._is_man = is_man | |
self._bill = bill | |
def print_final_bill(self) -> None: | |
percentage_off = 0.0 | |
if self._age > 60: | |
percentage_off += 0.05 | |
if not self._is_man: | |
percentage_off += 0.05 | |
print(f"Bill Amount: ${self._bill - (self._bill * percentage_off)}") | |
# ------------------------------------ | |
class BillPayer(ABC): | |
@abstractmethod | |
def calculate_bill(self, amount_due: float) -> None: | |
pass | |
class Command(ABC): | |
@abstractmethod | |
def execute_calculation_bill(self, amount_due: float) -> None: | |
pass | |
class WomanOver60(BillPayer): | |
def calculate_bill(self, amount_due: float) -> None: | |
print(f"Bill amount for Woman over 60: ${amount_due - (amount_due * 0.10)}") | |
class ManOver60(BillPayer): | |
def calculate_bill(self, amount_due: float) -> None: | |
print(f"Bill amount for Woman over 60: ${amount_due - (amount_due * 0.05)}") | |
class ManUnder60(BillPayer): | |
def calculate_bill(self, amount_due: float) -> None: | |
print(f"Bill amount for Woman over 60: ${amount_due}") | |
class Waiter(Command): | |
def __init__(self, the_payer: BillPayer): | |
self.the_payer = the_payer | |
def execute_calculation_bill(self, amount_due: float) -> None: | |
self.the_payer.calculate_bill(amount_due) | |
class CashRegister: | |
def __init__(self, the_command: Command): | |
self.the_command = the_command | |
def print_final_bill(self, amount_due: float) -> None: | |
self.the_command.execute_calculation_bill(amount_due) | |
def get_woman_over_60() -> BillPayer: | |
return WomanOver60() | |
def get_man_over_60() -> BillPayer: | |
return ManOver60() | |
def get_man_under_60() -> BillPayer: | |
return ManUnder60() | |
class CustomerGroup: | |
def __init__(self): | |
self.customers: list[BillPayer] = [] | |
def add(self, new_payer: BillPayer) -> None: | |
self.customers.append(new_payer) | |
def get(self, customer_index: int) -> BillPayer: | |
return self.customers[customer_index] | |
if __name__ == "__main__": | |
# old code | |
bill_smith = Customer(62, True, 12) | |
bill_smith.print_final_bill() | |
print() | |
# new code | |
sally_may = get_woman_over_60() | |
the_waiter = Waiter(sally_may) | |
calculate_bill = CashRegister(the_waiter) | |
calculate_bill.print_final_bill(12.00) | |
paul_thumb = get_man_over_60() | |
the_waiter = Waiter(paul_thumb) | |
calculate_bill = CashRegister(the_waiter) | |
calculate_bill.print_final_bill(12.00) | |
customer_group = CustomerGroup() | |
customer_group.add(get_man_under_60()) | |
customer_group.get(0).calculate_bill(12) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from abc import ABC, abstractmethod | |
class EnemyShip: | |
def __init__(self, current_level: int): | |
if current_level <= 5: | |
self.name: str = "Galax" | |
self.attack_power: int = 5 | |
self.spaces_moved_per_turn: int = 2 | |
elif current_level > 5 or current_level <= 10: | |
self.name: str = "Galaxian" | |
self.attack_power: int = 10 | |
self.spaces_moved_per_turn: int = 3 | |
elif current_level > 10: | |
self.name: str = "Galaxian Prime" | |
self.attack_power: int = 15 | |
self.spaces_moved_per_turn: int = 4 | |
def move_ship(self) -> None: | |
print(f"{self.name} moves {self.spaces_moved_per_turn} spaces") | |
def make_ship_attack(self) -> None: | |
print(f"{self.name} does {self.attack_power} damage") | |
class GalaxianPrime(EnemyShip): | |
def __init__(self, current_level: int): | |
super().__init__(current_level) | |
def move_ship(self) -> None: | |
print( | |
f"{self.name} turns on forcefield and moves {self.spaces_moved_per_turn} spaces" | |
) | |
# ------------------------------------- | |
class Enemy(ABC): | |
@abstractmethod | |
def move_ship(self) -> None: | |
pass | |
@abstractmethod | |
def make_ship_attack(self) -> None: | |
pass | |
class Galax(Enemy): | |
def __init__(self): | |
self.attack_power: int = 5 | |
self.spaces_moved_per_turn: int = 2 | |
def move_ship(self) -> None: | |
print(f"Galax moves {self.spaces_moved_per_turn} spaces") | |
def make_ship_attack(self) -> None: | |
print(f"Galax does {self.attack_power} damage") | |
class GalaxPrime: | |
def __init__(self): | |
self.name: str = "Galaxian Prime" | |
self.attack_power: int = 15 | |
self.spaces_moved_per_turn: int = 4 | |
def turn_on_force_field(self) -> None: | |
print(f"{self.name} turns on force field") | |
def warp_to_space(self) -> None: | |
print(f"{self.name} warps {self.spaces_moved_per_turn} spaces") | |
def charge_phasers(self) -> None: | |
print(f"{self.name} charges phasers") | |
def fire_phasers(self) -> None: | |
print(f"{self.name} fires phasers for {self.attack_power}") | |
class EnemyAdapter(Enemy): | |
def __init__(self, galax_prime: GalaxPrime): | |
self.galax_prime = galax_prime | |
def move_ship(self) -> None: | |
self.galax_prime.turn_on_force_field() | |
self.galax_prime.warp_to_space() | |
def make_ship_attack(self) -> None: | |
self.galax_prime.charge_phasers() | |
self.galax_prime.fire_phasers() | |
if __name__ == "__main__": | |
level1_ship = EnemyShip(6) | |
level1_ship.move_ship() | |
level1_ship.make_ship_attack() | |
prime_time = GalaxianPrime(15) | |
prime_time.move_ship() | |
prime_time.make_ship_attack() | |
# --------------------- | |
print() | |
galax = Galax() | |
galax_prime = EnemyAdapter(GalaxPrime()) | |
galax.move_ship() | |
galax.make_ship_attack() | |
galax_prime.move_ship() | |
galax_prime.make_ship_attack() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class ATMAccessBad: | |
CARD_ENTERED = "CARD ENTERED" | |
VALID_CARD = "VALID CARD" | |
VALID_PIN = "VALID_PIN" | |
VALID_CASH_REQUEST = "VALID_CASH_REQUEST" | |
DENIED = "DENIED" | |
CARD_NUMBER = 123456789 | |
PIN_NUMBER = 1234 | |
CARD_BALANCE = 1000.00 | |
def __init__(self): | |
self.state: str = ATMAccessBad.CARD_ENTERED | |
def verify_card(self, card_number: int) -> None: | |
if ATMAccessBad.CARD_NUMBER == card_number: | |
self.state = ATMAccessBad.VALID_CARD | |
else: | |
self.state = ATMAccessBad.DENIED | |
def verify_pin(self, pin_number: int) -> None: | |
if ATMAccessBad.PIN_NUMBER == pin_number: | |
self.state = ATMAccessBad.VALID_PIN | |
else: | |
self.state = ATMAccessBad.DENIED | |
def verify_withdrawal_amount(self, withdrawal_request: float) -> None: | |
if ATMAccessBad.CARD_BALANCE > withdrawal_request: | |
self.state = ATMAccessBad.VALID_CASH_REQUEST | |
else: | |
self.state = ATMAccessBad.DENIED | |
# ---------------------- | |
class ATMCardState: | |
def __init__(self, name: str): | |
self.name = name | |
def __str__(self): | |
return self.name | |
class ATMAccess: | |
CARD_ENTERED = ATMCardState("CARD ENTERED") | |
VALID_CARD = ATMCardState("VALID CARD") | |
VALID_PIN = ATMCardState("VALID_PIN") | |
VALID_CASH_REQUEST = ATMCardState("VALID_CASH_REQUEST") | |
DENIED = ATMCardState("DENIED") | |
CARD_NUMBER: int = 123456789 | |
PIN_NUMBER: int = 1234 | |
CARD_BALANCE: float = 1000.00 | |
def __init__(self): | |
self.state: ATMCardState = ATMAccess.CARD_ENTERED | |
@property | |
def state(self) -> str: | |
return str(self._state) | |
@state.setter | |
def state(self, value: ATMCardState): | |
if not isinstance(value, ATMCardState): | |
raise TypeError( | |
f"state only accepts ATMCardState object. {type(value)} was assigned" | |
) | |
self._state = value | |
def verify_card(self, card_number: int) -> None: | |
if ( | |
self.state == str(ATMAccess.CARD_ENTERED) | |
and ATMAccess.CARD_NUMBER == card_number | |
): | |
self.state = ATMAccess.VALID_CARD | |
else: | |
self.state = ATMAccess.DENIED | |
def verify_pin(self, pin_number: int) -> None: | |
if ( | |
self.state == str(ATMAccess.VALID_CARD) | |
and ATMAccess.PIN_NUMBER == pin_number | |
): | |
self.state = ATMAccess.VALID_PIN | |
else: | |
self.state = ATMAccess.DENIED | |
def verify_withdrawal_amount(self, withdrawal_request: float) -> None: | |
if ( | |
self.state == str(ATMAccess.VALID_PIN) | |
and ATMAccess.CARD_BALANCE > withdrawal_request | |
): | |
self.state = ATMAccess.VALID_CASH_REQUEST | |
else: | |
self.state = ATMAccess.DENIED | |
if __name__ == "__main__": | |
user = ATMAccessBad() | |
print(user.state) | |
user.verify_card(123456789) | |
print(user.state) | |
user.verify_pin(1234) | |
print(user.state) | |
user.verify_withdrawal_amount(99) | |
print(user.state) | |
# --------------- | |
print() | |
user_new = ATMAccess() | |
print(user_new.state) | |
user_new.verify_card(123456789) | |
print(user_new.state) | |
user_new.verify_pin(1234) | |
print(user_new.state) | |
user_new.verify_withdrawal_amount(99) | |
print(user_new.state) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from abc import ABC, abstractmethod | |
class HairCutOptions: | |
def __init__(self, name: str): | |
self.name = name | |
def __str__(self): | |
return self.name | |
class CalculateHairCut: | |
BASIC_CUT = HairCutOptions("BASIC CUT") | |
PERM = HairCutOptions("PERM") | |
HAIR_FROSTING = HairCutOptions("HAIR FROSTING") | |
def __init__(self, options: HairCutOptions): | |
if not isinstance(options, HairCutOptions): | |
raise TypeError( | |
f"Options must be of HairCutOptions. {type(options)} was assigned" | |
) | |
self.option_picked = options | |
def get_description(self) -> str: | |
if self.option_picked == CalculateHairCut.PERM: | |
return f"Trim the hair.\nAdd chemicals and put hair in rollers." | |
elif self.option_picked == CalculateHairCut.HAIR_FROSTING: | |
return f"Trim the hair.\nAdd chemicals and put hair in foil." | |
else: | |
return "Trim the hair." | |
def get_cost(self) -> float: | |
if self.option_picked == CalculateHairCut.PERM: | |
return 85.00 | |
elif self.option_picked == CalculateHairCut.HAIR_FROSTING: | |
return 100.00 | |
else: | |
return 10.00 | |
# -------------------------- | |
class HairCut(ABC): | |
@abstractmethod | |
def get_description(self) -> str: | |
pass | |
@abstractmethod | |
def get_cost(self) -> float: | |
pass | |
class HairCutDecorator(HairCut): | |
def __init__(self, hair_cut: HairCut): | |
self.hair_cut = hair_cut | |
def get_description(self) -> str: | |
return self.hair_cut.get_description() | |
def get_cost(self) -> float: | |
return self.hair_cut.get_cost() | |
class RegularHairCut(HairCut): | |
def get_description(self) -> str: | |
return "Trim the hair." | |
def get_cost(self) -> float: | |
return 10.00 | |
class Perm(HairCutDecorator): | |
def __init__(self, hair_cut: HairCut): | |
super().__init__(hair_cut) | |
def get_description(self) -> str: | |
return ( | |
f"{self.hair_cut.get_description()}\nAdd chemicals and put hair in rollers." | |
) | |
def get_cost(self) -> float: | |
return self.hair_cut.get_cost() + 75.00 | |
if __name__ == "__main__": | |
perm_cut = CalculateHairCut(CalculateHairCut.PERM) | |
print("SERVICES") | |
print(perm_cut.get_description()) | |
print(f"Price: ${perm_cut.get_cost()}") | |
# --------------------- | |
print() | |
perm_and_cut = Perm(RegularHairCut()) | |
print("SERVICES") | |
print(perm_and_cut.get_description()) | |
print(f"Price: ${perm_and_cut.get_cost()}") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from abc import ABC, abstractmethod | |
class Visitable(ABC): | |
@abstractmethod | |
def accept(self, visitor: "Visitor") -> float: | |
pass | |
class SalesTrainee(Visitable): | |
def __init__(self, sick_days: int, failed_tests: int, salary: float): | |
self.sick_days = sick_days | |
self.failed_tests = failed_tests | |
self.salary = salary | |
def accept(self, visitor: "Visitor") -> float: | |
return visitor.visit_trainee(self) | |
class Salesman(Visitable): | |
def __init__(self, total_sales_amount: float, new_customers: int): | |
self.total_sales_amount = total_sales_amount | |
self.new_customers = new_customers | |
def accept(self, visitor: "Visitor") -> float: | |
return visitor.visit_sales_man(self) | |
class Boss(Visitable): | |
def __init__( | |
self, total_sales_amount: float, new_customers: int, office_expense: float | |
): | |
self.total_sales_amount = total_sales_amount | |
self.new_customers = new_customers | |
self.office_expenses = office_expense | |
def accept(self, visitor: "Visitor") -> float: | |
return visitor.visit_boss(self) | |
class Visitor(ABC): | |
@abstractmethod | |
def visit_trainee(self, trainee: SalesTrainee) -> float: | |
pass | |
@abstractmethod | |
def visit_sales_man(self, sales_man: Salesman) -> float: | |
pass | |
@abstractmethod | |
def visit_boss(self, boss: Boss) -> float: | |
pass | |
class YearlyBonusVisitor(Visitor): | |
def visit_trainee(self, trainee: SalesTrainee) -> float: | |
print("Trainees Yearly Bonus") | |
yearly_bonus_percentage = ( | |
0.10 if trainee.sick_days < 10 and trainee.failed_tests < 2 else 0.02 | |
) | |
yearly_bonus_amount = trainee.salary * yearly_bonus_percentage | |
return yearly_bonus_amount | |
def visit_sales_man(self, sales_man: Salesman) -> float: | |
print("Salesman Yearly Bonus") | |
yearly_bonus_percentage = ( | |
0.12 | |
if sales_man.total_sales_amount > 100000 and sales_man.new_customers > 50 | |
else 0.04 | |
) | |
yearly_bonus_amount = sales_man.total_sales_amount * yearly_bonus_percentage | |
return yearly_bonus_amount | |
def visit_boss(self, boss: Boss) -> float: | |
print("Boss Yearly Bonus") | |
yearly_bonus_percentage = ( | |
0.15 if boss.office_expenses < 50000 and boss.new_customers > 1000 else 0.04 | |
) | |
yearly_bonus_amount = boss.total_sales_amount * yearly_bonus_percentage | |
return yearly_bonus_amount | |
if __name__ == "__main__": | |
yearly_bonus_calculator = YearlyBonusVisitor() | |
brad_trainee = SalesTrainee(5, 1, 20000) | |
tom_salesman = Salesman(150000, 62) | |
ross_boss = Boss(1000000, 1200, 40000) | |
print("YEARLY BONUS") | |
print(brad_trainee.accept(yearly_bonus_calculator)) | |
print(tom_salesman.accept(yearly_bonus_calculator)) | |
print(ross_boss.accept(yearly_bonus_calculator)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from abc import ABC, abstractmethod | |
class Monster(ABC): | |
def __init__(self, name: str): | |
self.name = name | |
self.attack_power: MonsterAttackPower = None | |
self.attack_range: MonsterAttackRange = None | |
@abstractmethod | |
def make_monster(self) -> None: | |
pass | |
def check_if_victim_in_range(self) -> None: | |
print(f"{self.name} checks if victim is {self.attack_range}") | |
def attack_the_victim(self) -> None: | |
print(f"{self.name} attacks the victim for {self.attack_power}") | |
def __str__(self): | |
info_on_monster = ( | |
f"{self.name} attacks anything {self.attack_range} for {self.attack_power}" | |
) | |
return info_on_monster | |
class MonsterAttackPower(ABC): | |
@abstractmethod | |
def __str__(self): | |
pass | |
class BasicAttack(MonsterAttackPower): | |
def __str__(self): | |
return "10 in damage" | |
class MediumAttack(MonsterAttackPower): | |
def __str__(self): | |
return "20 in damage" | |
class MonsterAttackRange(ABC): | |
@abstractmethod | |
def __str__(self): | |
pass | |
class BasicRange(MonsterAttackRange): | |
def __str__(self): | |
return "5 away" | |
class MediumRange(MonsterAttackRange): | |
def __str__(self): | |
return "10 away" | |
class MonsterFactory(ABC): | |
@abstractmethod | |
def assign_attack_power(self) -> MonsterAttackPower: | |
pass | |
@abstractmethod | |
def assign_attack_range(self) -> MonsterAttackRange: | |
pass | |
class ZombieFactory(MonsterFactory): | |
def assign_attack_power(self) -> MonsterAttackPower: | |
return BasicAttack() | |
def assign_attack_range(self) -> MonsterAttackRange: | |
return BasicRange() | |
class VampireFactory(MonsterFactory): | |
def assign_attack_power(self) -> MonsterAttackPower: | |
return MediumAttack() | |
def assign_attack_range(self) -> MonsterAttackRange: | |
return MediumRange() | |
class Zombie(Monster): | |
def __init__(self, name: str, monster_factory: MonsterFactory): | |
super().__init__(name) | |
self.monster_factory = monster_factory | |
def make_monster(self) -> None: | |
print("Making a Zombie") | |
self.attack_power = self.monster_factory.assign_attack_power() | |
self.attack_range = self.monster_factory.assign_attack_range() | |
class Vampire(Monster): | |
def __init__(self, name: str, monster_factory: MonsterFactory): | |
super().__init__(name) | |
self.monster_factory = monster_factory | |
def make_monster(self) -> None: | |
print("Making a Vampire") | |
self.attack_power = self.monster_factory.assign_attack_power() | |
self.attack_range = self.monster_factory.assign_attack_range() | |
class MonsterBuilder(ABC): | |
@abstractmethod | |
def make_monster(self, type_of_monster: str) -> Monster: | |
pass | |
def order_a_monster(self, type_of_monster: str) -> Monster: | |
the_monster = self.make_monster(type_of_monster) | |
the_monster.make_monster() | |
the_monster.check_if_victim_in_range() | |
the_monster.attack_the_victim() | |
return the_monster | |
class OrderAMonster(MonsterBuilder): | |
def make_monster(self, type_of_monster: str) -> Monster: | |
the_monster = None | |
if type_of_monster == "Zombie": | |
monster_factory = ZombieFactory() | |
the_monster = Zombie("Zombie Bob", monster_factory) | |
elif type_of_monster == "Vampire": | |
monster_factory = VampireFactory() | |
the_monster = Vampire("Vampire Paul", monster_factory) | |
return the_monster | |
if __name__ == "__main__": | |
monster_builder = OrderAMonster() | |
zombie = monster_builder.order_a_monster("Zombie") | |
print(zombie, "\n") | |
vampire = monster_builder.order_a_monster("Vampire") | |
print(vampire, "\n") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Player: | |
def __init__(self, name: str, scores: list[int]): | |
self.name: str = name | |
self.scores: list[int] = scores | |
def print_players_info(players: list[Player]) -> None: | |
print(f"{'Name': <15} Avg Score") | |
for _ in range(25): | |
print("`", end="") | |
print() | |
for player in players: | |
player_name = player.name | |
print(f"{player_name: <15}", end="") | |
total_score = 0 | |
count_of_scores = 0 | |
for score in player.scores: | |
total_score += score | |
count_of_scores += 1 | |
average_score = total_score / count_of_scores | |
print(f"{average_score:.2f}") | |
def print_players_info_new(players: list[Player]) -> None: | |
print_title() | |
for player in players: | |
print(f"{player.name: <15}", end="") | |
average_score = sum(player.scores) / len(player.scores) | |
print(f"{average_score:.2f}") | |
def print_title() -> None: | |
print(f"{'Name':<15} Avg Score") | |
for _ in range(25): | |
print("`", end="") | |
print() | |
if __name__ == "__main__": | |
rob = Player("Rob", [23, 32, 43]) | |
john = Player("John", [22, 22, 43]) | |
harry = Player("Harry", [23, 32, 32]) | |
# old code | |
print_players_info([rob, john, harry]) | |
print("\n") | |
# new code | |
print_players_info_new([rob, john, harry]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Product: | |
def __init__( | |
self, | |
quantity: int, | |
name: str, | |
price: float, | |
shipping_cost: float, | |
total_cost: float, | |
): | |
self.quantity = quantity | |
self.name = name | |
self.price = price | |
self.shipping_cost = shipping_cost | |
self.total_cost = total_cost | |
def get_total_cost(quantity: float, price: float) -> float: | |
quantity_discount = 0.0 | |
if (quantity > 50) or ((quantity * price) > 500): | |
quantity_discount = 0.10 | |
elif (quantity > 25) or ((quantity * price) > 100): | |
quantity_discount = 0.07 | |
elif (quantity > 10) or ((quantity * price) > 50): | |
quantity_discount = 0.05 | |
discount = ((quantity - 1) * quantity_discount) * price | |
return (quantity * price) - discount | |
def get_total_cost_new(quantity: float, price: float) -> float: | |
quantity_discount = 0.0 | |
is_over_50_products = (quantity > 50) or ((quantity * price) > 500) | |
is_over_25_products = (quantity > 25) or ((quantity * price) > 100) | |
is_over_10_products = (quantity > 10) or ((quantity * price) > 50) | |
if is_over_50_products: | |
quantity_discount = 0.10 | |
elif is_over_25_products: | |
quantity_discount = 0.07 | |
elif is_over_10_products: | |
quantity_discount = 0.05 | |
discount = ((quantity - 1) * quantity_discount) * price | |
return (quantity * price) - discount | |
def print_product_costs_info(products: list[Product]) -> None: | |
for product in products: | |
print( | |
f"Total cost for {product.quantity} {product.name} is ${product.total_cost}" | |
) | |
print(f"Cost per product {product.total_cost / product.quantity}") | |
print( | |
f"Savings per product {(product.price + product.shipping_cost) - (product.total_cost / product.quantity)}" | |
) | |
print("--------------------------") | |
def print_product_costs_info_new(products: list[Product]) -> None: | |
for product in products: | |
num_products = product.quantity | |
product_name = product.name | |
cost = product.total_cost | |
cost_with_discount = product.total_cost / product.quantity | |
cost_without_discount = product.price + product.shipping_cost | |
print(f"Total cost for {num_products} {product_name} is ${cost}") | |
print(f"Cost per product {cost_with_discount}") | |
print(f"Savings per product {cost_without_discount - cost_with_discount}") | |
print("--------------------------") | |
def print_discounted_cost( | |
total_cost: int, num_products: int, shipping_cost: float, discount: float | |
) -> None: | |
temp = total_cost / num_products | |
temp += shipping_cost | |
temp -= discount | |
print("Discounted cost: ", temp) | |
def print_discounted_cost_new( | |
total_cost: int, num_products: int, shipping_cost: float, discount: float | |
) -> None: | |
individual_product_cost = total_cost / num_products | |
product_cost_and_shipping = individual_product_cost + shipping_cost | |
discounted_product_cost = product_cost_and_shipping - discount | |
print("Discounted cost: ", discounted_product_cost) | |
if __name__ == "__main__": | |
print("Total cost:", get_total_cost(100, 3)) | |
print("Total cost:", get_total_cost_new(100, 3)) | |
print("\n") | |
product1 = Product(50, "Wheat", 102.0, 23.0, 125.0) | |
product2 = Product(20, "Barley", 102.0, 20.0, 122.0) | |
print_product_costs_info([product1, product2]) | |
print("\n") | |
print_product_costs_info_new([product1, product2]) | |
print("\n") | |
print_discounted_cost(100, 10, 10.0, 20.0) | |
print_discounted_cost_new(100, 10, 10.0, 20.0) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import datetime | |
class Customer: | |
def __init__( | |
self, | |
first_name: str, | |
last_name: str, | |
street: str, | |
city: str, | |
state: str, | |
postal_code: int, | |
birthday: str, | |
): | |
self.first_name = first_name | |
self.last_name = last_name | |
self.street = street | |
self.city = city | |
self.state = state | |
self.postal_code = postal_code | |
self.birthday = birthday | |
class Address: | |
def __init__(self, street: str, city: str, state: str, postal_code: int): | |
self.street = street | |
self.city = city | |
self.state = state | |
self.postal_code = postal_code | |
def __str__(self): | |
return f"{self.street} {self.city} {self.state} {self.postal_code}" | |
class CustomerNew: | |
def __init__( | |
self, | |
first_name: str, | |
last_name: str, | |
address: Address, | |
birthday: datetime.datetime, | |
): | |
self.first_name = first_name | |
self.last_name = last_name | |
self.address = address | |
self.birthday = birthday | |
if __name__ == "__main__": | |
sally_smith = Customer( | |
"Sally", "Smith", "123 Main St", "Perry", "Iowa", 50220, "1974-21-12" | |
) | |
sally_smith_new = CustomerNew( | |
"Sally", | |
"Smith", | |
Address("123 Main St", "Perry", "Iowa", 50220), | |
datetime.datetime(1974, 12, 21), | |
) | |
print(f"Customer name: {sally_smith_new.first_name} {sally_smith_new.last_name}") | |
print(f"Customer address: {sally_smith_new.address}") | |
print(f"Customer birthdate in isoformat: {sally_smith_new.birthday.isoformat()}") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from abc import ABC | |
class Customer(ABC): | |
PREMIER = 2 | |
VALUED = 1 | |
DEAD_BEAT = 3 | |
def __init__(self): | |
self._customer_rating: str = "" | |
def get_customer_rating(self) -> str: | |
return self._customer_rating | |
def set_customer_rating(self, _customer_rating: str) -> None: | |
self._customer_rating = _customer_rating | |
class Premier(Customer): | |
def __init__(self): | |
super().__init__() | |
super().set_customer_rating("Premier Customer") | |
class Valued(Customer): | |
def __init__(self): | |
super().__init__() | |
super().set_customer_rating("Valued Customer") | |
class Deadbeat(Customer): | |
def __init__(self): | |
super().__init__() | |
super().set_customer_rating("Deadbeat Customer") | |
def get_customer(customer_type: int) -> Customer: | |
if customer_type == Customer.PREMIER: | |
return Premier() | |
elif customer_type == Customer.VALUED: | |
return Valued() | |
elif customer_type == Customer.DEAD_BEAT: | |
return Deadbeat() | |
else: | |
raise RuntimeError("Invalid customer type") | |
if __name__ == "__main__": | |
# Old code | |
premier_customer = Premier() | |
print(premier_customer.get_customer_rating()) | |
# New code | |
premier_customer_new = get_customer(Customer.PREMIER) | |
print(premier_customer_new.get_customer_rating()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def print_bag_fees(bag_weights: list[int]) -> None: | |
num_bags = len(bag_weights) | |
bag_fees = 0 | |
for i in range(num_bags): | |
if i <= 1: | |
if bag_weights[i] < 50: | |
if i == 0: | |
bag_fees += 25 | |
else: | |
bag_fees += 35 | |
elif bag_weights[i] < 70: | |
bag_fees += 100 | |
elif (i > 1) and (bag_weights[i] < 70): | |
bag_fees += 150 | |
else: | |
bag_fees += 200 | |
print("Bag fees:", bag_fees) | |
def print_bag_fees_new(bag_weights: list[int]) -> None: | |
bag_fees = 0 | |
for bag_number in range(len(bag_weights)): | |
if bag_weights[bag_number] < 50: | |
bag_fees += _fee_for_bag_under_50_lbs(bag_number) | |
elif bag_weights[bag_number] < 70: | |
bag_fees += _fee_for_bag_50_to_70_lbs(bag_number) | |
else: | |
bag_fees += 200 | |
print("Bag fees:", bag_fees) | |
def _fee_for_bag_under_50_lbs(bag_number: int) -> int: | |
return 25 if bag_number < 1 else 35 | |
def _fee_for_bag_50_to_70_lbs(bag_number: int) -> int: | |
return 100 if bag_number < 2 else 150 | |
# -------------------------------------- | |
# Replacing conditional with polymorphism | |
# -------------------------------------- | |
class Animal: | |
def __init__(self, sound: str): | |
self._sound = sound | |
def get_sound(self) -> str: | |
return self._sound | |
def set_sound(self, sound) -> None: | |
self._sound = sound | |
class Dog(Animal): | |
def __init__(self, sound): | |
super().__init__(sound) | |
class Cat(Animal): | |
def __init__(self, sound): | |
super().__init__(sound) | |
def make_sound(animal_name: str) -> None: | |
if animal_name == "Dog": | |
print("Woof") | |
elif animal_name == "Cat": | |
print("Meow") | |
else: | |
raise RuntimeError("Unknown animal") | |
def make_sound_new(animal: Animal) -> None: | |
print(animal.get_sound()) | |
if __name__ == "__main__": | |
print_bag_fees([25, 55, 75]) | |
print_bag_fees_new([25, 55, 75]) | |
rex = Dog("Woof") | |
sophie = Cat("Meow") | |
make_sound(type(rex).__name__) | |
make_sound(type(sophie).__name__) | |
make_sound_new(rex) | |
make_sound_new(sophie) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from abc import ABC, abstractmethod | |
class Employees: | |
"""Problem with this approach: when we had to add new feature like bonus | |
amount, we had to change so many parts of this class.""" | |
def __init__( | |
self, bonus: bool = False, salary: float = 0.0, bonus_amount: float = 0.15 | |
): | |
self._has_bonus = bonus | |
self._salary = salary | |
self._bonus_amount = bonus_amount | |
def get_salary(self) -> float: | |
if self._has_bonus: | |
return self._salary + (self._salary * self._bonus_amount) | |
return self._salary | |
def set_salary(self, salary: float) -> None: | |
self._salary = salary | |
# ------------------------------------------- | |
class Pay(ABC): | |
@abstractmethod | |
def get_pay(self, salary: float) -> float: | |
pass | |
class GetsBonus(Pay): | |
def get_pay(self, salary: float) -> float: | |
return salary + (salary * 0.15) | |
class NoBonus(Pay): | |
def get_pay(self, salary: float) -> float: | |
return salary | |
class Bonus20Percent(Pay): | |
def get_pay(self, salary: float) -> float: | |
return salary + (salary * 0.20) | |
class EmployeesNew: | |
def __init__(self, salary: float = 0.0, pay_type: Pay = NoBonus()): | |
self._salary = salary | |
self._pay_type = pay_type | |
def set_bonus_option(self, pay_type: Pay) -> None: | |
self._pay_type = pay_type | |
def get_pay(self) -> float: | |
return self._pay_type.get_pay(self._salary) | |
if __name__ == "__main__": | |
sales_man = Employees(True, 15000.0, 0.20) | |
secretary = Employees(False, 20000.0) | |
print(sales_man.get_salary()) | |
print(secretary.get_salary()) | |
sales_man_new = EmployeesNew(15000.0) | |
secretary_new = EmployeesNew(25000.0) | |
print("Salesman:", sales_man_new.get_pay()) | |
print("Secretary:", secretary_new.get_pay()) | |
sales_man_new.set_bonus_option(GetsBonus()) | |
secretary_new.set_bonus_option(Bonus20Percent()) | |
print("Salesman:", sales_man_new.get_pay()) | |
print("Secretary:", secretary_new.get_pay()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from abc import ABC, abstractmethod | |
class Hamburger: | |
def __init__(self, wants_condiments: bool): | |
self.wants_condiments = wants_condiments | |
def make_sandwich(self) -> None: | |
print("\n---------- NEW ORDER ----------\n") | |
self.cut_bun() | |
self.add_meat() | |
self.add_vegetables() | |
if self.wants_condiments: | |
self.add_condiments() | |
self.wrap_sandwich() | |
def cut_bun(self) -> None: | |
print("The bun was cut") | |
def add_meat(self) -> None: | |
print("Hamburger added") | |
def add_vegetables(self) -> None: | |
print("Lettuce, onions, and tomatoes") | |
def wrap_sandwich(self) -> None: | |
print("The sandwich was wrapped") | |
def add_condiments(self) -> None: | |
print("Special sauce added") | |
class VeggieSub: | |
def __init__(self, wants_condiments: bool): | |
self.wants_condiments = wants_condiments | |
def make_sandwich(self) -> None: | |
print("\n---------- NEW ORDER ----------\n") | |
self.cut_bun() | |
self.add_vegetables() | |
if self.wants_condiments: | |
self.add_condiments() | |
self.wrap_sandwich() | |
def cut_bun(self) -> None: | |
print("The bun was cut") | |
def add_vegetables(self) -> None: | |
print("Lettuce, onions, and tomatoes") | |
def wrap_sandwich(self) -> None: | |
print("The sandwich was wrapped") | |
def add_condiments(self) -> None: | |
print("Vinegar and oil added") | |
# ------------------------------------------------- | |
class Sandwich(ABC): | |
def make_sandwich(self) -> None: | |
print("\n---------- NEW ORDER ----------\n") | |
self.cut_bun() | |
if self.wants_meat(): | |
self.add_meat() | |
self.add_vegetables() | |
if self.wants_condiments(): | |
self.add_condiments() | |
self.wrap_sandwich() | |
@abstractmethod | |
def add_meat(self) -> None: | |
pass | |
@abstractmethod | |
def add_condiments(self) -> None: | |
pass | |
def cut_bun(self) -> None: | |
print("The bun was cut") | |
def add_vegetables(self) -> None: | |
print("Lettuce, onions, and tomatoes") | |
def wrap_sandwich(self) -> None: | |
print("The sandwich was wrapped") | |
def wants_meat(self) -> bool: | |
return True | |
def wants_condiments(self) -> bool: | |
return True | |
class HamburgerNew(Sandwich): | |
def add_meat(self) -> None: | |
print("Hamburger added") | |
def add_condiments(self) -> None: | |
print("Special sauce added") | |
class VeggieSubNew(Sandwich): | |
def wants_meat(self) -> bool: | |
return False | |
def add_meat(self) -> None: | |
pass | |
def add_condiments(self) -> None: | |
print("Vinegar and oil added") | |
if __name__ == "__main__": | |
customer1 = Hamburger(True) | |
customer1.make_sandwich() | |
customer2 = VeggieSub(True) | |
customer2.make_sandwich() | |
# new code | |
customer1_new = HamburgerNew() | |
customer1_new.make_sandwich() | |
customer2_new = VeggieSubNew() | |
customer2_new.make_sandwich() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from abc import ABC, abstractmethod | |
class ProductComponent(ABC): | |
def add(self, new_product_component: "ProductComponent") -> None: | |
pass | |
def remove(self, new_product_component: "ProductComponent") -> None: | |
pass | |
def get_product_component(self, component_index: int) -> "ProductComponent": | |
return None | |
def get_product_group_name(self) -> str: | |
return None | |
@abstractmethod | |
def display_product_info(self) -> None: | |
pass | |
class ProductGroup(ProductComponent): | |
def __init__(self, product_group_name: str): | |
self._product_components: list[ProductComponent] = [] | |
self._product_group_name = product_group_name | |
def add(self, new_product_component: ProductComponent) -> None: | |
self._product_components.append(new_product_component) | |
def remove(self, new_product_component: ProductComponent) -> None: | |
self._product_components.remove(new_product_component) | |
def get_product_component(self, component_index: int) -> "ProductComponent": | |
return self._product_components[component_index] | |
def get_product_group_name(self) -> str: | |
return self._product_group_name | |
def display_product_info(self): | |
print(self.get_product_group_name()) | |
for product_component in self._product_components: | |
product_component.display_product_info() | |
print() | |
class Product(ProductComponent): | |
def __init__(self, product_name: str, product_price: float): | |
self._product_name = product_name | |
self._product_price = product_price | |
def get_product_name(self) -> str: | |
return self._product_name | |
def get_product_price(self) -> float: | |
return self._product_price | |
def set_product_name(self, product_name: str) -> None: | |
self._product_name = product_name | |
def set_product_price(self, product_price: float) -> None: | |
self._product_price = product_price | |
def display_product_info(self) -> None: | |
print(f"{self.get_product_name()} ${self.get_product_price()}") | |
if __name__ == "__main__": | |
every_product = ProductGroup("All Products\n") | |
produce = ProductGroup("Produce") | |
cereal = ProductGroup("Cereal") | |
every_product.add(produce) | |
every_product.add(cereal) | |
produce.add(Product("Tomato", 1.99)) | |
produce.add(Product("Orange", 0.99)) | |
produce.add(Product("Potato", 0.35)) | |
cereal.add(Product("Special K", 3.68)) | |
cereal.add(Product("Cheerios", 3.68)) | |
cereal.add(Product("Raisin Bran", 3.68)) | |
every_product.display_product_info() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment