+ All Categories
Home > Documents > From Objects to Functions

From Objects to Functions

Date post: 03-Dec-2021
Category:
Upload: others
View: 15 times
Download: 0 times
Share this document with a friend
11
Extracted from: From Objects to Functions Build Your Software Faster and Safer with Functional Programming and Kotlin This PDF file contains pages extracted from From Objects to Functions, published by the Pragmatic Bookshelf. For more information or to purchase a paperback or PDF copy, please visit http://www.pragprog.com. Note: This extract contains some colored text (particularly in code listing). This is available only in online versions of the books. The printed versions are black and white. Pagination might vary between the online and printed versions; the content is otherwise identical. Copyright © 2021 The Pragmatic Programmers, LLC. All rights reserved. No part of this publication may be reproduced, stored in a retrieval system, or transmitted, in any form, or by any means, electronic, mechanical, photocopying, recording, or otherwise, without the prior consent of the publisher. The Pragmatic Bookshelf Raleigh, North Carolina
Transcript
Page 1: From Objects to Functions

Extracted from:

From Objects to FunctionsBuild Your Software Faster and Safer

with Functional Programming and Kotlin

This PDF file contains pages extracted from From Objects to Functions, publishedby the Pragmatic Bookshelf. For more information or to purchase a paperback or

PDF copy, please visit http://www.pragprog.com.

Note: This extract contains some colored text (particularly in code listing). Thisis available only in online versions of the books. The printed versions are blackand white. Pagination might vary between the online and printed versions; the

content is otherwise identical.

Copyright © 2021 The Pragmatic Programmers, LLC.

All rights reserved.

No part of this publication may be reproduced, stored in a retrieval system, or transmitted,in any form, or by any means, electronic, mechanical, photocopying, recording, or otherwise,

without the prior consent of the publisher.

The Pragmatic BookshelfRaleigh, North Carolina

Page 2: From Objects to Functions
Page 3: From Objects to Functions
Page 4: From Objects to Functions

From Objects to FunctionsBuild Your Software Faster and Safer

with Functional Programming and Kotlin

Uberto Barbini

The Pragmatic BookshelfRaleigh, North Carolina

Page 5: From Objects to Functions

Many of the designations used by manufacturers and sellers to distinguish their productsare claimed as trademarks. Where those designations appear in this book, and The PragmaticProgrammers, LLC was aware of a trademark claim, the designations have been printed ininitial capital letters or in all capitals. The Pragmatic Starter Kit, The Pragmatic Programmer,Pragmatic Programming, Pragmatic Bookshelf, PragProg and the linking g device are trade-marks of The Pragmatic Programmers, LLC.

Every precaution was taken in the preparation of this book. However, the publisher assumesno responsibility for errors or omissions, or for damages that may result from the use ofinformation (including program listings) contained herein.

For our complete catalog of hands-on, practical, and Pragmatic content for software devel-opers, please visit https://pragprog.com.

For sales, volume licensing, and support, please contact [email protected].

For international rights, please contact [email protected].

Copyright © 2021 The Pragmatic Programmers, LLC.

All rights reserved. No part of this publication may be reproduced, stored in a retrieval system,or transmitted, in any form, or by any means, electronic, mechanical, photocopying, recording,or otherwise, without the prior consent of the publisher.

ISBN-13: 978-1-68050-845-1Encoded using the finest acid-free high-entropy binary digits.Book version: B1.0—September 21, 2021

Page 6: From Objects to Functions

Starting a New Story to Modify a ListLet’s consider the second story: adding an item to a list. We start writing theDDT, it’s not difficult but at first writing a DDT from scratch can be a bitdaunting. The trick is to think in term of a use-case scenario, how would wedescribe it using small actors’ steps, to make the DDT compile we can addthe steps as empty functions in the actor until we are satisfied with the sce-nario code.

class ModifyAToDoListDDT: ZettaiDDT(allActions()){❶

val ann by NamedActor(::ToDoListOwner)

@DDTfun `The list owner can add new items`() = ddtScenario {❷

setup {ann.`starts with a list`("diy", emptyList())❸

}.thenPlay(ann.`can add #item to #listname`("paint the shelf", "diy"),❹ann.`can add #item to #listname`("fix the gate", "diy"),ann.`can add #item to #listname`("change the lock", "diy"),ann.`can see #listname with #itemnames`("diy", listOf(❺

"fix the gate", "paint the shelf", "change the lock"))).wip(LocalDate.of(2021, 12, 31), "Not implemented yet")❻

}}

❶ The name of the test class reflect the user story name.

❷ Each test represents a scenario of the story.

❸ We start with a list without items.

❹ Then we call a new step to add an item, for three times.

❺ Finally we check that the list has the three items now.

❻ We mark the test as work-in-progress until it will pass.

Note how we marked the new test with the wip method at the end. That isshort for work-in-progress since, as we said in the first chapter, our DDT willnot pass for a while, until we will finish the implementation at the end of thischapter.

• Click HERE to purchase this book now. discuss

Page 7: From Objects to Functions

Joe asks:

Why Do We Use Randomly Generated Values inthe Unit Tests but Specific Examples in the DDTs?

This is a very good question. In the case of our unit tests, we want to make sure thatwe cover all corner cases and our functions are pure and total. For this reason, ran-domly generated values give us more confidence in the results, and since the testsare very fast, we can run them many times.

In case of tests simulating the actual use-cases, like in our DDTs, we cannot testhundreds or thousands of cases. Besides, we are interested in specific values thatmimic an actual user interaction as closely as possible.

