Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enforce immutability and insertion iteration order properties of ZplCommand.parameters #20

Conversation

jbduncan
Copy link

@jbduncan jbduncan commented Sep 20, 2024

This PR is a follow-up to #18.

In #18, I described a way of eliminating the need to make parameters a LinkedHashMap publicly without losing insertion iteration ordering.

This PR is a bit different to how I described it, but the main change is that ZplCommand becomes an abstract class whose constructor accepts a list of ZPL parameters to enforce insertion ordering, and ZplCommand.parameters becomes an unmodifiable map of these parameters.

Advantages

  1. People can't accidentally do the following:

    override val parameters: Map<CharSequence, Any?> = hashMapOf()

    ...which loses insertion ordering.

  2. People can't be naughty and do this:

    val barCode = BarCode(...)
    
    (barCode.parameters as MutableMap<CharSequence, Any?>)["foo"] = "bar"

    ...since it now throws an UnsupportedOperationException. This is due to the use of java.util.Collections.unmodifiableMap.

Drawbacks

  1. ZplCommand is changed in a backwards-incompatible way, but this is probably fine as we're still on version 0.x.
  2. It's arguable if java.util.Collections.unmodifiableMap is necessary. On one hand, it guards against Kotlin's read-only collections being able to be casted into mutable collections. On the other hand, it's Java-specific (though we're targeting JVM/Android anyway) and it's not idiomatic in Kotlin.

Thoughts?

abstract class ZplCommand(
parameters: List<Pair<CharSequence, Any?>> = emptyList()
) {
val parameters: Map<CharSequence, Any?> = unmodifiableMap(parameters.toMap())
Copy link
Author

@jbduncan jbduncan Sep 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As alluded to in the OP, this use of unmodifiableMap is what I'm most ambivalent about, as on one hand it stops parameters being casted into a mutable map, but on the other hand it's not idiomatic in Kotlin and just .toMap() is what most people use by default.

What do you think?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @jbduncan,

I took at look at this and I think it made me realise we don't have to use a raw Map as the parameters here - we can just wrap it in another class where we have full control of the implementation and usage. Have a look at #21

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! I'm closing this PR in favour of yours then. :)

@jbduncan
Copy link
Author

Closing In response to #20 (comment). See @itsmattking's follow-up PR: #21.

@jbduncan jbduncan closed this Sep 23, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants