O padrão de projeto Factory é um padrão de projeto de criação que é dividido em dois padrões: o método Factory e o Abstract Factory.
A idéia por trás do padrão de projeto Factory é criar um novo objeto. Mas por que devemos usar o padrão de projeto Factory?
- Criação de objetos mais fácil:Por vezes, a criação de objectos pode ser demasiado complicada. Mesmo que tenha menos parâmetros ou menos informações sobre a classe, pode criar o objeto sem ter de lidar com eles.
- Menos dependente: O padrão de desenho Factory permite a comunicação através da abstração de objectos, impedindo a classe de se ligar diretamente a um objeto. Isso diminui as dependências da classe e a torna mais flexível. Além disso, o código torna-se mais testável.
- O inicializador não é descritivo:Em vez de usar o padrão de projeto Factory, podemos usar init para a criação de objetos. Além disso, podemos ter vários métodos init para diferentes propósitos. Embora os argumentos do método init sejam diferentes, ele ainda é chamado de init, tornando difícil entender como os métodos init diferem uns dos outros. Quando o padrão de projeto Factory é aplicado, o método associado pode ser nomeado de acordo com sua função, levando a uma estrutura de inicialização mais detalhada.
Vamos supor que existe uma classe com o nome “Arroz”. Um objeto de Arroz (para preparar uma mesa de arroz, neste caso) pode ser criado de duas formas diferentes: cozinhando as matérias-primas (arroz, óleo, água e sal) ou utilizando um pacote pronto a comer.
typealias Gram = Doubleclass Rice {
let rice: Gram
let oil: Gram
let water: Gram
let salt: Gram
init(rice: Gram, oil: Gram, water: Gram, salt: Gram) {
self.rice = rice
self.oil = oil
self.water = water
self.salt = salt
}
init(readyToEatPacket: Gram) {
rice = readyToEatPacket * 0.6
oil = readyToEatPacket * 0.1
water = readyToEatPacket * 0.38
salt = readyToEatPacket * 0.02
}
}
Método de fabrico
O método Factory é um tipo de método estático que cria um objeto.
class Rice {
...private init(rice: Gram, oil: Gram, water: Gram, salt: Gram) {
...
}
private init(readyToEatPacket: Gram) {
...
}
static func createFromRawRice(rice: Gram, oil: Gram, water: Gram, salt: Gram) -> Rice {
return Rice(rice: rice, oil: oil, water: water, salt: salt)
}
static func createFromPackagedRice(readyToEatPacket: Gram) -> Rice {
return Rice(readyToEatPacket: readyToEatPacket)
}
}
let riceFromRawRice = Rice.createFromRawRice(rice: 200, oil: 40, water: 400, salt: 10)
print(riceFromRawRice.rice) // Prints "200"
let readyToEatRice = Rice.createFromPackagedRice(readyToEatPacket: 100)
print(readyToEatRice.rice) // Prints "60"
Temos dois métodos Factory diferentes: createFromRawRice e createFromPackagedRice. Estes métodos estão localizados dentro da classe. Utilizam os parâmetros necessários e, em seguida, devolvem o objeto Arroz. A principal vantagem do design Factory em relação aos inicializadores é o facto de ter um nome mais descritivo. Neste caso, é fácil perceber qual o material a utilizar para fazer o Arroz.
Uma das características notáveis desta implementação é que os métodos do inicializador são agora privados. Como resultado, os únicos métodos responsáveis pela criação de um objeto são os métodos Factory.
Fábrica
No padrão Factory, à exceção do método Factory, os métodos que criam um objeto são subcontratados a uma classe específica.
class RiceFactory {
static func createFromRawRice(rice: Gram, oil: Gram, water: Gram, salt: Gram) -> Rice {
return Rice(rice: rice, oil: oil, water: water, salt: salt)
}static func createFromPackagedRice(readyToEatPacket: Gram) -> Rice {
return Rice(readyToEatPacket: readyToEatPacket)
}
}
let riceFromRawRice = RiceFactory.createFromPackagedRice(readyToEatPacket: 200)
// if createFromRawRice and createFromPackagedRice are not static;
let riceFactory = RiceFactory()
let riceFromRawRice = riceFactory.createFromPackagedRice(readyToEatPacket: 200)
A classe RiceFactory possui métodos Factory. É opcional que esses métodos sejam estáticos. Por outro lado, os métodos init de Rice não são mais privados. Este é um tópico importante a ser considerado desta forma.
Fábrica Interna
Inner Factory significa basicamente que a classe fábrica está dentro da classe relacionada.
class Rice {
...private init(rice: Gram, oil: Gram, water: Gram, salt: Gram) {
...
}
private init(readyToEatPacket: Gram) {
...
}
class RiceFactory {
static func createFromRawRice(rice: Gram, oil: Gram, water: Gram, salt: Gram) -> Rice {
return Rice(rice: rice, oil: oil, water: water, salt: salt)
}
static func createFromPackagedRice(readyToEatPacket: Gram) -> Rice {
return Rice(readyToEatPacket: readyToEatPacket)
}
}
}
let rice = Rice.RiceFactory.createFromRawRice(rice: 100, oil: 10, water: 200, salt: 5)
Como pode ver no snippet acima, a RiceFactory está localizada dentro da Rice. Os inicializadores tornaram-se privados. Novamente, os métodos estáticos podem ser substituídos por métodos não-estáticos e, consequentemente, o uso pode ser o seguinte.
let rice = Rice.RiceFactory().createFromRawRice(rice: 100, oil: 10, water: 200, salt: 5)
Se este método for utilizado, são criados vários objectos RiceFactory. Além disso, se certas informações forem mantidas na RiceFactory, elas serão perdidas a cada criação da RiceFactory. Para resolver este problema, a classe RiceFactory pode ser transformada em Singleton e, em seguida, é utilizada uma instância estática para aceder à fábrica. A implementação está abaixo.
class Rice {
...private init(rice: Gram, oil: Gram, water: Gram, salt: Gram) {
...
}
private init(readyToEatPacket: Gram) {
...
}
static let factory = RiceFactory.instance
class RiceFactory {
private init() {}
static let instance = RiceFactory()
func createFromRawRice(rice: Gram, oil: Gram, water: Gram, salt: Gram) -> Rice {
return Rice(rice: rice, oil: oil, water: water, salt: salt)
}
func createFromPackagedRice(readyToEatPacket: Gram) -> Rice {
return Rice(readyToEatPacket: readyToEatPacket)
}
}
}
let rice = Rice.factory.createFromRawRice(rice: 100, oil: 10, water: 200, salt: 5)
Fábrica Abstrata
O Abstract Factory define uma interface para a criação de famílias de produtos sem especificar as suas classes concretas. A criação é implementada por classes concretas. Cada classe concreta é responsável pela criação de um tipo. O esquema do Abstract Factory é o seguinte.
protocol FourWheelerVehicle {
func drive()
}class Car: FourWheelerVehicle {
func drive() {
print("Car is driven")
}
}
class Truck: FourWheelerVehicle {
func drive() {
print("Truck is driven")
}
}
protocol TwoWheelerVehicle {
func ride()
}
class Motorcycle: TwoWheelerVehicle {
func ride() {
print("Motorcycle is ridden")
}
}
class Scooter: TwoWheelerVehicle {
func ride() {
print("Scooter is ridden")
}
}
protocol VehicleFactory {
func manufactureFourWheelerVehicle() -> FourWheelerVehicle
func manufactureTwoWheelerVehicle() -> TwoWheelerVehicle
}
class XBrandFactory: VehicleFactory {
func manufactureFourWheelerVehicle() -> FourWheelerVehicle {
print("Car has been manufactured")
return Car()
}
func manufactureTwoWheelerVehicle() -> TwoWheelerVehicle {
print("Motorcycle has been manufactured")
return Motorcycle()
}
}
class YBrandFactory: VehicleFactory {
func manufactureFourWheelerVehicle() -> FourWheelerVehicle {
print("Truck has been manufactured")
return Truck()
}
func manufactureTwoWheelerVehicle() -> TwoWheelerVehicle {
print("Scooter has been manufactured")
return Scooter()
}
}
class ABCMotorVehicles {
private var brand: String
private var vehicleFactory: VehicleFactory?
init(brand: String) {
self.brand = brand
}
private func initVehicleFactory() {
if brand == "X" {
vehicleFactory = XBrandFactory()
} else {
vehicleFactory = YBrandFactory()
}
}
func manufactureFourWheelerVehicle() -> FourWheelerVehicle {
initVehicleFactory()
return vehicleFactory!.manufactureFourWheelerVehicle()
}
func manufactureTwoWheelerVehicle() -> TwoWheelerVehicle {
initVehicleFactory()
return vehicleFactory!.manufactureTwoWheelerVehicle()
}
}
var company = ABCMotorVehicles(brand: "X")
let productA1 = company.manufactureFourWheelerVehicle() // Prints "Car has been manufactured"
productA1.drive() // Prints "Car is driven"
let productB1 = company.manufactureTwoWheelerVehicle() // Prints "Motorcycle has been manufactured"
productB1.ride() // Prints "Motorcycle is ridden"
company = ABCMotorVehicles(brand: "Y")
let productA2 = company.manufactureFourWheelerVehicle() // Prints "Truck has been manufactured"
productA2.drive() // Prints "Truck is driven"
let productB2 = company.manufactureTwoWheelerVehicle() // Prints "Scooter has been manufactured"
productB2.ride() // Prints "Scooter is ridden"
Temos um longo trecho de código. Vamos torná-lo significativo.
FourWheelerVehicle -> AbstractProductA
Carro -> ProdutoA1
Camião -> ProdutoA2
Veículo de duas rodas -> AbstractProductB
Motociclo -> ProdutoB1
Scooter -> ProdutoB2
VehicleFactory -> AbstractFactory
XBrandVehicleFactory -> ConcreteFactory1
YBrandVehicleFactory -> ConcreteFactory2
Cliente -> ABCMotorVehicles
Como pode ver na correspondência, a fábrica abstrata é VehicleFactory. VehicleFactory definiu uma interface, e depois XBrandVehicleFactory e YBrandVehicleFactory conformaram-na. O cliente ABCMotorVehicles recebe apenas o parâmetro da marca, depois inicializa a VehicleFactory de acordo com a marca. Uma vez que o cliente conhece a marca, pode criar produtos (Carro, Camião, etc.) em conformidade com os protocolos AbstractProduct (FourWheelerVehicle, etc.).