Structural Design Patterns - Kotlin Flashcards

1
Q

Structural Design Patterns

A

In software engineering, structural design patterns are design patterns that ease the design by identifying a simple way to realize relationships between entities.

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

Adapter Pattern

A

The adapter pattern is used to provide a link between two otherwise incompatible types by wrapping the “adaptee” with a class that supports the interface required by the client.

Adapter pattern works as a bridge between two incompatible interfaces. This type of design pattern comes under structural pattern as this pattern combines the capability of two independent interfaces.

“The adapter pattern makes two incompatible interfaces compatible without changing their existing code”.

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

Adapter Pattern Example

A

Example :

interface Temperature {
var temperature: Double
}

class CelsiusTemperature(override var temperature: Double) : Temperature

class FahrenheitTemperature(var celsiusTemperature: CelsiusTemperature) : Temperature {

override var temperature: Double
get() = convertCelsiusToFahrenheit(celsiusTemperature.temperature)
set(temperatureInF) {
celsiusTemperature.temperature = convertFahrenheitToCelsius(temperatureInF)
}

private fun convertFahrenheitToCelsius(f: Double): Double = (f - 32) * 5 / 9

private fun convertCelsiusToFahrenheit(c: Double): Double = (c * 9 / 5) + 32
}

Usage:

val celsiusTemperature = CelsiusTemperature(0.0)
val fahrenheitTemperature = FahrenheitTemperature(celsiusTemperature)

celsiusTemperature.temperature = 36.6
println(“${celsiusTemperature.temperature} C -> ${fahrenheitTemperature.temperature} F”)

fahrenheitTemperature.temperature = 100.0
println(“${fahrenheitTemperature.temperature} F -> ${celsiusTemperature.temperature} C”)

Output :

  1. 6 C -> 97.88000000000001 F
  2. 0 F -> 37.77777777777778 C
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

Decorator Pattern

A

The decorator pattern is used to extend or alter the functionality of objects at run-time by wrapping them in an object of a decorator class. This provides a flexible alternative to using inheritance to modify behaviour.

The Decorator pattern allows a user to add new functionality to an existing object without altering its structure. This type of design pattern comes under structural pattern as this pattern acts as a wrapper to the existing class.

That is there will be additional features added to the original object. The component, which adds these additional features is called the Decorator.

The Decorator class in this pattern act as a wrapper object which dynamically attaches additional features to the original object at run-time.

“Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to sub-classing for extending functionality”.

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

Decorator Pattern Example

A

Example

interface CoffeeMachine {
 fun makeSmallCoffee()
 fun makeLargeCoffee()
}
class NormalCoffeeMachine : CoffeeMachine {
 override fun makeSmallCoffee() = println("Normal: Making small coffee")

override fun makeLargeCoffee() = println(“Normal: Making large coffee”)
}

//Decorator:
class EnhancedCoffeeMachine(val coffeeMachine: CoffeeMachine) : CoffeeMachine by coffeeMachine {
// overriding behaviour
 override fun makeLargeCoffee() {
 println("Enhanced: Making large coffee")
 coffeeMachine.makeLargeCoffee()
 }
// extended behaviour
 fun makeCoffeeWithMilk() {
 println("Enhanced: Making coffee with milk")
 coffeeMachine.makeSmallCoffee()
 println("Enhanced: Adding milk")
 }
}

Usage :

val normalMachine = NormalCoffeeMachine()
val enhancedMachine = EnhancedCoffeeMachine(normalMachine)

// non-overridden behaviour
enhancedMachine.makeSmallCoffee()
// overriding behaviour
enhancedMachine.makeLargeCoffee()
// extended behaviour
enhancedMachine.makeCoffeeWithMilk()

Output :

Normal: Making small coffee

Enhanced: Making large coffee
Normal: Making large coffee

Enhanced: Making coffee with milk
Normal: Making small coffee
Enhanced: Adding milk

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

Facade Pattern

A

