官术网_书友最值得收藏!

Factory

We'll start with the Factory Method formalized in the book Design Patterns by Gang of Four.

This is one of the first patterns I teach my students. They're usually very anxious about the whole concept of design patterns, since it has an aura of mystery and complexity.  So, what I do is ask them the following question.

Assume you have some class declaration, for example:

class Cat {
val name = "Cat"
}

Could you write a function that returns a new instance of the class? Most of them would succeed:

fun catFactory() : Cat {
return Cat()
}

Check that everything works:

val c = catFactory() 
println(c.name) // Indeed prints "Cat"

Well, that's really simple, right? 

Now, based on the argument we provide it, can this method create one of two objects?

Let's say we now have a Dog:

class Dog {
val name = "Dog"
}

Choosing between two types of objects to instantiate would require only passing an argument:

fun animalFactory(animalType: String) : Cat {
return Cat()
}

Of course, we can't always return a Cat now. So we create a common interface to be returned:

interface Animal {
val name : String
}

What's left is to use the when expression to return an instance of the correct class:

return when(animalType.toLowerCase()) {
"cat" -> Cat()
"dog" -> Dog()
else -> throw RuntimeException("Unknown animal $animalType")
}

That's what Factory Method is all about:

  • Get some value.
  • Return one of the objects that implement the common interface.

This pattern is very useful when creating objects from a configuration. Imagine we have a text file with the following contents that came from a veterinary clinic:

dog, dog, cat, dog, cat, cat

Now we would like to create an empty profile for each animal. Assuming we've already read the file contents and split them into a list, we can do the following:

val animalTypes = listOf("dog", "dog", "cat", "dog", "cat", "cat")

for (t in animalTypes) {
val c = animalFactory(t)
println(c.name)
}

listOf is a function that comes from the Kotlin standard library that creates an immutable list of provided objects.

If your Factory Method doesn't need to have a state, we can leave it as a function.

But what if we want to assign a unique sequential identifier for each animal? Take a look at the following code block:

interface Animal {
val id : Int
// Same as before
}

class Cat(override val id: Int) : Animal {
// Same as before
}

class Dog(override val id: Int) : Animal {
// Same as before
}

Note that we can override values inside the constructor.

Our factory becomes a proper class now:

class AnimalFactory { 
var counter = 0

fun createAnimal(animalType: String) : Animal {
return when(animalType.trim().toLowerCase()) {
"cat" -> Cat(++counter)
"dog" -> Dog(++counter)
else -> throw RuntimeException("Unknown animal $animalType")
}
}
}

So we'll have to initialize it:

val factory = AnimalFactory()
for (t in animalTypes) {
val c = factory.createAnimal(t)
println("${c.id} - ${c.name}")
}

Output for the preceding code is as follows:

1 - Dog 
2 - Dog
3 - Cat
4 - Dog
5 - Cat
6 - Cat

This was a pretty straightforward example. We provided a common interface for our objects (Animal, in this case), then based on some arguments, we decided which concrete class to instantiate. 

What if we decided to support different breeds? Take a look at the following code:

val animalTypes = listOf("dog" to "bulldog", 
"dog" to "beagle",
"cat" to "persian",
"dog" to "poodle",
"cat" to "russian blue",
"cat" to "siamese")

Much like the downTo function we saw in Chapter 1, Getting Started with Kotlin, it looks like an operator, but it's a function that creates a pair of objects: (cat, siamese, in our case). We'll come back to it when we discuss the infix function in depth.

We can delegate the actual object instantiation to other factories:

class AnimalFactory {
var counter = 0
private val dogFactory = DogFactory()
private val catFactory = CatFactory()

fun createAnimal(animalType: String, animalBreed: String) : Animal {
return when(animalType.trim().toLowerCase()) {
"cat" -> catFactory.createDog(animalBreed, ++counter)
"dog" -> dogFactory.createDog(animalBreed, ++counter)
else -> throw RuntimeException("Unknown animal $animalType")
}
}
}

The factory repeats the same pattern again:

class DogFactory {
fun createDog(breed: String, id: Int) = when(breed.trim().toLowerCase()) {
"beagle" -> Beagle(id)
"bulldog" -> Bulldog(id)
else -> throw RuntimeException("Unknown dog breed $breed")
}
}

You can make sure that you understand this example by implementing Beagle, Bulldog, CatFactory, and all the different breeds of cats by yourself.

The last point to note is how we're now calling our AnimalFactory with a pair of arguments:

for ((type, breed) in animalTypes) {
val c = factory.createAnimal(type, breed)
println(c.name)
}

This is called a destructuring declaration, and is useful especially when dealing with such pairs of data. 

主站蜘蛛池模板: 寿阳县| 宁强县| 赤城县| 哈巴河县| 江川县| 洱源县| 阆中市| 松江区| 车险| 登封市| 丰镇市| 义马市| 历史| 平南县| 河间市| 宜春市| 和林格尔县| 拉孜县| 清丰县| 和政县| 洪泽县| 滨州市| 韩城市| 和平区| 庆安县| 广饶县| 彰武县| 宁乡县| 乌兰县| 阳原县| 综艺| 隆林| 临清市| 三门县| 巴中市| 仁布县| 乐陵市| 讷河市| 尚义县| 望城县| 海盐县|