w1d3 Flashcards
What does “code smell” refer to?
Errors that, while not impeding functionality, indicate weakness in design which can cause slowdowns or other bugs, and has a great risk of leading to future problems.
What should we do with smelly code?
Refactor it!
What are some smelly code symptoms?
Duplicated/similar code
Long methods
Too many parameters (could indicate overly complex method)
Data clump (a set of data always passed around together)
Long method chains (methods should only talk to their immediate “friends”; Law of Demeter
What is “Indecent Exposure”?
Making too many methods public; only make them public (or protected for that matter) if there is a GOOD reason and a need to do so; keep class interfaces simple & minimal
What is “Speculative Generality”?
THERE IS NO NEED TO FUTURE PROOF YOUR CODE! DEAL WITH PERTINENT EXAMPLES FIRST! Don’t solve abstract problems.
What is a “God object”?
An object that is tightly connected to all other objects in the system. Avoid these; remember indecent exposure and that classes should have minimal impact on one another.
What should you do to commented or unused code?
Clean it up & delete it
WHAT IS GOING ON?!
[4] pry(main)> arr_of_arrs = Array.new(3, []) => [[], [], []] [5] pry(main)> arr_of_arrs[0] << "a" => ["a"] [6] pry(main)> arr_of_arrs => [["a"], ["a"], ["a"]]
The reason is that only two arrays are created in the example: (1) arr_of_arrs , (2) the single empty array passed into the Array constructor ( [] ). arr_of_arrs stores three references to the same empty array.
WHAT IS GOING ON?!
[7] pry(main)> arr_of_arrs = Array.new(3) { Array.new } => [[], [], []] [8] pry(main)> arr_of_arrs[0] << "a" => ["a"] [9] pry(main)> arr_of_arrs => [["a"], [], []]
Here, instead of passing a reference to a single empty array (which would be stored at three locations), we’ve passed a block. The block will be run to produce a value to store for each position in the array. The block constructs a new array each time it is run.
I wrote Array.new in the block to make it clear that a new array is constructed each time the block is executed, but you could equivalently write [] in the block.
Given the following code, why does the line ‘arr2[0] += 1’ not change the other elements of arr’?
arr2 = Array.new(3, 1)
arr2[0] += 1
arr2[0] == 2
arr2[1] == 1
arr2[2] == 1
B/C Fixnum#+ creates a NEW NUMBER OBJECT, b/c it does NOT mutate the original object. In essence, we are able to overwrite the value at arr2[0] with an entirely new object, returned by Fixnum#+
Here is an in-depth explanation:
- First, fetch the number at position 0 (which is 1 ).
- Next, add one to this number. This creates a new number object. The + operation does not mutate the original object.
- Finally, assign a reference to the new object ( 2 ) to position 0 of arr .
Bottom line: we never mutate any number. We produce a new one and reset arr2[0] to refer to the new object. That’s why none of the other indices are affected.
What does “immutable” refer to? What are some immutable classes?
An immutable class contains no methods which modify the data they contain; they produce new values. Fixnum, Float, and nil are some examples.
Explain the following hash declaration:
Hash.new([])
The argument to Hash::new provides a default value for keys, whether or not that key exists (this is nil by default)
WHAT IS GOING ON?!
[7] pry(main)> cats = Hash.new([]) => {} [8] pry(main)> cats["John"] << "Kiki" => ["Kiki"] [9] pry(main)> cats => {} [10] pry(main)> cats["Raul"] => ["Kiki"]
On line 8, we try to get a value for cats[“John”] . “John” is not a key in the hash, so the default (an empty array) is returned. We then mutate the default value by adding “Kiki” to it.
We never set a value for “John” though, so this is not stored in the Hash (see the result of line 9).
Later, when we try to access some other non-present key ( “Raul” ), the default value is returned again. But since we mutated the value by shovelling “Kiki” in, this is no longer empty. This is bad, because we never meant for “Raul” to own “Kiki” .
WHAT IS GOING ON?!
[11] pry(main)> cats2 = Hash.new() { [] }
=> {}
[12] pry(main)> cats2[“Devon”] «_space;“Breakfast”
=> [“Breakfast”]
[13] pry(main)> cats2[“George”]
=> []
The shovel operator mutates the value of cats2[“Devon”], but b/c of the syntax for the ::new method, each value in the hash is now a distinct object, and not a reference to the same object. Note, however, that cats2 is still and empty array; nothing was set.
WHAT IS GOING ON?!
[17] pry(main)> cats3 = Hash.new { |h, k| h[k] = [] } => {} [18] pry(main)> cats3["Devon"] => [] [19] pry(main)> cats3 => {"Devon"=>[]} [20] pry(main)> cats3["John"] ["Kiki"] [21] pry(main)> cats3 => {"Devon"=>[], "John"=>["Kiki"]}
Here we’ve modified the block to take two arguments: when Hash needs a default value, it will pass itself ( h ) and the key ( k ). The block will not only create an empty array, but also assign it to the hash.
You can see one somewhat funny side-effect when we look up “Devon” ; even when we just want to lookup a value, if it is not present we’ll incur the side-effect of mutating the hash (the key “Devon” got added).