Wednesday, May 3, 2023

Abstract Factory in Kotlin comparative analysis with Java

 Abstract Factory 

Abstract Factory is a greatly misunderstood pattern. It has a notorious reputation for being very complex and bizarre but actually, it's quite simple. If you understood the Factory Method, you'll understand this one in no time. This is because Abstract Factory is a factory of factories. That's all there is to it, really. The factory is a function or class that's able to create other classes. Abstract Factory is a class that creates factories.

You may understand that and still wonder what the usages of such a pattern may be. The main usage of Abstract Factory in the real world would probably be frameworks, most notably Spring Framework, which uses the notion of Abstract Factory to create its components out of annotations and XML files. But since creating our own framework may be quite tiresome, let's take another example where this pattern will be very useful—a strategy game. We'll call it Cats Craft 2: Revenge of the Dogs.

Abstract Factory in action Our strategy game will consist of buildings and units. Let's start with declaring what all buildings share:

interface Building<in UnitType, out ProducedUnit> 

        where UnitType : Enum<*>, ProducedUnit : Unit {

    fun build(type: UnitType) : ProducedUnit

}

All buildings should implement the build() function. Here we see generics in Kotlin for the first time, so let's discuss them a bit.

Introduction to generics in Kotlin Generics are a way to specify the relationships between types. Well, that didn't help explain much, did it? Let's try again. Generics are an abstraction of types. Nope, still awful. We'll try an example, then:

val listOfStrings = mutableListOf("a", "b", "c")  Ok, that's easy; we've covered it a lot of times. This code simply creates a list of strings. But what does it actually mean? Let's try the following line of code: 

listOfStrings.add(1)  This line doesn't compile. That's because the mutableListOf() function uses generics:

 public fun <T> mutableListOf(vararg elements: T): MutableList<T>

Generics create an expectation. No matter which type we use to create our list, from now on we can only put that type in it. It's a great language feature because, on the one hand, we can generalize our data structures or algorithms. No matter what types they hold, they'll still work in exactly the same way.

On the other hand, we still have type safety. The listOfStrings. first() function is guaranteed to return a String (in this case) and nothing else. In terms of generics, Kotlin uses an approach that is similar to, but slightly different from, Java. We won't cover all the aspects of generics in this section, but will only provide some guidance to better understand this example. As we go on, we'll encounter more uses of generics. Let's look at another example. We'll create a class called Box. Boring, I know: 

class Box<T> { 

    private var inside: T? = null 

    fun put(t: T) { 

        inside = t 

    }    

    fun get(): T? = inside 

}  

What's great about this box, though, is that by using generics, I can put just about anything in it, for example, a cat: class Cat  

When I create an instance of a box, I specify what it can hold: 

val box = Box<Cat>()  

At compile time, the generics will make sure that it will only hold objects of the correct type:

box.put(Cat()) // This will work 

val cat = box.get() // This will always return a Cat, because that's what our box holds 

box.put("Cat") // This won't work, String is not a Cat  As you may know, Java uses the wildcards ? extends, and super keywords to specify read-only and write-only types. Kotlin uses the concepts of in, out, and where. A type that is marked as in can be used as a parameter but not as a return value. This is also called covariance. In fact, it means that we can return ProducedUnit or something that inherits from it, but not something that is above ProducedUnit in the hierarchy. Types that are marked as out can be used only as a return value, not as a parameter. This is called contravariance. Furthermore, we may introduce constraints on types using the where keyword. In our case, we require that the first type implements the Type interface, while the second type implements the Unit interface. The names of the types themselves, UnitType and ProducedUnit, could be anything we want, T and P, for example. But for the sake of clarity, we'll use more verbose names.

Back to our bases HQ is a special building that can produce other buildings. It keeps track of all the buildings it had built up until now. The same type of building can be built more than once: class HQ {

    val buildings = mutableListOf<Building<*, Unit>>()

 fun buildBarracks(): Barracks {

        val b = Barracks()

        buildings.add(b)

        return b

    }

 fun buildVehicleFactory(): VehicleFactory {

        val vf = VehicleFactory()

        buildings.add(vf)

        return vf

    }

}

 You may be wondering what the star (*) means as regards generics. It's called a star projection, and it means I don't know anything about this type. It's similar to Java's raw types, but it's type safe. All other buildings produce units. Units can be either infantry or armored vehicle:

 interface Unit 

 interface Vehicle : Unit

interface Infantry : Unit Infantry can be either riflemen or rocket soldier: class Rifleman : Infantry

 class RocketSoldier : Infantry

 enum class InfantryUnits {

    RIFLEMEN,

    ROCKET_SOLDIER

}

 Here we see the enum keyword for the first time. Vehicles are either tanks or armored personnel carriers (APCs):

 class APC : Vehicle

 class Tank : Vehicle

 enum class VehicleUnits {

    APC,

    TANK

A barracks is a building that produces infantry: class Barracks : Building<InfantryUnits, Infantry> {

    override fun build(type: InfantryUnits): Infantry {

        return when (type) {

            RIFLEMEN -> Rifleman()

            ROCKET_SOLDIER -> RocketSoldier()

        }

    }

}

We don't need the else block in our when. That's because we use enum, and Kotlin makes sure that when on enum is exhaustive. A vehicle factory is a building that produces different types of armored vehicles: 

class VehicleFactory : Building<VehicleUnits, Vehicle> {

    override fun build(type: VehicleUnits) = when (type) {

        APC -> APC()

        TANK -> Tank()

    }

We can make sure that we can build different units now:

 val hq = HQ()

val barracks1 = hq.buildBarracks()

val barracks2 = hq.buildBarracks()

val vehicleFactory1 = hq.buildVehicleFactory() And now on to producing units: val units = listOf(

        barracks1.build(InfantryUnits.RIFLEMEN),

        barracks2.build(InfantryUnits.ROCKET_SOLDIER),

        barracks2.build(InfantryUnits.ROCKET_SOLDIER),

        vehicleFactory1.build(VehicleUnits.TANK),

        vehicleFactory1.build(VehicleUnits.APC),

        vehicleFactory1.build(VehicleUnits.APC)

)

 We've already seen the listOf() function from the standard library. It will create a read-only list of different units that our

buildings produce. You can iterate over this list and make sure that those are indeed the units we require.

Making improvements One may claim that having the VehicleFactory and Barracks classes is too cumbersome. They don't have any state, after all. Instead, we can replace them with objects. Instead of the previous implementation of buildBarracks(), we can have the following:

 fun buildBarracks(): Building<InfantryUnits, Infantry> {

    val b = object : Building<InfantryUnits, Infantry> {

        override fun build(type: InfantryUnits): Infantry {

            return when (type) {

                InfantryUnits.RIFLEMEN -> Rifleman()

                InfantryUnits.ROCKET_SOLDIER -> RocketSoldier()

            }

        }

    }

    buildings.add(b)

    return b

We've already seen two different usages of the object keyword: once in the Singleton design pattern, and another time in the Factory Method design pattern. Here is the third way we can use it: for creating anonymous classes on the fly. After all, Barracks is a building that, given InfantryUnitType, produces Infantry.

If our logic is straightforward, we can even shorten the declaration a bit more: 

fun buildVehicleFactory(): Building<VehicleUnits, Vehicle> {

    val vf = object : Building<VehicleUnits, Vehicle> {

        override fun build(type: VehicleUnits) = when (type) {

            VehicleUnits.APC -> APC()

            VehicleUnits.TANK -> Tank()

        }

    }

    buildings.add(vf)

 return vf

We said that Abstract Factory combines a number of related factories. So, what's common to all factories in our case? They're all buildings and they all produce units. Having that principle in mind, you could apply it to many different cases. If you're familiar with strategy games, usually they have at least two different factions. Each may have different structures and units. To achieve that, you can repeat this pattern as many times as needed. Let's assume we have two different factions now, cats and dogs, and Tanks and Rocket Infantry are only prerogatives of this faction. Dogs have Heavy Tanks and Grenadiers instead. What changes do we need to make in our system? First, HQ becomes an interface:

interface HQ {

    fun buildBarracks(): Building<InfantryUnits, Infantry>

    fun buildVehicleFactory(): Building<VehicleUnits, Vehicle>

What was HQ previously now becomes CatHQ: 

class CatHQ : HQ { 

// Remember to add override to your methods

And DogHQ will have to repeat the same steps, but with a different construction logic. This ability to accommodate big changes is what makes Abstract Factory so powerful in some use cases.

Code Examples ;

AbstractFactoryDesignPatterrn.kt 

interface Types

enum class InfantryUnits : Types {
RIFLEMEN,
ROCKET_SOLDIER
}

enum class VehicleUnits : Types {
APC,
TANK
}

interface Building<in UnitType, out ProducedUnit>
where UnitType : Types, ProducedUnit : Unit {
fun build(type: UnitType): ProducedUnit
}


interface HQ {
fun buildBarracks(): Building<InfantryUnits, Infantry>
fun buildVehicleFactory(): Building<VehicleUnits, Vehicle>
}

class CatHQ : HQ {
val buildings = mutableListOf<Building<*, Unit>>()

override fun buildBarracks(): Building<InfantryUnits, Infantry> {
val b = object : Building<InfantryUnits, Infantry> {
override fun build(type: InfantryUnits): Infantry {
return when (type) {
InfantryUnits.RIFLEMEN -> Rifleman()
InfantryUnits.ROCKET_SOLDIER -> RockerSoldier()
}
}
}
buildings.add(b)
return b
}

override fun buildVehicleFactory(): Building<VehicleUnits, Vehicle> {
val vf = object : Building<VehicleUnits, Vehicle> {
override fun build(type: VehicleUnits) = when (type) {
VehicleUnits.APC -> APC()
VehicleUnits.TANK -> Tank()
}
}
buildings.add(vf)

return vf
}
}

interface Unit

interface Vehicle : Unit

interface Infantry : Unit

class Rifleman : Infantry

class RockerSoldier : Infantry

class APC : Vehicle

class Tank : Vehicle
fun main(args: Array<String>) {
// Singleton test
for (i in 1..10) {
SingletonAnalysisWithJava.increment()
}
// Factory Method test
val animalTypes = listOf(
"dog" to "bulldog",
"dog" to "beagle",
"cat" to "persian",
"cat" to "russian blue",
"cat" to "siamese"
)

val factory = FactoryMethod()
for ((type, breed) in animalTypes) {
val c = factory.createAnimal(type, breed)
println(c)
}
// Static Factory Method
val n = StaticFactoryMethod()
println(StaticFactoryMethod.valueOf("123"))

// AbstractFactory Method test
val hq = CatHQ()
val barracks1 = hq.buildBarracks()
val barracks2 = hq.buildBarracks()
val vehicleFactory1 = hq.buildVehicleFactory()
val units = listOf(
barracks1.build(InfantryUnits.RIFLEMEN),
barracks2.build(InfantryUnits.ROCKET_SOLDIER),
barracks2.build(InfantryUnits.ROCKET_SOLDIER),
vehicleFactory1.build(VehicleUnits.TANK),
vehicleFactory1.build(VehicleUnits.APC),
vehicleFactory1.build(VehicleUnits.APC)
)
}


No comments: