-
Notifications
You must be signed in to change notification settings - Fork 1
lessons
Miguel Gamboa edited this page Oct 25, 2022
·
48 revisions
Lessons:
- 12-09-2022 - Lesson 01 - Introduction and Kotlin Characteristics
- 14-09-2022 - Lesson 02 - Encapsulation and Limit Mutability
- 14-09-2022 - Lesson 03 - Unit Testing
- 19-09-2022 - Lesson 04 - Types
- 21-09-2022 - Lesson 05 - Types
- 21-09-2022 - Lesson 06 - Enumerated Types
- 26-09-2022 - Lesson 07 - Polymorphism versus Pattern Matching
- 28-09-2022 - Lesson 08 - Domain model
- 28-09-2022 - Lesson 09 - Manage instances and states
- 04-10-2022 - Lesson 10 - OOP versus Functional
- 10-10-2022 - Lesson 11 - Generics
- 14-10-2022 - Lesson 13 - Lab 1 - Workout
- 14-10-2022 - Lesson 14 - Lab 2 - Workout
- 17-10-2022 - Lesson 12 - Unit test of console IO
- 19-10-2022 - Lesson 15 - Storage, Data Mapper and Factory
- 19-10-2022 - Lesson 16 - Serialization and TicTactToe between processes
- 24-10-2022 - Lesson 17 - Serialization and TicTactToe between processes
- Characterizing Kotlin programming environment:
- Statically typed
- For JVM and for native and cross-platform
- Multiparadigm: imperative, object-oriented and functional.
- Programming languages retrospective and comparison with Kotlin.
- Since functional programming languages to object oriented, Lisp, Java, JavaScript, C#, Scala, etc
- Software and source code quality criteria: Reliability, Testability, Readability and Extensibility.
- Lessons approach:
- Git repository
- Multi-module project (one module per lesson) with gradle wrapper (version 7.5.1)
- Kotlin release 1.6....
- IntelliJ 2022
- Homework workouts
- Restricting and managing access to object’s state (i.e. instance properties or fields).
- Hide properties inside the class (
private
) - Provide methods/functions to access object’s state:
- e.g.
NaifDate
member functionsnextMonth()
andaddDays(inc)
- e.g.
- Limit Mutability
- Kotlin mutable properties (
var
) versus immutable (val
) - Implement
Stack
interface based on auxiliaryclass Node<T>(val item: T, val next: Node<T>?)
with the expected behavior:
interface Stack<T> {
fun push(item: T)
fun peek(): T
fun isEmpty(): Boolean
fun pop(): T
} |
fun main() {
val stk = StackImpl<String>()
stk.push("ISEL")
stk.push("TDS")
println(stk.peek()) // TDS
while( !stk.isEmpty() ) println( stk.pop() ) // TDS ISEL
stk.peek() // throws NoSuchElementException
} |
- "isolate each part of the program and show that the individual parts are correct"
- E.g. refactoring the former
main()
in individual unit tests:new instance of Stack should be empty
last pushed item is the peeked item
after pop of a Stack with single item it stays empty
pop an empty stack throws NoSuchElementException
peek an empty stack throws NoSuchElementException
- Unit testing frameworks (e.g. JUnit, TestNG, etc) provide:
- Strict, written contract, through annotations (e.g.
@Test
,@Expect
, etc). - API to express and evaluate the expected behavior (i.e. assertions).
- Automated runtime to scan, perform and collect tests results.
- Strict, written contract, through annotations (e.g.
- E.g.
kotlin.test.
API:assertEquals(expected, actual)
assertTrue(condition)
assertFailsWith<ExceptionClass> { ... /* block */ ... }
- Limit Mutability, E.g. Implementation of an immutable
FixedStack
- Implementation of an immutable
NaifDate
- Classes and data classes (provides implementation of canonical methods based on its properties)
- Classes may have a primary constructor and one or more secondary constructors.
- The primary constructor is a part of the class header.
- Initialization code can be placed in initializer blocks prefixed with the
init
keyword. -
Any
- the root of the Kotlin class hierarchy- Canonical functions:
equals
,hashCode
etoString
- Canonical functions:
- Modifiers:
abstract
,open
, andoverride
- E.g.
override fun toString(): String = "$day-$month-$year"
- Classes and data classes (provides implementation of canonical methods based on its properties)
- Inspecting resulting data classes with
javap
(e.g.toString()
andequals()
) - Equality - Structural equality (
==
a check forequals()
) - Referential equality (
===
two references point to the same object) - Properties with custom accessors - called every time you access the property (this way you can implement a computed property)
- Property versus Custom getter:
-
val nextMonth = month % 12 + 1
=>private final int nestMonth;
(JVM field) -
val nextMonth get() = month % 12 + 1
=>private final int getNextMonth();
(JVM function)
-
- Implement a prefix calculator for integer expressions.
- Object oriented approach =>
interface IntExpr { fun eval() : Int }
-
IntExpr
<|-----
IntExprLiteral
-
IntExpr
<|-----
IntExprOperator
-
- v1 – without typed operator - Operator is a
Char
- v2 – with a typed operator (i.e. Strongly Typed)
- Enumerated Type (e.g. Kotlin
enum class
):- A data type with a set of named values, i.e. constants (e.g.
SUM
,DIV
, etc) - Each constant is an instance of its enumerated type (e.g.
Operator
)
- A data type with a set of named values, i.e. constants (e.g.
-
Companion Object - its members can be called simply by using the class name as the qualifier (e.g.
Operator.parse(...)
) - E.g.
enum class Operator(private val opr: Char) {
...
companion object {
fun parse(opr: Char) : Operator {
return values().find { it.opr == opr } ?: throw IllegalArgumentException()
}
}
}
- Object declarations -
object
- singleton pattern. - Operator overloading, e.g. binary operations
- Association
--->
versus Inheritance-----|>
- UML classes diagrams.
- E.g.
NaifDate
withMonth
andIntExprOperator
withOperator
- Sealed classes and interfaces represent restricted class hierarchies that provide more control over inheritance
-
sealed
versusopen
- Implementing functional approach for
IntExpr
- Dynamic dispatch (polymorphism) versus Pattern matching
- E.g. dynamic dispatch -
invokeinterface
- E.g. pattern matching
fun IntExpr.eval(): Int = when(this) {
is IntExprLiteral -> nr
is IntExprOperator -> opr.eval(a.eval(), b.eval())
}
- Structuring modules to build a TicTacToe game for UI console and GUI (graphical user interface):
tictactoe-model
-
ui
– console single-device -
storage
- persistence in Document Database - NoSQL | MongoDB -
ui
– console between devices -
gui
- graphical user interface
-
tictactoe-model
:Player
,Position
,Move
andBoard
- runtime checks —
require()
andcheck()
-
IllegalArgumentException
andIllegalStateException
- Manage
Position
instances private constructor
- Make
Position(..., ...)
forward to:companion object { operator fun invoke(l: Int, c: Int): Position {...}}
- Parentheses are translated to calls to
invoke
with appropriate number of arguments. - Manage invalid states
- Type checks and casts
- "Unsafe" cast operator, E.g.
(board as BoardWin).winner
-
lesson10-tictactoe-ui
module depending oflesson09-tictactoe-model
-
lesson10-tictactoe-ui\build.gradle.kts
contains:-
dependencies { implementation(project(":lesson09-tictactoe-model"))
... }
-
-
Command processing loop (i.e.
readCommandsOop
):- Read console input arguments
- Parse arguments and find corresponding command
- Execute command and show:
- the new State (i.e.
Board
) on success - error message if failed.
- the new State (i.e.
fun readCommandsOop(cmds: Map<String, CommandOop>)
- Command OOP versus Functional:
interface CommandOop {
fun action(board: Board?, args: List<String>) : Board?
fun show(board: Board)
val syntax : String
}
...
object CmdQuitOop : CommandOop {
override fun action(board: Board?, args: List<String>) = null
override fun show(board: Board) {}
override val syntax: String get() = "quit"
}
|
class CommandFp (
val action: (Board?, List<String>) -> Board?,
val show: (Board?) -> Unit,
val syntax : String
)
...
val cmdQuitFp = CommandFp(
action = { _, _ -> null },
show = { },
syntax = "quit"
)
|
- Generic classes with type parameters, e.g.
class A<T> { ... }
- Generic functions with type parameters, e.g.
fun <T> foo() { ... }
- Generic classes or functions are used with type arguments:
A<String>()
foo<Int>()
- If the type arguments can be inferred, for example, from the actual parameters, you can omit the type arguments
- E.g.
Board
may be inferred inreadCommandsOop<Board>(mapOf("QUIT" to QuitCommandOop, ..))
- Refactor
tictactoe-ui
with an auxiliaryui-generic
module.
- Kotlin Lambdas
-
return
with label e.g.return@filter
- Function Reference
- Standard input and output, i.e.
System.in
andSystem.out
- Redirect standard IO, i.e.
System.setIn()
andSystem.setOut()
-
java.io
andInputStream
andOutputStream
hierarchy. - Filters e.g.
PrintStream
- IO in memory
ByteArrayInputStream
andByteArrayOutputStream
- Implementing an utility function
fun redirectInOut(stmts: List<String>, block: () -> Unit) : List<String>
- Review development plan for TicTacToe:
-
tictactoe-model
-
ui
– console single-device -
storage
- persistence in MongoDB or File system. -
ui
– console between different processes and/or devices -
gui
- graphical user interface
-
- Design storage operations for
Board
of tictactoe domain. - Use a unique identifier such as a String
name
. - CRUD - four basic operations of persistent storage.
- Design generic interface
Storage<K, T>
inspired by Data Mapper design pattern. - Implementation of
FileStorage<K,V>
for file system. - Dependency of factory, i.e.
() -> T
inspired by Abstract Factory Pattern
interface Storage<K, T> {
fun new(id: K): T
fun load(id: K): T?
fun save(id: K, obj: T)
fun delete(id: K)
}
- Serialization - process of translating object state into a stream that can be stored or transmitted, and reconstructed.
serialize(object) -> stream
deserialize(stream) -> object
- Resulting stream can be in binary or text format (e.g. XML, YAML, JSON, or other).
- Given
obj: Any
thendeserialize(serialize(obj)) == obj
- Define interface for String serialization:
interface StringSerializer<T> {
fun write(obj: T): String
fun parse(input: String): T
}
- Unit tests for
FileStorage
with a simpleDummyEntity
- Serialization of a complex graph of objects.
- E.g.
Board
--->
Move
--->
Position
- Implement auxiliary functions
serialize()
anddeserializeTo...()
on each entity:Board
,Move
. - Unit test for
FileStorage<String, Board>
with aStringSerializer<Board>
TicTactToe between processes:
- Remove argument Player (
X | O
) of command play, e.g.play X 1 1
. - Instead of
play X 1 1
now should beplay 1 1
. - Each
tictactoe-ui
process should keep itsPlayer
and theBoard
. - New entity
Game
. data class Game(val name: String, val board: Board, val player: Player = CROSS)
- Serialization of
Board
with different kind of boards:BoardRun
,BoardDraw
andBoardWin
. -
KClass
- Kotlin class information of a class. - Deserialization based on information of
KClass
of each object.
when(kind) {
BoardRun::class.simpleName -> BoardRun(moves, lastPlayer)
BoardDraw::class.simpleName -> BoardDraw(moves)
BoardWin::class.simpleName -> BoardWin(moves, lastPlayer)
}
-
MyClass::class
- getting the runtime reference to a Kotlin class. - Reimplement commands for
Game
, i.e.CommandOop<Game>
- New commands depend of
Storage<String, Board>
- Deploy and run tictactoe UI:
gradlew lesson17-tictactoe-ui-storage:jar
java -jar lesson17-tictactoe-ui-storage\build\libs\lesson17-tictactoe-ui-storage.jar