简体   繁体   中英

Functional way to Wrap multiple methods into one

I am fairly new to the World of Function style Programming I was trying some code in Kotlin, given snippet below is what I did, but Looks so much Imperative to me

@EmailTemplate("onCallTemplate")
fun retrieveNextOnCallCreateMailRecipientAndSendMail(time: LocalDateTime, trial: Boolean = true) {

    val emailTemplateID =
            extractEmailTemplateValue(
                    "retrieveNextOnCallAndSendMail")

    val messageTemplate = emailTemplateID?.let { messageTemplateAccessPoint.findByID(it) }


    val lisOfOnCalls = dataHubCommunicator
            .listOfOnCallToSendNotificationFromDataHub(time)

    val listOfMailRecipient = lisOfOnCalls
            ?.let { onCallListToMailRecipient.buildMailRecipientFromOnCalls(it) }


    listOfMailRecipient
            ?.let {
                messageTemplate
                        ?.let { it1 ->
                            emailSender
                                    .sendNotificationToOnCallPersons(it, it1, trial)
                        }
            }

    log.info("Message Has been sent Successfully")

}

this function works like an aggregator it retrieves and invokes corresponding values/states from other functions. What I don't like about this method is declaring the variable(val/var) everywhere to store the values to pass on other methods.

I cannot do this either for sake of readability

onCallListToMailRecipient.buildMailRecipientFromOnCalls(dataHubCommunicator
    .listOfOnCallToSendNotificationFromDataHub(time))

Is there any declarative way to write this method.What changes can I do to make it more declarative/functional.

You could merge a few computations and reduce the number of local vars. Also, the last sequence of two let is rather useless, simply call the function with your local variables. I added some null checks instead.

val messageTemplate = extractEmailTemplateValue("retrieveNextOnCallAndSendMail")?.let {
    messageTemplateAccessPoint.findByID(it) 
} ?: throw IllegalStateException (“Todo”)

val listOfMailRecipient = dataHubCommunicator.listOfOnCallToSendNotificationFromDataHub(time)?.let { 
    onCallListToMailRecipient.buildMailRecipientFromOnCalls(it) 
} ?: throw IllegalStateException (“Todo”)

emailSender
    .sendNotificationToOnCallPersons(listOfMailRecipient, messageTemplate, trial).also{
    println("Message Has been sent Successfully")
}

You can try something like this

fun retrieveNextOnCallCreateMailRecipientAndSendMail(time: LocalDateTime, trial: Boolean = true) {
    doWithListOfMail(time) { l ->
        doWithTemplate('retrieveNextOnCallAndSendMail') { t ->
            emailSender.sendNotificationToOnCallPersons(l, t, trial)
        }
    }
    log.info("Message Has been sent Successfully")
}
fun doWithTemplate(id: String, action: (Template) -> Void){
    val emailTemplateID = extractEmailTemplateValue(id)
    val messageTemplate = emailTemplateID?.let { 
        messageTemplateAccessPoint.findByID(it) 
    }
    messageTemplate?.let(action)    
}
fun doWithListOfMail(time : LocalDateTime, action: (List) -> Void) {
    val lisOfOnCalls = dataHubCommunicator
        .listOfOnCallToSendNotificationFromDataHub(time)
    val listOfMailRecipient = lisOfOnCalls?.let {
        onCallListToMailRecipient.buildMailRecipientFromOnCalls(it) 
    }
    listOfMailRecipient?.let(action)
}

You might omit the val temporary variables when they are only used in the next statement, using let on each successive line instead. Applying that rule of thumb, your example might look like:

val messageTemplate = extractEmailTemplateValue("retrieveNextOnCallAndSendMail")
    ?.let(messageTemplateAccessPoint::findByID)

dataHubCommunicator.listOfOnCallToSendNotificationFromDataHub(time)
    ?.let(onCallListToMailRecipient::buildMailRecipientFromOnCalls)
    ?.let {
        messageTemplate?.let {
            emailSender.sendNotificationToOnCallPersons(it, messageTemplate, trial)
        }
    }

It may not be more readable, but it is less bulky.

Things may depend on nullity for some of these, if you like the let family you can do something like this as well:

fun retrieveNextOnCallCreateMailRecipientAndSendMail(time: LocalDateTime, trial: Boolean = true) =
    dataHubCommunicator
        .listOfOnCallToSendNotificationFromDataHub(time)
        .let {
            onCallListToMailRecipient.buildMailRecipientFromOnCalls(it)
        }
        .apply {
            val template = messageTemplateAccessPoint.findByID(
                    extractEmailTemplateValue("retrieveNextOnCallAndSendMail"))

            emailSender.sendNotificationToOnCallPersons(this, template, trial)
        }
        .also {
            log.info("Message has been sent successfully to ${it}")
        }
        // if you have nullable recipient and want to detect failure
        // ?: log.info("Message not sent")

Note: new lines in blocks help for IDEA to show hints, so you get the automatic variables + some free type information: IDEA提示

Notes: in general I would hold back on the ?. is everything really nullable around there? If we need to specify it1 to distinguish from it because of nesting might as well take the opportunity to name the variable nicely.

Appendix of declaration stubs for type safety testing (I assumed non-null, but questionmarks can be added wherever):

interface OnCall
interface MessageTemplate
interface MessageTemplateID
interface MailRecipient
interface MessageTemplateAccessPoint {
    fun findByID(it: MessageTemplateID): MessageTemplate
}

interface DataHubCommunicator {
    fun listOfOnCallToSendNotificationFromDataHub(time: LocalDateTime): List<OnCall>
}

interface OnCallListToMailRecipient {
    fun buildMailRecipientFromOnCalls(oncalls: List<OnCall>): MailRecipient
}

interface EmailSender {
    fun sendNotificationToOnCallPersons(recipient: MailRecipient, template: MessageTemplate, trial: Boolean)
}

fun extractEmailTemplateValue(name: String): MessageTemplateID = TODO()

val dataHubCommunicator: DataHubCommunicator = TODO()
val messageTemplateAccessPoint: MessageTemplateAccessPoint = TODO()
val onCallListToMailRecipient: OnCallListToMailRecipient = TODO()
val emailSender: EmailSender = TODO()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM