Creational Design Patterns - Kotlin Flashcards

1
Q

Creational Design Patterns

A

In software engineering, creational design patterns are design patterns that deal with object creation mechanisms, trying to create objects in a manner suitable to the situation. The basic form of object creation could result in design problems or added complexity to the design. Creational design patterns solve this problem by somehow controlling this object creation.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

Creational Design Patterns

Builder / Assembler Pattern

A

Builder pattern aims to “Separate the construction of a complex object from its representation so that the same construction process can create different representations”.

Builder Pattern says that “construct a complex object from simple objects using step-by-step approach”.

It is mostly used when object can’t be created in single step like in the de-serialization of a complex object.

Builder Pattern is a creational design pattern which allows us to construct complex objects. While creating objects with Builder Pattern, we can create different types and representations of objects which are using the same builder code.

With the Builder Pattern, we do not get all data which we will need in the class on constructor method. Instead of getting data on constructor method, we are getting all inputs step by step in separated setter methods with inner class which is called as Builder.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

Builder Pattern Example 1

A

Let’s examine Builder Pattern with a simple example.

Let’s imagine that we want to create a dialog to show on screen. This dialog will have title, icon, message, positive button and negative button. Builder pattern is good pattern to develop a class for this problem.

First of all, we are defining private constructor because we don't want to set values directly with Dialog class.
Then we define a Builder class to build our object. Fields' setter methods will private. Thus, we will only set them with the methods which we have created.

With the build method, we are creating Dialog class according to the values which we have defined.

class Dialog private constructor(
private val title: String?,
private val message: String?,
private val icon: String?,
private val positiveButtonText: String?,
private val negativeButtonText: String?)
{
fun showDialog() {
println(“Title: $title\n” +
“Message: $message\n” +
“Icon: $icon\n” +
“Positive Button Text: $positiveButtonText\n” +
“Negative Button Text: $negativeButtonText”)
}

class Builder {
var title: String = “”
private set
var message: String = “”
private set
var icon: String = “”
private set
var positiveButtonText: String = “”
private set
var negativeButtonText: String = “”
private set

**fun setTitle(title: String) = apply { this.title = title }
 fun setMessage(message: String) = apply { this.message = message }
 fun setIcon(icon: String) = apply { this.icon = icon }
 fun setPositiveButtonText(positiveButtonText: String) = apply { this.positiveButtonText = positiveButtonText }
 fun setNegativeButtonText(negativeButtonText: String) = apply { this.negativeButtonText = negativeButtonText }**

fun build() = Dialog(title, message, icon, positiveButtonText, negativeButtonText)
}
}

fun main() {
val dialog = Dialog.Builder()
.setTitle(“Exit”)
.setMessage(“Are you sure that you want to leave the app?”)
.setIcon(“Exit image”)
.setPositiveButtonText(“Yes”)
.setNegativeButtonText(“No”)
.build()
dialog.showDialog()
}

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

Builder Pattern Example 2

A

Example:

// Let's assume that Dialog class is provided by external library.
// We have only access to Dialog public interface which cannot be changed.

class Dialog {

fun showTitle() = println(“showing title”)

fun setTitle(text: String) = println(“setting title text $text”)

fun setTitleColor(color: String) = println(“setting title color $color”)

fun showMessage() = println(“showing message”)

fun setMessage(text: String) = println(“setting message $text”)

fun setMessageColor(color: String) = println(“setting message color $color”)

fun showImage(bitmapBytes: ByteArray) = println(“showing image with size ${bitmapBytes.size}”)

fun show() = println("showing dialog $this")
}
//Builder:
class DialogBuilder() {
 constructor(init: DialogBuilder.() -\> Unit) : this() {
 init()
 }

private var titleHolder: TextView? = null
private var messageHolder: TextView? = null
private var imageHolder: File? = null

fun title(init: TextView.() -\> Unit) {
 titleHolder = TextView().apply { init() }
 }
fun message(init: TextView.() -\> Unit) {
 messageHolder = TextView().apply { init() }
 }
fun image(init: () -\> File) {
 imageHolder = init()
 }
fun build(): Dialog {
 val dialog = Dialog()

titleHolder?.apply {
dialog.setTitle(text)
dialog.setTitleColor(color)
dialog.showTitle()
}

messageHolder?.apply {
dialog.setMessage(text)
dialog.setMessageColor(color)
dialog.showMessage()
}

imageHolder?.apply {
dialog.showImage(readBytes())
}

return dialog
}

class TextView {
 var text: String = ""
 var color: String = "#00000"
 }
}

Usage:

//Function that creates dialog builder and builds Dialog
fun dialog(init: DialogBuilder.() -\> Unit): Dialog {
 return DialogBuilder(init).build()
}

val dialog: Dialog = dialog {
title {
text = “Dialog Title”
}
message {
text = “Dialog Message”
color = “#333333”
}
image {
File.createTempFile(“image”, “jpg”)
}
}

dialog.show()

Output:

setting title text Dialog Title
setting title color #00000
showing title
setting message Dialog Message
setting message color #333333
showing message
showing image with size 0
showing dialog Dialog@5f184fc6

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

Factory Method Pattern

A

The factory pattern is used to replace class constructors, abstracting the process of object generation so that the type of the object instantiated can be determined at run-time.

A Factory Pattern or Factory Method Pattern says that just define an interface or abstract class for creating an object but let the subclasses decide which class to instantiate.

In other words, subclasses are responsible to create the instance of the class. The Factory Method Pattern is also known as Virtual Constructor.

The principle is simple, All the objects should be created in a subclasses and they all implement a specific function that we define in an Interface.

All the subclasses should implement the interface so that every class will have the same methods.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

Factory Method Pattern Example 1

A

Let me give you an example. While developing Android applications, just a few years ago, we have been using only Google Mobile Services. Then, Huawei have been created their own mobile services which has been called as Huawei Mobile Services.

This situation has brought a new challenge for mobile developers which is about managing two different mobile services while developing Android applications.

Let’s think about an idea that mobile services will show the map on screen and will authenticate the user. First, we need to create an interface called as MobileService.

**interface MobileService {
 fun authenticateUser()
 fun showMap()
}**

Now, we need to create real mobile services which will implement our MobileService interface. For that, I have created two class which are called as HuaweiMobileServices and GoogleMobileServices.

These mobile services are doing similar operations but in different ways. For example, Huawei authenticate users with Huawei ID and Google authenticate users with Gmail. And Huawei is using Petal Maps for navigation and Google is using Google Maps.

**class HuaweiMobileServices: MobileService {
 override fun authenticateUser() {
 println("User has been authenticated with Huawei ID")
 }**

override fun showMap() {
println(“Petal Maps has been started for navigation”)
}
}

**class GoogleMobileServices: MobileService {
 override fun authenticateUser() {
 println("User has been authenticated with Gmail")
 }**

override fun showMap() {
println(“Google Maps has been started for navigation”)
}
}

Now, all we need to do is creating Factory for these mobile service classes which we have implemented from MobileService interface.
We can send any different data to separate different type of mobile services. It can be like String, Integer or some Enum values.

I will explain by doing with Enum.

**class MobileServiceFactory(){
 fun getMobileService(mobileServiceType: MobileServiceType): MobileService {
 return when(mobileServiceType) {
 MobileServiceType.HUAWEI -\> HuaweiMobileServices()
 MobileServiceType.GOOGLE -\> GoogleMobileServices()
 }
 }
}**

enum class MobileServiceType(){
HUAWEI,
GOOGLE
}

Now, let’s test it on main method and see the output to understand what we did above:

**fun main() {
 var mobileService = MobileServiceFactory()
 val hms = mobileService.getMobileService(MobileServiceType.HUAWEI)
 val gms = mobileService.getMobileService(MobileServiceType.GOOGLE)**

println(“Huawei:”)
hms.apply {
authenticateUser()
showMap()
}
println(“Google:”)
gms.apply {
authenticateUser()
showMap()
}
}

Output :

Huawei:
User has been authenticated with Huawei ID
Petal Maps has been started for navigation
Google:
User has been authenticated with Gmail
Google Maps has been started for navigation

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

