Skip to content

Instantly share code, notes, and snippets.

@soc
Last active August 29, 2015 14:19
Show Gist options
  • Save soc/9852db8e3e14382663b8 to your computer and use it in GitHub Desktop.
Save soc/9852db8e3e14382663b8 to your computer and use it in GitHub Desktop.

Avoid abbreviations

  • Often there are multiple ways to abbreviate a name, but usually only one unabbreviated name. Don't make your users guess.
  • Sometimes it's tempting to abbreviate names, but consider that having consistency across libraries is often more important, than a particularly nice or clever abbreviation.
  • Users can rename things on import. This makes it straightforward to look up an abbreviated name, if the user decides to abbreviate something.

Avoid symbolic names

  • Sometimes it is tempting to pick a symbolic name, but consider that it makes it hard for users to search for these names on the web, and causes issues with pronouncing these names.
  • As a general rule of a thumb: If the cost/benefit ratio of a symbolic name is lower than ???, it's probably not worth it.

Constructors

  • Try to have only one constructor per class.
  • The constructor arguments should directly correspond to fields, and shouldn't do much more than initializing the instance and ensuring that the instance is in a valid state after construction.

Factories

  • The rules from Constructors should be treated as recommendations.

  • Avoid overloading apply, opt for separate methods with descriptive names instead.

  • Use fromX if there is a considerable difference between the arguments to the method and the values of the instance, e. g. when parsing or otherwise transforming the inputs before passing them to the constructor:

    class Point(val x: Double, val y: Double)
    object Point {
      def apply(x: Double, y: Double) = new Point(x, y) // Good
      def apply(str: String) = { // Avoid!
        val xy = str.split(":")
        assert(xy.length == 2)
        new Point(xy(0).toDouble, xy(1).toDouble)
      }
      def fromString(str: String) = { // Better
        val xy = str.split(":")
        assert(xy.length == 2)
        new Point(xy(0).toDouble, xy(1).toDouble)
      }
    }

Methods

  • Use toX/to[X] for changing the data structure of a value, e. g. someArray.toList, 123.toDouble.
  • Use asX/as[X] for more fundamental conversions/transformations, like marshaling, e. g. userDatabaseRow.as[User], userInstance.as[XML].

Parameters

  • Define types to constrain the inputs. This makes it easier to reject wrong values, reduces the necessary amount of validation inside the methods and helps the user to use the method correctly:

    def addEmail(email: String) // Avoid!
    def addEmail(email: EMail)  // Better
  • Avoid long parameter lists. If they cannot be avoided, encourage users to use named parameters for clarity.

  • Avoid boolean parameters. Use enum types instead to increase clarity and allow for future extensions:

    def checkAccess(isAdmin: Boolean) // Avoid!
    checkAccess(true); checkAccess(false); ...
    def checkAccess(role: Role)       // Better
    checkAccess(Role.ADMINISTRATOR); checkAccess(Role.DEVELOPER); checkAccess(Role.INTERN); ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment