- PVSM.RU - https://www.pvsm.ru -

Изначальное решение этой задачи было реализовано с помощью объектно-ориентированного подхода на Go и включало классы для следующих «объектов»: Car, Product, Store. Класс Car представлял конкретную машину в магазине, а класс Product – некий товар, включая машины. В свою очередь, класс Store представлял сам магазин, включая список товаров на складе и список проданных товаров.
Однако после оценки этого решения нас попросили отрефакторить его код, чтобы устранить некоторые проблемы и улучшить общую структуру. Далее мы разберём исходное решение и его отрефакторенную версию, попутно обсудив внесённые изменения и их причины.
Джон только что открыл магазин по продаже машин. Он присвоил каждому автомобилю ценник и выставил их на продажу. Теперь ему нужно вести складской учёт, чтобы контролировать проданные и оставшиеся товары. Например, ему необходимо видеть:
С помощью ООП-принципов на Go нужно создать простые классы для следующих «объектов»:
Класс Car может содержать любые атрибуты автомобиля.
Класс Product должен содержать атрибуты товара, то есть его наименование, количество на складе и стоимость. Машина является товаром, но в магазине есть и другие товары, поэтому атрибуты машины можно возвести к Product. Класс Product должен содержать методы для отображения товара и его статуса – на складе либо продан.
В классе Store должны присутствовать следующие атрибуты и методы:
Это не приложение для интерфейса командной строки или интернета. Идея в том, чтобы продемонстрировать процесс решения задачи, используя знания, полученные на занятиях. Преподавателю необходимо увидеть, как ты мыслишь в роли программиста, и предложенную реализацию он будет оценивать, анализируя одну строку за другой.
На языке Go необходимо реализовать классы для управления товарными запасами и продажами магазина. Его владельцу нужно управлять списком автомобилей, которым он присвоил ценники, отслеживая продажи и остатки. Для этого мы создаём следующие классы:
Car: представляет конкретную машину в магазине. Содержит поле Product, которое означает, что в нём есть все атрибуты и методы структуры Product.Product: представляет товар в магазине, включая машины. Содержит атрибуты товара, такие как его наименование (Name), остаток на складе (Quantity) и цена (Price). Реализует интерфейс ProductInterface, определяющий методы, которые должен реализовывать Product.Store: представляет магазин по продаже товаров, включая машины. Содержит список товаров на складе и список проданных товаров. Реализует интерфейс StoreInterface, определяющий методы, которые должен реализовывать Store.Вот моя первая реализация:
package main
import "fmt"
// Класс Product должен иметь атрибуты товара (то есть наименование, количество на складе и цену)
type Product struct {
Name string
Quantity int
Price float64
}
// Car. Машина является лишь одним из товаров, то есть в магазине могут быть и другие, поэтому её атрибут также относится к Product.
type Car struct {
Product
}
// ProductInterface Класс Product должен содержать методы для отображения товара и его статуса – продан или на складе.
type ProductInterface interface {
DisplayProduct()
DisplayStatus()
}
// Класс Store должен содержать:
// функцию DisplayProduct для отображения товара.
func (p Product) DisplayProduct() {
fmt.Printf("Product: %s", p.Name)
}
// функцию DisplayStatus для отображения статуса товара.
func (p Product) DisplayStatus() {
if p.Quantity > 0 {
fmt.Println("In stock")
} else {
fmt.Println("Out of stock")
}
}
// Класс Store должен содержать следующие атрибуты и методы: количество товаров в магазине, добавление товара, вывод списка всех элементов товаров, продажа элемента, вывод списка проданных товаров и их общей суммы.
type Store struct {
Product []ProductInterface
soldProduct []ProductInterface
}
// StoreInterface. Класс Store должен содержать методы для добавления товара, вывода всех товаров, продажи товара и вывода списка проданных товаров.
type StoreInterface interface {
AddProduct(ProductInterface)
ListProducts()
SellProduct(string)
ListSoldProducts()
}
// AddProduct. Класс Store должен содержать методы для добавления товара, вывода всех товаров, продажи товара и вывода списка проданных товаров.
func (s *Store) AddProduct(p ProductInterface) {
s.Product = append(s.Product, p)
}
// ListProducts. Класс Store должен содержать методы для добавления товара, вывода всех товаров, продажи товара и вывода списка проданных товаров.
func (s *Store) ListProducts() {
for _, p := range s.Product {
p.DisplayProduct()
}
}
// SellProduct. Класс Store должен содержать методы для добавления товара, вывода всех товаров, продажи товара и вывода списка проданных товаров.
func (s *Store) SellProduct(name string) {
// Перебор товаров магазина.
for i, p := range s.Product {
// Если товар найден, он удаляется из магазина и добавляется в срез проданных товаров.
if p.(Car).Name == name {
s.soldProduct = append(s.soldProduct, p)
s.Product = append(s.Product[:i], s.Product[i+1:]...)
}
}
}
// ListSoldProducts. Класс Store должен содержать методы для добавления товара, вывода всех товаров, продажи товара и вывода списка проданных товаров.
func (s *Store) ListSoldProducts() {
for _, p := range s.soldProduct {
p.DisplayProduct()
}
}
func main() {
//Создание магазина.
store := Store{}
//Создание товара.
car := Car{Product{Name: "Toyota", Quantity: 10, Price: 100000}}
//Добавление товара в магазин.
store.AddProduct(car)
//Продажа товара.
store.SellProduct("Toyota")
//Вывод списка всех товаров магазина.
store.ListProducts()
//Вывод всех проданных товаров.
store.ListSoldProducts()
}
В изначальной реализации были некоторые проблемы, требующие решения. Одна из них заключалась в том, что методы структуры Product содержали получателей указателей и получателей значений, что документацией Go не рекомендуется. Кроме того, структура Car содержала поле Product, но оно не было определено как тип.
Чтобы исправить эти проблемы, мы изменили реализацию так:
Car как содержащую поле Product. Это значит, что у неё есть все атрибуты и методы структуры Product.Product как реализующую интерфейс ProductInterface, определяющий методы, которые должен реализовывать Product.Store как реализующую интерфейс StoreInterface, определяющий методы, которые должен реализовывать Store.Product, чтобы они содержали только получателей указателей.
После этих изменений реализация стала корректной и завершённой. Класс Store можно использовать для управления товарным учётом и продажами, а классы Product и Car – для представления и управления товарами и машинами.
Далее приводится итоговая реализация, но сначала я хочу перестроить вопрос, оттолкнувшись от решения. Процесс создания классов для объектов Car, Product и Store можно реализовать следующими этапами:
1. Определить требования для каждого класса:
В класс Car нужно внести атрибуты, описывающие конкретную машину, а именно её Make, Model и Year.
В класс Product нужно включить атрибуты, описывающие товар, а именно его Name, Quantity и Price. Этот класс также должен содержать методы для отображения товара и его статуса (продан или на складе).
В класс Store нужно добавить атрибуты, отслеживающие количество товаров на складе, список товаров на складе и список проданных товаров. Этот класс также должен содержать методы для добавления товара, вывода списка товаров, продажи товара и вывода списка проданных товаров.
2. Реализовать классы на Go:
Здесь мы начнём с реализации класса Product, который будет использоваться классами Car и Store. Он будет иметь атрибуты Name, Quantity и Price, а также методы DisplayProduct() и DisplayStatus().
Далее реализуем класс Car, имеющий тип Product. Этот класс будет содержать атрибуты, наследуемые от класса Product, а также дополнительные, относящиеся конкретно к автомобилю, то есть Make, Model и Year.
Наконец, реализуем класс Store, который будет содержать атрибуты SoldProducts и Products для отслеживания проданных товаров и товаров на складе соответственно. В нём также будут присутствовать методы AddProduct(), ListProducts(), SellProduct() и ListSoldProducts().
3. Протестировать реализацию на предмет соответствия требованиям.
Для проверки реализации мы создадим экземпляры каждого класса и будем использовать методы для выполнения различных задач, таких как добавление товаров, их продажа и вывод списков. Помимо этого, мы добавим обработку ошибок для сценариев, в которых товар оказывается не найден или отсутствует на складе.
package main
import "fmt"
// Класс Car представляет конкретную машину.
type Car struct {
Make string
Model string
Year int
Product
}
// Класс Product представляет товар в магазине, включая машины.
// Он содержит атрибуты товара, такие как его name, quantity и price.
type Product struct {
Name string
Quantity int
Price float64
}
// Интерфейс ProductInterface определяет методы, которые должен реализовывать Product.
type ProductInterface interface {
DisplayProduct()
DisplayStatus()
UpdateQuantity(int) error
}
// DisplayProduct – это метод класса Product, отображающий информацию о товаре.
func (p Product) DisplayProduct() {
fmt.Printf("Product: %sn", p.Name)
fmt.Printf("Quantity: %dn", p.Quantity)
fmt.Printf("Price: $%.2fn", p.Price)
}
// DisplayStatus – это метод класса Product, отображающий статус товара (продан или на складе).
func (p Product) DisplayStatus() {
if p.Quantity > 0 {
fmt.Println("Status: In stock")
} else {
fmt.Println("Status: Out of stock")
}
}
// UpdateQuantity – это метод класса Product, обновляющий количество товара на складе.
func (p Product) UpdateQuantity(quantity int) error {
if p.Quantity+quantity < 0 {
return fmt.Errorf("cannot set quantity to a negative value")
}
p.Quantity += quantity
return nil
}
// Класс Store представляет магазин, продающий товары, в том числе машины.
// Он содержит список товаров на складе и список проданных товаров.
type Store struct {
Products []ProductInterface
SoldProducts []ProductInterface
}
// Интерфейс StoreInterface определяет методы, которые должен реализовывать Store.
type StoreInterface interface {
AddProduct(ProductInterface)
ListProducts()
SellProduct(string) error
ListSoldProducts()
SearchProduct(string) ProductInterface
}
// AddProduct – это метод класса Store, добавляющий товар в список товаров на складе.
func (s *Store) AddProduct(p ProductInterface) {
s.Products = append(s.Products, p)
}
// ListProducts – это метод класса Store, выводящий список всех товаров на складе.
func (s *Store) ListProducts() {
for _, p := range s.Products {
p.DisplayProduct()
p.DisplayStatus()
fmt.Println()
}
}
// SellProduct – это метод класса Store, продающий товар из списка товаров на складе и добавляющий его в список проданных товаров.
func (s *Store) SellProduct(name string) error {
// Поиск товара в списке товаров на складе.
product := s.SearchProduct(name)
if product == nil {
return fmt.Errorf("product not found")
}
//Утверждение, что товар имеет тип Product.
p, ok := product.(*Product)
if !ok {
return fmt.Errorf("product is not a Product type")
}
// Проверяет, достаточно ли на складе товара для его продажи.
if p.Quantity < 1 {
return fmt.Errorf("product is out of stock")
}
// Удаляет товар из списка товаров на складе и добавляет его в список проданных товаров.
for i, p := range s.Products {
// Утверждает, что переменная p имеет тип Product.
p, ok := p.(*Product)
if !ok {
return fmt.Errorf("product has wrong type")
}
if p.Name == name {
s.SoldProducts = append(s.SoldProducts, p)
s.Products = append(s.Products[:i], s.Products[i+1:]...)
break
}
}
// Обновляет количество товаров на складе.
err := product.UpdateQuantity(-1)
if err != nil {
return err
}
return nil
}
// ListSoldProducts – это метод класса Store, выводящий список всех проданных товаров.
func (s *Store) ListSoldProducts() {
for _, p := range s.SoldProducts {
p.DisplayProduct()
fmt.Println()
}
}
// SearchProduct – это метод класса Store, ищущий товар с заданным наименованием в списке товаров на складе.
// При обнаружении этого товара он его возвращает. В противном случае возвращается нуль.
func (s *Store) SearchProduct(name string) ProductInterface {
for _, p := range s.Products {
// Утверждает, что переменная p имеет тип Product.
p, ok := p.(*Product)
if !ok {
return nil
}
if p.Name == name {
return p
}
}
return nil
}
func main() {
// Создаёт новый магазин.
store := &Store{}
// Добавляет в магазин машины.
store.AddProduct(&Car{Make: "Toyota", Model: "Camry", Year: 2020, Product: Product{Name: "Toyota Camry", Quantity: 3, Price: 30000}})
store.AddProduct(&Car{Make: "Honda", Model: "Accord", Year: 2021, Product: Product{Name: "Honda Accord", Quantity: 5, Price: 35000}})
store.AddProduct(&Car{Make: "Ford", Model: "Mustang", Year: 2019, Product: Product{Name: "Ford Mustang", Quantity: 2, Price: 40000}})
// Выводит список товаров в магазине.
fmt.Println("Products in stock:")
store.ListProducts()
fmt.Println()
// Продаёт машину из магазина.
err := store.SellProduct("Toyota Camry")
if err != nil {
fmt.Println(err)
}
// Снова выводит список товаров.
fmt.Println("nProducts in stock:")
store.ListProducts()
fmt.Println()
// Выводит проданные товары.
fmt.Println("nSold products:")
store.ListSoldProducts()
}
Перед нами стояла задача создать классы для управления магазином по продаже автомобилей с помощью принципов ООП на Go.
Магазин должен управлять списком доступных для продажи автомобилей, присваивать им цены и показывать покупателям. Он также должен отслеживать количество проданных машин, общую выручку и список выполненных заказов.
Для решения этой задачи мы определили три класса: Car, Product и Store. Класс Car представляет конкретный автомобиль и может содержать любые выбранные нами атрибуты. Класс Product представляет товар в магазине, включая машины. Он содержит атрибуты товара, такие как Name, Quantity и Price. Кроме того, этот класс содержит методы для отображения товара и его статуса (продан либо на складе).
Класс Store представляет сам магазин и содержит такие атрибуты, как количество проданных и оставшихся товаров, а также методы для добавления товара, вывода списка всех товаров, продажи товара и вывода списка проданных товаров.
В изначальной реализации классы Product и Car были определены верно, а вот с классом Store были проблемы. Во-первых, срез Product в структуре Store был определён как срез значений Product при том, что должен был быть определён как срез значений ProductInterface, поскольку класс Product не реализует интерфейс ProductInterface. Это вызывало ошибку компиляции при попытке использовать метод SellProduct, так как значения Product в срезе Product не содержали необходимых методов.
Ещё одна проблема базовой реализации заключалась в методе SellProduct класса Store. В нём значение продаваемого Product удалялось из среза Product, но в срез soldProduct не добавлялось. В результате метод ListSoldProducts всегда возвращал пустой срез.
Для исправления этих недочётов мы изменили класс Store, чтобы определить срез Product как срез значений ProductInterface, а также добавили в метод SellProduct строчку кода для внесения проданного Product в срез soldProduct. Кроме того, мы добавили в этот метод обработку ошибок для случаев, когда товар не был найден или отсутствовал на складе.
Напоследок скажу, что процесс решения этого экзамена с последующим рефакторингом кода для улучшения его структуры позволил нам углубить своё понимание ООП в Go и применить лучшие практики для создания обслуживаемых и масштабируемых систем. В этой статье мы поделились своим опытом и, надеюсь, предоставили полезную информацию для других людей, решающих подобные задачи.
Автор: Дмитрий Брайт
Источник [1]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/programmirovanie/382038
Ссылки в тексте:
[1] Источник: https://habr.com/ru/post/710008/?utm_source=habrahabr&utm_medium=rss&utm_campaign=710008
Нажмите здесь для печати.