I have a task that says I need to write an implementation of four universal methods in the ShopImpl
class.
If I was working with arrays, for example, or with dictionaries, I would write an implementation of the methods, but in this case I just don't understand where, for example, I need to add Product
in the addNewProduct(product: Product)
method.
The task says, for example, that it is necessary to implement a method that adds a new object to the Shop. But the Shop is a protocol. How can I add something to the protocol. Please help me with understanding what the implementation of the universal addNewProduct(product: Product)
method should look like in this case.
/**
The task is to implement the Shop protocol (you can do that in this file directly).
- No database or any other storage is required, just store data in memory
- No any smart search, use String method contains (case sensitive/insensitive - does not matter)
*/
struct Product {
let id: String; // unique identifier
let name: String;
let producer: String;
}
protocol Shop {
/**
Adds a new product object to the Shop.
- Parameter product: product to add to the Shop
- Returns: false if the product with same id already exists in the Shop, true – otherwise.
*/
func addNewProduct(product: Product) -> Bool
/**
Deletes the product with the specified id from the Shop.
- Returns: true if the product with same id existed in the Shop, false – otherwise.
*/
func deleteProduct(id: String) -> Bool
/**
- Returns: 10 product names containing the specified string.
If there are several products with the same name, producer's name is added to product's name in the format "<producer> - <product>",
otherwise returns simply "<product>".
*/
func listProductsByName(searchString: String) -> Set<String>
/**
- Returns: 10 product names whose producer contains the specified string,
result is ordered by producers.
*/
func listProductsByProducer(searchString: String) -> [String]
}
// TODO: your implementation goes here
class ShopImpl: Shop {
func addNewProduct(product: Product) -> Bool {
// your code
}
func deleteProduct(id: String) -> Bool {
// your code
}
func listProductsByName(searchString: String) -> Set<String> {
// your code
}
func listProductsByProducer(searchString: String) -> [String] {
// your code
}
}
Btw, tests for above methods look this way:
func test(lib: Shop) {
assert(!lib.deleteProduct(id: "1"))
assert(lib.addNewProduct(product: Product(id: "1", name: "1", producer: "Lex")))
assert(!lib.addNewProduct(product: Product(id: "1", name: "any name because we check id only", producer: "any producer")))
assert(lib.deleteProduct(id: "1"))
assert(lib.addNewProduct(product: Product(id: "3", name: "Some Product3", producer: "Some Producer2")))
assert(lib.addNewProduct(product: Product(id: "4", name: "Some Product1", producer: "Some Producer3")))
assert(lib.addNewProduct(product: Product(id: "2", name: "Some Product2", producer: "Some Producer2")))
assert(lib.addNewProduct(product: Product(id: "1", name: "Some Product1", producer: "Some Producer1")))
assert(lib.addNewProduct(product: Product(id: "5", name: "Other Product5", producer: "Other Producer4")))
assert(lib.addNewProduct(product: Product(id: "6", name: "Other Product6", producer: "Other Producer4")))
assert(lib.addNewProduct(product: Product(id: "7", name: "Other Product7", producer: "Other Producer4")))
assert(lib.addNewProduct(product: Product(id: "8", name: "Other Product8", producer: "Other Producer4")))
assert(lib.addNewProduct(product: Product(id: "9", name: "Other Product9", producer: "Other Producer4")))
assert(lib.addNewProduct(product: Product(id: "10", name: "Other Product10", producer: "Other Producer4")))
assert(lib.addNewProduct(product: Product(id: "11", name: "Other Product11", producer: "Other Producer4")))
var byNames: Set<String> = lib.listProductsByName(searchString: "Product")
assert(byNames.count == 10)
byNames = lib.listProductsByName(searchString: "Some Product")
assert(byNames.count == 4)
assert(byNames.contains("Some Producer3 - Some Product1"))
assert(byNames.contains("Some Product2"))
assert(byNames.contains("Some Product3"))
assert(!byNames.contains("Some Product1"))
assert(byNames.contains("Some Producer1 - Some Product1"))
var byProducer: [String] = lib.listProductsByProducer(searchString: "Producer")
assert(byProducer.count == 10)
byProducer = lib.listProductsByProducer(searchString: "Some Producer")
assert(byProducer.count == 4)
assert(byProducer[0] == "Some Product1")
assert(byProducer[1] == "Some Product2" || byProducer[1] == "Some Product3")
assert(byProducer[2] == "Some Product2" || byProducer[2] == "Some Product3")
assert(byProducer[3] == "Some Product1")
}
test(lib: ShopImpl())
The idea of that design is to define a protocol that is independent of any particular implementation. That's called an abstraction.
The Shop
protocol defines an operation that must be implemented by a any shop. You may chose to implement your shop in memory using an array or a dictionary; but you may as well chose to implement a persistent shop using some data base or let the shop outsource the list of products to another class. The key idea is that the classes/object that use the Shop
may add a product to a shop, regardless of any specific implementation choice. So the same code would work with an array-based shop implementation or a database based implementation.
Of course, the Shop
protocol does not do anything by itself. It only defines a "contract", i.e. what methods with what arguments and what behavior. You therefore need a class (or a structure) ShopImpl
. When we say that Shop
adds a product to the shop, it's not the protocol that adds something: it's understood that it is an object of a class/struct that complies with the Shop
protocol.
You can visualise this as follows:
Your code would then look like:
var aShop : Shop // aShop is a shop
aShop = ShopImpl() // we intantiate aShop using a shop implementation.
//aShop = DBShopImpl(dbserver:"www.example.com/db")
let p1 = Product(id:1)
let p2 = Product(id:2)
aShop.addNewProduct(product:p1)
aShop.addNewProduct(product:p2)
As you see, I could use another shop implementation. We are flexible as long as the class used complies with the Shop
.