Work in ProgressIn a normal unit test we expect the test to fail for only a few minutes and wewill definitely not commit it in any case if it is not green. So having a failingunit tests is not recommended.

Conversely, for DDTs and all end-to-end tests, it is acceptable for them tostay broken for days. They will pass only when the story is completely finished.

We can see here a symmetrical principle: DDTs are the first tests we writeand they will be the last to pass. It is also a good practice to write all the DDTswe need to complete the story we are working on at the beginning and thenproceed to implement and make them pass one by one.

What we need is a clear way to mark the tests we are working on, letting themrun but ignoring the failure. We can also specify a tentative date to completethem, after which date the test will not be ignored anymore. Then, if we forgotto fix them in time, they will break the build and it will remind us to finishthem or to delete them if not needed anymore.

Without the WIP notation we had to remember that, until we complete them,some acceptance tests will fail every time we run them. This is not a big issueright now since we have just two acceptance tests, but as soon as we starthaving many tests, the “accepted failures” would make the report very confus-ing.

• 4

• Click HERE to purchase this book now. discuss

Page 8: From Objects to Functions

Domain-Driven Test Process

The process of working with DDTs looks like a V:

1. We start with the LocalHttp version first so we can sort out the “plumbing”of our architecture.

2. When we arrive at the point where we need some domain logic, we shouldswitch to DomainOnly DDTs and we let them guide us on what is needed.

3. Then we develop the needed components one by one, using unit tests.4. After that, we can fix the DomainOnly DDTs until they pass.5. Finally, we return to the LocalHttp DDT and make sure the final infrastruc-

ture is working as expected.

In this “V” diagram we are now at phase one of the DDT to modify a list. Weare going to add the new methods on the actors and the HTTP actions so wecan make the test compile first.

Joe asks:

Why Are We Starting from the HTTP and Not fromthe Hub?

As we discussed in the first chapter, writing a functionality starting from the externallayers as the UI and going to the internal ones is called "outside-in" style, whilstwriting it from the internal domain and proceed to the external layers; this is called"inside-out" style.

Which style should we use? There is no unique answer and it really depends on ourconstraints and acceptance criteria. DDTs are defined by the user actions on theexternal layer of the system, that’s why starting from the HTTP layer makes sense.

On the other side, if we wanted to develop a specific algorithm to solve a problem butwe didn’t care about the external layers, it would make more sense to use the inside-out style.

• Click HERE to purchase this book now. discuss

Starting a New Story to Modify a List • 5

Page 9: From Objects to Functions

Actor StepTo make it compile we need to add a new step to the actor:

data class ToDoListOwner(override val name: String):DdtActor<ZettaiActions>() {

val user = User(name)

fun `can add #item to #listname`(itemName: String, listName: String) =step(itemName, listName) {

val item = ToDoItem(itemName)updateListItem(user, ListName(listName), item)

}

//rest of the methods}

As we saw, using the # words on the method name will replace them with theactual values when we run test. To create the step as a nested test, we areusing the step method of the DdtActor. The step itself calls the updateListItem onthe actions with the correct parameters.

This is a general pattern, the actor steps only contain logic to call the actionsbut they don’t interact with the application directly. Here we are sending acommand to the application, so we don’t have any result to verify. In case ofsteps that query the status of the application—like in the case of can see#listnamewith #itemnames step—we would verify also that the result is what we expect.

HTTP Actions CallFirst we add the updateListItem method to the ZettaiActions interface, then we canimplement in the HTTP and domain instance.

As we are in the first point of our V diagram we will leave the domain actionwith just a TODO in the implementation.

Instead we will concentrate on the HTTP implementation of actions first. Weneed to submit an HTTP webform to the server with the item name and theitem due date fields:

data class HttpActions(val env: String = "local"): ZettaiActions {

override fun updateListItem(user: User,listName: ListName, item: ToDoItem) {

val response = submitToZettai(todoListUrl(user, listName),listOf( "itemname" to item.description,

"itemdue" to item.dueDate?.toString()))

• 6

• Click HERE to purchase this book now. discuss

Page 10: From Objects to Functions

expectThat(response.status).isEqualTo(Status.SEE_OTHER)}

private fun submitToZettai(path: String, webForm: Form): Response =client(log(

Request(Method.POST,"http://localhost:$zettaiPort/$path")

.body(webForm.toBody())))

//...}

Since the DDT now compiles, we can run it now. This is how it looks in theIDE:

You can see that only the first step passes; the rest is all in progress.

Handle Different PagesTo progress on the implementation of our feature, we need to add the newHTTP end point to add a new item to a given list. To do this we need to adda new route that would map our request.

But we have a new problem: so far we created a function that returns anHTML page with a to-do list from a request, but this is only one among themany kinds of requests we need to handle with our web service. How can wereturn different pages or API according to the request type?

We don’t want to modify the function we already wrote; it is finished as it isand it should not care about the other kinds of calls. We also don’t want to

• Click HERE to purchase this book now. discuss

Starting a New Story to Modify a List • 7

Page 11: From Objects to Functions

write a function too specific with hardcoded values, because we could notreuse it. We want a generic reusable solution to this problem.

How can we combine together functions that handle different kinds of Requestwithout changing our existing code? With another function, of course! Moreprecisely we need a function that takes a collection of functions as input andwill return a new function.

In order to learn how to code it, let’s leave our Zettai app for a moment, andlet’s imagine we are writing a web service for operating on some data so wecan learn how to define routes using higher-order functions.

• 8

• Click HERE to purchase this book now. discuss


Recommended