Swift: creating a factory function that takes a class type as a parameter and displays the constructor of this class

I would like to create a factory function that takes a class type and returns a constructor so that I can use this constructor to instantiate this class later.

Imagine that I have two classes: Apple and Orange, which are subclasses of Fruit. They should be initialized with unknownNumber , which I will only know about later.

 class Apple: Fruit { init(unknownNumber: Int) { ... } } class Orange: Fruit { init(unknownNumber: Int) { ... } } 

I would like to create a factory function that accepts a class type so that I can later call this function and initialize a specific subclass of Fruit with an unknown number.

 //concept: func makeFruit(typeOfFruit) -> (Int) -> Fruit { return { (unknownNumber: Int) -> Fruit in return typeOfFruit(unknownNumber) } } 

To create orangeFactory, I can do:

 let orangeFactory = makeFruit(Orange) // then at a later time when I have the unknown number let orangeInstance = orangeFactory(unknownNumber) 

I am aware of the possibility of simply making unknownNumber lazy variable, but in my particular case unknownNumber is not just a number and it includes other processes, so I would like to create only an object when I have everything to keep the structure simple.

Is something similar possible in Swift? I search the Internet and cannot find direct answers. Any help would be greatly appreciated!

+5
source share
3 answers

Let me work back. In your makeFruit function makeFruit you need to declare the typeOfFruit parameter as a metatype of your Fruit superclass and explicitly reference the initializer:

 func makeFruit(typeOfFruit: Fruit.Type) -> (Int) -> Fruit { return { (unknownNumber: Int) -> Fruit in return typeOfFruit.init(unknownNumber: unknownNumber) } } 

You can only use the metatype with required initializers, so init must be marked as such:

 class Fruit { required init(unknownNumber: Int) { // ... } } 

The rest should work:

 let orangeMaker = makeFruit(Orange.self) let tenOranges = orangeMaker(10) 
+4
source

If you intend to use the class itself as an identifier for the factory entry, you really don't need a factory. The factory pattern creates an indirect relationship between an arbitrary identifier and the corresponding object class.

An easy way to do this in Swift is to use a dictionary:

 var fruitFactory:[String:Fruit.Type] = [:] fruitFactory["Apple"] = Apple.self fruitFactory["Orange"] = Orange.self fruitFactory["Banana"] = Fruit.self fruitFactory["Grape"] = Fruit.self let tenOranges = fruitFactory["Orange"]!.init(unknownNumber:10) 

note that your initializer in the Fruit class should be marked as required for this.

+1
source

You can declare Fruit as a protocol that provides the required init method and use generics support in Switchf:

 protocol Fruit { init(unknownNumber: Int) } class Apple: Fruit { required init(unknownNumber: Int) { } } class Orange: Fruit { required init(unknownNumber: Int) { } } func makeFruit<T: Fruit>(cls: T.Type) -> Int -> T { return { T(unknownNumber: $0) } } makeFruit(Apple.self)(10) // returns an Apple makeFruit(Orange.self)(15) // returns an Orange 

It also gives you type safety as a result of the makeFruit function, similar to the type specified by the cls parameter.

Note that this is not a factory function, but simply a redirect. But you can go even further and configure makeFruit for some of the fruits, and that is what makes it a factory function:

 class Apple: Fruit { required init(unknownNumber: Int) { } init(color: String, unknownNumber: Int) { } } func makeFruit<T: Fruit>(cls: T.Type) -> Int -> T { return { T(unknownNumber: $0) } } func makeFruit(cls: Apple.Type) -> Int -> Apple { return { Apple(color: "red", unknownNumber: $0) } } makeFruit(Orange.self)(15) // an Orange makeFruit(Apple.self)(10) // a red Apple 

Assuming the Apple class has an initializer that accepts color, we can override makeFruit for that particular class and pass the default color (or, possibly, calculated, depending on factory specifications). This is without losing the type of security that grows on you with Swift.

0
source

Source: https://habr.com/ru/post/1240478/


All Articles