Зачем нужен?
Паттерн Factory method применяется когда создатель(фреймворк) должен стандартизировать архитектурную модель для клиентов и одновременно позволить клиентам определять свои объекты предметной области(domain objets).
Обеспечивает соблюдение принципа инверсии зависимостей. Уменьшает связанность классов.
Определение
- определяет интерфейс создания объекта, но позволяет субклассам выбрать класс создаваемого экземпляра. Таким образом, Фабричный метод делегирует операцию создания экземпляра субклассам.
Реализация
Суперкласс определяет стандартное поведение и делегирует детали создания субклассам, определенным пользователем. Для создания объектов используется наследование.
Существует несколько реализаций: статичный метод(вместо метода инициализации) или абстрактный метод(реализация определяется подклассами)
- Спроектируйте аргументы фабричного метода(по каким харектиристикам фабричный метод будет выбирать конкретный продукт)
- Объявите констуктуры приватными или защищенными
- Рассмотрите возможность создания внутреннего “object pool”, который позволит возвращать один и тот же экземпляр, вместо создания нового каждый раз
Пример
Фабричный метод похож на абстрактную фабрику, но в нем отсутствует акцент на семействах.
Допустим у нас есть сеть пиццерий по всей стране. В Нью-Йорке жители любят тонкий слой теста, немного специй. В Чикаго - толстый слой теста, много сыра.
Мы хотим, чтобы процесс приготовления был стандартизирован и одинаков во всех пиццерий, но при этом учитывались особенности местной кухни.
Для этого объявим общий интерфейс для пиццерий, при этом делегируем реализацию фабричного метода субклассам:
// абстрактный класс создатель
protocol PizzaStore {
func orderPizza(type: String) -> Pizza
// фабричный метод
func createPizza(type: String) -> Pizza
}
extension PizzaStore {
func orderPizza(type: String) -> Pizza {
// для создание пиццы используем фабричный метод
let pizza = createPizza(type)
pizza.prepare()
pizza.bake()
pizza.cut()
pizza.box()
return pizza
}
}
Объявим конкретные пиццерии, учитывающие местные обычаи - для этого реализуем фабричный метод
// конкретный класс-создатель
class NYPizzaStore: PizzaStore {
func createPizza(type: String) -> Pizza {
switch type {
case "cheese":
return NYStyleCheesePizza()
case "veggie":
return NYStyleVeggiePizza()
case "clam":
return NYStyleClamPizza()
case "pepperoni":
return NYStylePepperoniPizza()
default:
return NYStyleCheesePizza()
}
}
}
// конкретный класс-создатель
class ChikagoPizzaStore: PizzaStore {
func createPizza(type: String) -> Pizza {
switch type {
case "cheese":
return ChikagoStyleCheesePizza()
case "veggie":
return ChikagoStyleVeggiePizza()
case "clam":
return ChikagoStyleClamPizza()
case "pepperoni":
return ChikagoStylePepperoniPizza()
default:
return ChikagoStyleCheesePizza()
}
}
}
Объявим абстрактный продукт и стандартизируем процесс приготовления
// абстрактный продукт
protocol Pizza {
var name: String { get }
var dough: String { get }
var sauce: String { get }
var toppings: [String] { get }
func prepare()
func bake()
func cut()
func box()
}
// добавляем реализацию по умолчанию
extension Pizza {
func prepare() {
print("Preparing \(name)")
print("Tossing dough...")
print("Adding sauce")
print("Adding toppings \(toppings)")
}
func bake() {
print("Bake for 25 minutes at 350")
}
func cut() {
print("Cutting the pizza into diagonal slices")
}
func box() {
print("Place pizza in the official PizzaStore box")
}
}
Создадим конкретные продукты
// конкретные продукты
class NYStyleCheesePizza: Pizza {
let name = "Ny style Sauce and Cheese Pizza"
let dough = "Thin Crust Dough"
let sauce = "Marinara Sauce"
let toppings = ["Grated Reggiano Cheese"]
}
class NYStyleVeggiePizza: Pizza {
let name = "Ny style Sauce and Veggie Pizza"
let dough = "Thin Crust Dough"
let sauce = "Marinara Sauce"
let toppings = ["Veggie"]
}
class NYStyleClamPizza: Pizza {
let name = "Ny style Sauce and Clam Pizza"
let dough = "Thin Crust Dough"
let sauce = "Marinara Sauce"
let toppings = ["Clam"]
}
class NYStylePepperoniPizza: Pizza {
let name = "Ny style Sauce and Pepperoni Pizza"
let dough = "Thin Crust Dough"
let sauce = "Marinara Sauce"
let toppings = ["Pepperoni"]
}
class ChikagoStyleCheesePizza: Pizza {
let name = "Chikago style Deep Dish Cheese Pizza"
let dough = "Extra Thick Crust Dough"
let sauce = "Plum Tomato Sauce"
let toppings = ["Shredded Mozarella Cheese"]
func cut() {
print("Cutting pizza into square slices")
}
}
class ChikagoStyleVeggiePizza: Pizza {
let name = "Chikago style Deep Dish Veggie Pizza"
let dough = "Extra Thick Crust Dough"
let sauce = "Plum Tomato Sauce"
let toppings = ["Veggie"]
func cut() {
print("Cutting pizza into square slices")
}
}
class ChikagoStyleClamPizza: Pizza {
let name = "Chikago style Deep Dish Clam Pizza"
let dough = "Extra Thick Crust Dough"
let sauce = "Plum Tomato Sauce"
let toppings = ["Clam"]
func cut() {
print("Cutting pizza into square slices")
}
}
class ChikagoStylePepperoniPizza: Pizza {
let name = "Chikago style Deep Dish Pepperoni Pizza"
let dough = "Extra Thick Crust Dough"
let sauce = "Plum Tomato Sauce"
let toppings = ["Pepperoni"]
func cut() {
print("Cutting pizza into square slices")
}
}
}
Посетим пиццерию в Нью-Йорке и закажем сырную пиццу
let nyStore = NYPizzaStore()
let NYPizza = nyStore.orderPizza("cheese")
print(NYPizza.name)
/*
Preparing Ny style Sauce and Cheese Pizza
Tossing dough...
Adding sauce
Adding toppings ["Grated Reggiano Cheese"]
Bake for 25 minutes at 350
Cutting the pizza into diagonal slices
Place pizza in the official PizzaStore box
*/
}
Посетим пиццерию в Чикаго и аналогично закажем сырную пиццу
let chikagoStore = ChikagoPizzaStore()
let ChikagoPizza = chikagoStore.orderPizza("cheese")
print(ChikagoPizza.name)
/*
Preparing Chikago style Deep Dish Cheese Pizza
Tossing dough...
Adding sauce
Adding toppings ["Shredded Mozarella Cheese"]
Bake for 25 minutes at 350
Cutting pizza into square slices
Place pizza in the official PizzaStore box
Ny style Sauce and Cheese Pizza
Chikago style Deep Dish Cheese Pizza
*/
}