Convenience Store Challenge
Місцевий магазин має проблеми з системою реєстрації платежів. Власник попросив вас поглянути на нього та запропонувати деякі ідеї щодо оновлення. Дивлячись на дерев’яні рами з рядами намистин, ви думаєте, це буде складним завданням застосувати сучасний підхід до цього місця та реорганізувати процеси. На щастя, ви розробник Python, який знає, як працювати з різними доходами та результатами даних. Через деякий час обговорювалися обсяги робіт і часові рамки та були написані деякі примітки, щоб розділити весь проект на окремі частини для поетапної реалізації.
Ваші нотатки ось,
Товари та кошик
Бажано почати з основ - об’єктів, з якими ви збираєтеся працювати. На першому етапі головною метою є впровадження моделей даних - класів для роботи з даними в майбутньому.
Товар
Цей клас представляє товари, які можна придбати в магазині.
Кожен екземпляр продукту повинен мати такі атрибути:
name
- назва товару (прик. «apple», «juice»)price
- ціна за одну одиницю товару (прик. 3655, 500, 12999)unit
- розмір одиниці окремого товару (прик. 1, 0.500, 12)
Наприклад: яблуко коштує 1059 за кожні 0,1 кг. Це означає, що
name
зберігає"apple"
,price
є1059
і `` unit`` дорівнює0,1
.Клас
Product
повинен реалізовувати методget_total
для обчислення загальної ціни на вказану кількість товару для покупки. Бажану кількість буде передано як необов’язковий аргумент числового типу (int
абоfloat
). Якщо аргумент кількості пропущено, просто використовуйте значення атрибутаunit
.
Пояснення коду
- class conv_store.Product(name: str, price: int, unit: int | float)
Реалізація моделі товару
- Змінні:
name – назва товару
price – вартість однієї одиниці товару (прик. 3655, 500, 12999)
unit – розмір одиниці окремого товару (прик. 1, 0.500, 12)
Цей клас представляє товари, які можна придбати в магазині.
- get_total(quantity: int | float | None = None) int
Повертає загальну ціну за вказану кількість товару
- Параметри:
quantity (int | float, optional) – кількість для покупки, за замовчуванням None
- Повертає:
вартість вказаної кількості товару
- Тип повернення:
int
Якщо аргумент кількості пропущено, замість нього слід використовувати значення атрибута одиниці.
Тестові приклади
product_obj = Product()
product_obj.name = "candy"
product_obj.price = 1059 # 1059 coins
product_obj.unit = 0.1 # for each 0.1
assert product_obj.get_total(0.7) == 7413 # purchase 0.7 units
assert product_obj.get_total() == 1059
Кошик для покупок
Цей клас представляє контейнер для продуктів. Його основна відповідальність - зберігати інформацію про покупки та їх кількість.
Кожен екземпляр кошика повинен зберігати дані про об’єкти
Продукт
у ньому та відповідне значення кількості для кожного окремого продукту.ShoppingCart
має реалізувати методadd_product
, щоб помістити вказану кількість у кошик. Аргументquantity
є необов’язковим, якщо пропущено, просто використовує натомість значенняProduct.unit
.ShoppingCart
має застосувати методget_total
для обчислення загальної ціни всього вмісту кошика.
Пояснення коду
- class conv_store.ShoppingCart
Реалізація моделі кошика
- Змінні:
products – товар, доданий до екземпляра кошика
quantities – відповідна кількість для товару у кошику
Загалом кошик для покупок – це контейнер для продуктів. Екземпляри цього класу обробляють продукт і відповідну кількість для кожного товару в екземплярі кошика для покупок.
- add_product(product: Product, quantity: int | float | None = None) None
Додає товар до кошика
- Параметри:
product – екземпляр товару, щоб додати до кошика
quantity (int | float, optional) – кількість продукту для додавання. За замовчуванням значення одиниці продукту.
Цей метод додає екземпляр продукту та відповідне значення кількості до кошика.
- get_total() int
Повертає загальну вартість усіх товарів у кошику
- Повертає:
загальна вартість кошика
- Тип повернення:
int
Тестові приклади
product_obj = Product()
product_obj.name = "juice"
product_obj.price = 3655
product_obj.unit = 1
cart_obj = ShoppingCart()
cart_obj.add_product(product_obj, 3) # put 3 packs of juice to cart
cart_obj.add_product(product_obj) # add one more (unit = 1)
assert cart_obj.get_total() == 14620 # 3655 x 4
Ініціалізація, представлення та приведення типів
Важко встановлювати властивості одну за одною, також неінформативноотримувати представлення рядків об’єктів за замовчуванням. Настав час це виправити.
Product
має бути ініціалізований усіма необхідними даними, без значень за замовчуванням.Застосуйте
ShoppingCart.__init__
, щоб розділити продукти та кількість між різними візками.Надайте представлення, зрозумілі людині. Наприклад:
Product('juice', 35.66, 1)
<ShoppingCart>
Під час приведення екземпляра продукту до типу
str
він повинен дорівнювати значенню атрибутаname
.Під час приведення екземпляра продукту до типу
float
він має дорівнювати значенню його атрибутаprice
.Під час приведення екземпляра кошика для покупок до типу
float
він має дорівнювати загальному значенню ціни.Під час трансляції екземпляра кошика для покупок до
bool
вважайте йогоTrue
, якщо принаймні один продукт приєднаний до поточного кошика.Запровадити підтримку оператора рівності для ваших об’єктів:
вважати продукти рівними, якщо всі їхні властивості однакові
вважати кошики рівними, якщо продукти та відповідна кількість однакові
Тестові приклади
candy = Product("candy", 1059, 0.1)
sweet = Product("candy", 1059, 0.1)
juice = Product("juice", 3655, 1)
cart_1 = ShoppingCart()
cart_2 = ShoppingCart()
cart_1.add_product(candy, 1)
cart_1.add_product(sweet, 0.5)
cart_2.add_product(juice)
assert cart_1.get_total() == 15885
assert str(candy) == "candy"
assert float(candy) == 10.59
assert float(cart_2) == 36.55
assert candy == sweet
assert sweet != juice
assert cart
Опрацювання платежів
Власник попросив вас запровадити гнучку платіжну систему. Придбання картки покупок складається з кількох кроків:
перевірка кошика - він не повинен бути порожнім або вже придбаним
підтвердження платежу - різні типи платежу вимагають різних перевірок
купівля кошика
Наразі в магазині доступні два типи оплати: готівка та кредитна картка, але їх можна продовжити в будь-який час.
Оновіть клас
ShoppingCart
для обробки стануpurchased
. Зробіть цю властивість protected, оскільки до неї не слід звертатися поза екземпляром картки.Реалізуйте клас
PaymentValidator
зis_valid
, який не приймає аргументів і повертає значення логічного типу. Це абстрактний клас для майбутнього використання.Реалізуйте клас
PaymentProcessor
з методомpurchase
, який бере об’єктShoppingCart
і нічого не повертає. Це абстрактний клас для майбутнього використання.Успадкувати
CashPaymentValidator
від базового валідатора. Екземпляри цього класу вважаються завжди дійсними.Успадкувати
CodeValidator
від основного валідатора.Екземпляри цього класу створено за допомогою аргументу
security_code
.Метод
is_valid
має запитати у клієнта код безпеки та перевірити його зі збереженим значенням. Якщо коди однакові, оплата вважається дійсною.
Створіть
CashPaymentProcessor
, який поєднує поведінкуCashValidator
іPaymentProcessor
. Під час купівлі в кошику з’являються повідомлення «Обробка готівкового платежу…» і «Рахунок у кошику : {float total}» слід роздрукувати.Створіть
CardPaymentProcessor
, який поєднує поведінкуCodeValidator
іPaymentProcessor
. Під час купівлі кошику слід роздрукувати повідомлення «Обробка платежу карткою…» і «Код безпеки : {code}».
Тестові приклади
cart = ShoppingCart()
cart.add_product(Product("juice", 3655, 1), 1)
cash_processor = CashPaymentProcessor()
cash_processor.purchase(cart) # Cart bill: 36.55
card_processor = CardPaymentProcessor("1234")
card_processor.purchase(cart) # Security code: 1234
Більше покращень для кошиків для покупок
Зробіть ваш
ShoppingCart
справжнім контейнеромРеалізуйте
len(cart_obj)
і змусьте його повертати кількість продуктів у кошику.Реалізуйте поведінку
cart[...]
, щоб повертатиtuple
, що містить товар та відповідну кількість (type hint:Tuple[Product, Union[int, float]]
).
Зробіть ваш
ShoppingCart
повторюваним - дозвольте йому надавати примірник продукту та відповідну кількість для кожної ітерації.Уникайте дублювання продуктів. Якщо хтось намагається покласти продукт у кошик, і цей продукт уже там присутній, не робіть цього вдруге - змініть натомість відповідне значення кількості.
Застосуйте метод
remove_product
, щоб повністю видалити деякі продукти з кошика.Застосуйте
sub_product
, щоб зменшити кількість продукту. Якщо кількість дорівнює 0 (нуль) або менше - вилучіть продукт із кошика.
Тестові приклади
candy = Product("candy", 1059, 0.1)
sweet = Product("candy", 1059, 0.1)
juice = Product("juice", 3655, 1)
cart = ShoppingCart()
cart.add_product(candy, 0.75)
cart.add_product(sweet, 0.75)
cart.add_product(juice, 3)
assert len(cart) == 2
assert cart[0] == candy, 1.5 # this may use other value as key
for cart_item, purchase in zip(cart, ((candy, 1.5), (juice, 3))):
assert cart_item == purchase
cart.remove_product(candy)
assert len(cart) == 1
cart.sub_product(juice, 2)
assert cart[0][1] == 2
cart.sub_product(juice, 2)
assert not cart
Тестування програмного забезпечення
Додати автотести для моделей ShoppingCart
і Product
.
Тести мають бути розташовані всередині каталогу «tests».
Для тестування використовуватимуться бібліотеки
pytest
іcoverage
.Залежності проекту потрібно оновити.