Episode 04 – Function Structure

Clean Code

Episode 04

The forth episode talks about how the function structure should be. What things to do and to avoid when creating functions.

  • Arguments
    • How many arguments should a function have?
    • Need to treat argument as liability instead of asset to make a function more readable.
  • Three arguments max
    • 0 is best, 1-2 is ok but 3 is border too many. If 3 or more variable is cohesive, then why not make them into an object. Too many arguments makes it more difficult for the reader to keep track of them.
    • Using setter instead of constructor with many arguments.
  • No Boolean arguments ever
    • Passing a Boolean into the function means it only does two things. Instead, write two functions, one for the True case and the other for the False case.
  • Innies not Outies
    • Use input only arguments instead of output arguments to minimize confusion for the reader. Use return instead
  • The Null Defense
    • Passing Null into a function is similar to Boolean because there is a case for Null and another one for non-Null. Instead, create two functions for each cases.
    • Defensive programming means you don’t trust your team or their unit test. Use good suite of test to guard against null value in a team environment.
  • The step down rule
    • The scissor rule where public class/method on top and private’s at the bottom. So you can take a  scissor and cut the page along the public and private section and hand the top part to user.
    • The step down rule put important stuff at the top and then details at the bottom.  Therefore, public method at the top and private at the bottom. Therefore, function calls are point downward.
  • Switches and cases
    • Switch statements are missed opportunity to use polymorphism. Switch statement is not OO.
    • If module A call module B, at run time, there is a flow of control dependency, however, there is also a source code dependency. By using a polymorphic interface, I, module A depends on I but module B derive from I. Thus, the source code dependency is reverse from B to A for the point of view of B yet maintain the same flow of control. As a result, you can deploy module A and B separately.
    • However, switch cases likely to have multiple dependency outward to other modules (Fan-out problem) and make independent deploy-ability impossible.
      • One solution is to use polymorphism to invert the source-code dependency. Change the switch statement into a abstract base class and have the other module become its derive class to implement the cases.
      • Another solution is to move the switch statement to a place where it can not do any harm (e.g. in the main partition)
    • Application partition is where most of the code lives. Main partition is where low level module (factory, configuration) reside and should be keep small. The main partition should only point toward the application partition (Dependency Injection).
    • Independently deployable means independently developable. Therefore, switch statement also ruin the independent developability and cause team to collide with each other.
  • Paradigms
  • Functional Programming
  • Side Effect
      • When a function changes a variable outlive the function call, that is a side effect that changes the behaviour of the function or other function next time the function is called.
      • These side effect usually created by pair of function that must be call in order (e.g. set & get, open & close, new & delete) or temporal coupling.

    Eliminate temporal coupling by group the pair of the function that need to execute in order inside another new function and ensure that new function execution will leave the program in the same state that it was before the call (Passing a block).

  • Command Query separation
    • Command changes the state of the system and return nothing. Query does not change the state of the system but return a value. CQS says function that change state should not return value. Those return value should not change state.
    • Therefore, it is clear that if a function return void, it has side effect.
    • Instead of return null when a command failed (setter or new… etc), throw an exception to maintain the command to return void.
  • Tell don’t ask
    • Avoid query altogether. Tell other object to do the work and not ask for their state. The object knows its own state and know what it needs to do base on its information.
    • Train wreck, long chain of query violates the law of Demeter, where it is a bad idea for a function to know the navigation of the system. It coupled the function with the whole system.
    • The law of Demeter:
      • You may call methods of objects that are:
        • passed as arguments
        • created locally
        • instance variables of the class of your method
        • globals
      • You may NOT call methods on objects that are
        • returned from a previous method call
  • Structured Programming
    • Structured Programming says that a program should be compose of the following three structure
      • Sequence is an arrangement of blocks in sequence of time. Exit of one block immediately goes into the second.
      • Selection is a boolean expression split the flow of control into two blocks. One of the block execute and the path way rejoin.
      • Iteration is a repeated execution of a block until some boolean expression satisfy.
    • Dijkstra argue that if a program is only compose of these three structure, then you can prove the program’s correctness. A provable program is an understandable program.
  • Early Returns
    • Guard returns only take you to the end of the function, thus, maintain the single entry and exit of the function.
    • Break and mid-loop return add complication and create additional exit condition.
    • Anything that makes the code harder to reason in sequential manner will add confusion to the reader, which is your first priority, code readability for clean code.
    • Keeping function small will eventually make each function to have single entry and exit point.
  • Error Handling
    • Error Handling is important, but if it obscure logic, then its wrong
  • Errors First
    • Best to write error handling code before writing the rest of the code.
  • Prefer Exceptions
    • throw exception instead of returning an error code by the function itself.
  • Exceptions are for Callers
    • Scope to the class that throw the exception and name with as much information as possible.
  • Use unchecked Exceptions
    • derive exception from run-time exceptions
  • Special Cases
  • Null is not an Error
  • Null is a value
    • exception is reserve for things that should not fail. Null should be use for things that might fail from time to time like finding an element in a list.
  • Trying is one thing
    • Function should do one thing, error handling is one thing.
  • Other References
    • Structured Programming by Dijkstra, Hoare, & Dahi.
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s