The facade pattern is used to define a simplified interface to a more complex subsystem.

Facade pattern hides the complexities of the system and provides an interface to the client using which the client can access the system. This type of design pattern comes under structural pattern as this pattern adds an interface to the existing system to hide its complexities.

This pattern involves a single class which provides simplified methods required by client and delegates calls to methods of the existing system classes.

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

Facade Pattern Example

A

Example:

class ComplexSystemStore(val filePath: String) {

init {
println(“Reading data from file: $filePath”)
}

val store = HashMap()

fun store(key: String, payload: String) {
 store.put(key, payload)
 }

fun read(key: String): String = store[key] ?: “”

fun commit() = println("Storing cached data: $store to file: $filePath")
}

data class User(val login: String)

//Facade:
class UserRepository {
 val systemPreferences = ComplexSystemStore("/data/default.prefs")

fun save(user: User) {
systemPreferences.store(“USER_KEY”, user.login)
systemPreferences.commit()
}

fun findFirst(): User = User(systemPreferences.read("USER\_KEY"))
}

Usage :

val userRepository = UserRepository()
val user = User(“dbacinski”)
userRepository.save(user)
val resultUser = userRepository.findFirst()
println(“Found stored user: $resultUser”)

Output :

Reading data from file: /data/default.prefs
Storing cached data: {USER_KEY=dbacinski} to file: /data/default.prefs
Found stored user: User(login=dbacinski)

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

Protection Proxy Pattern

A

The proxy pattern is used to provide a surrogate or placeholder object, which references an underlying object. Protection proxy is restricting access.

The Proxy Pattern is used to create a representative object that controls access to another object, which may be remote, expensive to create or in need of being secured.

The Proxy can be very useful in controlling access to the original object, especially when objects should have different access rights.

In the Proxy Pattern, a client does not directly talk to the original object, it delegates it calls to the proxy object which calls the methods of the original object.

The important point is that the client does not know about the proxy, the proxy acts as an original object for the client.

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

Protection Proxy Pattern Example

A

Example :

interface File {
 fun read(name: String)
}
class NormalFile : File {
 override fun read(name: String) = println("Reading file: $name")
}
//Proxy:
class SecuredFile : File {
 val normalFile = NormalFile()
 var password: String = ""

override fun read(name: String) {
if (password == “secret”) {
println(“Password is correct: $password”)
normalFile.read(name)
} else {
println(“Incorrect password. Access denied!”)
}
}
}

Usage:

val securedFile = SecuredFile()
securedFile.read(“readme.md”)

securedFile.password = “secret”
securedFile.read(“readme.md”)

Output:

Incorrect password. Access denied!
Password is correct: secret
Reading file: readme.md

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

Composite Pattern

A

The composite pattern is used to compose zero-or-more similar objects so that they can be manipulated as one object.

Composite pattern is a partitioning design pattern and describes a group of objects that are treated the same way as a single instance of the same type of object. The intent of a composite is to ‘compose’ objects into tree structures to represent part-whole hierarchies. It allows you to have a tree structure and ask each node in the tree structure to perform a task.

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

Composite Pattern Example

A

Example :

open class Equipment(private var price: Int, private var name: String) {
 open fun getPrice(): Int = price
}

/*
[composite]
*/

open class Composite(name: String) : Equipment(0, name) {
 val equipments = ArrayList()
fun add(equipment: Equipment) {
 this.equipments.add(equipment)
 }

override fun getPrice(): Int {
return equipments.map { it.getPrice() }.sum()
}
}

/*
leafs
*/

class Cabbinet : Composite("cabbinet")
class FloppyDisk : Equipment(70, "Floppy Disk")
class HardDrive : Equipment(250, "Hard Drive")
class Memory : Equipment(280, "Memory")

Usage:

var cabbinet = Cabbinet()
cabbinet.add(FloppyDisk())
cabbinet.add(HardDrive())
cabbinet.add(Memory())
println(cabbinet.getPrice())

Output:

600

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