Factory Method Pattern Example 2

A

Example :

sealed class Country {
 object USA : Country() //Kotlin 1.0 could only be an inner class or object
}
object Spain : Country() //Kotlin 1.1 declared as top level class/object in the same file
class Greece(val someProperty: String) : Country()
data class Canada(val someProperty: String) : Country() //Kotlin 1.1 data class extends other class
//object Poland : Country()

class Currency(
val code: String
)

object CurrencyFactory {

fun currencyForCountry(country: Country): Currency =
when (country) {
is Greece -> Currency(“EUR”)
is Spain -> Currency(“EUR”)
is Country.USA -> Currency(“USD”)
is Canada -> Currency(“CAD”)
} //try to add a new country Poland, it won’t even compile without adding new branch to ‘when’
}

Usage:

val greeceCurrency = CurrencyFactory.currencyForCountry(Greece(“”)).code
println(“Greece currency: $greeceCurrency”)

val usaCurrency = CurrencyFactory.currencyForCountry(Country.USA).code
println(“USA currency: $usaCurrency”)

assertThat(greeceCurrency).isEqualTo(“EUR”)
assertThat(usaCurrency).isEqualTo(“USD”)

Ouput :

Greece currency: EUR
US currency: USD
UK currency: No Currency Code Available

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

Abstract Factory Pattern

A

Abstract Factory Pattern says that just define an interface or abstract class for creating families of related (or dependent) objects but without specifying their concrete sub-classes.

Abstract Factory pattern is almost similar to Factory Pattern is considered as another layer of abstraction over factory pattern. Abstract Factory patterns work around a super-factory which creates other factories.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

Abstract Factory Pattern Example 1

A

Let’s imagine that we will create UI classes for website and these UI classes will be different for desktop users and mobile device users. To do that, we will have different products for different UI objects such as Button, ImageView, TextView, EditText, Dialog and etc.
And also, these UI objects will be different for desktop and mobile users. Abstract Factory can help us in this scenario.

For example, let’s think that dialog and imageview will be different on desktop and mobile device.
First, we will define a dialog interface and each device type will implement this interface.

**interface Dialog {
 fun showPopupDialog()
}
class DesktopDialog: Dialog {
 override fun showPopupDialog() {
 println("Desktop dialog will be shown")
 }
}
class MobileDeviceDialog: Dialog {
 override fun showPopupDialog() {
 println("Mobile device dialog will be shown")
 }
}**

Then, we will create an interface for our second UI object which is ImageView.
Let’s make some difference in here. For example, users will be able to see image bigger when they click to image on desktop. But, users will not be able to see image bigger on mobile device and nothing will happen when users click on image.

**interface ImageView {
 fun showImage()
}
class DesktopImageView: ImageView {
 override fun showImage() {
 println("Image will be shown on desktop and will be clickable.")
 }
}
class MobileDeviceImageView: ImageView {
 override fun showImage() {
 println("Image will be shown on mobile device and nothing will happen when user click on image.")
 }
}**

Now, all we need to do is focusing on creating factories.

**interface UIFactory{
 fun createDialog(): Dialog
 fun createImageView(): ImageView
}**

Then, we need to create a factory class for each device type and these factory classes will implement the main UIFactory class for creating UI objects.

**class DesktopUIFactory: UIFactory {
 override fun createDialog(): Dialog = DesktopDialog()
 override fun createImageView(): ImageView = DesktopImageView()
}
class MobileDeviceUIFactory: UIFactory {
 override fun createDialog(): Dialog = MobileDeviceDialog()
 override fun createImageView(): ImageView = MobileDeviceImageView()
}**

Now, we will create a base class called as Device. And this device will be created depends on the device type such as desktop or mobile device.

**class Device(uiFactory: UIFactory) {
 var dialog: Dialog = uiFactory.createDialog()
 var imageView: ImageView = uiFactory.createImageView()
}**

For now, we need to create an enum class to getting device type easily.

enum class DeviceType{
MOBILE,
DESKTOP
}

Now, we will test all things which we did. First test will be for mobile device.

fun main() {
lateinit var uiFactory: UIFactory

**var deviceType = DeviceType.MOBILE
 uiFactory = when (deviceType) {
 DeviceType.MOBILE -\> {
 MobileDeviceUIFactory()
 }
 DeviceType.DESKTOP -\> {
 DesktopUIFactory()
 }
 }
 var device: Device = Device(uiFactory)**

device.apply {
dialog.showPopupDialog()
imageView.showImage()
}
}

Output :

Mobile device dialog will be shown
Image will be shown on mobile device and nothing will happen when user click on image.

Now, just change the device type as Desktop on main method.

var deviceType = DeviceType.DESKTOP

Output:

Desktop dialog will be shown
Image will be shown on desktop and will be clickable.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

Abstract Factory Pattern Example 2

A

Example:

interface Plant

class OrangePlant : Plant

class ApplePlant : Plant

abstract class PlantFactory {
 abstract fun makePlant(): Plant

companion object {
inline fun createFactory(): PlantFactory = when (T::class) {
OrangePlant::class -> OrangeFactory()
ApplePlant::class -> AppleFactory()
else -> throw IllegalArgumentException()
}
}
}

class AppleFactory : PlantFactory() {
 override fun makePlant(): Plant = ApplePlant()
}
class OrangeFactory : PlantFactory() {
 override fun makePlant(): Plant = OrangePlant()
}

Usage :

val plantFactory = PlantFactory.createFactory()
val plant = plantFactory.makePlant()
println(“Created plant: $plant”)

Output :

Created plant: OrangePlant@4f023edb

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

Singleton Pattern

A

The singleton pattern ensures that only one object of a particular class is ever created. All further references to objects of the singleton class refer to the same underlying instance. There are very few applications, do not overuse this pattern!

Singleton pattern is a design solution where an application wants to have one and only one instance of any class, in all possible scenarios without any exceptional condition.

The singleton pattern is a design pattern that restricts the instantiation of a class to one object. This is useful when exactly one object is needed to coordinate actions across the system.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

Singleton Pattern Example

A

The singleton pattern ensures that only one object of a particular class is ever created. All further references to objects of the singleton class refer to the same underlying instance. There are very few applications, do not overuse this pattern!

Example:

object PrinterDriver {
init {
println(“Initializing with object: $this”)
}

fun print() = println("Printing with object: $this")
}

Usage :

println(“Start”)
PrinterDriver.print()
PrinterDriver.print()

Output :

Start
Initializing with object: PrinterDriver@6ff3c5b5
Printing with object: PrinterDriver@6ff3c5b5
Printing with object: PrinterDriver@6ff3c5b5

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

Singleton Pattern - Java Vs Kotlin

A

Singleton is one of the creational design pattern that lets us to create one instance of the class and provide a global access point to this instance.

For singleton, we should define all constructors as private. Then, we should define one static instance of the class which will hold existing instance or will create new one.

Applying singleton pattern is so easy in Kotlin. But first, I want to give an example in Java to make it more understandable.

**public class SingletonMainClass {
 public static void main(String[] args) {
 SingletonPattern singletonPattern = SingletonPattern.getInstance();
 singletonPattern.writeHelloWorld();
 }
}
class SingletonPattern {
 private static SingletonPattern singletonPattern;
 private static Object synchronizedObject = new Object();**

private SingletonPattern() { }

**public static SingletonPattern getInstance() {
 if (singletonPattern == null) {
 synchronized (SingletonPattern.class) {
 if (singletonPattern == null) {
 singletonPattern = new SingletonPattern();
 }
 }
 }
 return singletonPattern;
 }**

public void writeHelloWorld() {
System.out.println(“Hello world!”);
}
}

As you see above, there are so much works when we want to implement Singleton Pattern design pattern to our classes that we need to do.

Unlike Java, Kotlin provides so easy feature for this. This feature is called as object. When we define classes as object, this object will be singleton automatically.

fun main() {
SingletonPattern.writeHelloWorld()
}

**object SingletonPattern {
 fun writeHelloWorld() {
 println("Hello World!")
 }
}**
How well did you know this?
1
Not at all
2
3
4
5
Perfectly