嗨,我是 Fly,用 Ruby 寫 Chatbot 並挑戰30天分享心得
為確保不會沒靈感
每日含 Ruby 主題文章增加內容
https://github.com/leo424y/clean-code-ruby
Bad:
class UserSettings
def initialize(user)
@user = user
end
def change_settings(settings)
return unless valid_credentials?
# ...
end
def valid_credentials?
# ...
end
end
Good:
class UserAuth
def initialize(user)
@user = user
end
def valid_credentials?
# ...
end
end
class UserSettings
def initialize(user)
@user = user
@auth = UserAuth.new(user)
end
def change_settings(settings)
return unless @auth.valid_credentials?
# ...
end
end
便於擴充
Bad:
class Adapter
attr_reader :name
end
class AjaxAdapter < Adapter
def initialize
super()
@name = 'ajaxAdapter'
end
end
class NodeAdapter < Adapter
def initialize
super()
@name = 'nodeAdapter'
end
end
class HttpRequester
def initialize(adapter)
@adapter = adapter
end
def fetch(url)
adapter_name = @adapter.name
if adapter_name == 'ajaxAdapter'
make_ajax_call(url)
elsif adapter_name == 'httpNodeAdapter'
make_http_call(url)
end
end
def make_ajax_call(url)
# ...
end
def make_http_call(url)
# ...
end
end
Good:
class Adapter
attr_reader :name
end
class AjaxAdapter < Adapter
def initialize
super()
@name = 'ajaxAdapter'
end
def request(url)
# ...
end
end
class NodeAdapter < Adapter
def initialize
super()
@name = 'nodeAdapter'
end
def request(url)
# ...
end
end
class HttpRequester
def initialize(adapter)
@adapter = adapter
end
def fetch(url)
@adapter.request(url)
end
end
Bad:
class Rectangle
def initialize
@width = 0
@height = 0
end
def color=(color)
# ...
end
def render(area)
# ...
end
def width=(width)
@width = width
end
def height=(height)
@height = height
end
def area
@width * @height
end
end
class Square < Rectangle
def width=(width)
@width = width
@height = width
end
def height=(height)
@width = height
@height = height
end
end
def render_large_rectangles(rectangles)
rectangles.each do |rectangle|
rectangle.width = 4
rectangle.height = 5
area = rectangle.area # BAD: Returns 25 for Square. Should be 20.
rectangle.render(area)
end
end
rectangles = [Rectangle.new, Rectangle.new, Square.new]
render_large_rectangles(rectangles)
Good:
class Shape
def color=(color)
# ...
end
def render(area)
# ...
end
end
class Rectangle < Shape
def initialize(width, height)
super()
@width = width
@height = height
end
def area
@width * @height
end
end
class Square < Shape
def initialize(length)
super()
@length = length
end
def area
@length * @length
end
end
def render_large_shapes(shapes)
shapes.each do |shape|
area = shape.area
shape.render(area)
end
end
shapes = [Rectangle.new(4, 5), Rectangle.new(4, 5), Square.new(5)]
render_large_shapes(shapes)
Bad:
class Rectangle
def initialize
@width = 0
@height = 0
end
def color=(color)
# ...
end
def render(area)
# ...
end
def width=(width)
@width = width
end
def height=(height)
@height = height
end
def area
@width * @height
end
end
class Square < Rectangle
def width=(width)
@width = width
@height = width
end
def height=(height)
@width = height
@height = height
end
end
def render_large_rectangles(rectangles)
rectangles.each do |rectangle|
rectangle.width = 4
rectangle.height = 5
area = rectangle.area # BAD: Returns 25 for Square. Should be 20.
rectangle.render(area)
end
end
rectangles = [Rectangle.new, Rectangle.new, Square.new]
render_large_rectangles(rectangles)
Good:
class Shape
def color=(color)
# ...
end
def render(area)
# ...
end
end
class Rectangle < Shape
def initialize(width, height)
super()
@width = width
@height = height
end
def area
@width * @height
end
end
class Square < Shape
def initialize(length)
super()
@length = length
end
def area
@length * @length
end
end
def render_large_shapes(shapes)
shapes.each do |shape|
area = shape.area
shape.render(area)
end
end
shapes = [Rectangle.new(4, 5), Rectangle.new(4, 5), Square.new(5)]
render_large_shapes(shapes)
Bad:
class Car
# used by Driver
def open
# ...
end
# used by Driver
def start_engine
# ...
end
# used by Mechanic
def change_engine
# ...
end
end
class Driver
def drive
@car.open
@car.start_engine
end
end
class Mechanic
def do_stuff
@car.change_engine
end
end
Good:
# used by Driver only
class Car
def open
# ...
end
def start_engine
# ...
end
end
# used by Mechanic only
class CarInternals
def change_engine
# ...
end
end
class Driver
def drive
@car.open
@car.start_engine
end
end
class Mechanic
def do_stuff
@car_internals.change_engine
end
end
Bad:
class InventoryRequester
def initialize
@req_methods = ['HTTP']
end
def request_item(item)
# ...
end
end
class InventoryTracker
def initialize(items)
@items = items
# BAD: We have created a dependency on a specific request implementation.
@requester = InventoryRequester.new
end
def request_items
@items.each do |item|
@requester.request_item(item)
end
end
end
inventory_tracker = InventoryTracker.new(['apples', 'bananas'])
inventory_tracker.request_items
Good:
class InventoryTracker
def initialize(items, requester)
@items = items
@requester = requester
end
def request_items
@items.each do |item|
@requester.request_item(item)
end
end
end
class InventoryRequesterV1
def initialize
@req_methods = ['HTTP']
end
def request_item(item)
# ...
end
end
class InventoryRequesterV2
def initialize
@req_methods = ['WS']
end
def request_item(item)
# ...
end
end
# By constructing our dependencies externally and injecting them, we can easily
# substitute our request module for a fancy new one that uses WebSockets.
inventory_tracker = InventoryTracker.new(['apples', 'bananas'], InventoryRequesterV2.new)
inventory_tracker.request_items